diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 25dfc0f..35eb1dd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,5 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="" vcs="Git" />
-    <mapping directory="$PROJECT_DIR$/app/src/main/cpp/libcxx" vcs="Git" />
   </component>
 </project>
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c6a5fc3..7d39699 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -8,6 +8,16 @@ android {
     ndkVersion = "26.1.10909125"
     buildToolsVersion = "34.0.0"
 
+    packaging {
+        jniLibs {
+            excludes += "**/libdobby.so"
+        }
+    }
+
+    buildFeatures {
+        prefab = true
+    }
+
     defaultConfig {
         applicationId = "es.chiteroman.playintegrityfix"
         minSdk = 26
@@ -16,10 +26,15 @@ android {
         versionName = "1.0"
 
         externalNativeBuild {
-            ndk {
-                jobs = Runtime.getRuntime().availableProcessors()
-                abiFilters += "armeabi-v7a"
-                abiFilters += "arm64-v8a"
+            cmake {
+                arguments += "-DANDROID_STL=none"
+                arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
+
+                cppFlags += "-std=c++20"
+                cppFlags += "-fno-exceptions"
+                cppFlags += "-fno-rtti"
+                cppFlags += "-fvisibility=hidden"
+                cppFlags += "-fvisibility-inlines-hidden"
             }
         }
     }
@@ -38,8 +53,13 @@ android {
     }
 
     externalNativeBuild {
-        ndkBuild {
-            path = file("src/main/cpp/Android.mk")
+        cmake {
+            path = file("src/main/cpp/CMakeLists.txt")
+            version = "3.22.1"
         }
     }
+}
+
+dependencies {
+    implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
 }
\ No newline at end of file
diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk
deleted file mode 100644
index 9632627..0000000
--- a/app/src/main/cpp/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := zygisk
-LOCAL_SRC_FILES := main.cpp
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-
-LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c)
-LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c)
-LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/xdl/*.c)
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl
-
-ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
-  LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm/*.c)
-  LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm
-endif
-
-ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
-  LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm64/*.c)
-  LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm64
-endif
-
-LOCAL_STATIC_LIBRARIES := libcxx
-LOCAL_LDLIBS := -llog
-include $(BUILD_SHARED_LIBRARY)
-
-include $(LOCAL_PATH)/libcxx/Android.mk
\ No newline at end of file
diff --git a/app/src/main/cpp/Application.mk b/app/src/main/cpp/Application.mk
deleted file mode 100644
index f3a9a1e..0000000
--- a/app/src/main/cpp/Application.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-APP_STL      := none
-APP_CFLAGS   := -Oz -fvisibility=hidden -fvisibility-inlines-hidden
-APP_CPPFLAGS := -std=c++20 -fno-exceptions -fno-rtti
\ No newline at end of file
diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..fd7fcab
--- /dev/null
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.22.1)
+
+project("playintegrityfix")
+
+add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp)
+
+add_subdirectory(Dobby)
+
+SET_OPTION(Plugin.Android.BionicLinkerUtil ON)
+
+find_package(cxx REQUIRED CONFIG)
+
+target_link_libraries(dobby cxx::cxx)
+
+target_link_libraries(dobby_static cxx::cxx)
+
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE cxx::cxx log dobby_static)
\ No newline at end of file
diff --git a/app/src/main/cpp/Dobby b/app/src/main/cpp/Dobby
new file mode 160000
index 0000000..b0176de
--- /dev/null
+++ b/app/src/main/cpp/Dobby
@@ -0,0 +1 @@
+Subproject commit b0176de574104726bb68dff3b77ee666300fc338
diff --git a/app/src/main/cpp/json.hpp b/app/src/main/cpp/json.hpp
new file mode 100644
index 0000000..4d1a37a
--- /dev/null
+++ b/app/src/main/cpp/json.hpp
@@ -0,0 +1,24596 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online      *
+ * documentation of the public API at https://json.nlohmann.me. This URL    *
+ * 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.                                                     *
+\****************************************************************************/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#include <algorithm> // all_of, find, for_each
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <functional> // hash, less
+#include <initializer_list> // initializer_list
+#ifndef JSON_NO_IO
+    #include <iosfwd> // istream, ostream
+#endif  // JSON_NO_IO
+#include <iterator> // random_access_iterator_tag
+#include <memory> // unique_ptr
+#include <numeric> // accumulate
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
+#include <vector> // vector
+
+// #include <nlohmann/adl_serializer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
+        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2
+            #warning "Already included a different version of the library!"
+        #endif
+    #endif
+#endif
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_PATCH 2   // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+    #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+#endif
+
+// Construct the namespace ABI tags component
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
+    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
+
+#define NLOHMANN_JSON_ABI_TAGS                                       \
+    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
+            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
+            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
+
+// Construct the namespace version component
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+    _v ## major ## _ ## minor ## _ ## patch
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+
+#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+#define NLOHMANN_JSON_NAMESPACE_VERSION
+#else
+#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
+    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+                                           NLOHMANN_JSON_VERSION_MINOR, \
+                                           NLOHMANN_JSON_VERSION_PATCH)
+#endif
+
+// Combine namespace components
+#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+
+#ifndef NLOHMANN_JSON_NAMESPACE
+#define NLOHMANN_JSON_NAMESPACE               \
+    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+            NLOHMANN_JSON_ABI_TAGS,           \
+            NLOHMANN_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
+    namespace nlohmann                               \
+    {                                                \
+    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+                NLOHMANN_JSON_ABI_TAGS,              \
+                NLOHMANN_JSON_NAMESPACE_VERSION)     \
+    {
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_END
+#define NLOHMANN_JSON_NAMESPACE_END                                     \
+    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
+    }  // namespace nlohmann
+#endif
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // transform
+#include <array> // array
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <map> // map
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
+#include <utility> // pair, declval
+#include <valarray> // valarray
+
+// #include <nlohmann/detail/exceptions.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // nullptr_t
+#include <exception> // exception
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+#include <vector> // vector
+
+// #include <nlohmann/detail/value_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
+
+// #include <nlohmann/detail/macro_scope.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility> // declval, pair
+// #include <nlohmann/detail/meta/detected.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <type_traits>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename ...Ts> struct make_void
+{
+    using type = void;
+};
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// https://en.cppreference.com/w/cpp/experimental/is_detected
+struct nonesuch
+{
+    nonesuch() = delete;
+    ~nonesuch() = delete;
+    nonesuch(nonesuch const&) = delete;
+    nonesuch(nonesuch const&&) = delete;
+    void operator=(nonesuch const&) = delete;
+    void operator=(nonesuch&&) = delete;
+};
+
+template<class Default,
+         class AlwaysVoid,
+         template<class...> class Op,
+         class... Args>
+struct detector
+{
+    using value_t = std::false_type;
+    using type = Default;
+};
+
+template<class Default, template<class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+{
+    using value_t = std::true_type;
+    using type = Op<Args...>;
+};
+
+template<template<class...> class Op, class... Args>
+using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<class...> class Op, class... Args>
+struct is_detected_lazy : is_detected<Op, Args...> { };
+
+template<template<class...> class Op, class... Args>
+using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or = detector<Default, void, Op, Args...>;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+template<class Expected, template<class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<class To, template<class...> class Op, class... Args>
+using is_detected_convertible =
+    std::is_convertible<detected_t<Op, Args...>, To>;
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/thirdparty/hedley/hedley.hpp>
+
+
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>
+// SPDX-License-Identifier: MIT
+
+/* Hedley - https://nemequ.github.io/hedley
+ * Created by Evan Nemerson <evan@nemerson.com>
+ */
+
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
+#if defined(JSON_HEDLEY_VERSION)
+    #undef JSON_HEDLEY_VERSION
+#endif
+#define JSON_HEDLEY_VERSION 15
+
+#if defined(JSON_HEDLEY_STRINGIFY_EX)
+    #undef JSON_HEDLEY_STRINGIFY_EX
+#endif
+#define JSON_HEDLEY_STRINGIFY_EX(x) #x
+
+#if defined(JSON_HEDLEY_STRINGIFY)
+    #undef JSON_HEDLEY_STRINGIFY
+#endif
+#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)
+
+#if defined(JSON_HEDLEY_CONCAT_EX)
+    #undef JSON_HEDLEY_CONCAT_EX
+#endif
+#define JSON_HEDLEY_CONCAT_EX(a,b) a##b
+
+#if defined(JSON_HEDLEY_CONCAT)
+    #undef JSON_HEDLEY_CONCAT
+#endif
+#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)
+
+#if defined(JSON_HEDLEY_CONCAT3_EX)
+    #undef JSON_HEDLEY_CONCAT3_EX
+#endif
+#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c
+
+#if defined(JSON_HEDLEY_CONCAT3)
+    #undef JSON_HEDLEY_CONCAT3
+#endif
+#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)
+
+#if defined(JSON_HEDLEY_VERSION_ENCODE)
+    #undef JSON_HEDLEY_VERSION_ENCODE
+#endif
+#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)
+    #undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
+
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #undef JSON_HEDLEY_GNUC_VERSION
+#endif
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION)
+    #undef JSON_HEDLEY_MSVC_VERSION
+#endif
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#endif
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #undef JSON_HEDLEY_INTEL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #undef JSON_HEDLEY_PGI_VERSION
+#endif
+#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)
+    #undef JSON_HEDLEY_PGI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #undef JSON_HEDLEY_SUNPRO_VERSION
+#endif
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)
+    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#endif
+#if defined(__EMSCRIPTEN__)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #undef JSON_HEDLEY_ARM_VERSION
+#endif
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)
+    #undef JSON_HEDLEY_ARM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #undef JSON_HEDLEY_IBM_VERSION
+#endif
+#if defined(__ibmxl__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)
+    #undef JSON_HEDLEY_IBM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #undef JSON_HEDLEY_TI_VERSION
+#endif
+#if \
+    defined(__TI_COMPILER_VERSION__) && \
+    ( \
+      defined(__TMS470__) || defined(__TI_ARM__) || \
+      defined(__MSP430__) || \
+      defined(__TMS320C2000__) \
+    )
+#if (__TI_COMPILER_VERSION__ >= 16000000)
+    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)
+    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #undef JSON_HEDLEY_TI_CL430_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)
+    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))
+    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)
+    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)
+    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #undef JSON_HEDLEY_CRAY_VERSION
+#endif
+#if defined(_CRAYC)
+    #if defined(_RELEASE_PATCHLEVEL)
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
+    #else
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)
+    #undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #undef JSON_HEDLEY_IAR_VERSION
+#endif
+#if defined(__IAR_SYSTEMS_ICC__)
+    #if __VER__ > 1000
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
+    #else
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)
+    #undef JSON_HEDLEY_IAR_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #undef JSON_HEDLEY_TINYC_VERSION
+#endif
+#if defined(__TINYC__)
+    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)
+    #undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #undef JSON_HEDLEY_DMC_VERSION
+#endif
+#if defined(__DMC__)
+    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)
+    #undef JSON_HEDLEY_DMC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #undef JSON_HEDLEY_COMPCERT_VERSION
+#endif
+#if defined(__COMPCERT_VERSION__)
+    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)
+    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #undef JSON_HEDLEY_PELLES_VERSION
+#endif
+#if defined(__POCC__)
+    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)
+    #undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #undef JSON_HEDLEY_GCC_VERSION
+#endif
+#if \
+    defined(JSON_HEDLEY_GNUC_VERSION) && \
+    !defined(__clang__) && \
+    !defined(JSON_HEDLEY_INTEL_VERSION) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_ARM_VERSION) && \
+    !defined(JSON_HEDLEY_CRAY_VERSION) && \
+    !defined(JSON_HEDLEY_TI_VERSION) && \
+    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
+    !defined(__COMPCERT__) && \
+    !defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_ATTRIBUTE
+#endif
+#if \
+  defined(__has_attribute) && \
+  ( \
+    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+  )
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#else
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#endif
+#if \
+    defined(__has_cpp_attribute) && \
+    defined(__cplusplus) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#endif
+#if !defined(__cplusplus) || !defined(__has_cpp_attribute)
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#elif \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_IAR_VERSION) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
+    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_BUILTIN)
+    #undef JSON_HEDLEY_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_FEATURE)
+    #undef JSON_HEDLEY_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GCC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_EXTENSION)
+    #undef JSON_HEDLEY_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_WARNING)
+    #undef JSON_HEDLEY_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_HAS_WARNING(warning) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)
+    #undef JSON_HEDLEY_GNUC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_WARNING)
+    #undef JSON_HEDLEY_GCC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+    #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+    #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+    #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
+/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
+#    if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
+#      if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      else
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      endif
+#    else
+#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    endif
+#  endif
+#endif
+#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x
+#endif
+
+#if defined(JSON_HEDLEY_CONST_CAST)
+    #undef JSON_HEDLEY_CONST_CAST
+#endif
+#if defined(__cplusplus)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
+#elif \
+  JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_REINTERPRET_CAST)
+    #undef JSON_HEDLEY_REINTERPRET_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_CAST)
+    #undef JSON_HEDLEY_STATIC_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_CPP_CAST)
+    #undef JSON_HEDLEY_CPP_CAST
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast")
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
+    ((T) (expr)) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("diag_suppress=Pe137") \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
+#  endif
+#else
+#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
+#if defined(JSON_HEDLEY_DEPRECATED)
+    #undef JSON_HEDLEY_DEPRECATED
+#endif
+#if defined(JSON_HEDLEY_DEPRECATED_FOR)
+    #undef JSON_HEDLEY_DEPRECATED_FOR
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
+#elif \
+    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated")
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
+#else
+    #define JSON_HEDLEY_DEPRECATED(since)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)
+#endif
+
+#if defined(JSON_HEDLEY_UNAVAILABLE)
+    #undef JSON_HEDLEY_UNAVAILABLE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
+#else
+    #define JSON_HEDLEY_UNAVAILABLE(available_since)
+#endif
+
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#endif
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+#elif defined(_Check_return_) /* SAL */
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
+#else
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)
+#endif
+
+#if defined(JSON_HEDLEY_SENTINEL)
+    #undef JSON_HEDLEY_SENTINEL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
+#else
+    #define JSON_HEDLEY_SENTINEL(position)
+#endif
+
+#if defined(JSON_HEDLEY_NO_RETURN)
+    #undef JSON_HEDLEY_NO_RETURN
+#endif
+#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NO_RETURN __noreturn
+#elif \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+    #define JSON_HEDLEY_NO_RETURN _Noreturn
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#else
+    #define JSON_HEDLEY_NO_RETURN
+#endif
+
+#if defined(JSON_HEDLEY_NO_ESCAPE)
+    #undef JSON_HEDLEY_NO_ESCAPE
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)
+    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))
+#else
+    #define JSON_HEDLEY_NO_ESCAPE
+#endif
+
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #undef JSON_HEDLEY_UNREACHABLE
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)
+    #undef JSON_HEDLEY_UNREACHABLE_RETURN
+#endif
+#if defined(JSON_HEDLEY_ASSUME)
+    #undef JSON_HEDLEY_ASSUME
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)
+#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
+    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
+#elif \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+    #if defined(__cplusplus)
+        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)
+    #endif
+#endif
+#if \
+    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
+#elif defined(JSON_HEDLEY_ASSUME)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+#if !defined(JSON_HEDLEY_ASSUME)
+    #if defined(JSON_HEDLEY_UNREACHABLE)
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)
+    #endif
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #if  \
+        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))
+    #else
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()
+    #endif
+#else
+    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)
+#endif
+#if !defined(JSON_HEDLEY_UNREACHABLE)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+
+JSON_HEDLEY_DIAGNOSTIC_PUSH
+#if JSON_HEDLEY_HAS_WARNING("-Wpedantic")
+    #pragma clang diagnostic ignored "-Wpedantic"
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus)
+    #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0)
+    #if defined(__clang__)
+        #pragma clang diagnostic ignored "-Wvariadic-macros"
+    #elif defined(JSON_HEDLEY_GCC_VERSION)
+        #pragma GCC diagnostic ignored "-Wvariadic-macros"
+    #endif
+#endif
+#if defined(JSON_HEDLEY_NON_NULL)
+    #undef JSON_HEDLEY_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+    #define JSON_HEDLEY_NON_NULL(...)
+#endif
+JSON_HEDLEY_DIAGNOSTIC_POP
+
+#if defined(JSON_HEDLEY_PRINTF_FORMAT)
+    #undef JSON_HEDLEY_PRINTF_FORMAT
+#endif
+#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
+#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
+#else
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
+#endif
+
+#if defined(JSON_HEDLEY_CONSTEXPR)
+    #undef JSON_HEDLEY_CONSTEXPR
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)
+    #endif
+#endif
+#if !defined(JSON_HEDLEY_CONSTEXPR)
+    #define JSON_HEDLEY_CONSTEXPR
+#endif
+
+#if defined(JSON_HEDLEY_PREDICT)
+    #undef JSON_HEDLEY_PREDICT
+#endif
+#if defined(JSON_HEDLEY_LIKELY)
+    #undef JSON_HEDLEY_LIKELY
+#endif
+#if defined(JSON_HEDLEY_UNLIKELY)
+    #undef JSON_HEDLEY_UNLIKELY
+#endif
+#if defined(JSON_HEDLEY_UNPREDICTABLE)
+    #undef JSON_HEDLEY_UNPREDICTABLE
+#endif
+#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
+#endif
+#if \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))
+#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )
+#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
+#elif \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
+  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \
+    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)
+#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))
+#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))
+#endif
+#if !defined(JSON_HEDLEY_UNPREDICTABLE)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)
+#endif
+
+#if defined(JSON_HEDLEY_MALLOC)
+    #undef JSON_HEDLEY_MALLOC
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_MALLOC __declspec(restrict)
+#else
+    #define JSON_HEDLEY_MALLOC
+#endif
+
+#if defined(JSON_HEDLEY_PURE)
+    #undef JSON_HEDLEY_PURE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PURE __attribute__((__pure__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+#  define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \
+    )
+#  define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
+#else
+#  define JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_CONST)
+    #undef JSON_HEDLEY_CONST
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_CONST __attribute__((__const__))
+#elif \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_CONST _Pragma("no_side_effect")
+#else
+    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_RESTRICT)
+    #undef JSON_HEDLEY_RESTRICT
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT restrict
+#elif \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RESTRICT __restrict
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT _Restrict
+#else
+    #define JSON_HEDLEY_RESTRICT
+#endif
+
+#if defined(JSON_HEDLEY_INLINE)
+    #undef JSON_HEDLEY_INLINE
+#endif
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    (defined(__cplusplus) && (__cplusplus >= 199711L))
+    #define JSON_HEDLEY_INLINE inline
+#elif \
+    defined(JSON_HEDLEY_GCC_VERSION) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)
+    #define JSON_HEDLEY_INLINE __inline__
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_INLINE __inline
+#else
+    #define JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_ALWAYS_INLINE)
+    #undef JSON_HEDLEY_ALWAYS_INLINE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \
+    )
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
+#else
+#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_NEVER_INLINE)
+    #undef JSON_HEDLEY_NEVER_INLINE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#else
+    #define JSON_HEDLEY_NEVER_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_PRIVATE)
+    #undef JSON_HEDLEY_PRIVATE
+#endif
+#if defined(JSON_HEDLEY_PUBLIC)
+    #undef JSON_HEDLEY_PUBLIC
+#endif
+#if defined(JSON_HEDLEY_IMPORT)
+    #undef JSON_HEDLEY_IMPORT
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+#  define JSON_HEDLEY_PRIVATE
+#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)
+#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)
+#else
+#  if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    ( \
+      defined(__TI_EABI__) && \
+      ( \
+        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
+      ) \
+    ) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
+#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__("default")))
+#  else
+#    define JSON_HEDLEY_PRIVATE
+#    define JSON_HEDLEY_PUBLIC
+#  endif
+#  define JSON_HEDLEY_IMPORT    extern
+#endif
+
+#if defined(JSON_HEDLEY_NO_THROW)
+    #undef JSON_HEDLEY_NO_THROW
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)
+#else
+    #define JSON_HEDLEY_NO_THROW
+#endif
+
+#if defined(JSON_HEDLEY_FALL_THROUGH)
+    #undef JSON_HEDLEY_FALL_THROUGH
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])
+#elif defined(__fallthrough) /* SAL */
+    #define JSON_HEDLEY_FALL_THROUGH __fallthrough
+#else
+    #define JSON_HEDLEY_FALL_THROUGH
+#endif
+
+#if defined(JSON_HEDLEY_RETURNS_NON_NULL)
+    #undef JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
+#elif defined(_Ret_notnull_) /* SAL */
+    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
+#else
+    #define JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+
+#if defined(JSON_HEDLEY_ARRAY_PARAM)
+    #undef JSON_HEDLEY_ARRAY_PARAM
+#endif
+#if \
+    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+    !defined(__STDC_NO_VLA__) && \
+    !defined(__cplusplus) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)
+#else
+    #define JSON_HEDLEY_ARRAY_PARAM(name)
+#endif
+
+#if defined(JSON_HEDLEY_IS_CONSTANT)
+    #undef JSON_HEDLEY_IS_CONSTANT
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)
+    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#endif
+/* JSON_HEDLEY_IS_CONSTEXPR_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #undef JSON_HEDLEY_IS_CONSTEXPR_
+#endif
+#if \
+    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
+#endif
+#if !defined(__cplusplus)
+#  if \
+       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
+#endif
+#  elif \
+       ( \
+          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
+          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \
+          !defined(JSON_HEDLEY_PGI_VERSION) && \
+          !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
+#endif
+#  elif \
+       defined(JSON_HEDLEY_GCC_VERSION) || \
+       defined(JSON_HEDLEY_INTEL_VERSION) || \
+       defined(JSON_HEDLEY_TINYC_VERSION) || \
+       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \
+       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \
+       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \
+       defined(__clang__)
+#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \
+        sizeof(void) != \
+        sizeof(*( \
+                  1 ? \
+                  ((void*) ((expr) * 0L) ) : \
+((struct { char v[sizeof(void) * 2]; } *) 1) \
+                ) \
+              ) \
+                                            )
+#  endif
+#endif
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))
+#else
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_BEGIN_C_DECLS)
+    #undef JSON_HEDLEY_BEGIN_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_END_C_DECLS)
+    #undef JSON_HEDLEY_END_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_C_DECL)
+    #undef JSON_HEDLEY_C_DECL
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" {
+    #define JSON_HEDLEY_END_C_DECLS }
+    #define JSON_HEDLEY_C_DECL extern "C"
+#else
+    #define JSON_HEDLEY_BEGIN_C_DECLS
+    #define JSON_HEDLEY_END_C_DECLS
+    #define JSON_HEDLEY_C_DECL
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_ASSERT)
+    #undef JSON_HEDLEY_STATIC_ASSERT
+#endif
+#if \
+  !defined(__cplusplus) && ( \
+      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
+      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
+      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+      defined(_Static_assert) \
+    )
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
+#elif \
+  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
+#else
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)
+#endif
+
+#if defined(JSON_HEDLEY_NULL)
+    #undef JSON_HEDLEY_NULL
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)
+    #elif defined(NULL)
+        #define JSON_HEDLEY_NULL NULL
+    #else
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)
+    #endif
+#elif defined(NULL)
+    #define JSON_HEDLEY_NULL NULL
+#else
+    #define JSON_HEDLEY_NULL ((void*) 0)
+#endif
+
+#if defined(JSON_HEDLEY_MESSAGE)
+    #undef JSON_HEDLEY_MESSAGE
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_MESSAGE(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(message msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)
+#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_WARNING)
+    #undef JSON_HEDLEY_WARNING
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_WARNING(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(clang warning msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_REQUIRE)
+    #undef JSON_HEDLEY_REQUIRE
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_MSG)
+    #undef JSON_HEDLEY_REQUIRE_MSG
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)
+#  if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat")
+#    define JSON_HEDLEY_REQUIRE(expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), #expr, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), msg, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error")))
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error")))
+#  endif
+#else
+#  define JSON_HEDLEY_REQUIRE(expr)
+#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS)
+    #undef JSON_HEDLEY_FLAGS
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
+    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+    #define JSON_HEDLEY_FLAGS
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS_CAST)
+    #undef JSON_HEDLEY_FLAGS_CAST
+#endif
+#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        _Pragma("warning(disable:188)") \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)
+#endif
+
+#if defined(JSON_HEDLEY_EMPTY_BASES)
+    #undef JSON_HEDLEY_EMPTY_BASES
+#endif
+#if \
+    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
+#else
+    #define JSON_HEDLEY_EMPTY_BASES
+#endif
+
+/* Remaining macros are deprecated. */
+
+#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
+#else
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)
+    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#endif
+#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)
+    #undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)
+    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#endif
+#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)
+    #undef JSON_HEDLEY_CLANG_HAS_WARNING
+#endif
+#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)
+
+#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */
+
+
+// This file contains all internal macro definitions (except those affecting ABI)
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+// exclude unsupported compilers
+#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
+    #if defined(__clang__)
+        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+            #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
+            #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #endif
+#endif
+
+// C++ language standard detection
+// if the user manually specified the used c++ version this is skipped
+#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
+    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
+        #define JSON_HAS_CPP_20
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+        #define JSON_HAS_CPP_14
+    #endif
+    // the cpp 11 flag is always specified because it is the minimal required version
+    #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)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif defined(__cpp_lib_experimental_filesystem)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif !defined(__has_include)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif __has_include(<filesystem>)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif __has_include(<experimental/filesystem>)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #endif
+
+        // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
+        #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__clang_major__) && __clang_major__ < 7
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(_MSC_VER) && _MSC_VER < 1914
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before iOS 13
+        #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before macOS Catalina
+        #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+    #endif
+#endif
+
+#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_FILESYSTEM
+    #define JSON_HAS_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_THREE_WAY_COMPARISON
+    #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
+    #define JSON_NO_UNIQUE_ADDRESS
+#endif
+
+// disable documentation warnings on clang
+#if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdocumentation"
+    #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#endif
+
+// allow disabling exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
+    #define JSON_THROW(exception) throw exception
+    #define JSON_TRY try
+    #define JSON_CATCH(exception) catch(exception)
+    #define JSON_INTERNAL_CATCH(exception) catch(exception)
+#else
+    #include <cstdlib>
+    #define JSON_THROW(exception) std::abort()
+    #define JSON_TRY if(true)
+    #define JSON_CATCH(exception) if(false)
+    #define JSON_INTERNAL_CATCH(exception) if(false)
+#endif
+
+// override exception macros
+#if defined(JSON_THROW_USER)
+    #undef JSON_THROW
+    #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+    #undef JSON_TRY
+    #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+    #undef JSON_CATCH
+    #define JSON_CATCH JSON_CATCH_USER
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_CATCH_USER
+#endif
+#if defined(JSON_INTERNAL_CATCH_USER)
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
+#endif
+
+// allow overriding assert
+#if !defined(JSON_ASSERT)
+    #include <cassert> // assert
+    #define JSON_ASSERT(x) assert(x)
+#endif
+
+// allow to access some private functions (needed by the test suite)
+#if defined(JSON_TESTS_PRIVATE)
+    #define JSON_PRIVATE_UNLESS_TESTED public
+#else
+    #define JSON_PRIVATE_UNLESS_TESTED private
+#endif
+
+/*!
+@brief macro to briefly define a mapping between an enum and JSON
+@def NLOHMANN_JSON_SERIALIZE_ENUM
+@since version 3.4.0
+*/
+#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \
+    template<typename BasicJsonType>                                                            \
+    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \
+        {                                                                                       \
+            return ej_pair.first == e;                                                          \
+        });                                                                                     \
+        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \
+    }                                                                                           \
+    template<typename BasicJsonType>                                                            \
+    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+        {                                                                                       \
+            return ej_pair.second == j;                                                         \
+        });                                                                                     \
+        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \
+    }
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \
+    template<template<typename, typename, typename...> class ObjectType,   \
+             template<typename, typename...> class ArrayType,              \
+             class StringType, class BooleanType, class NumberIntegerType, \
+             class NumberUnsignedType, class NumberFloatType,              \
+             template<typename> class AllocatorType,                       \
+             template<typename, typename = void> class JSONSerializer,     \
+             class BinaryType>
+
+#define NLOHMANN_BASIC_JSON_TPL                                            \
+    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \
+    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
+    AllocatorType, JSONSerializer, BinaryType>
+
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
+        NLOHMANN_JSON_PASTE64, \
+        NLOHMANN_JSON_PASTE63, \
+        NLOHMANN_JSON_PASTE62, \
+        NLOHMANN_JSON_PASTE61, \
+        NLOHMANN_JSON_PASTE60, \
+        NLOHMANN_JSON_PASTE59, \
+        NLOHMANN_JSON_PASTE58, \
+        NLOHMANN_JSON_PASTE57, \
+        NLOHMANN_JSON_PASTE56, \
+        NLOHMANN_JSON_PASTE55, \
+        NLOHMANN_JSON_PASTE54, \
+        NLOHMANN_JSON_PASTE53, \
+        NLOHMANN_JSON_PASTE52, \
+        NLOHMANN_JSON_PASTE51, \
+        NLOHMANN_JSON_PASTE50, \
+        NLOHMANN_JSON_PASTE49, \
+        NLOHMANN_JSON_PASTE48, \
+        NLOHMANN_JSON_PASTE47, \
+        NLOHMANN_JSON_PASTE46, \
+        NLOHMANN_JSON_PASTE45, \
+        NLOHMANN_JSON_PASTE44, \
+        NLOHMANN_JSON_PASTE43, \
+        NLOHMANN_JSON_PASTE42, \
+        NLOHMANN_JSON_PASTE41, \
+        NLOHMANN_JSON_PASTE40, \
+        NLOHMANN_JSON_PASTE39, \
+        NLOHMANN_JSON_PASTE38, \
+        NLOHMANN_JSON_PASTE37, \
+        NLOHMANN_JSON_PASTE36, \
+        NLOHMANN_JSON_PASTE35, \
+        NLOHMANN_JSON_PASTE34, \
+        NLOHMANN_JSON_PASTE33, \
+        NLOHMANN_JSON_PASTE32, \
+        NLOHMANN_JSON_PASTE31, \
+        NLOHMANN_JSON_PASTE30, \
+        NLOHMANN_JSON_PASTE29, \
+        NLOHMANN_JSON_PASTE28, \
+        NLOHMANN_JSON_PASTE27, \
+        NLOHMANN_JSON_PASTE26, \
+        NLOHMANN_JSON_PASTE25, \
+        NLOHMANN_JSON_PASTE24, \
+        NLOHMANN_JSON_PASTE23, \
+        NLOHMANN_JSON_PASTE22, \
+        NLOHMANN_JSON_PASTE21, \
+        NLOHMANN_JSON_PASTE20, \
+        NLOHMANN_JSON_PASTE19, \
+        NLOHMANN_JSON_PASTE18, \
+        NLOHMANN_JSON_PASTE17, \
+        NLOHMANN_JSON_PASTE16, \
+        NLOHMANN_JSON_PASTE15, \
+        NLOHMANN_JSON_PASTE14, \
+        NLOHMANN_JSON_PASTE13, \
+        NLOHMANN_JSON_PASTE12, \
+        NLOHMANN_JSON_PASTE11, \
+        NLOHMANN_JSON_PASTE10, \
+        NLOHMANN_JSON_PASTE9, \
+        NLOHMANN_JSON_PASTE8, \
+        NLOHMANN_JSON_PASTE7, \
+        NLOHMANN_JSON_PASTE6, \
+        NLOHMANN_JSON_PASTE5, \
+        NLOHMANN_JSON_PASTE4, \
+        NLOHMANN_JSON_PASTE3, \
+        NLOHMANN_JSON_PASTE2, \
+        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
+#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
+#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
+#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
+#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
+#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
+#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
+#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
+#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
+#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
+#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
+#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
+#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
+#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
+#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
+#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
+#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
+#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
+#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
+#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
+#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
+#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
+#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
+#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
+#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
+#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
+#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
+#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
+#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
+#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
+#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
+#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
+#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
+#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
+#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
+#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
+#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
+#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
+#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
+#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
+#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
+#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
+#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
+#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
+#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
+#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
+#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
+#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
+#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
+#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
+#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
+#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
+#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
+
+#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
+    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
+    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+
+// inspired from https://stackoverflow.com/a/26745591
+// allows to call any std function as if (e.g. with begin):
+// using std::begin; begin(x);
+//
+// it allows using the detected idiom to retrieve the return type
+// of such an expression
+#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \
+    namespace detail {                                                            \
+    using std::std_name;                                                          \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    }                                                                             \
+    \
+    namespace detail2 {                                                           \
+    struct std_name##_tag                                                         \
+    {                                                                             \
+    };                                                                            \
+    \
+    template<typename... T>                                                       \
+    std_name##_tag std_name(T&&...);                                              \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name                                              \
+    {                                                                             \
+        static constexpr auto const value = ::nlohmann::detail::                  \
+                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
+    };                                                                            \
+    } /* namespace detail2 */ \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \
+    {                                                                             \
+    }
+
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_USE_IMPLICIT_CONVERSIONS 1
+#endif
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_EXPLICIT
+#else
+    #define JSON_EXPLICIT explicit
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+    #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#ifndef JSON_USE_GLOBAL_UDLS
+    #define JSON_USE_GLOBAL_UDLS 1
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+    #include <compare> // partial_ordering
+#endif
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+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
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_escape.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief replace all occurrences of a substring by another string
+
+@param[in,out] s  the string to manipulate; changed so that all
+               occurrences of @a f are replaced with @a t
+@param[in]     f  the substring to replace with @a t
+@param[in]     t  the string to replace @a f
+
+@pre The search string @a f must not be empty. **This precondition is
+enforced with an assertion.**
+
+@since version 2.0.0
+*/
+template<typename StringType>
+inline void replace_substring(StringType& s, const StringType& f,
+                              const StringType& t)
+{
+    JSON_ASSERT(!f.empty());
+    for (auto pos = s.find(f);                // find first occurrence of f
+            pos != StringType::npos;          // make sure f was found
+            s.replace(pos, f.size(), t),      // replace with t, and
+            pos = s.find(f, pos + t.size()))  // find next occurrence of f
+    {}
+}
+
+/*!
+ * @brief string escaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to escape
+ * @return    escaped string
+ *
+ * Note the order of escaping "~" to "~0" and "/" to "~1" is important.
+ */
+template<typename StringType>
+inline StringType escape(StringType s)
+{
+    replace_substring(s, StringType{"~"}, StringType{"~0"});
+    replace_substring(s, StringType{"/"}, StringType{"~1"});
+    return s;
+}
+
+/*!
+ * @brief string unescaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to unescape
+ * @return    unescaped string
+ *
+ * Note the order of escaping "~1" to "/" and "~0" to "~" is important.
+ */
+template<typename StringType>
+static void unescape(StringType& s)
+{
+    replace_substring(s, StringType{"~1"}, StringType{"/"});
+    replace_substring(s, StringType{"~0"}, StringType{"~"});
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/position_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// struct to capture the start position of the current token
+struct position_t
+{
+    /// the total number of characters read
+    std::size_t chars_read_total = 0;
+    /// the number of characters read in the current line
+    std::size_t chars_read_current_line = 0;
+    /// the number of lines read
+    std::size_t lines_read = 0;
+
+    /// conversion to size_t to preserve SAX interface
+    constexpr operator size_t() const
+    {
+        return chars_read_total;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2018 The Abseil Authors
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // index_sequence, make_index_sequence, index_sequence_for
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#ifdef JSON_HAS_CPP_14
+
+// the following utilities are natively available in C++14
+using std::enable_if_t;
+using std::index_sequence;
+using std::make_index_sequence;
+using std::index_sequence_for;
+
+#else
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
+// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
+
+//// START OF CODE FROM GOOGLE ABSEIL
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+//   template< class T, T... Ints >
+//   void user_function(integer_sequence<T, Ints...>);
+//
+//   int main()
+//   {
+//     // user_function's `T` will be deduced to `int` and `Ints...`
+//     // will be deduced to `0, 1, 2, 3, 4`.
+//     user_function(make_integer_sequence<int, 5>());
+//   }
+template <typename T, T... Ints>
+struct integer_sequence
+{
+    using value_type = T;
+    static constexpr std::size_t size() noexcept
+    {
+        return sizeof...(Ints);
+    }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal
+{
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen
+{
+    using type =
+        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
+};
+
+template <typename T>
+struct Gen<T, 0>
+{
+    using type = integer_sequence<T>;
+};
+
+}  // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+//// END OF CODE FROM GOOGLE ABSEIL
+
+#endif
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+    static JSON_INLINE_VARIABLE constexpr T value{};
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename T>
+    constexpr T static_const<T>::value;
+#endif
+
+template<typename T, typename... Args>
+inline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
+{
+    return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <limits> // numeric_limits
+#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
+#include <utility> // declval
+#include <tuple> // tuple
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // random_access_iterator_tag
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename It, typename = void>
+struct iterator_types {};
+
+template<typename It>
+struct iterator_types <
+    It,
+    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
+    typename It::reference, typename It::iterator_category >>
+{
+    using difference_type = typename It::difference_type;
+    using value_type = typename It::value_type;
+    using pointer = typename It::pointer;
+    using reference = typename It::reference;
+    using iterator_category = typename It::iterator_category;
+};
+
+// This is required as some compilers implement std::iterator_traits in a way that
+// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
+template<typename T, typename = void>
+struct iterator_traits
+{
+};
+
+template<typename T>
+struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
+            : iterator_types<T>
+{
+};
+
+template<typename T>
+struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
+{
+    using iterator_category = std::random_access_iterator_tag;
+    using value_type = T;
+    using difference_type = ptrdiff_t;
+    using pointer = T*;
+    using reference = T&;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/call_std/begin.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/call_std/end.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
+    #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+    #include <cstdint> // int64_t, uint64_t
+    #include <map> // map
+    #include <memory> // allocator
+    #include <string> // string
+    #include <vector> // vector
+
+    // #include <nlohmann/detail/abi_macros.hpp>
+
+
+    /*!
+    @brief namespace for Niels Lohmann
+    @see https://github.com/nlohmann
+    @since version 1.0.0
+    */
+    NLOHMANN_JSON_NAMESPACE_BEGIN
+
+    /*!
+    @brief default JSONSerializer template argument
+
+    This serializer ignores the template arguments and uses ADL
+    ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
+    for serialization.
+    */
+    template<typename T = void, typename SFINAE = void>
+    struct adl_serializer;
+
+    /// a class to store JSON values
+    /// @sa https://json.nlohmann.me/api/basic_json/
+    template<template<typename U, typename V, typename... Args> class ObjectType =
+    std::map,
+    template<typename U, typename... Args> class ArrayType = std::vector,
+    class StringType = std::string, class BooleanType = bool,
+    class NumberIntegerType = std::int64_t,
+    class NumberUnsignedType = std::uint64_t,
+    class NumberFloatType = double,
+    template<typename U> class AllocatorType = std::allocator,
+    template<typename T, typename SFINAE = void> class JSONSerializer =
+    adl_serializer,
+    class BinaryType = std::vector<std::uint8_t>>
+    class basic_json;
+
+    /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+    /// @sa https://json.nlohmann.me/api/json_pointer/
+    template<typename RefStringType>
+    class json_pointer;
+
+    /*!
+    @brief default specialization
+    @sa https://json.nlohmann.me/api/json/
+    */
+    using json = basic_json<>;
+
+    /// @brief a minimal map-like container that preserves insertion order
+    /// @sa https://json.nlohmann.me/api/ordered_map/
+    template<class Key, class T, class IgnoredLess, class Allocator>
+    struct ordered_map;
+
+    /// @brief specialization that maintains the insertion order of object keys
+    /// @sa https://json.nlohmann.me/api/ordered_json/
+    using ordered_json = basic_json<nlohmann::ordered_map>;
+
+    NLOHMANN_JSON_NAMESPACE_END
+
+#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+/*!
+@brief detail namespace with internal helper functions
+
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+
+/////////////
+// helpers //
+/////////////
+
+// Note to maintainers:
+//
+// Every trait in this file expects a non CV-qualified type.
+// The only exceptions are in the 'aliases for detected' section
+// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
+//
+// In this case, T has to be properly CV-qualified to constraint the function arguments
+// (e.g. to_json(BasicJsonType&, const T&))
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// used by exceptions create() member functions
+// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
+// false_type otherwise
+template<typename BasicJsonContext>
+struct is_basic_json_context :
+    std::integral_constant < bool,
+    is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
+    || std::is_same<BasicJsonContext, std::nullptr_t>::value >
+{};
+
+//////////////////////
+// json_ref helpers //
+//////////////////////
+
+template<typename>
+class json_ref;
+
+template<typename>
+struct is_json_ref : std::false_type {};
+
+template<typename T>
+struct is_json_ref<json_ref<T>> : std::true_type {};
+
+//////////////////////////
+// aliases for detected //
+//////////////////////////
+
+template<typename T>
+using mapped_type_t = typename T::mapped_type;
+
+template<typename T>
+using key_type_t = typename T::key_type;
+
+template<typename T>
+using value_type_t = typename T::value_type;
+
+template<typename T>
+using difference_type_t = typename T::difference_type;
+
+template<typename T>
+using pointer_t = typename T::pointer;
+
+template<typename T>
+using reference_t = typename T::reference;
+
+template<typename T>
+using iterator_category_t = typename T::iterator_category;
+
+template<typename T, typename... Args>
+using to_json_function = decltype(T::to_json(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+using from_json_function = decltype(T::from_json(std::declval<Args>()...));
+
+template<typename T, typename U>
+using get_template_function = decltype(std::declval<T>().template get<U>());
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T, typename = void>
+struct has_from_json : std::false_type {};
+
+// trait checking if j.get<T> is valid
+// use this trait instead of std::is_constructible or std::is_convertible,
+// both rely on, or make use of implicit conversions, and thus fail when T
+// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
+template <typename BasicJsonType, typename T>
+struct is_getable
+{
+    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, from_json_function, serializer,
+        const BasicJsonType&, T&>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T, typename = void>
+struct has_non_default_from_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<T, from_json_function, serializer,
+        const BasicJsonType&>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
+template<typename BasicJsonType, typename T, typename = void>
+struct has_to_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
+        T>::value;
+};
+
+template<typename T>
+using detect_key_compare = typename T::key_compare;
+
+template<typename T>
+struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
+
+// obtains the actual object key comparator
+template<typename BasicJsonType>
+struct actual_object_comparator
+{
+    using object_t = typename BasicJsonType::object_t;
+    using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
+    using type = typename std::conditional < has_key_compare<object_t>::value,
+          typename object_t::key_compare, object_comparator_t>::type;
+};
+
+template<typename BasicJsonType>
+using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
+
+///////////////////
+// is_ functions //
+///////////////////
+
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B> struct conjunction<B> : B { };
+template<class B, class... Bn>
+struct conjunction<B, Bn...>
+: 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 > { };
+
+// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
+// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
+// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
+template <typename T>
+struct is_default_constructible : std::is_default_constructible<T> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<const std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename... Ts>
+struct is_default_constructible<std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename... Ts>
+struct is_default_constructible<const std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+
+template <typename T, typename... Args>
+struct is_constructible : std::is_constructible<T, Args...> {};
+
+template <typename T1, typename T2>
+struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
+
+template <typename T1, typename T2>
+struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
+
+template <typename... Ts>
+struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
+
+template <typename... Ts>
+struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
+
+
+template<typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template<typename T>
+struct is_iterator_traits<iterator_traits<T>>
+{
+  private:
+    using traits = iterator_traits<T>;
+
+  public:
+    static constexpr auto value =
+        is_detected<value_type_t, traits>::value &&
+        is_detected<difference_type_t, traits>::value &&
+        is_detected<pointer_t, traits>::value &&
+        is_detected<iterator_category_t, traits>::value &&
+        is_detected<reference_t, traits>::value;
+};
+
+template<typename T>
+struct is_range
+{
+  private:
+    using t_ref = typename std::add_lvalue_reference<T>::type;
+
+    using iterator = detected_t<result_of_begin, t_ref>;
+    using sentinel = detected_t<result_of_end, t_ref>;
+
+    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
+    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
+    // but reimplementing these would be too much work, as a lot of other concepts are used underneath
+    static constexpr auto is_iterator_begin =
+        is_iterator_traits<iterator_traits<iterator>>::value;
+
+  public:
+    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
+};
+
+template<typename R>
+using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
+
+template<typename T>
+using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
+
+// The following implementation of is_complete_type is taken from
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+// and is written by Xiang Fan who agreed to using it in this library.
+
+template<typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template<typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+         typename = void>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type_impl <
+    BasicJsonType, CompatibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
+    is_detected<key_type_t, CompatibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    // macOS's is_constructible does not play well with nonesuch...
+    static constexpr bool value =
+        is_constructible<typename object_t::key_type,
+        typename CompatibleObjectType::key_type>::value &&
+        is_constructible<typename object_t::mapped_type,
+        typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type
+    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         typename = void>
+struct is_constructible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type_impl <
+    BasicJsonType, ConstructibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
+    is_detected<key_type_t, ConstructibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    static constexpr bool value =
+        (is_default_constructible<ConstructibleObjectType>::value &&
+         (std::is_move_assignable<ConstructibleObjectType>::value ||
+          std::is_copy_assignable<ConstructibleObjectType>::value) &&
+         (is_constructible<typename ConstructibleObjectType::key_type,
+          typename object_t::key_type>::value &&
+          std::is_same <
+          typename object_t::mapped_type,
+          typename ConstructibleObjectType::mapped_type >::value)) ||
+        (has_from_json<BasicJsonType,
+         typename ConstructibleObjectType::mapped_type>::value ||
+         has_non_default_from_json <
+         BasicJsonType,
+         typename ConstructibleObjectType::mapped_type >::value);
+};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type
+    : is_constructible_object_type_impl<BasicJsonType,
+      ConstructibleObjectType> {};
+
+template<typename BasicJsonType, typename CompatibleStringType>
+struct is_compatible_string_type
+{
+    static constexpr auto value =
+        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+};
+
+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 =
+        conjunction <
+        is_constructible<laundered_type, typename BasicJsonType::string_t>,
+        is_detected_exact<typename BasicJsonType::string_t::value_type,
+        value_type_t, laundered_type >>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
+struct is_compatible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type_impl <
+    BasicJsonType, CompatibleArrayType,
+    enable_if_t <
+    is_detected<iterator_t, CompatibleArrayType>::value&&
+    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
+{
+    static constexpr bool value =
+        is_constructible<BasicJsonType,
+        range_value_t<CompatibleArrayType>>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type
+    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+struct is_constructible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t<std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value >>
+            : std::true_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t < !std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value&&
+    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+    is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
+is_detected<range_value_t, ConstructibleArrayType>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
+        is_complete_type <
+        detected_t<range_value_t, ConstructibleArrayType >>::value >>
+{
+    using value_type = range_value_t<ConstructibleArrayType>;
+
+    static constexpr bool value =
+        std::is_same<value_type,
+        typename BasicJsonType::array_t::value_type>::value ||
+        has_from_json<BasicJsonType,
+        value_type>::value ||
+        has_non_default_from_json <
+        BasicJsonType,
+        value_type >::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type
+    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+         typename = void>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl <
+    RealIntegerType, CompatibleNumberIntegerType,
+    enable_if_t < std::is_integral<RealIntegerType>::value&&
+    std::is_integral<CompatibleNumberIntegerType>::value&&
+    !std::is_same<bool, CompatibleNumberIntegerType>::value >>
+{
+    // is there an assert somewhere on overflows?
+    using RealLimits = std::numeric_limits<RealIntegerType>;
+    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+    static constexpr auto value =
+        is_constructible<RealIntegerType,
+        CompatibleNumberIntegerType>::value &&
+        CompatibleLimits::is_integer &&
+        RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+    : is_compatible_integer_type_impl<RealIntegerType,
+      CompatibleNumberIntegerType> {};
+
+template<typename BasicJsonType, typename CompatibleType, typename = void>
+struct is_compatible_type_impl: std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type_impl <
+    BasicJsonType, CompatibleType,
+    enable_if_t<is_complete_type<CompatibleType>::value >>
+{
+    static constexpr bool value =
+        has_to_json<BasicJsonType, CompatibleType>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
+
+template<typename T1, typename T2>
+struct is_constructible_tuple : std::false_type {};
+
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
+
+template<typename BasicJsonType, typename T>
+struct is_json_iterator_of : std::false_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
+{};
+
+// checks if a given type T is a template specialization of Primary
+template<template <typename...> class Primary, typename T>
+struct is_specialization_of : std::false_type {};
+
+template<template <typename...> class Primary, typename... Args>
+struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
+
+template<typename T>
+using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;
+
+// checks if A and B are comparable using Compare functor
+template<typename Compare, typename A, typename B, typename = void>
+struct is_comparable : std::false_type {};
+
+template<typename Compare, typename A, typename B>
+struct is_comparable<Compare, A, B, void_t<
+decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
+decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
+>> : std::true_type {};
+
+template<typename T>
+using detect_is_transparent = typename T::is_transparent;
+
+// type trait to check if KeyType can be used as object key (without a BasicJsonType)
+// see is_usable_as_basic_json_key_type below
+template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_key_type = typename std::conditional <
+                              is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
+                              && !(ExcludeObjectKeyType && std::is_same<KeyType,
+                                   ObjectKeyType>::value)
+                              && (!RequireTransparentComparator
+                                  || is_detected <detect_is_transparent, Comparator>::value)
+                              && !is_json_pointer<KeyType>::value,
+                              std::true_type,
+                              std::false_type >::type;
+
+// type trait to check if KeyType can be used as object key
+// true if:
+//   - KeyType is comparable with BasicJsonType::object_t::key_type
+//   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
+//   - the comparator is transparent or RequireTransparentComparator is false
+//   - KeyType is not a JSON iterator or json_pointer
+template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_basic_json_key_type = typename std::conditional <
+        is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
+        typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
+        RequireTransparentComparator, ExcludeObjectKeyType>::value
+        && !is_json_iterator_of<BasicJsonType, KeyType>::value,
+        std::true_type,
+        std::false_type >::type;
+
+template<typename ObjectType, typename KeyType>
+using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
+
+// type trait to check if object_t has an erase() member functions accepting KeyType
+template<typename BasicJsonType, typename KeyType>
+using has_erase_with_key_type = typename std::conditional <
+                                is_detected <
+                                detect_erase_with_key_type,
+                                typename BasicJsonType::object_t, KeyType >::value,
+                                std::true_type,
+                                std::false_type >::type;
+
+// a naive helper to check if a type is an ordered_map (exploits the fact that
+// ordered_map inherits capacity() from std::vector)
+template <typename T>
+struct is_ordered_map
+{
+    using one = char;
+
+    struct two
+    {
+        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    };
+
+    template <typename C> static one test( decltype(&C::capacity) ) ;
+    template <typename C> static two test(...);
+
+    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+};
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
+T conditional_static_cast(U value)
+{
+    return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+T conditional_static_cast(U value)
+{
+    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);
+}
+
+template<bool Value>
+using bool_constant = std::integral_constant<bool, Value>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_c_string
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_c_string()
+{
+    using TUnExt = typename std::remove_extent<T>::type;
+    using TUnCVExt = typename std::remove_cv<TUnExt>::type;
+    using TUnPtr = typename std::remove_pointer<T>::type;
+    using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
+    return
+        (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
+        || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
+}
+
+}  // namespace impl
+
+// checks whether T is a [cv] char */[cv] char[] C string
+template<typename T>
+struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
+
+template<typename T>
+using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_transparent
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_transparent()
+{
+    return is_detected<detect_is_transparent, T>::value;
+}
+
+}  // namespace impl
+
+// checks whether T has a member named is_transparent
+template<typename T>
+struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstring> // strlen
+#include <string> // string
+#include <utility> // forward
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+inline std::size_t concat_length()
+{
+    return 0;
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest);
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest);
+
+template<typename... Args>
+inline std::size_t concat_length(const char /*c*/, Args&& ... rest)
+{
+    return 1 + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest)
+{
+    // cppcheck-suppress ignoredReturnValue
+    return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest)
+{
+    return str.size() + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType>
+inline void concat_into(OutStringType& /*out*/)
+{}
+
+template<typename StringType, typename Arg>
+using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template<typename OutStringType, typename Arg, typename... Args,
+         enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
+{
+    out.append(std::forward<Arg>(arg));
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
+{
+    out += std::forward<Arg>(arg);
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.begin(), arg.end());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.data(), arg.size());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType = std::string, typename... Args>
+inline OutStringType concat(Args && ... args)
+{
+    OutStringType str;
+    str.reserve(concat_length(std::forward<Args>(args)...));
+    concat_into(str, std::forward<Args>(args)...);
+    return str;
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+////////////////
+// exceptions //
+////////////////
+
+/// @brief general exception of the @ref basic_json class
+/// @sa https://json.nlohmann.me/api/basic_json/exception/
+class exception : public std::exception
+{
+  public:
+    /// returns the explanatory string
+    const char* what() const noexcept override
+    {
+        return m.what();
+    }
+
+    /// the id of the exception
+    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
+
+  protected:
+    JSON_HEDLEY_NON_NULL(3)
+    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
+
+    static std::string name(const std::string& ename, int id_)
+    {
+        return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
+    }
+
+    static std::string diagnostics(std::nullptr_t /*leaf_element*/)
+    {
+        return "";
+    }
+
+    template<typename BasicJsonType>
+    static std::string diagnostics(const BasicJsonType* leaf_element)
+    {
+#if JSON_DIAGNOSTICS
+        std::vector<std::string> tokens;
+        for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
+        {
+            switch (current->m_parent->type())
+            {
+                case value_t::array:
+                {
+                    for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
+                    {
+                        if (&current->m_parent->m_value.array->operator[](i) == current)
+                        {
+                            tokens.emplace_back(std::to_string(i));
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    for (const auto& element : *current->m_parent->m_value.object)
+                    {
+                        if (&element.second == current)
+                        {
+                            tokens.emplace_back(element.first.c_str());
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::null: // LCOV_EXCL_LINE
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:   // LCOV_EXCL_LINE
+                    break; // LCOV_EXCL_LINE
+            }
+        }
+
+        if (tokens.empty())
+        {
+            return "";
+        }
+
+        auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+                                   [](const std::string & a, const std::string & b)
+        {
+            return concat(a, '/', detail::escape(b));
+        });
+        return concat('(', str, ") ");
+#else
+        static_cast<void>(leaf_element);
+        return "";
+#endif
+    }
+
+  private:
+    /// an exception object as storage for error messages
+    std::runtime_error m;
+};
+
+/// @brief exception indicating a parse error
+/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
+class parse_error : public exception
+{
+  public:
+    /*!
+    @brief create a parse error exception
+    @param[in] id_       the id of the exception
+    @param[in] pos       the position where the error occurred (or with
+                         chars_read_total=0 if the position cannot be
+                         determined)
+    @param[in] what_arg  the explanatory string
+    @return parse_error object
+    */
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("parse_error", id_), "parse error",
+                               position_string(pos), ": ", exception::diagnostics(context), what_arg);
+        return {id_, pos.chars_read_total, w.c_str()};
+    }
+
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("parse_error", id_), "parse error",
+                               (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
+                               ": ", exception::diagnostics(context), what_arg);
+        return {id_, byte_, w.c_str()};
+    }
+
+    /*!
+    @brief byte index of the parse error
+
+    The byte index of the last read character in the input file.
+
+    @note For an input with n bytes, 1 is the index of the first character and
+          n+1 is the index of the terminating null byte or the end of file.
+          This also holds true when reading a byte vector (CBOR or MessagePack).
+    */
+    const std::size_t byte;
+
+  private:
+    parse_error(int id_, std::size_t byte_, const char* what_arg)
+        : exception(id_, what_arg), byte(byte_) {}
+
+    static std::string position_string(const position_t& pos)
+    {
+        return concat(" at line ", std::to_string(pos.lines_read + 1),
+                      ", column ", std::to_string(pos.chars_read_current_line));
+    }
+};
+
+/// @brief exception indicating errors with iterators
+/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
+class invalid_iterator : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    invalid_iterator(int id_, const char* what_arg)
+        : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating executing a member function with a wrong type
+/// @sa https://json.nlohmann.me/api/basic_json/type_error/
+class type_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating access out of the defined range
+/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
+class out_of_range : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating other library errors
+/// @sa https://json.nlohmann.me/api/basic_json/other_error/
+class other_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// dispatching helper struct
+template <class T> struct identity_tag {};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
+#include <experimental/filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::experimental::filesystem;
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#elif JSON_HAS_FILESYSTEM
+#include <filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::filesystem;
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#endif
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
+    }
+    n = nullptr;
+}
+
+// overloads for basic_json template parameters
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
+                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+                         int > = 0 >
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::boolean:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
+    }
+    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template <
+    typename BasicJsonType, typename StringType,
+    enable_if_t <
+        std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
+        && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
+        && !std::is_same<typename BasicJsonType::string_t, StringType>::value
+        && !is_json_ref<StringType>::value, int > = 0 >
+inline void from_json(const BasicJsonType& j, StringType& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+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>
+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>
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.clear();
+    std::transform(j.rbegin(), j.rend(),
+                   std::front_inserter(l), [](const BasicJsonType & i)
+    {
+        return i.template get<T>();
+    });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.resize(j.size());
+    std::transform(j.begin(), j.end(), std::begin(l),
+                   [](const BasicJsonType & elem)
+    {
+        return elem.template get<T>();
+    });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType>
+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*>();
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
+                          priority_tag<2> /*unused*/)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
+    j.template get<typename ConstructibleArrayType::value_type>(),
+    void())
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    ret.reserve(j.size());
+    std::transform(j.begin(), j.end(),
+                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+                                 priority_tag<0> /*unused*/)
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    std::transform(
+        j.begin(), j.end(), std::inserter(ret, end(ret)),
+        [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template < typename BasicJsonType, typename ConstructibleArrayType,
+           enable_if_t <
+               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
+               !is_basic_json<ConstructibleArrayType>::value,
+               int > = 0 >
+auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
+-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
+j.template get<typename ConstructibleArrayType::value_type>(),
+void())
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    from_json_array_impl(j, arr, priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename T, std::size_t... Idx >
+std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
+        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
+{
+    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
+}
+
+template < typename BasicJsonType, typename T, std::size_t N >
+auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
+-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
+    }
+
+    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
+}
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
+    }
+
+    ConstructibleObjectType ret;
+    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+    using value_type = typename ConstructibleObjectType::value_type;
+    std::transform(
+        inner_object->begin(), inner_object->end(),
+        std::inserter(ret, ret.begin()),
+        [](typename BasicJsonType::object_t::value_type const & p)
+    {
+        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
+    });
+    obj = std::move(ret);
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t <
+               std::is_arithmetic<ArithmeticType>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+               int > = 0 >
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        case value_t::boolean:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType, typename... Args, std::size_t... Idx>
+std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
+{
+    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
+}
+
+template < typename BasicJsonType, class A1, class A2 >
+std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
+{
+    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
+            std::forward<BasicJsonType>(j).at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+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> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
+{
+    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+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...> {});
+}
+
+template<typename BasicJsonType, typename TupleRelated>
+auto from_json(BasicJsonType&& j, TupleRelated&& t)
+-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
+           typename = enable_if_t < !std::is_constructible <
+                                        typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+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 >>
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+#endif
+
+struct from_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(const BasicJsonType& j, T&& val) const
+    noexcept(noexcept(from_json(j, std::forward<T>(val))))
+    -> decltype(from_json(j, std::forward<T>(val)))
+    {
+        return from_json(j, std::forward<T>(val));
+    }
+};
+
+}  // 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)
+{
+#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
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <iterator> // begin, end
+#include <string> // string
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+#include <iterator> // input_iterator_tag
+#include <string> // string, to_string
+#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
+
+#if JSON_HAS_RANGES
+    #include <ranges> // enable_borrowed_range
+#endif
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename string_type>
+void int_to_string( string_type& target, std::size_t value )
+{
+    // For ADL
+    using std::to_string;
+    target = to_string(value);
+}
+template<typename IteratorType> class iteration_proxy_value
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    using value_type = iteration_proxy_value;
+    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{};
+    /// an index for arrays (used to create key names)
+    std::size_t array_index = 0;
+    /// last stringified array index
+    mutable std::size_t array_index_last = 0;
+    /// a string representation of the array index
+    mutable string_type array_index_str = "0";
+    /// an empty string (to return a reference for primitive values)
+    string_type empty_str{};
+
+  public:
+    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)
+    const iteration_proxy_value& operator*() const
+    {
+        return *this;
+    }
+
+    /// increment operator (needed for range-based for)
+    iteration_proxy_value& operator++()
+    {
+        ++anchor;
+        ++array_index;
+
+        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
+    {
+        return anchor == o.anchor;
+    }
+
+    /// inequality operator (needed for range-based for)
+    bool operator!=(const iteration_proxy_value& o) const
+    {
+        return anchor != o.anchor;
+    }
+
+    /// return key of the iterator
+    const string_type& key() const
+    {
+        JSON_ASSERT(anchor.m_object != nullptr);
+
+        switch (anchor.m_object->type())
+        {
+            // use integer array index as key
+            case value_t::array:
+            {
+                if (array_index != array_index_last)
+                {
+                    int_to_string( array_index_str, array_index );
+                    array_index_last = array_index;
+                }
+                return array_index_str;
+            }
+
+            // use key from the object
+            case value_t::object:
+                return anchor.key();
+
+            // use an empty key for all primitive types
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return empty_str;
+        }
+    }
+
+    /// return value of the iterator
+    typename IteratorType::reference value() const
+    {
+        return anchor.value();
+    }
+};
+
+/// proxy class for the items() function
+template<typename IteratorType> class iteration_proxy
+{
+  private:
+    /// the container to iterate
+    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) {}
+
+    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() const noexcept
+    {
+        return iteration_proxy_value<IteratorType>(container->begin());
+    }
+
+    /// return iterator end (needed for range-based for)
+    iteration_proxy_value<IteratorType> end() const noexcept
+    {
+        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
+template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
+{
+    return i.key();
+}
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
+{
+    return i.value();
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// The Addition to the STD Namespace is required to add
+// Structured Bindings Support to the iteration_proxy_value class
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+namespace std
+{
+
+#if defined(__clang__)
+    // Fix: https://github.com/nlohmann/json/issues/1401
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template<typename IteratorType>
+class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
+            : public std::integral_constant<std::size_t, 2> {};
+
+template<std::size_t N, typename IteratorType>
+class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
+{
+  public:
+    using type = decltype(
+                     get<N>(std::declval <
+                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
+};
+#if defined(__clang__)
+    #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
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////
+// constructors //
+//////////////////
+
+/*
+ * Note all external_constructor<>::construct functions need to call
+ * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
+ * allocated value (e.g., a string). See bug issue
+ * https://github.com/nlohmann/json/issues/2865 for more information.
+ */
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::boolean;
+        j.m_value = b;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value = s;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value = std::move(s);
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleStringType,
+               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleStringType& str)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::binary>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::binary;
+        j.m_value = typename BasicJsonType::binary_t(b);
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::binary;
+        j.m_value = typename BasicJsonType::binary_t(std::move(b));
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_float;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_unsigned;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_integer;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = arr;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = std::move(arr);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleArrayType,
+               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = value_t::array;
+        j.m_value.array->reserve(arr.size());
+        for (const bool x : arr)
+        {
+            j.m_value.array->push_back(x);
+            j.set_parent(j.m_value.array->back());
+        }
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename T,
+             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+    static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = value_t::array;
+        j.m_value.array->resize(arr.size());
+        if (arr.size() > 0)
+        {
+            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
+        }
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value = obj;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value = std::move(obj);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleObjectType,
+               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, T b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template < typename BasicJsonType, typename BoolRef,
+           enable_if_t <
+               ((std::is_same<std::vector<bool>::reference, BoolRef>::value
+                 && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
+                || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
+                    && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
+                                      typename BasicJsonType::boolean_t >::value))
+               && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const BoolRef& 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>
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+    external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+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>
+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>
+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>
+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>
+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>
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+    external_constructor<value_t::array>::construct(j, e);
+}
+
+template < typename BasicJsonType, typename CompatibleArrayType,
+           enable_if_t < is_compatible_array_type<BasicJsonType,
+                         CompatibleArrayType>::value&&
+                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
+                         !is_basic_json<CompatibleArrayType>::value,
+                         int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType>
+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>
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+{
+    external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+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 >
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+    external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+    external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template <
+    typename BasicJsonType, typename T, std::size_t N,
+    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 >
+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 >
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+{
+    j = { p.first, p.second };
+}
+
+// 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>
+inline void to_json(BasicJsonType& j, const T& b)
+{
+    j = { {b.key(), b.value()} };
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+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>
+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>
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
+{
+    j = p.string();
+}
+#endif
+
+struct to_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)), void())
+    {
+        return to_json(j, std::forward<T>(val));
+    }
+};
+}  // 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)
+{
+#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
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @sa https://json.nlohmann.me/api/adl_serializer/
+template<typename ValueType, typename>
+struct adl_serializer
+{
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
+        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
+    {
+        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+    }
+
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j) noexcept(
+    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
+    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
+    {
+        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
+    }
+
+    /// @brief convert any value type to a JSON value
+    /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
+        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
+    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+    {
+        ::nlohmann::to_json(j, std::forward<TargetType>(val));
+    }
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/byte_container_with_subtype.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t, uint64_t
+#include <tuple> // tie
+#include <utility> // move
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief an internal type for a backed binary type
+/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+  public:
+    using container_type = BinaryType;
+    using subtype_type = std::uint64_t;
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype() noexcept(noexcept(container_type()))
+        : container_type()
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    bool operator==(const byte_container_with_subtype& rhs) const
+    {
+        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
+               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
+    }
+
+    bool operator!=(const byte_container_with_subtype& rhs) const
+    {
+        return !(rhs == *this);
+    }
+
+    /// @brief sets the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
+    void set_subtype(subtype_type subtype_) noexcept
+    {
+        m_subtype = subtype_;
+        m_has_subtype = true;
+    }
+
+    /// @brief return the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
+    constexpr subtype_type subtype() const noexcept
+    {
+        return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
+    }
+
+    /// @brief return whether the value has a subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
+    constexpr bool has_subtype() const noexcept
+    {
+        return m_has_subtype;
+    }
+
+    /// @brief clears the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
+    void clear_subtype() noexcept
+    {
+        m_subtype = 0;
+        m_has_subtype = false;
+    }
+
+  private:
+    subtype_type m_subtype = 0;
+    bool m_has_subtype = false;
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/hash.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// boost::hash_combine
+inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
+{
+    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
+    return seed;
+}
+
+/*!
+@brief hash a JSON value
+
+The hash function tries to rely on std::hash where possible. Furthermore, the
+type of the JSON value is taken into account to have different hash values for
+null, 0, 0U, and false, etc.
+
+@tparam BasicJsonType basic_json specialization
+@param j JSON value to hash
+@return hash value of j
+*/
+template<typename BasicJsonType>
+std::size_t hash(const BasicJsonType& j)
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+    const auto type = static_cast<std::size_t>(j.type());
+    switch (j.type())
+    {
+        case BasicJsonType::value_t::null:
+        case BasicJsonType::value_t::discarded:
+        {
+            return combine(type, 0);
+        }
+
+        case BasicJsonType::value_t::object:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j.items())
+            {
+                const auto h = std::hash<string_t> {}(element.key());
+                seed = combine(seed, h);
+                seed = combine(seed, hash(element.value()));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::array:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j)
+            {
+                seed = combine(seed, hash(element));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::string:
+        {
+            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::boolean:
+        {
+            const auto h = std::hash<bool> {}(j.template get<bool>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_integer:
+        {
+            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_unsigned:
+        {
+            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_float:
+        {
+            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::binary:
+        {
+            auto seed = combine(type, j.get_binary().size());
+            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
+            seed = combine(seed, h);
+            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
+            for (const auto byte : j.get_binary())
+            {
+                seed = combine(seed, std::hash<std::uint8_t> {}(byte));
+            }
+            return seed;
+        }
+
+        default:                   // LCOV_EXCL_LINE
+            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            return 0;              // LCOV_EXCL_LINE
+    }
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstdio> // snprintf
+#include <cstring> // memcpy
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+#ifndef JSON_NO_IO
+    #include <cstdio>   // FILE *
+    #include <istream>  // istream
+#endif                  // JSON_NO_IO
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// the supported input formats
+enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
+
+////////////////////
+// input adapters //
+////////////////////
+
+#ifndef JSON_NO_IO
+/*!
+Input adapter for stdio file access. This adapter read only 1 byte and do not use any
+ buffer. This adapter is a very low level adapter.
+*/
+class file_input_adapter
+{
+  public:
+    using char_type = char;
+
+    JSON_HEDLEY_NON_NULL(2)
+    explicit file_input_adapter(std::FILE* f) noexcept
+        : m_file(f)
+    {
+        JSON_ASSERT(m_file != nullptr);
+    }
+
+    // make class move-only
+    file_input_adapter(const file_input_adapter&) = delete;
+    file_input_adapter(file_input_adapter&&) noexcept = default;
+    file_input_adapter& operator=(const file_input_adapter&) = delete;
+    file_input_adapter& operator=(file_input_adapter&&) = delete;
+    ~file_input_adapter() = default;
+
+    std::char_traits<char>::int_type get_character() noexcept
+    {
+        return std::fgetc(m_file);
+    }
+
+  private:
+    /// the file pointer to read from
+    std::FILE* m_file;
+};
+
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input.  Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter
+{
+  public:
+    using char_type = char;
+
+    ~input_stream_adapter()
+    {
+        // clear stream flags; we use underlying streambuf I/O, do not
+        // maintain ifstream flags, except eof
+        if (is != nullptr)
+        {
+            is->clear(is->rdstate() & std::ios::eofbit);
+        }
+    }
+
+    explicit input_stream_adapter(std::istream& i)
+        : is(&i), sb(i.rdbuf())
+    {}
+
+    // delete because of pointer members
+    input_stream_adapter(const input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&&) = delete;
+
+    input_stream_adapter(input_stream_adapter&& rhs) noexcept
+        : is(rhs.is), sb(rhs.sb)
+    {
+        rhs.is = nullptr;
+        rhs.sb = nullptr;
+    }
+
+    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+    // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+    // end up as the same value, e.g. 0xFFFFFFFF.
+    std::char_traits<char>::int_type get_character()
+    {
+        auto res = sb->sbumpc();
+        // set eof manually, as we don't use the istream interface.
+        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
+        {
+            is->clear(is->rdstate() | std::ios::eofbit);
+        }
+        return res;
+    }
+
+  private:
+    /// the associated input stream
+    std::istream* is = nullptr;
+    std::streambuf* sb = nullptr;
+};
+#endif  // JSON_NO_IO
+
+// General-purpose iterator-based adapter. It might not be as fast as
+// theoretically possible for some containers, but it is extremely versatile.
+template<typename IteratorType>
+class iterator_input_adapter
+{
+  public:
+    using char_type = typename std::iterator_traits<IteratorType>::value_type;
+
+    iterator_input_adapter(IteratorType first, IteratorType last)
+        : current(std::move(first)), end(std::move(last))
+    {}
+
+    typename std::char_traits<char_type>::int_type get_character()
+    {
+        if (JSON_HEDLEY_LIKELY(current != end))
+        {
+            auto result = std::char_traits<char_type>::to_int_type(*current);
+            std::advance(current, 1);
+            return result;
+        }
+
+        return std::char_traits<char_type>::eof();
+    }
+
+  private:
+    IteratorType current;
+    IteratorType end;
+
+    template<typename BaseInputAdapter, size_t T>
+    friend struct wide_string_input_helper;
+
+    bool empty() const
+    {
+        return current == end;
+    }
+};
+
+
+template<typename BaseInputAdapter, size_t T>
+struct wide_string_input_helper;
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 4>
+{
+    // UTF-32
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-32 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (wc <= 0xFFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else if (wc <= 0x10FFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 4;
+            }
+            else
+            {
+                // unknown character
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+        }
+    }
+};
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 2>
+{
+    // UTF-16
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-16 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (0xD800 > wc || wc >= 0xE000)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else
+            {
+                if (JSON_HEDLEY_UNLIKELY(!input.empty()))
+                {
+                    const auto wc2 = static_cast<unsigned int>(input.get_character());
+                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
+                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
+                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
+                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
+                    utf8_bytes_filled = 4;
+                }
+                else
+                {
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                    utf8_bytes_filled = 1;
+                }
+            }
+        }
+    }
+};
+
+// Wraps another input apdater to convert wide character types into individual bytes.
+template<typename BaseInputAdapter, typename WideCharType>
+class wide_string_input_adapter
+{
+  public:
+    using char_type = char;
+
+    wide_string_input_adapter(BaseInputAdapter base)
+        : base_adapter(base) {}
+
+    typename std::char_traits<char>::int_type get_character() noexcept
+    {
+        // check if buffer needs to be filled
+        if (utf8_bytes_index == utf8_bytes_filled)
+        {
+            fill_buffer<sizeof(WideCharType)>();
+
+            JSON_ASSERT(utf8_bytes_filled > 0);
+            JSON_ASSERT(utf8_bytes_index == 0);
+        }
+
+        // use buffer
+        JSON_ASSERT(utf8_bytes_filled > 0);
+        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
+        return utf8_bytes[utf8_bytes_index++];
+    }
+
+  private:
+    BaseInputAdapter base_adapter;
+
+    template<size_t T>
+    void fill_buffer()
+    {
+        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
+    }
+
+    /// a buffer for UTF-8 bytes
+    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
+
+    /// index to the utf8_codes array for the next valid byte
+    std::size_t utf8_bytes_index = 0;
+    /// number of valid bytes in the utf8_codes array
+    std::size_t utf8_bytes_filled = 0;
+};
+
+
+template<typename IteratorType, typename Enable = void>
+struct iterator_input_adapter_factory
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using adapter_type = iterator_input_adapter<iterator_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(std::move(first), std::move(last));
+    }
+};
+
+template<typename T>
+struct is_iterator_of_multibyte
+{
+    using value_type = typename std::iterator_traits<T>::value_type;
+    enum
+    {
+        value = sizeof(value_type) > 1
+    };
+};
+
+template<typename IteratorType>
+struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using base_adapter_type = iterator_input_adapter<iterator_type>;
+    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(base_adapter_type(std::move(first), std::move(last)));
+    }
+};
+
+// General purpose iterator-based input
+template<typename IteratorType>
+typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
+{
+    using factory_type = iterator_input_adapter_factory<IteratorType>;
+    return factory_type::create(first, last);
+}
+
+// Convenience shorthand from container to iterator
+// Enables ADL on begin(container) and end(container)
+// Encloses the using declarations in namespace for not to leak them to outside scope
+
+namespace container_input_adapter_factory_impl
+{
+
+using std::begin;
+using std::end;
+
+template<typename ContainerType, typename Enable = void>
+struct container_input_adapter_factory {};
+
+template<typename ContainerType>
+struct container_input_adapter_factory< ContainerType,
+       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
+       {
+           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+           static adapter_type create(const ContainerType& container)
+{
+    return input_adapter(begin(container), end(container));
+}
+       };
+
+}  // namespace container_input_adapter_factory_impl
+
+template<typename ContainerType>
+typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
+{
+    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+#ifndef JSON_NO_IO
+// Special cases with fast paths
+inline file_input_adapter input_adapter(std::FILE* file)
+{
+    return file_input_adapter(file);
+}
+
+inline input_stream_adapter input_adapter(std::istream& stream)
+{
+    return input_stream_adapter(stream);
+}
+
+inline input_stream_adapter input_adapter(std::istream&& stream)
+{
+    return input_stream_adapter(stream);
+}
+#endif  // JSON_NO_IO
+
+using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
+
+// Null-delimited strings, and the like.
+template < typename CharT,
+           typename std::enable_if <
+               std::is_pointer<CharT>::value&&
+               !std::is_array<CharT>::value&&
+               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+               sizeof(typename std::remove_pointer<CharT>::type) == 1,
+               int >::type = 0 >
+contiguous_bytes_input_adapter input_adapter(CharT b)
+{
+    auto length = std::strlen(reinterpret_cast<const char*>(b));
+    const auto* ptr = reinterpret_cast<const char*>(b);
+    return input_adapter(ptr, ptr + length);
+}
+
+template<typename T, std::size_t N>
+auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+    return input_adapter(array, array + N);
+}
+
+// This class only handles inputs of input_buffer_adapter type.
+// It's required so that expressions like {ptr, len} can be implicitly cast
+// to the correct adapter.
+class span_input_adapter
+{
+  public:
+    template < typename CharT,
+               typename std::enable_if <
+                   std::is_pointer<CharT>::value&&
+                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+                   sizeof(typename std::remove_pointer<CharT>::type) == 1,
+                   int >::type = 0 >
+    span_input_adapter(CharT b, std::size_t l)
+        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
+
+    template<class IteratorType,
+             typename std::enable_if<
+                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+                 int>::type = 0>
+    span_input_adapter(IteratorType first, IteratorType last)
+        : ia(input_adapter(first, last)) {}
+
+    contiguous_bytes_input_adapter&& get()
+    {
+        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
+    }
+
+  private:
+    contiguous_bytes_input_adapter ia;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef>
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief SAX interface
+
+This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
+Each function is called in different situations while the input is parsed. The
+boolean return value informs the parser whether to continue processing the
+input.
+*/
+template<typename BasicJsonType>
+struct json_sax
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @brief a null value was read
+    @return whether parsing should proceed
+    */
+    virtual bool null() = 0;
+
+    /*!
+    @brief a boolean value was read
+    @param[in] val  boolean value
+    @return whether parsing should proceed
+    */
+    virtual bool boolean(bool val) = 0;
+
+    /*!
+    @brief an integer number was read
+    @param[in] val  integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_integer(number_integer_t val) = 0;
+
+    /*!
+    @brief an unsigned integer number was read
+    @param[in] val  unsigned integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_unsigned(number_unsigned_t val) = 0;
+
+    /*!
+    @brief a floating-point number was read
+    @param[in] val  floating-point value
+    @param[in] s    raw token value
+    @return whether parsing should proceed
+    */
+    virtual bool number_float(number_float_t val, const string_t& s) = 0;
+
+    /*!
+    @brief a string value was read
+    @param[in] val  string value
+    @return whether parsing should proceed
+    @note It is safe to move the passed string value.
+    */
+    virtual bool string(string_t& val) = 0;
+
+    /*!
+    @brief a binary value was read
+    @param[in] val  binary value
+    @return whether parsing should proceed
+    @note It is safe to move the passed binary value.
+    */
+    virtual bool binary(binary_t& val) = 0;
+
+    /*!
+    @brief the beginning of an object was read
+    @param[in] elements  number of object elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_object(std::size_t elements) = 0;
+
+    /*!
+    @brief an object key was read
+    @param[in] val  object key
+    @return whether parsing should proceed
+    @note It is safe to move the passed string.
+    */
+    virtual bool key(string_t& val) = 0;
+
+    /*!
+    @brief the end of an object was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_object() = 0;
+
+    /*!
+    @brief the beginning of an array was read
+    @param[in] elements  number of array elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_array(std::size_t elements) = 0;
+
+    /*!
+    @brief the end of an array was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_array() = 0;
+
+    /*!
+    @brief a parse error occurred
+    @param[in] position    the position in the input where the error occurs
+    @param[in] last_token  the last read token
+    @param[in] ex          an exception object describing the error
+    @return whether parsing should proceed (must return false)
+    */
+    virtual bool parse_error(std::size_t position,
+                             const std::string& last_token,
+                             const detail::exception& ex) = 0;
+
+    json_sax() = default;
+    json_sax(const json_sax&) = default;
+    json_sax(json_sax&&) noexcept = default;
+    json_sax& operator=(const json_sax&) = default;
+    json_sax& operator=(json_sax&&) noexcept = default;
+    virtual ~json_sax() = default;
+};
+
+
+namespace detail
+{
+/*!
+@brief SAX implementation to create a JSON value from SAX events
+
+This class implements the @ref json_sax interface and processes the SAX events
+to create a JSON value which makes it basically a DOM parser. The structure or
+hierarchy of the JSON value is managed by the stack `ref_stack` which contains
+a pointer to the respective array or object for each recursion depth.
+
+After successful parsing, the value that is passed by reference to the
+constructor contains the parsed value.
+
+@tparam BasicJsonType  the JSON type
+*/
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @param[in,out] r  reference to a JSON value that is manipulated while
+                       parsing
+    @param[in] allow_exceptions_  whether parse errors yield exceptions
+    */
+    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
+        : root(r), allow_exceptions(allow_exceptions_)
+    {}
+
+    // make class move-only
+    json_sax_dom_parser(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    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;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+    */
+    template<typename Value>
+    JSON_HEDLEY_RETURNS_NON_NULL
+    BasicJsonType* handle_value(Value&& v)
+    {
+        if (ref_stack.empty())
+        {
+            root = BasicJsonType(std::forward<Value>(v));
+            return &root;
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
+            return &(ref_stack.back()->m_value.array->back());
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_object());
+        JSON_ASSERT(object_element);
+        *object_element = BasicJsonType(std::forward<Value>(v));
+        return object_element;
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using parser_callback_t = typename BasicJsonType::parser_callback_t;
+    using parse_event_t = typename BasicJsonType::parse_event_t;
+
+    json_sax_dom_callback_parser(BasicJsonType& r,
+                                 const parser_callback_t cb,
+                                 const bool allow_exceptions_ = true)
+        : root(r), callback(cb), allow_exceptions(allow_exceptions_)
+    {
+        keep_stack.push_back(true);
+    }
+
+    // make class move-only
+    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_callback_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        // check callback for object start
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::object, true);
+        ref_stack.push_back(val.second);
+
+        // check object limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool key(string_t& val)
+    {
+        BasicJsonType k = BasicJsonType(val);
+
+        // check callback for key
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
+        key_keep_stack.push_back(keep);
+
+        // add discarded value at given key and store the reference for later
+        if (keep && ref_stack.back())
+        {
+            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
+        }
+
+        return true;
+    }
+
+    bool end_object()
+    {
+        if (ref_stack.back())
+        {
+            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+            {
+                // discard object
+                *ref_stack.back() = discarded;
+            }
+            else
+            {
+                ref_stack.back()->set_parents();
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
+        {
+            // remove discarded value
+            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
+            {
+                if (it->is_discarded())
+                {
+                    ref_stack.back()->erase(it);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::array, true);
+        ref_stack.push_back(val.second);
+
+        // check array limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool end_array()
+    {
+        bool keep = true;
+
+        if (ref_stack.back())
+        {
+            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
+            if (keep)
+            {
+                ref_stack.back()->set_parents();
+            }
+            else
+            {
+                // discard array
+                *ref_stack.back() = discarded;
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        // remove discarded value
+        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->pop_back();
+        }
+
+        return true;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @param[in] v  value to add to the JSON value we build during parsing
+    @param[in] skip_callback  whether we should skip calling the callback
+               function; this is required after start_array() and
+               start_object() SAX events, because otherwise we would call the
+               callback function with an empty array or object, respectively.
+
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+
+    @return pair of boolean (whether value should be kept) and pointer (to the
+            passed value in the ref_stack hierarchy; nullptr if not kept)
+    */
+    template<typename Value>
+    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
+    {
+        JSON_ASSERT(!keep_stack.empty());
+
+        // do not handle this value if we know it would be added to a discarded
+        // container
+        if (!keep_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // create value
+        auto value = BasicJsonType(std::forward<Value>(v));
+
+        // check callback
+        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+
+        // do not handle this value if we just learnt it shall be discarded
+        if (!keep)
+        {
+            return {false, nullptr};
+        }
+
+        if (ref_stack.empty())
+        {
+            root = std::move(value);
+            return {true, &root};
+        }
+
+        // skip this value if we already decided to skip the parent
+        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
+        if (!ref_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // we now only expect arrays and objects
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        // array
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->emplace_back(std::move(value));
+            return {true, &(ref_stack.back()->m_value.array->back())};
+        }
+
+        // object
+        JSON_ASSERT(ref_stack.back()->is_object());
+        // check if we should store an element for the current key
+        JSON_ASSERT(!key_keep_stack.empty());
+        const bool store_element = key_keep_stack.back();
+        key_keep_stack.pop_back();
+
+        if (!store_element)
+        {
+            return {false, nullptr};
+        }
+
+        JSON_ASSERT(object_element);
+        *object_element = std::move(value);
+        return {true, object_element};
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// stack to manage which values to keep
+    std::vector<bool> keep_stack {};
+    /// stack to manage which object keys to keep
+    std::vector<bool> key_keep_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// callback function
+    const parser_callback_t callback = nullptr;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+    /// a discarded value for the callback
+    BasicJsonType discarded = BasicJsonType::value_t::discarded;
+};
+
+template<typename BasicJsonType>
+class json_sax_acceptor
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    bool null()
+    {
+        return true;
+    }
+
+    bool boolean(bool /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_integer(number_integer_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool string(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool binary(binary_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool key(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool end_object()
+    {
+        return true;
+    }
+
+    bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool end_array()
+    {
+        return true;
+    }
+
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
+    {
+        return false;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/lexer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+  public:
+    /// token types for the parser
+    enum class token_type
+    {
+        uninitialized,    ///< indicating the scanner is uninitialized
+        literal_true,     ///< the `true` literal
+        literal_false,    ///< the `false` literal
+        literal_null,     ///< the `null` literal
+        value_string,     ///< a string -- use get_string() for actual value
+        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value
+        value_integer,    ///< a signed integer -- use get_number_integer() for actual value
+        value_float,      ///< an floating point number -- use get_number_float() for actual value
+        begin_array,      ///< the character for array begin `[`
+        begin_object,     ///< the character for object begin `{`
+        end_array,        ///< the character for array end `]`
+        end_object,       ///< the character for object end `}`
+        name_separator,   ///< the name separator `:`
+        value_separator,  ///< the value separator `,`
+        parse_error,      ///< indicating a parse error
+        end_of_input,     ///< indicating the end of the input buffer
+        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)
+    };
+
+    /// return name of values of type token_type (only used for errors)
+    JSON_HEDLEY_RETURNS_NON_NULL
+    JSON_HEDLEY_CONST
+    static const char* token_type_name(const token_type t) noexcept
+    {
+        switch (t)
+        {
+            case token_type::uninitialized:
+                return "<uninitialized>";
+            case token_type::literal_true:
+                return "true literal";
+            case token_type::literal_false:
+                return "false literal";
+            case token_type::literal_null:
+                return "null literal";
+            case token_type::value_string:
+                return "string literal";
+            case token_type::value_unsigned:
+            case token_type::value_integer:
+            case token_type::value_float:
+                return "number literal";
+            case token_type::begin_array:
+                return "'['";
+            case token_type::begin_object:
+                return "'{'";
+            case token_type::end_array:
+                return "']'";
+            case token_type::end_object:
+                return "'}'";
+            case token_type::name_separator:
+                return "':'";
+            case token_type::value_separator:
+                return "','";
+            case token_type::parse_error:
+                return "<parse error>";
+            case token_type::end_of_input:
+                return "end of input";
+            case token_type::literal_or_value:
+                return "'[', '{', or a literal";
+            // LCOV_EXCL_START
+            default: // catch non-enum values
+                return "unknown token";
+                // LCOV_EXCL_STOP
+        }
+    }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+        : ia(std::move(adapter))
+        , ignore_comments(ignore_comments_)
+        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+    {}
+
+    // delete because of pointer members
+    lexer(const lexer&) = delete;
+    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    lexer& operator=(lexer&) = delete;
+    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~lexer() = default;
+
+  private:
+    /////////////////////
+    // locales
+    /////////////////////
+
+    /// return the locale-dependent decimal point
+    JSON_HEDLEY_PURE
+    static char get_decimal_point() noexcept
+    {
+        const auto* loc = localeconv();
+        JSON_ASSERT(loc != nullptr);
+        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+    }
+
+    /////////////////////
+    // scan functions
+    /////////////////////
+
+    /*!
+    @brief get codepoint from 4 hex characters following `\u`
+
+    For input "\u c1 c2 c3 c4" the codepoint is:
+      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+    between the ASCII value of the character and the desired integer value.
+
+    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+            non-hex character)
+    */
+    int get_codepoint()
+    {
+        // this function only makes sense after reading `\u`
+        JSON_ASSERT(current == 'u');
+        int codepoint = 0;
+
+        const auto factors = { 12u, 8u, 4u, 0u };
+        for (const auto factor : factors)
+        {
+            get();
+
+            if (current >= '0' && current <= '9')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+            }
+            else if (current >= 'A' && current <= 'F')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+            }
+            else if (current >= 'a' && current <= 'f')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+            }
+            else
+            {
+                return -1;
+            }
+        }
+
+        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+        return codepoint;
+    }
+
+    /*!
+    @brief check if the next byte(s) are inside a given range
+
+    Adds the current byte and, for each passed range, reads a new byte and
+    checks if it is inside the range. If a violation was detected, set up an
+    error message and return false. Otherwise, return true.
+
+    @param[in] ranges  list of integers; interpreted as list of pairs of
+                       inclusive lower and upper bound, respectively
+
+    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+         1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+    @return true if and only if no range violation was detected
+    */
+    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
+    {
+        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+        add(current);
+
+        for (auto range = ranges.begin(); range != ranges.end(); ++range)
+        {
+            get();
+            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))
+            {
+                add(current);
+            }
+            else
+            {
+                error_message = "invalid string: ill-formed UTF-8 byte";
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /*!
+    @brief scan a string literal
+
+    This function scans a string according to Sect. 7 of RFC 8259. While
+    scanning, bytes are escaped and copied into buffer token_buffer. Then the
+    function returns successfully, token_buffer is *not* null-terminated (as it
+    may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+    string.
+
+    @return token_type::value_string if string could be successfully scanned,
+            token_type::parse_error otherwise
+
+    @note In case of errors, variable error_message contains a textual
+          description.
+    */
+    token_type scan_string()
+    {
+        // reset token_buffer (ignore opening quote)
+        reset();
+
+        // we entered the function by reading an open quote
+        JSON_ASSERT(current == '\"');
+
+        while (true)
+        {
+            // get next character
+            switch (get())
+            {
+                // end of file while parsing string
+                case std::char_traits<char_type>::eof():
+                {
+                    error_message = "invalid string: missing closing quote";
+                    return token_type::parse_error;
+                }
+
+                // closing quote
+                case '\"':
+                {
+                    return token_type::value_string;
+                }
+
+                // escapes
+                case '\\':
+                {
+                    switch (get())
+                    {
+                        // quotation mark
+                        case '\"':
+                            add('\"');
+                            break;
+                        // reverse solidus
+                        case '\\':
+                            add('\\');
+                            break;
+                        // solidus
+                        case '/':
+                            add('/');
+                            break;
+                        // backspace
+                        case 'b':
+                            add('\b');
+                            break;
+                        // form feed
+                        case 'f':
+                            add('\f');
+                            break;
+                        // line feed
+                        case 'n':
+                            add('\n');
+                            break;
+                        // carriage return
+                        case 'r':
+                            add('\r');
+                            break;
+                        // tab
+                        case 't':
+                            add('\t');
+                            break;
+
+                        // unicode escapes
+                        case 'u':
+                        {
+                            const int codepoint1 = get_codepoint();
+                            int codepoint = codepoint1; // start with codepoint1
+
+                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
+                            {
+                                error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+                                return token_type::parse_error;
+                            }
+
+                            // check if code point is a high surrogate
+                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
+                            {
+                                // expect next \uxxxx entry
+                                if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+                                {
+                                    const int codepoint2 = get_codepoint();
+
+                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
+                                    {
+                                        error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+                                        return token_type::parse_error;
+                                    }
+
+                                    // check if codepoint2 is a low surrogate
+                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+                                    {
+                                        // overwrite codepoint
+                                        codepoint = static_cast<int>(
+                                                        // high surrogate occupies the most significant 22 bits
+                                                        (static_cast<unsigned int>(codepoint1) << 10u)
+                                                        // low surrogate occupies the least significant 15 bits
+                                                        + static_cast<unsigned int>(codepoint2)
+                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise
+                                                        // in the result, so we have to subtract with:
+                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+                                                        - 0x35FDC00u);
+                                    }
+                                    else
+                                    {
+                                        error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                        return token_type::parse_error;
+                                    }
+                                }
+                                else
+                                {
+                                    error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                    return token_type::parse_error;
+                                }
+                            }
+                            else
+                            {
+                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
+                                {
+                                    error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+                                    return token_type::parse_error;
+                                }
+                            }
+
+                            // result of the above calculation yields a proper codepoint
+                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+                            // translate codepoint into bytes
+                            if (codepoint < 0x80)
+                            {
+                                // 1-byte characters: 0xxxxxxx (ASCII)
+                                add(static_cast<char_int_type>(codepoint));
+                            }
+                            else if (codepoint <= 0x7FF)
+                            {
+                                // 2-byte characters: 110xxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else if (codepoint <= 0xFFFF)
+                            {
+                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else
+                            {
+                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+
+                            break;
+                        }
+
+                        // other characters after escape
+                        default:
+                            error_message = "invalid string: forbidden character after backslash";
+                            return token_type::parse_error;
+                    }
+
+                    break;
+                }
+
+                // invalid control characters
+                case 0x00:
+                {
+                    error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+                    return token_type::parse_error;
+                }
+
+                case 0x01:
+                {
+                    error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+                    return token_type::parse_error;
+                }
+
+                case 0x02:
+                {
+                    error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+                    return token_type::parse_error;
+                }
+
+                case 0x03:
+                {
+                    error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+                    return token_type::parse_error;
+                }
+
+                case 0x04:
+                {
+                    error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+                    return token_type::parse_error;
+                }
+
+                case 0x05:
+                {
+                    error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+                    return token_type::parse_error;
+                }
+
+                case 0x06:
+                {
+                    error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+                    return token_type::parse_error;
+                }
+
+                case 0x07:
+                {
+                    error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+                    return token_type::parse_error;
+                }
+
+                case 0x08:
+                {
+                    error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+                    return token_type::parse_error;
+                }
+
+                case 0x09:
+                {
+                    error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+                    return token_type::parse_error;
+                }
+
+                case 0x0A:
+                {
+                    error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+                    return token_type::parse_error;
+                }
+
+                case 0x0B:
+                {
+                    error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+                    return token_type::parse_error;
+                }
+
+                case 0x0C:
+                {
+                    error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+                    return token_type::parse_error;
+                }
+
+                case 0x0D:
+                {
+                    error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+                    return token_type::parse_error;
+                }
+
+                case 0x0E:
+                {
+                    error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+                    return token_type::parse_error;
+                }
+
+                case 0x0F:
+                {
+                    error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+                    return token_type::parse_error;
+                }
+
+                case 0x10:
+                {
+                    error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+                    return token_type::parse_error;
+                }
+
+                case 0x11:
+                {
+                    error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+                    return token_type::parse_error;
+                }
+
+                case 0x12:
+                {
+                    error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+                    return token_type::parse_error;
+                }
+
+                case 0x13:
+                {
+                    error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+                    return token_type::parse_error;
+                }
+
+                case 0x14:
+                {
+                    error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+                    return token_type::parse_error;
+                }
+
+                case 0x15:
+                {
+                    error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+                    return token_type::parse_error;
+                }
+
+                case 0x16:
+                {
+                    error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+                    return token_type::parse_error;
+                }
+
+                case 0x17:
+                {
+                    error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+                    return token_type::parse_error;
+                }
+
+                case 0x18:
+                {
+                    error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+                    return token_type::parse_error;
+                }
+
+                case 0x19:
+                {
+                    error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+                    return token_type::parse_error;
+                }
+
+                case 0x1A:
+                {
+                    error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+                    return token_type::parse_error;
+                }
+
+                case 0x1B:
+                {
+                    error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+                    return token_type::parse_error;
+                }
+
+                case 0x1C:
+                {
+                    error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+                    return token_type::parse_error;
+                }
+
+                case 0x1D:
+                {
+                    error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+                    return token_type::parse_error;
+                }
+
+                case 0x1E:
+                {
+                    error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+                    return token_type::parse_error;
+                }
+
+                case 0x1F:
+                {
+                    error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+                    return token_type::parse_error;
+                }
+
+                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+                case 0x20:
+                case 0x21:
+                case 0x23:
+                case 0x24:
+                case 0x25:
+                case 0x26:
+                case 0x27:
+                case 0x28:
+                case 0x29:
+                case 0x2A:
+                case 0x2B:
+                case 0x2C:
+                case 0x2D:
+                case 0x2E:
+                case 0x2F:
+                case 0x30:
+                case 0x31:
+                case 0x32:
+                case 0x33:
+                case 0x34:
+                case 0x35:
+                case 0x36:
+                case 0x37:
+                case 0x38:
+                case 0x39:
+                case 0x3A:
+                case 0x3B:
+                case 0x3C:
+                case 0x3D:
+                case 0x3E:
+                case 0x3F:
+                case 0x40:
+                case 0x41:
+                case 0x42:
+                case 0x43:
+                case 0x44:
+                case 0x45:
+                case 0x46:
+                case 0x47:
+                case 0x48:
+                case 0x49:
+                case 0x4A:
+                case 0x4B:
+                case 0x4C:
+                case 0x4D:
+                case 0x4E:
+                case 0x4F:
+                case 0x50:
+                case 0x51:
+                case 0x52:
+                case 0x53:
+                case 0x54:
+                case 0x55:
+                case 0x56:
+                case 0x57:
+                case 0x58:
+                case 0x59:
+                case 0x5A:
+                case 0x5B:
+                case 0x5D:
+                case 0x5E:
+                case 0x5F:
+                case 0x60:
+                case 0x61:
+                case 0x62:
+                case 0x63:
+                case 0x64:
+                case 0x65:
+                case 0x66:
+                case 0x67:
+                case 0x68:
+                case 0x69:
+                case 0x6A:
+                case 0x6B:
+                case 0x6C:
+                case 0x6D:
+                case 0x6E:
+                case 0x6F:
+                case 0x70:
+                case 0x71:
+                case 0x72:
+                case 0x73:
+                case 0x74:
+                case 0x75:
+                case 0x76:
+                case 0x77:
+                case 0x78:
+                case 0x79:
+                case 0x7A:
+                case 0x7B:
+                case 0x7C:
+                case 0x7D:
+                case 0x7E:
+                case 0x7F:
+                {
+                    add(current);
+                    break;
+                }
+
+                // U+0080..U+07FF: bytes C2..DF 80..BF
+                case 0xC2:
+                case 0xC3:
+                case 0xC4:
+                case 0xC5:
+                case 0xC6:
+                case 0xC7:
+                case 0xC8:
+                case 0xC9:
+                case 0xCA:
+                case 0xCB:
+                case 0xCC:
+                case 0xCD:
+                case 0xCE:
+                case 0xCF:
+                case 0xD0:
+                case 0xD1:
+                case 0xD2:
+                case 0xD3:
+                case 0xD4:
+                case 0xD5:
+                case 0xD6:
+                case 0xD7:
+                case 0xD8:
+                case 0xD9:
+                case 0xDA:
+                case 0xDB:
+                case 0xDC:
+                case 0xDD:
+                case 0xDE:
+                case 0xDF:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+                case 0xE0:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+                case 0xE1:
+                case 0xE2:
+                case 0xE3:
+                case 0xE4:
+                case 0xE5:
+                case 0xE6:
+                case 0xE7:
+                case 0xE8:
+                case 0xE9:
+                case 0xEA:
+                case 0xEB:
+                case 0xEC:
+                case 0xEE:
+                case 0xEF:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+                case 0xED:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+                case 0xF0:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+                case 0xF1:
+                case 0xF2:
+                case 0xF3:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+                case 0xF4:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // remaining bytes (80..C1 and F5..FF) are ill-formed
+                default:
+                {
+                    error_message = "invalid string: ill-formed UTF-8 byte";
+                    return token_type::parse_error;
+                }
+            }
+        }
+    }
+
+    /*!
+     * @brief scan a comment
+     * @return whether comment could be scanned successfully
+     */
+    bool scan_comment()
+    {
+        switch (get())
+        {
+            // single-line comments skip input until a newline or EOF is read
+            case '/':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case '\n':
+                        case '\r':
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                            return true;
+
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            // multi-line comments skip input until */ is read
+            case '*':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                        {
+                            error_message = "invalid comment; missing closing '*/'";
+                            return false;
+                        }
+
+                        case '*':
+                        {
+                            switch (get())
+                            {
+                                case '/':
+                                    return true;
+
+                                default:
+                                {
+                                    unget();
+                                    continue;
+                                }
+                            }
+                        }
+
+                        default:
+                            continue;
+                    }
+                }
+            }
+
+            // unexpected character after reading '/'
+            default:
+            {
+                error_message = "invalid comment; expecting '/' or '*' after '/'";
+                return false;
+            }
+        }
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(float& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtof(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(double& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtod(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(long double& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtold(str, endptr);
+    }
+
+    /*!
+    @brief scan a number literal
+
+    This function scans a string according to Sect. 6 of RFC 8259.
+
+    The function is realized with a deterministic finite state machine derived
+    from the grammar described in RFC 8259. Starting in state "init", the
+    input is read and used to determined the next state. Only state "done"
+    accepts the number. State "error" is a trap state to model errors. In the
+    table below, "anything" means any character but the ones listed before.
+
+    state    | 0        | 1-9      | e E      | +       | -       | .        | anything
+    ---------|----------|----------|----------|---------|---------|----------|-----------
+    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]
+    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]
+    zero     | done     | done     | exponent | done    | done    | decimal1 | done
+    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done
+    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]
+    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done
+    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]
+    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]
+    any2     | any2     | any2     | done     | done    | done    | done     | done
+
+    The state machine is realized with one label per state (prefixed with
+    "scan_number_") and `goto` statements between them. The state machine
+    contains cycles, but any cycle can be left when EOF is read. Therefore,
+    the function is guaranteed to terminate.
+
+    During scanning, the read bytes are stored in token_buffer. This string is
+    then converted to a signed integer, an unsigned integer, or a
+    floating-point number.
+
+    @return token_type::value_unsigned, token_type::value_integer, or
+            token_type::value_float if number could be successfully scanned,
+            token_type::parse_error otherwise
+
+    @note The scanner is independent of the current locale. Internally, the
+          locale's decimal point is used instead of `.` to work with the
+          locale-dependent converters.
+    */
+    token_type scan_number()  // lgtm [cpp/use-of-goto]
+    {
+        // reset token_buffer to store the number's bytes
+        reset();
+
+        // the type of the parsed number; initially set to unsigned; will be
+        // changed if minus sign, decimal point or exponent is read
+        token_type number_type = token_type::value_unsigned;
+
+        // state (init): we just found out we need to scan a number
+        switch (current)
+        {
+            case '-':
+            {
+                add(current);
+                goto scan_number_minus;
+            }
+
+            case '0':
+            {
+                add(current);
+                goto scan_number_zero;
+            }
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            // all other characters are rejected outside scan_number()
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+scan_number_minus:
+        // state: we just parsed a leading minus sign
+        number_type = token_type::value_integer;
+        switch (get())
+        {
+            case '0':
+            {
+                add(current);
+                goto scan_number_zero;
+            }
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after '-'";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_zero:
+        // state: we just parse a zero (maybe with a leading minus sign)
+        switch (get())
+        {
+            case '.':
+            {
+                add(decimal_point_char);
+                goto scan_number_decimal1;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_any1:
+        // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            case '.':
+            {
+                add(decimal_point_char);
+                goto scan_number_decimal1;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_decimal1:
+        // state: we just parsed a decimal point
+        number_type = token_type::value_float;
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_decimal2;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after '.'";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_decimal2:
+        // we just parsed at least one number after a decimal point
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_decimal2;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_exponent:
+        // we just parsed an exponent
+        number_type = token_type::value_float;
+        switch (get())
+        {
+            case '+':
+            case '-':
+            {
+                add(current);
+                goto scan_number_sign;
+            }
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+            {
+                error_message =
+                    "invalid number; expected '+', '-', or digit after exponent";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_sign:
+        // we just parsed an exponent sign
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after exponent sign";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_any2:
+        // we just parsed a number after the exponent or exponent sign
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_done:
+        // unget the character after the number (we only read it to know that
+        // we are done scanning a number)
+        unget();
+
+        char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        errno = 0;
+
+        // try to parse integers first and fall back to floats
+        if (number_type == token_type::value_unsigned)
+        {
+            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_unsigned = static_cast<number_unsigned_t>(x);
+                if (value_unsigned == x)
+                {
+                    return token_type::value_unsigned;
+                }
+            }
+        }
+        else if (number_type == token_type::value_integer)
+        {
+            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_integer = static_cast<number_integer_t>(x);
+                if (value_integer == x)
+                {
+                    return token_type::value_integer;
+                }
+            }
+        }
+
+        // this code is reached if we parse a floating-point number or if an
+        // integer conversion above failed
+        strtof(value_float, token_buffer.data(), &endptr);
+
+        // we checked the number format before
+        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+        return token_type::value_float;
+    }
+
+    /*!
+    @param[in] literal_text  the literal text to expect
+    @param[in] length        the length of the passed literal text
+    @param[in] return_type   the token type to return on success
+    */
+    JSON_HEDLEY_NON_NULL(2)
+    token_type scan_literal(const char_type* literal_text, const std::size_t length,
+                            token_type return_type)
+    {
+        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+        for (std::size_t i = 1; i < length; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+            {
+                error_message = "invalid literal";
+                return token_type::parse_error;
+            }
+        }
+        return return_type;
+    }
+
+    /////////////////////
+    // input management
+    /////////////////////
+
+    /// reset token_buffer; current character is beginning of token
+    void reset() noexcept
+    {
+        token_buffer.clear();
+        token_string.clear();
+        token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+    }
+
+    /*
+    @brief get next character from the input
+
+    This function provides the interface to the used input adapter. It does
+    not throw in case the input reached EOF, but returns a
+    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters
+    for use in error messages.
+
+    @return character read from the input
+    */
+    char_int_type get()
+    {
+        ++position.chars_read_total;
+        ++position.chars_read_current_line;
+
+        if (next_unget)
+        {
+            // just reset the next_unget variable and work with current
+            next_unget = false;
+        }
+        else
+        {
+            current = ia.get_character();
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+        }
+
+        if (current == '\n')
+        {
+            ++position.lines_read;
+            position.chars_read_current_line = 0;
+        }
+
+        return current;
+    }
+
+    /*!
+    @brief unget current character (read it again on next get)
+
+    We implement unget by setting variable next_unget to true. The input is not
+    changed - we just simulate ungetting by modifying chars_read_total,
+    chars_read_current_line, and token_string. The next call to get() will
+    behave as if the unget character is read again.
+    */
+    void unget()
+    {
+        next_unget = true;
+
+        --position.chars_read_total;
+
+        // in case we "unget" a newline, we have to also decrement the lines_read
+        if (position.chars_read_current_line == 0)
+        {
+            if (position.lines_read > 0)
+            {
+                --position.lines_read;
+            }
+        }
+        else
+        {
+            --position.chars_read_current_line;
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            JSON_ASSERT(!token_string.empty());
+            token_string.pop_back();
+        }
+    }
+
+    /// add a character to token_buffer
+    void add(char_int_type c)
+    {
+        token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+    }
+
+  public:
+    /////////////////////
+    // value getters
+    /////////////////////
+
+    /// return integer value
+    constexpr number_integer_t get_number_integer() const noexcept
+    {
+        return value_integer;
+    }
+
+    /// return unsigned integer value
+    constexpr number_unsigned_t get_number_unsigned() const noexcept
+    {
+        return value_unsigned;
+    }
+
+    /// return floating-point value
+    constexpr number_float_t get_number_float() const noexcept
+    {
+        return value_float;
+    }
+
+    /// return current string value (implicitly resets the token; useful only once)
+    string_t& get_string()
+    {
+        return token_buffer;
+    }
+
+    /////////////////////
+    // diagnostics
+    /////////////////////
+
+    /// return position of last read token
+    constexpr position_t get_position() const noexcept
+    {
+        return position;
+    }
+
+    /// return the last read token (for errors only).  Will never contain EOF
+    /// (an arbitrary value that is not a valid char value, often -1), because
+    /// 255 may legitimately occur.  May contain NUL, which should be escaped.
+    std::string get_token_string() const
+    {
+        // escape control characters
+        std::string result;
+        for (const auto c : token_string)
+        {
+            if (static_cast<unsigned char>(c) <= '\x1F')
+            {
+                // escape control characters
+                std::array<char, 9> cs{{}};
+                static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                result += cs.data();
+            }
+            else
+            {
+                // add character as is
+                result.push_back(static_cast<std::string::value_type>(c));
+            }
+        }
+
+        return result;
+    }
+
+    /// return syntax error message
+    JSON_HEDLEY_RETURNS_NON_NULL
+    constexpr const char* get_error_message() const noexcept
+    {
+        return error_message;
+    }
+
+    /////////////////////
+    // actual scanner
+    /////////////////////
+
+    /*!
+    @brief skip the UTF-8 byte order mark
+    @return true iff there is no BOM or the correct BOM has been skipped
+    */
+    bool skip_bom()
+    {
+        if (get() == 0xEF)
+        {
+            // check if we completely parse the BOM
+            return get() == 0xBB && get() == 0xBF;
+        }
+
+        // the first character is not the beginning of the BOM; unget it to
+        // process is later
+        unget();
+        return true;
+    }
+
+    void skip_whitespace()
+    {
+        do
+        {
+            get();
+        }
+        while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+    }
+
+    token_type scan()
+    {
+        // initially, skip the BOM
+        if (position.chars_read_total == 0 && !skip_bom())
+        {
+            error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+            return token_type::parse_error;
+        }
+
+        // read next character and ignore whitespace
+        skip_whitespace();
+
+        // ignore comments
+        while (ignore_comments && current == '/')
+        {
+            if (!scan_comment())
+            {
+                return token_type::parse_error;
+            }
+
+            // skip following whitespace
+            skip_whitespace();
+        }
+
+        switch (current)
+        {
+            // structural characters
+            case '[':
+                return token_type::begin_array;
+            case ']':
+                return token_type::end_array;
+            case '{':
+                return token_type::begin_object;
+            case '}':
+                return token_type::end_object;
+            case ':':
+                return token_type::name_separator;
+            case ',':
+                return token_type::value_separator;
+
+            // literals
+            case 't':
+            {
+                std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
+                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+            }
+            case 'f':
+            {
+                std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
+                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+            }
+            case 'n':
+            {
+                std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
+                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
+            }
+
+            // string
+            case '\"':
+                return scan_string();
+
+            // number
+            case '-':
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                return scan_number();
+
+            // end of input (the null byte is needed when parsing from
+            // string literals)
+            case '\0':
+            case std::char_traits<char_type>::eof():
+                return token_type::end_of_input;
+
+            // error
+            default:
+                error_message = "invalid literal";
+                return token_type::parse_error;
+        }
+    }
+
+  private:
+    /// input adapter
+    InputAdapterType ia;
+
+    /// whether comments should be ignored (true) or signaled as errors (false)
+    const bool ignore_comments = false;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// whether the next get() call should just return current
+    bool next_unget = false;
+
+    /// the start position of the current token
+    position_t position {};
+
+    /// raw input token string (for error messages)
+    std::vector<char_type> token_string {};
+
+    /// buffer for variable-length tokens (numbers, strings)
+    string_t token_buffer {};
+
+    /// a description of occurred lexer errors
+    const char* error_message = "";
+
+    // number values
+    number_integer_t value_integer = 0;
+    number_unsigned_t value_unsigned = 0;
+    number_float_t value_float = 0;
+
+    /// the decimal point
+    const char_int_type decimal_point_char = '.';
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // size_t
+#include <utility> // declval
+#include <string> // string
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using null_function_t = decltype(std::declval<T&>().null());
+
+template<typename T>
+using boolean_function_t =
+    decltype(std::declval<T&>().boolean(std::declval<bool>()));
+
+template<typename T, typename Integer>
+using number_integer_function_t =
+    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
+
+template<typename T, typename Unsigned>
+using number_unsigned_function_t =
+    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
+
+template<typename T, typename Float, typename String>
+using number_float_function_t = decltype(std::declval<T&>().number_float(
+                                    std::declval<Float>(), std::declval<const String&>()));
+
+template<typename T, typename String>
+using string_function_t =
+    decltype(std::declval<T&>().string(std::declval<String&>()));
+
+template<typename T, typename Binary>
+using binary_function_t =
+    decltype(std::declval<T&>().binary(std::declval<Binary&>()));
+
+template<typename T>
+using start_object_function_t =
+    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
+
+template<typename T, typename String>
+using key_function_t =
+    decltype(std::declval<T&>().key(std::declval<String&>()));
+
+template<typename T>
+using end_object_function_t = decltype(std::declval<T&>().end_object());
+
+template<typename T>
+using start_array_function_t =
+    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
+
+template<typename T>
+using end_array_function_t = decltype(std::declval<T&>().end_array());
+
+template<typename T, typename Exception>
+using parse_error_function_t = decltype(std::declval<T&>().parse_error(
+        std::declval<std::size_t>(), std::declval<const std::string&>(),
+        std::declval<const Exception&>()));
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static constexpr bool value =
+        is_detected_exact<bool, null_function_t, SAX>::value &&
+        is_detected_exact<bool, boolean_function_t, SAX>::value &&
+        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
+        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
+        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
+        is_detected_exact<bool, start_object_function_t, SAX>::value &&
+        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, end_object_function_t, SAX>::value &&
+        is_detected_exact<bool, start_array_function_t, SAX>::value &&
+        is_detected_exact<bool, end_array_function_t, SAX>::value &&
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
+};
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax_static_asserts
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
+                  "Missing/invalid function: bool null()");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(
+        is_detected_exact<bool, number_integer_function_t, SAX,
+        number_integer_t>::value,
+        "Missing/invalid function: bool number_integer(number_integer_t)");
+    static_assert(
+        is_detected_exact<bool, number_unsigned_function_t, SAX,
+        number_unsigned_t>::value,
+        "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
+    static_assert(is_detected_exact<bool, number_float_function_t, SAX,
+                  number_float_t, string_t>::value,
+                  "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
+    static_assert(
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value,
+        "Missing/invalid function: bool string(string_t&)");
+    static_assert(
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
+        "Missing/invalid function: bool binary(binary_t&)");
+    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_object(std::size_t)");
+    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
+                  "Missing/invalid function: bool key(string_t&)");
+    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_object()");
+    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_array(std::size_t)");
+    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_array()");
+    static_assert(
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
+        "Missing/invalid function: bool parse_error(std::size_t, const "
+        "std::string&, const exception&)");
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+    error,   ///< throw a parse_error exception in case of a tag
+    ignore,  ///< ignore tags
+    store    ///< store tags as binary type
+};
+
+/*!
+@brief determine system byte order
+
+@return true if and only if system's byte order is little endian
+
+@note from https://stackoverflow.com/a/1001328/266378
+*/
+static inline bool little_endianness(int num = 1) noexcept
+{
+    return *reinterpret_cast<char*>(&num) == 1;
+}
+
+
+///////////////////
+// binary reader //
+///////////////////
+
+/*!
+@brief deserialization of CBOR, MessagePack, and UBJSON values
+*/
+template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>
+class binary_reader
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using json_sax_t = SAX;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    /*!
+    @brief create a binary reader
+
+    @param[in] adapter  input adapter to read from
+    */
+    explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+    }
+
+    // make class move-only
+    binary_reader(const binary_reader&) = delete;
+    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    binary_reader& operator=(const binary_reader&) = delete;
+    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~binary_reader() = default;
+
+    /*!
+    @param[in] format  the binary format to parse
+    @param[in] sax_    a SAX event processor
+    @param[in] strict  whether to expect the input to be consumed completed
+    @param[in] tag_handler  how to treat CBOR tags
+
+    @return whether parsing was successful
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool sax_parse(const input_format_t format,
+                   json_sax_t* sax_,
+                   const bool strict = true,
+                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        sax = sax_;
+        bool result = false;
+
+        switch (format)
+        {
+            case input_format_t::bson:
+                result = parse_bson_internal();
+                break;
+
+            case input_format_t::cbor:
+                result = parse_cbor_internal(true, tag_handler);
+                break;
+
+            case input_format_t::msgpack:
+                result = parse_msgpack_internal();
+                break;
+
+            case input_format_t::ubjson:
+            case input_format_t::bjdata:
+                result = parse_ubjson_internal();
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        // strict mode: next byte must be EOF
+        if (result && strict)
+        {
+            if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)
+            {
+                get_ignore_noop();
+            }
+            else
+            {
+                get();
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
+                                        exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
+            }
+        }
+
+        return result;
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @brief Reads in a BSON-object and passes it to the SAX-parser.
+    @return whether a valid BSON-value was passed to the SAX parser
+    */
+    bool parse_bson_internal()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
+        {
+            return false;
+        }
+
+        return sax->end_object();
+    }
+
+    /*!
+    @brief Parses a C-style string from the BSON input.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @return `true` if the \x00-byte indicating the end of the string was
+             encountered before the EOF; false` indicates an unexpected EOF.
+    */
+    bool get_bson_cstr(string_t& result)
+    {
+        auto out = std::back_inserter(result);
+        while (true)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
+            {
+                return false;
+            }
+            if (current == 0x00)
+            {
+                return true;
+            }
+            *out++ = static_cast<typename string_t::value_type>(current);
+        }
+    }
+
+    /*!
+    @brief Parses a zero-terminated string of length @a len from the BSON
+           input.
+    @param[in] len  The length (including the zero-byte at the end) of the
+                    string to be read.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 1
+    @return `true` if the string was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_string(const NumberType len, string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 1))
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
+        }
+
+        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
+    }
+
+    /*!
+    @brief Parses a byte array input of length @a len from the BSON input.
+    @param[in] len  The length of the byte array to be read.
+    @param[in,out] result  A reference to the binary variable where the read
+                            array is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 0
+    @return `true` if the byte array was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_binary(const NumberType len, binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 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_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
+        }
+
+        // All BSON binary values have a subtype
+        std::uint8_t subtype{};
+        get_number<std::uint8_t>(input_format_t::bson, subtype);
+        result.set_subtype(subtype);
+
+        return get_binary(input_format_t::bson, len, result);
+    }
+
+    /*!
+    @brief Read a BSON document element of the given @a element_type.
+    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
+    @param[in] element_type_parse_position The position in the input stream,
+               where the `element_type` was read.
+    @warning Not all BSON element types are supported yet. An unsupported
+             @a element_type will give rise to a parse_error.114:
+             Unsupported BSON record type 0x...
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_internal(const char_int_type element_type,
+                                     const std::size_t element_type_parse_position)
+    {
+        switch (element_type)
+        {
+            case 0x01: // double
+            {
+                double number{};
+                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0x02: // string
+            {
+                std::int32_t len{};
+                string_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
+            }
+
+            case 0x03: // object
+            {
+                return parse_bson_internal();
+            }
+
+            case 0x04: // array
+            {
+                return parse_bson_array();
+            }
+
+            case 0x05: // binary
+            {
+                std::int32_t len{};
+                binary_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
+            }
+
+            case 0x08: // boolean
+            {
+                return sax->boolean(get() != 0);
+            }
+
+            case 0x0A: // null
+            {
+                return sax->null();
+            }
+
+            case 0x10: // int32
+            {
+                std::int32_t value{};
+                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            case 0x12: // int64
+            {
+                std::int64_t value{};
+                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            default: // anything else not supported (yet)
+            {
+                std::array<char, 3> cr{{}};
+                static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                std::string cr_str{cr.data()};
+                return sax->parse_error(element_type_parse_position, cr_str,
+                                        parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief Read a BSON element list (as specified in the BSON-spec)
+
+    The same binary layout is used for objects and arrays, hence it must be
+    indicated with the argument @a is_array which one is expected
+    (true --> array, false --> object).
+
+    @param[in] is_array Determines if the element list being read is to be
+                        treated as an object (@a is_array == false), or as an
+                        array (@a is_array == true).
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_list(const bool is_array)
+    {
+        string_t key;
+
+        while (auto element_type = get())
+        {
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
+            {
+                return false;
+            }
+
+            const std::size_t element_type_parse_position = chars_read;
+            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
+            {
+                return false;
+            }
+
+            if (!is_array && !sax->key(key))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
+            {
+                return false;
+            }
+
+            // get_bson_cstr only appends
+            key.clear();
+        }
+
+        return true;
+    }
+
+    /*!
+    @brief Reads an array from the BSON input and passes it to the SAX-parser.
+    @return whether a valid BSON-array was passed to the SAX parser
+    */
+    bool parse_bson_array()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
+        {
+            return false;
+        }
+
+        return sax->end_array();
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true) or whether the last read character should
+                         be considered instead (false)
+    @param[in] tag_handler how CBOR tags should be treated
+
+    @return whether a valid CBOR value was passed to the SAX parser
+    */
+    bool parse_cbor_internal(const bool get_char,
+                             const cbor_tag_handler_t tag_handler)
+    {
+        switch (get_char ? get() : current)
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::cbor, "value");
+
+            // Integer 0x00..0x17 (0..23)
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0A:
+            case 0x0B:
+            case 0x0C:
+            case 0x0D:
+            case 0x0E:
+            case 0x0F:
+            case 0x10:
+            case 0x11:
+            case 0x12:
+            case 0x13:
+            case 0x14:
+            case 0x15:
+            case 0x16:
+            case 0x17:
+                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+            case 0x18: // Unsigned integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x19: // Unsigned integer (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            // Negative integer -1-0x00..-1-0x17 (-1..-24)
+            case 0x20:
+            case 0x21:
+            case 0x22:
+            case 0x23:
+            case 0x24:
+            case 0x25:
+            case 0x26:
+            case 0x27:
+            case 0x28:
+            case 0x29:
+            case 0x2A:
+            case 0x2B:
+            case 0x2C:
+            case 0x2D:
+            case 0x2E:
+            case 0x2F:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
+
+            case 0x38: // Negative integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
+                        - static_cast<number_integer_t>(number));
+            }
+
+            // Binary data (0x00..0x17 bytes follow)
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            case 0x58: // Binary data (one-byte uint8_t for n follows)
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            case 0x5F: // Binary data (indefinite length)
+            {
+                binary_t b;
+                return get_cbor_binary(b) && sax->binary(b);
+            }
+
+            // UTF-8 string (0x00..0x17 bytes follow)
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+            case 0x7F: // UTF-8 string (indefinite length)
+            {
+                string_t s;
+                return get_cbor_string(s) && sax->string(s);
+            }
+
+            // array (0x00..0x17 data items follow)
+            case 0x80:
+            case 0x81:
+            case 0x82:
+            case 0x83:
+            case 0x84:
+            case 0x85:
+            case 0x86:
+            case 0x87:
+            case 0x88:
+            case 0x89:
+            case 0x8A:
+            case 0x8B:
+            case 0x8C:
+            case 0x8D:
+            case 0x8E:
+            case 0x8F:
+            case 0x90:
+            case 0x91:
+            case 0x92:
+            case 0x93:
+            case 0x94:
+            case 0x95:
+            case 0x96:
+            case 0x97:
+                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)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x99: // array (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            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(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(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9F: // array (indefinite length)
+                return get_cbor_array(static_cast<std::size_t>(-1), tag_handler);
+
+            // map (0x00..0x17 pairs of data items follow)
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+                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)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xB9: // map (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            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(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(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBF: // map (indefinite length)
+                return get_cbor_object(static_cast<std::size_t>(-1), tag_handler);
+
+            case 0xC6: // tagged item
+            case 0xC7:
+            case 0xC8:
+            case 0xC9:
+            case 0xCA:
+            case 0xCB:
+            case 0xCC:
+            case 0xCD:
+            case 0xCE:
+            case 0xCF:
+            case 0xD0:
+            case 0xD1:
+            case 0xD2:
+            case 0xD3:
+            case 0xD4:
+            case 0xD8: // tagged item (1 bytes follow)
+            case 0xD9: // tagged item (2 bytes follow)
+            case 0xDA: // tagged item (4 bytes follow)
+            case 0xDB: // tagged item (8 bytes follow)
+            {
+                switch (tag_handler)
+                {
+                    case cbor_tag_handler_t::error:
+                    {
+                        auto last_token = get_token_string();
+                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                                exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+                    }
+
+                    case cbor_tag_handler_t::ignore:
+                    {
+                        // ignore binary subtype
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            default:
+                                break;
+                        }
+                        return parse_cbor_internal(true, tag_handler);
+                    }
+
+                    case cbor_tag_handler_t::store:
+                    {
+                        binary_t b;
+                        // use binary subtype and store in binary container
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            default:
+                                return parse_cbor_internal(true, tag_handler);
+                        }
+                        get();
+                        return get_cbor_binary(b) && sax->binary(b);
+                    }
+
+                    default:                 // LCOV_EXCL_LINE
+                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                        return false;        // LCOV_EXCL_LINE
+                }
+            }
+
+            case 0xF4: // false
+                return sax->boolean(false);
+
+            case 0xF5: // true
+                return sax->boolean(true);
+
+            case 0xF6: // null
+                return sax->null();
+
+            case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+            {
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // code from RFC 7049, Appendix D, Figure 3:
+                // As half-precision floating-point numbers were only added
+                // to IEEE 754 in 2008, today's programming platforms often
+                // still only have limited support for them. It is very
+                // easy to include at least decoding support for them even
+                // without such support. An example of a small decoder for
+                // half-precision floating-point numbers in the C language
+                // is shown in Fig. 3.
+                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+            {
+                float number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+            {
+                double number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            default: // anything else (0xFF is handled inside the other types)
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a CBOR string
+
+    This function first reads starting bytes to determine the expected
+    string length and then copies this number of bytes into a string.
+    Additionally, CBOR's strings with indefinite lengths are supported.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_cbor_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // UTF-8 string (0x00..0x17 bytes follow)
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            {
+                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7F: // UTF-8 string (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    string_t chunk;
+                    if (!get_cbor_string(chunk))
+                    {
+                        return false;
+                    }
+                    result.append(chunk);
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a CBOR byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into the byte array.
+    Additionally, CBOR's byte arrays with indefinite lengths are supported.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_cbor_binary(binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // Binary data (0x00..0x17 bytes follow)
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            {
+                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x58: // Binary data (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5F: // Binary data (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    binary_t chunk;
+                    if (!get_cbor_binary(chunk))
+                    {
+                        return false;
+                    }
+                    result.insert(result.end(), chunk.begin(), chunk.end());
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array or static_cast<std::size_t>(-1) for an
+                    array of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether array creation completed
+    */
+    bool get_cbor_array(const std::size_t len,
+                        const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        if (len != static_cast<std::size_t>(-1))
+        {
+            for (std::size_t i = 0; i < len; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+        else
+        {
+            while (get() != 0xFF)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object or static_cast<std::size_t>(-1) for an
+                    object of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether object creation completed
+    */
+    bool get_cbor_object(const std::size_t len,
+                         const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        if (len != 0)
+        {
+            string_t key;
+            if (len != static_cast<std::size_t>(-1))
+            {
+                for (std::size_t i = 0; i < len; ++i)
+                {
+                    get();
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                while (get() != 0xFF)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    /*!
+    @return whether a valid MessagePack value was passed to the SAX parser
+    */
+    bool parse_msgpack_internal()
+    {
+        switch (get())
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::msgpack, "value");
+
+            // positive fixint
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0A:
+            case 0x0B:
+            case 0x0C:
+            case 0x0D:
+            case 0x0E:
+            case 0x0F:
+            case 0x10:
+            case 0x11:
+            case 0x12:
+            case 0x13:
+            case 0x14:
+            case 0x15:
+            case 0x16:
+            case 0x17:
+            case 0x18:
+            case 0x19:
+            case 0x1A:
+            case 0x1B:
+            case 0x1C:
+            case 0x1D:
+            case 0x1E:
+            case 0x1F:
+            case 0x20:
+            case 0x21:
+            case 0x22:
+            case 0x23:
+            case 0x24:
+            case 0x25:
+            case 0x26:
+            case 0x27:
+            case 0x28:
+            case 0x29:
+            case 0x2A:
+            case 0x2B:
+            case 0x2C:
+            case 0x2D:
+            case 0x2E:
+            case 0x2F:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+            case 0x38:
+            case 0x39:
+            case 0x3A:
+            case 0x3B:
+            case 0x3C:
+            case 0x3D:
+            case 0x3E:
+            case 0x3F:
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            case 0x58:
+            case 0x59:
+            case 0x5A:
+            case 0x5B:
+            case 0x5C:
+            case 0x5D:
+            case 0x5E:
+            case 0x5F:
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            case 0x78:
+            case 0x79:
+            case 0x7A:
+            case 0x7B:
+            case 0x7C:
+            case 0x7D:
+            case 0x7E:
+            case 0x7F:
+                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+            // fixmap
+            case 0x80:
+            case 0x81:
+            case 0x82:
+            case 0x83:
+            case 0x84:
+            case 0x85:
+            case 0x86:
+            case 0x87:
+            case 0x88:
+            case 0x89:
+            case 0x8A:
+            case 0x8B:
+            case 0x8C:
+            case 0x8D:
+            case 0x8E:
+            case 0x8F:
+                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // fixarray
+            case 0x90:
+            case 0x91:
+            case 0x92:
+            case 0x93:
+            case 0x94:
+            case 0x95:
+            case 0x96:
+            case 0x97:
+            case 0x98:
+            case 0x99:
+            case 0x9A:
+            case 0x9B:
+            case 0x9C:
+            case 0x9D:
+            case 0x9E:
+            case 0x9F:
+                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // fixstr
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+            case 0xB8:
+            case 0xB9:
+            case 0xBA:
+            case 0xBB:
+            case 0xBC:
+            case 0xBD:
+            case 0xBE:
+            case 0xBF:
+            case 0xD9: // str 8
+            case 0xDA: // str 16
+            case 0xDB: // str 32
+            {
+                string_t s;
+                return get_msgpack_string(s) && sax->string(s);
+            }
+
+            case 0xC0: // nil
+                return sax->null();
+
+            case 0xC2: // false
+                return sax->boolean(false);
+
+            case 0xC3: // true
+                return sax->boolean(true);
+
+            case 0xC4: // bin 8
+            case 0xC5: // bin 16
+            case 0xC6: // bin 32
+            case 0xC7: // ext 8
+            case 0xC8: // ext 16
+            case 0xC9: // ext 32
+            case 0xD4: // fixext 1
+            case 0xD5: // fixext 2
+            case 0xD6: // fixext 4
+            case 0xD7: // fixext 8
+            case 0xD8: // fixext 16
+            {
+                binary_t b;
+                return get_msgpack_binary(b) && sax->binary(b);
+            }
+
+            case 0xCA: // float 32
+            {
+                float number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCB: // float 64
+            {
+                double number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCC: // uint 8
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCD: // uint 16
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCE: // uint 32
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCF: // uint 64
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xD0: // int 8
+            {
+                std::int8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD1: // int 16
+            {
+                std::int16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD2: // int 32
+            {
+                std::int32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD3: // int 64
+            {
+                std::int64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xDC: // array 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+            }
+
+            case 0xDD: // array 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
+            }
+
+            case 0xDE: // map 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+            }
+
+            case 0xDF: // map 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
+            }
+
+            // negative fixint
+            case 0xE0:
+            case 0xE1:
+            case 0xE2:
+            case 0xE3:
+            case 0xE4:
+            case 0xE5:
+            case 0xE6:
+            case 0xE7:
+            case 0xE8:
+            case 0xE9:
+            case 0xEA:
+            case 0xEB:
+            case 0xEC:
+            case 0xED:
+            case 0xEE:
+            case 0xEF:
+            case 0xF0:
+            case 0xF1:
+            case 0xF2:
+            case 0xF3:
+            case 0xF4:
+            case 0xF5:
+            case 0xF6:
+            case 0xF7:
+            case 0xF8:
+            case 0xF9:
+            case 0xFA:
+            case 0xFB:
+            case 0xFC:
+            case 0xFD:
+            case 0xFE:
+            case 0xFF:
+                return sax->number_integer(static_cast<std::int8_t>(current));
+
+            default: // anything else
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a MessagePack string
+
+    This function first reads starting bytes to determine the expected
+    string length and then copies this number of bytes into a string.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_msgpack_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // fixstr
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+            case 0xB8:
+            case 0xB9:
+            case 0xBA:
+            case 0xBB:
+            case 0xBC:
+            case 0xBD:
+            case 0xBE:
+            case 0xBF:
+            {
+                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0xD9: // str 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDA: // str 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDB: // str 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a MessagePack byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into a byte array.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_msgpack_binary(binary_t& result)
+    {
+        // helper function to set the subtype
+        auto assign_and_return_true = [&result](std::int8_t subtype)
+        {
+            result.set_subtype(static_cast<std::uint8_t>(subtype));
+            return true;
+        };
+
+        switch (current)
+        {
+            case 0xC4: // bin 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC5: // bin 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC6: // bin 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC7: // ext 8
+            {
+                std::uint8_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC8: // ext 16
+            {
+                std::uint16_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC9: // ext 32
+            {
+                std::uint32_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD4: // fixext 1
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 1, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD5: // fixext 2
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 2, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD6: // fixext 4
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 4, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD7: // fixext 8
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 8, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD8: // fixext 16
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 16, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            default:           // LCOV_EXCL_LINE
+                return false;  // LCOV_EXCL_LINE
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array
+    @return whether array creation completed
+    */
+    bool get_msgpack_array(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object
+    @return whether object creation completed
+    */
+    bool get_msgpack_object(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        string_t key;
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+            key.clear();
+        }
+
+        return sax->end_object();
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+
+    @return whether a valid UBJSON value was passed to the SAX parser
+    */
+    bool parse_ubjson_internal(const bool get_char = true)
+    {
+        return get_ubjson_value(get_char ? get_ignore_noop() : current);
+    }
+
+    /*!
+    @brief reads a UBJSON string
+
+    This function is either called after reading the 'S' byte explicitly
+    indicating a string, or in case of an object key where the 'S' byte can be
+    left out.
+
+    @param[out] result   created string
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+
+    @return whether string creation completed
+    */
+    bool get_ubjson_string(string_t& result, const bool get_char = true)
+    {
+        if (get_char)
+        {
+            get();  // TODO(niels): may we ignore N here?
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            case 'U':
+            {
+                std::uint8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'i':
+            {
+                std::int8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'I':
+            {
+                std::int16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'l':
+            {
+                std::int32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'L':
+            {
+                std::int64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
+    }
+
+    /*!
+    @param[out] dim  an integer vector storing the ND array dimensions
+    @return whether reading ND array size vector is successful
+    */
+    bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
+    {
+        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, no_ndarray)))
+        {
+            return false;
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
+                        {
+                            return false;
+                        }
+                        dim.push_back(dimlen);
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
+                    {
+                        return false;
+                    }
+                    dim.push_back(dimlen);
+                }
+            }
+        }
+        else
+        {
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
+                {
+                    return false;
+                }
+                dim.push_back(dimlen);
+                get_ignore_noop();
+            }
+        }
+        return true;
+    }
+
+    /*!
+    @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, bool& is_ndarray, char_int_type prefix = 0)
+    {
+        if (prefix == 0)
+        {
+            prefix = get_ignore_noop();
+        }
+
+        switch (prefix)
+        {
+            case 'U':
+            {
+                std::uint8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    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;
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    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;
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    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;
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    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;
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = conditional_static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    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;
+            }
+
+            case '[':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    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)))
+                {
+                    return false;
+                }
+                if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector
+                {
+                    result = dim.at(dim.size() - 1);
+                    return true;
+                }
+                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())))
+                    {
+                        return false;
+                    }
+                    result = 1;
+                    for (auto i : dim)
+                    {
+                        result *= i;
+                        if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be 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;
+                        }
+                    }
+                    is_ndarray = true;
+                    return sax->end_array();
+                }
+                result = 0;
+                return true;
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
+    }
+
+    /*!
+    @brief determine the type and size for a container
+
+    In the optimized UBJSON format, a type and a size can be provided to allow
+    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 inside_ndarray = false)
+    {
+        result.first = npos; // size
+        result.second = 0; // type
+        bool is_ndarray = false;
+
+        get_ignore_noop();
+
+        if (current == '$')
+        {
+            result.second = get();  // must not ignore 'N', because 'N' maybe the type
+            if (input_format == input_format_t::bjdata
+                    && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
+            {
+                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;
+            }
+
+            get_ignore_noop();
+            if (JSON_HEDLEY_UNLIKELY(current != '#'))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+                {
+                    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, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
+            }
+
+            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 == '#')
+        {
+            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;
+    }
+
+    /*!
+    @param prefix  the previously read or set type prefix
+    @return whether value creation completed
+    */
+    bool get_ubjson_value(const char_int_type prefix)
+    {
+        switch (prefix)
+        {
+            case std::char_traits<char_type>::eof():  // EOF
+                return unexpect_eof(input_format, "value");
+
+            case 'T':  // true
+                return sax->boolean(true);
+            case 'F':  // false
+                return sax->boolean(false);
+
+            case 'Z':  // null
+                return sax->null();
+
+            case 'U':
+            {
+                std::uint8_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'h':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // code from RFC 7049, Appendix D, Figure 3:
+                // As half-precision floating-point numbers were only added
+                // to IEEE 754 in 2008, today's programming platforms often
+                // still only have limited support for them. It is very
+                // easy to include at least decoding support for them even
+                // without such support. An example of a small decoder for
+                // half-precision floating-point numbers in the C language
+                // is shown in Fig. 3.
+                const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 'd':
+            {
+                float number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'D':
+            {
+                double number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'H':
+            {
+                return get_ubjson_high_precision_number();
+            }
+
+            case 'C':  // char
+            {
+                get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char")))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(current > 127))
+                {
+                    auto last_token = get_token_string();
+                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                            exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
+                }
+                string_t s(1, static_cast<typename string_t::value_type>(current));
+                return sax->string(s);
+            }
+
+            case 'S':  // string
+            {
+                string_t s;
+                return get_ubjson_string(s) && sax->string(s);
+            }
+
+            case '[':  // array
+                return get_ubjson_array();
+
+            case '{':  // object
+                return get_ubjson_object();
+
+            default: // anything else
+                break;
+        }
+        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, "value"), nullptr));
+    }
+
+    /*!
+    @return whether array creation completed
+    */
+    bool get_ubjson_array()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // 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 != npos && (size_and_type.second & (1 << 8)) != 0)
+        {
+            size_and_type.second &= ~(static_cast<char_int_type>(1) << 8);  // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
+            auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
+            {
+                return p.first < t;
+            });
+            string_t key = "_ArrayType_";
+            if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
+            }
+
+            string_t type = it->second; // sax->string() takes a reference
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second == 'C')
+            {
+                size_and_type.second = 'U';
+            }
+
+            key = "_ArrayData_";
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
+            {
+                return false;
+            }
+
+            for (std::size_t i = 0; i < size_and_type.first; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                {
+                    return false;
+                }
+            }
+
+            return (sax->end_array() && sax->end_object());
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                        {
+                            return false;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @return whether object creation completed
+    */
+    bool get_ubjson_object()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // do not accept ND-array size in objects in BJData
+        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 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, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
+        }
+
+        string_t key;
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != '}')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+                key.clear();
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    // Note, no reader for UBJSON binary types is implemented because they do
+    // not exist
+
+    bool get_ubjson_high_precision_number()
+    {
+        // get size of following number string
+        std::size_t size{};
+        bool no_ndarray = true;
+        auto res = get_ubjson_size_value(size, no_ndarray);
+        if (JSON_HEDLEY_UNLIKELY(!res))
+        {
+            return res;
+        }
+
+        // get number string
+        std::vector<char> number_vector;
+        for (std::size_t i = 0; i < size; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+            {
+                return false;
+            }
+            number_vector.push_back(static_cast<char>(current));
+        }
+
+        // parse number string
+        using ia_type = decltype(detail::input_adapter(number_vector));
+        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+        const auto result_number = number_lexer.scan();
+        const auto number_string = number_lexer.get_token_string();
+        const auto result_remainder = number_lexer.scan();
+
+        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+        {
+            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                    exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+
+        switch (result_number)
+        {
+            case token_type::value_integer:
+                return sax->number_integer(number_lexer.get_number_integer());
+            case token_type::value_unsigned:
+                return sax->number_unsigned(number_lexer.get_number_unsigned());
+            case token_type::value_float:
+                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+            case token_type::uninitialized:
+            case token_type::literal_true:
+            case token_type::literal_false:
+            case token_type::literal_null:
+            case token_type::value_string:
+            case token_type::begin_array:
+            case token_type::begin_object:
+            case token_type::end_array:
+            case token_type::end_object:
+            case token_type::name_separator:
+            case token_type::value_separator:
+            case token_type::parse_error:
+            case token_type::end_of_input:
+            case token_type::literal_or_value:
+            default:
+                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                        exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*!
+    @brief get next character from the input
+
+    This function provides the interface to the used input adapter. It does
+    not throw in case the input reached EOF, but returns a -'ve valued
+    `std::char_traits<char_type>::eof()` in that case.
+
+    @return character read from the input
+    */
+    char_int_type get()
+    {
+        ++chars_read;
+        return current = ia.get_character();
+    }
+
+    /*!
+    @return character read from the input after ignoring all 'N' entries
+    */
+    char_int_type get_ignore_noop()
+    {
+        do
+        {
+            get();
+        }
+        while (current == 'N');
+
+        return current;
+    }
+
+    /*
+    @brief read a number from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format   the current format (for diagnostics)
+    @param[out] result  number of type @a NumberType
+
+    @return whether conversion completed
+
+    @note This function needs to respect the system's endianness, because
+          bytes in CBOR, MessagePack, and UBJSON are stored in network order
+          (big endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType, bool InputIsLittleEndian = false>
+    bool get_number(const input_format_t format, NumberType& result)
+    {
+        // step 1: read input into array with system's byte order
+        std::array<std::uint8_t, sizeof(NumberType)> vec{};
+        for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
+            {
+                return false;
+            }
+
+            // reverse byte order prior to conversion if necessary
+            if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
+            {
+                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
+            }
+            else
+            {
+                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE
+            }
+        }
+
+        // step 2: convert array into number of type T and return
+        std::memcpy(&result, vec.data(), sizeof(NumberType));
+        return true;
+    }
+
+    /*!
+    @brief create a string by reading characters from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of characters to read
+    @param[out] result string created by reading @a len bytes
+
+    @return whether string creation completed
+
+    @note We can not reserve @a len bytes for the result, because @a len
+          may be too large. Usually, @ref unexpect_eof() detects the end of
+          the input before we run out of string memory.
+    */
+    template<typename NumberType>
+    bool get_string(const input_format_t format,
+                    const NumberType len,
+                    string_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<typename string_t::value_type>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @brief create a byte array by reading bytes from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of bytes to read
+    @param[out] result byte array created by reading @a len bytes
+
+    @return whether byte array creation completed
+
+    @note We can not reserve @a len bytes for the result, because @a len
+          may be too large. Usually, @ref unexpect_eof() detects the end of
+          the input before we run out of memory.
+    */
+    template<typename NumberType>
+    bool get_binary(const input_format_t format,
+                    const NumberType len,
+                    binary_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<std::uint8_t>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @param[in] format   the current format (for diagnostics)
+    @param[in] context  further context information (for diagnostics)
+    @return whether the last read character is not EOF
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool unexpect_eof(const input_format_t format, const char* context) const
+    {
+        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
+        {
+            return sax->parse_error(chars_read, "<end of file>",
+                                    parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+        }
+        return true;
+    }
+
+    /*!
+    @return a string representation of the last read byte
+    */
+    std::string get_token_string() const
+    {
+        std::array<char, 3> cr{{}};
+        static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        return std::string{cr.data()};
+    }
+
+    /*!
+    @param[in] format   the current format
+    @param[in] detail   a detailed error message
+    @param[in] context  further context information
+    @return a message string to use in the parse_error exceptions
+    */
+    std::string exception_message(const input_format_t format,
+                                  const std::string& detail,
+                                  const std::string& context) const
+    {
+        std::string error_msg = "syntax error while parsing ";
+
+        switch (format)
+        {
+            case input_format_t::cbor:
+                error_msg += "CBOR";
+                break;
+
+            case input_format_t::msgpack:
+                error_msg += "MessagePack";
+                break;
+
+            case input_format_t::ubjson:
+                error_msg += "UBJSON";
+                break;
+
+            case input_format_t::bson:
+                error_msg += "BSON";
+                break;
+
+            case input_format_t::bjdata:
+                error_msg += "BJData";
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        return concat(error_msg, ' ', context, ": ", detail);
+    }
+
+  private:
+    static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast<std::size_t>(-1);
+
+    /// input adapter
+    InputAdapterType ia;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// the number of characters read
+    std::size_t chars_read = 0;
+
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// input format
+    const input_format_t input_format = input_format_t::json;
+
+    /// the SAX parser
+    json_sax_t* sax = nullptr;
+
+    // excluded markers in bjdata optimized type
+#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
+    make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
+
+#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
+    make_array<bjd_type>(                      \
+    bjd_type{'C', "char"},                     \
+    bjd_type{'D', "double"},                   \
+    bjd_type{'I', "int16"},                    \
+    bjd_type{'L', "int64"},                    \
+    bjd_type{'M', "uint64"},                   \
+    bjd_type{'U', "uint8"},                    \
+    bjd_type{'d', "single"},                   \
+    bjd_type{'i', "int8"},                     \
+    bjd_type{'l', "int32"},                    \
+    bjd_type{'m', "uint32"},                   \
+    bjd_type{'u', "uint16"})
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // lookup tables
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
+        JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
+
+    using bjd_type = std::pair<char_int_type, string_t>;
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
+        JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
+
+#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
+#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+    constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;
+#endif
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/input/parser.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+////////////
+// parser //
+////////////
+
+enum class parse_event_t : std::uint8_t
+{
+    /// the parser read `{` and started to process a JSON object
+    object_start,
+    /// the parser read `}` and finished processing a JSON object
+    object_end,
+    /// the parser read `[` and started to process a JSON array
+    array_start,
+    /// the parser read `]` and finished processing a JSON array
+    array_end,
+    /// the parser read a key of a value in an object
+    key,
+    /// the parser finished reading a JSON value
+    value
+};
+
+template<typename BasicJsonType>
+using parser_callback_t =
+    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive descent parser.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+    using token_type = typename lexer_t::token_type;
+
+  public:
+    /// a parser reading from an input adapter
+    explicit parser(InputAdapterType&& adapter,
+                    const parser_callback_t<BasicJsonType> cb = nullptr,
+                    const bool allow_exceptions_ = true,
+                    const bool skip_comments = false)
+        : callback(cb)
+        , m_lexer(std::move(adapter), skip_comments)
+        , allow_exceptions(allow_exceptions_)
+    {
+        // read first token
+        get_token();
+    }
+
+    /*!
+    @brief public parser interface
+
+    @param[in] strict      whether to expect the last token to be EOF
+    @param[in,out] result  parsed JSON value
+
+    @throw parse_error.101 in case of an unexpected token
+    @throw parse_error.102 if to_unicode fails or surrogate error
+    @throw parse_error.103 if to_unicode fails
+    */
+    void parse(const bool strict, BasicJsonType& result)
+    {
+        if (callback)
+        {
+            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(),
+                                                    exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_errored())
+            {
+                result = value_t::discarded;
+                return;
+            }
+
+            // set top-level value to null if it was discarded by the callback
+            // function
+            if (result.is_discarded())
+            {
+                result = nullptr;
+            }
+        }
+        else
+        {
+            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_errored())
+            {
+                result = value_t::discarded;
+                return;
+            }
+        }
+
+        result.assert_invariant();
+    }
+
+    /*!
+    @brief public accept interface
+
+    @param[in] strict  whether to expect the last token to be EOF
+    @return whether the input is a proper JSON text
+    */
+    bool accept(const bool strict = true)
+    {
+        json_sax_acceptor<BasicJsonType> sax_acceptor;
+        return sax_parse(&sax_acceptor, strict);
+    }
+
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse(SAX* sax, const bool strict = true)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+        const bool result = sax_parse_internal(sax);
+
+        // strict mode: next byte must be EOF
+        if (result && strict && (get_token() != token_type::end_of_input))
+        {
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+        }
+
+        return result;
+    }
+
+  private:
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse_internal(SAX* sax)
+    {
+        // stack to remember the hierarchy of structured values we are parsing
+        // true = array; false = object
+        std::vector<bool> states;
+        // value to avoid a goto (see comment where set to true)
+        bool skip_to_state_evaluation = false;
+
+        while (true)
+        {
+            if (!skip_to_state_evaluation)
+            {
+                // invariant: get_token() was called before each iteration
+                switch (last_token)
+                {
+                    case token_type::begin_object:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing } -> we are done
+                        if (get_token() == token_type::end_object)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // parse key
+                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        // parse separator (:)
+                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                        }
+
+                        // remember we are now inside an object
+                        states.push_back(false);
+
+                        // parse values
+                        get_token();
+                        continue;
+                    }
+
+                    case token_type::begin_array:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing ] -> we are done
+                        if (get_token() == token_type::end_array)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // remember we are now inside an array
+                        states.push_back(true);
+
+                        // parse values (no need to call get_token)
+                        continue;
+                    }
+
+                    case token_type::value_float:
+                    {
+                        const auto res = m_lexer.get_number_float();
+
+                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
+                        }
+
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        break;
+                    }
+
+                    case token_type::literal_false:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_null:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_true:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_integer:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_string:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_unsigned:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::parse_error:
+                    {
+                        // using "uninitialized" to avoid "expected" message
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
+                    }
+
+                    case token_type::uninitialized:
+                    case token_type::end_array:
+                    case token_type::end_object:
+                    case token_type::name_separator:
+                    case token_type::value_separator:
+                    case token_type::end_of_input:
+                    case token_type::literal_or_value:
+                    default: // the last token was unexpected
+                    {
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+                    }
+                }
+            }
+            else
+            {
+                skip_to_state_evaluation = false;
+            }
+
+            // we reached this line after we successfully parsed a value
+            if (states.empty())
+            {
+                // empty stack: we reached the end of the hierarchy: done
+                return true;
+            }
+
+            if (states.back())  // array
+            {
+                // comma -> next value
+                if (get_token() == token_type::value_separator)
+                {
+                    // parse a new value
+                    get_token();
+                    continue;
+                }
+
+                // closing ]
+                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                    {
+                        return false;
+                    }
+
+                    // We are done with this array. Before we can parse a
+                    // new value, we need to evaluate the new state first.
+                    // By setting skip_to_state_evaluation to false, we
+                    // are effectively jumping to the beginning of this if.
+                    JSON_ASSERT(!states.empty());
+                    states.pop_back();
+                    skip_to_state_evaluation = true;
+                    continue;
+                }
+
+                return sax->parse_error(m_lexer.get_position(),
+                                        m_lexer.get_token_string(),
+                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
+            }
+
+            // states.back() is false -> object
+
+            // comma -> next value
+            if (get_token() == token_type::value_separator)
+            {
+                // parse key
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                }
+
+                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                {
+                    return false;
+                }
+
+                // parse separator (:)
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                }
+
+                // parse values
+                get_token();
+                continue;
+            }
+
+            // closing }
+            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                {
+                    return false;
+                }
+
+                // We are done with this object. Before we can parse a
+                // new value, we need to evaluate the new state first.
+                // By setting skip_to_state_evaluation to false, we
+                // are effectively jumping to the beginning of this if.
+                JSON_ASSERT(!states.empty());
+                states.pop_back();
+                skip_to_state_evaluation = true;
+                continue;
+            }
+
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
+        }
+    }
+
+    /// get next token from lexer
+    token_type get_token()
+    {
+        return last_token = m_lexer.scan();
+    }
+
+    std::string exception_message(const token_type expected, const std::string& context)
+    {
+        std::string error_msg = "syntax error ";
+
+        if (!context.empty())
+        {
+            error_msg += concat("while parsing ", context, ' ');
+        }
+
+        error_msg += "- ";
+
+        if (last_token == token_type::parse_error)
+        {
+            error_msg += concat(m_lexer.get_error_message(), "; last read: '",
+                                m_lexer.get_token_string(), '\'');
+        }
+        else
+        {
+            error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
+        }
+
+        if (expected != token_type::uninitialized)
+        {
+            error_msg += concat("; expected ", lexer_t::token_type_name(expected));
+        }
+
+        return error_msg;
+    }
+
+  private:
+    /// callback function
+    const parser_callback_t<BasicJsonType> callback = nullptr;
+    /// the type of the last read token
+    token_type last_token = token_type::uninitialized;
+    /// the lexer
+    lexer_t m_lexer;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <limits>  // numeric_limits
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+  private:
+    using difference_type = std::ptrdiff_t;
+    static constexpr difference_type begin_value = 0;
+    static constexpr difference_type end_value = begin_value + 1;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// iterator as signed integer type
+    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+  public:
+    constexpr difference_type get_value() const noexcept
+    {
+        return m_it;
+    }
+
+    /// set iterator to a defined beginning
+    void set_begin() noexcept
+    {
+        m_it = begin_value;
+    }
+
+    /// set iterator to a defined past the end
+    void set_end() noexcept
+    {
+        m_it = end_value;
+    }
+
+    /// return whether the iterator can be dereferenced
+    constexpr bool is_begin() const noexcept
+    {
+        return m_it == begin_value;
+    }
+
+    /// return whether the iterator is at end
+    constexpr bool is_end() const noexcept
+    {
+        return m_it == end_value;
+    }
+
+    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it == rhs.m_it;
+    }
+
+    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it < rhs.m_it;
+    }
+
+    primitive_iterator_t operator+(difference_type n) noexcept
+    {
+        auto result = *this;
+        result += n;
+        return result;
+    }
+
+    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it - rhs.m_it;
+    }
+
+    primitive_iterator_t& operator++() noexcept
+    {
+        ++m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator--() noexcept
+    {
+        --m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator+=(difference_type n) noexcept
+    {
+        m_it += n;
+        return *this;
+    }
+
+    primitive_iterator_t& operator-=(difference_type n) noexcept
+    {
+        m_it -= n;
+        return *this;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+    /// iterator for JSON objects
+    typename BasicJsonType::object_t::iterator object_iterator {};
+    /// iterator for JSON arrays
+    typename BasicJsonType::array_t::iterator array_iterator {};
+    /// generic iterator for all other types
+    primitive_iterator_t primitive_iterator {};
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iter_impl.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// forward declare, to be able to friend it later on
+template<typename IteratorType> class iteration_proxy;
+template<typename IteratorType> class iteration_proxy_value;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+@note An iterator is called *initialized* when a pointer to a JSON value has
+      been set (e.g., by a constructor or a copy assignment). If the iterator is
+      default-constructed, it is *uninitialized* and most methods are undefined.
+      **The library uses assertions to detect calls on uninitialized iterators.**
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+    /// the iterator with BasicJsonType of different const-ness
+    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+    /// allow basic_json to access private members
+    friend other_iter_impl;
+    friend BasicJsonType;
+    friend iteration_proxy<iter_impl>;
+    friend iteration_proxy_value<iter_impl>;
+
+    using object_t = typename BasicJsonType::object_t;
+    using array_t = typename BasicJsonType::array_t;
+    // 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
+    /// iterator_category, value_type, difference_type, pointer, and reference.
+    /// Note that value_type is required to be non-const, even for constant iterators.
+    using iterator_category = std::bidirectional_iterator_tag;
+
+    /// the type of the values when the iterator is dereferenced
+    using value_type = typename BasicJsonType::value_type;
+    /// a type to represent differences between iterators
+    using difference_type = typename BasicJsonType::difference_type;
+    /// defines a pointer to the type iterated over (value_type)
+    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+          typename BasicJsonType::const_pointer,
+          typename BasicJsonType::pointer>::type;
+    /// defines a reference to the type iterated over (value_type)
+    using reference =
+        typename std::conditional<std::is_const<BasicJsonType>::value,
+        typename BasicJsonType::const_reference,
+        typename BasicJsonType::reference>::type;
+
+    iter_impl() = default;
+    ~iter_impl() = default;
+    iter_impl(iter_impl&&) noexcept = default;
+    iter_impl& operator=(iter_impl&&) noexcept = default;
+
+    /*!
+    @brief constructor for a given JSON instance
+    @param[in] object  pointer to a JSON object for this iterator
+    @pre object != nullptr
+    @post The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    explicit iter_impl(pointer object) noexcept : m_object(object)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = typename object_t::iterator();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = typename array_t::iterator();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator = primitive_iterator_t();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @note The conventional copy constructor and copy assignment are implicitly
+          defined. Combined with the following converting constructor and
+          assignment, they support: (1) copy from iterator to iterator, (2)
+          copy from const iterator to const iterator, and (3) conversion from
+          iterator to const iterator. However conversion from const iterator
+          to iterator is not defined.
+    */
+
+    /*!
+    @brief const copy constructor
+    @param[in] other const iterator to copy from
+    @note This copy constructor had to be defined explicitly to circumvent a bug
+          occurring on msvc v19.0 compiler (VS 2015) debug build. For more
+          information refer to: https://github.com/nlohmann/json/issues/1608
+    */
+    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
+    {
+        if (&other != this)
+        {
+            m_object = other.m_object;
+            m_it = other.m_it;
+        }
+        return *this;
+    }
+
+    /*!
+    @brief converting constructor
+    @param[in] other  non-const iterator to copy from
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other  non-const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
+    {
+        m_object = other.m_object;
+        m_it = other.m_it;
+        return *this;
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief set the iterator to the first value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_begin() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_value.object->begin();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_value.array->begin();
+                break;
+            }
+
+            case value_t::null:
+            {
+                // set to end so begin()==end() is true: null is empty
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_begin();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @brief set the iterator past the last value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_end() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_value.object->end();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_value.array->end();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+        }
+    }
+
+  public:
+    /*!
+    @brief return a reference to the value pointed to by the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator*() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+                return m_it.object_iterator->second;
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+                return *m_it.array_iterator;
+            }
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief dereference the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    pointer operator->() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+                return &(m_it.object_iterator->second);
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+                return &*m_it.array_iterator;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief post-increment (it++)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-increment (++it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator++()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, 1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, 1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                ++m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief post-decrement (it--)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-decrement (--it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator--()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, -1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, -1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                --m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief comparison: equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator==(const IterImpl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                return (m_it.object_iterator == other.m_it.object_iterator);
+
+            case value_t::array:
+                return (m_it.array_iterator == other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: not equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator!=(const IterImpl& other) const
+    {
+        return !operator==(other);
+    }
+
+    /*!
+    @brief comparison: smaller
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<(const iter_impl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
+
+            case value_t::array:
+                return (m_it.array_iterator < other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: less than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<=(const iter_impl& other) const
+    {
+        return !other.operator < (*this);
+    }
+
+    /*!
+    @brief comparison: greater than
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>(const iter_impl& other) const
+    {
+        return !operator<=(other);
+    }
+
+    /*!
+    @brief comparison: greater than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>=(const iter_impl& other) const
+    {
+        return !operator<(other);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator+=(difference_type i)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, i);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator += i;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator-=(difference_type i)
+    {
+        return operator+=(-i);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator+(difference_type i) const
+    {
+        auto result = *this;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief addition of distance and iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    friend iter_impl operator+(difference_type i, const iter_impl& it)
+    {
+        auto result = it;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator-(difference_type i) const
+    {
+        auto result = *this;
+        result -= i;
+        return result;
+    }
+
+    /*!
+    @brief return difference
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    difference_type operator-(const iter_impl& other) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+                return m_it.array_iterator - other.m_it.array_iterator;
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return m_it.primitive_iterator - other.m_it.primitive_iterator;
+        }
+    }
+
+    /*!
+    @brief access to successor
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator[](difference_type n) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
+
+            case value_t::array:
+                return *std::next(m_it.array_iterator, n);
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief return the key of an object iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    const typename object_t::key_type& key() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        if (JSON_HEDLEY_LIKELY(m_object->is_object()))
+        {
+            return m_it.object_iterator->first;
+        }
+
+        JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
+    }
+
+    /*!
+    @brief return the value of an iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference value() const
+    {
+        return operator*();
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// associated JSON instance
+    pointer m_object = nullptr;
+    /// the actual iterator of the associated instance
+    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+
+// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////////
+// reverse_iterator //
+//////////////////////
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
+  It is possible to write to the pointed-to element (only if @a Base is
+  @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    /// shortcut to the reverse iterator adapter
+    using base_iterator = std::reverse_iterator<Base>;
+    /// the reference type for the pointed-to element
+    using reference = typename Base::reference;
+
+    /// create reverse iterator from iterator
+    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+        : base_iterator(it) {}
+
+    /// create reverse iterator from base class
+    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+    /// post-increment (it++)
+    json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+    }
+
+    /// pre-increment (++it)
+    json_reverse_iterator& operator++()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+    }
+
+    /// post-decrement (it--)
+    json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+    }
+
+    /// pre-decrement (--it)
+    json_reverse_iterator& operator--()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+    }
+
+    /// add to iterator
+    json_reverse_iterator& operator+=(difference_type i)
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+    }
+
+    /// add to iterator
+    json_reverse_iterator operator+(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+    }
+
+    /// subtract from iterator
+    json_reverse_iterator operator-(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+    }
+
+    /// return difference
+    difference_type operator-(const json_reverse_iterator& other) const
+    {
+        return base_iterator(*this) - base_iterator(other);
+    }
+
+    /// access to successor
+    reference operator[](difference_type n) const
+    {
+        return *(this->operator+(n));
+    }
+
+    /// return the key of an object iterator
+    auto key() const -> decltype(std::declval<Base>().key())
+    {
+        auto it = --this->base();
+        return it.key();
+    }
+
+    /// return the value of an iterator
+    reference value() const
+    {
+        auto it = --this->base();
+        return it.operator * ();
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/json_pointer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // all_of
+#include <cctype> // isdigit
+#include <cerrno> // errno, ERANGE
+#include <cstdlib> // strtoull
+#ifndef JSON_NO_IO
+    #include <iosfwd> // ostream
+#endif  // JSON_NO_IO
+#include <limits> // max
+#include <numeric> // accumulate
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer
+{
+    // allow basic_json to access private members
+    NLOHMANN_BASIC_JSON_TPL_DECLARATION
+    friend class basic_json;
+
+    template<typename>
+    friend class json_pointer;
+
+    template<typename T>
+    struct string_t_helper
+    {
+        using type = T;
+    };
+
+    NLOHMANN_BASIC_JSON_TPL_DECLARATION
+    struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>
+    {
+        using type = StringType;
+    };
+
+  public:
+    // for backwards compatibility accept BasicJsonType
+    using string_t = typename string_t_helper<RefStringType>::type;
+
+    /// @brief create JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
+    explicit json_pointer(const string_t& s = "")
+        : reference_tokens(split(s))
+    {}
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
+    string_t to_string() const
+    {
+        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+                               string_t{},
+                               [](const string_t& a, const string_t& b)
+        {
+            return detail::concat(a, '/', detail::escape(b));
+        });
+    }
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
+    operator string_t() const
+    {
+        return to_string();
+    }
+
+#ifndef JSON_NO_IO
+    /// @brief write string representation of the JSON pointer to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
+    {
+        o << ptr.to_string();
+        return o;
+    }
+#endif
+
+    /// @brief append another JSON pointer at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(const json_pointer& ptr)
+    {
+        reference_tokens.insert(reference_tokens.end(),
+                                ptr.reference_tokens.begin(),
+                                ptr.reference_tokens.end());
+        return *this;
+    }
+
+    /// @brief append an unescaped reference token at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(string_t token)
+    {
+        push_back(std::move(token));
+        return *this;
+    }
+
+    /// @brief append an array index at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(std::size_t array_idx)
+    {
+        return *this /= std::to_string(array_idx);
+    }
+
+    /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs,
+                                  const json_pointer& rhs)
+    {
+        return json_pointer(lhs) /= rhs;
+    }
+
+    /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
+    {
+        return json_pointer(lhs) /= std::move(token);
+    }
+
+    /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
+    {
+        return json_pointer(lhs) /= array_idx;
+    }
+
+    /// @brief returns the parent of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
+    json_pointer parent_pointer() const
+    {
+        if (empty())
+        {
+            return *this;
+        }
+
+        json_pointer res = *this;
+        res.pop_back();
+        return res;
+    }
+
+    /// @brief remove last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
+    void pop_back()
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        reference_tokens.pop_back();
+    }
+
+    /// @brief return last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/back/
+    const string_t& back() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        return reference_tokens.back();
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(const string_t& token)
+    {
+        reference_tokens.push_back(token);
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(string_t&& token)
+    {
+        reference_tokens.push_back(std::move(token));
+    }
+
+    /// @brief return whether pointer points to the root document
+    /// @sa https://json.nlohmann.me/api/json_pointer/empty/
+    bool empty() const noexcept
+    {
+        return reference_tokens.empty();
+    }
+
+  private:
+    /*!
+    @param[in] s  reference token to be converted into an array index
+
+    @return integer representation of @a s
+
+    @throw parse_error.106  if an array index begins with '0'
+    @throw parse_error.109  if an array index begins not with a digit
+    @throw out_of_range.404 if string @a s could not be converted to an integer
+    @throw out_of_range.410 if an array index exceeds size_type
+    */
+    template<typename BasicJsonType>
+    static typename BasicJsonType::size_type array_index(const string_t& s)
+    {
+        using size_type = typename BasicJsonType::size_type;
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
+        {
+            JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
+        }
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
+        {
+            JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
+        }
+
+        const char* p = s.c_str();
+        char* p_end = nullptr;
+        errno = 0; // strtoull doesn't reset errno
+        unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
+        if (p == p_end // invalid input or empty string
+                || errno == ERANGE // out of range
+                || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
+        {
+            JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
+        }
+
+        // only triggered on special platforms (like 32bit), see also
+        // https://github.com/nlohmann/json/pull/2203
+        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)
+        {
+            JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));   // LCOV_EXCL_LINE
+        }
+
+        return static_cast<size_type>(res);
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    json_pointer top() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        json_pointer result = *this;
+        result.reference_tokens = {reference_tokens[0]};
+        return result;
+    }
+
+  private:
+    /*!
+    @brief create and return a reference to the pointed to value
+
+    @complexity Linear in the number of reference tokens.
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.313 if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_and_create(BasicJsonType& j) const
+    {
+        auto* result = &j;
+
+        // in case no reference tokens exist, return a reference to the JSON value
+        // j which will be overwritten by a primitive value
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (result->type())
+            {
+                case detail::value_t::null:
+                {
+                    if (reference_token == "0")
+                    {
+                        // start a new array if reference token is 0
+                        result = &result->operator[](0);
+                    }
+                    else
+                    {
+                        // start a new object otherwise
+                        result = &result->operator[](reference_token);
+                    }
+                    break;
+                }
+
+                case detail::value_t::object:
+                {
+                    // create an entry in the object
+                    result = &result->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    // create an entry in the array
+                    result = &result->operator[](array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                /*
+                The following code is only reached if there exists a reference
+                token _and_ the current value is primitive. In this case, we have
+                an error situation, because primitive values may only occur as
+                single value; that is, with an empty list of reference tokens.
+                */
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
+            }
+        }
+
+        return *result;
+    }
+
+    /*!
+    @brief return a reference to the pointed to value
+
+    @note This version does not throw if a value is not present, but tries to
+          create nested values instead. For instance, calling this function
+          with pointer `"/this/that"` on a null value is equivalent to calling
+          `operator[]("this").operator[]("that")` on that value, effectively
+          changing the null value to an object.
+
+    @param[in] ptr  a JSON value
+
+    @return reference to the JSON value pointed to by the JSON pointer
+
+    @complexity Linear in the length of the JSON pointer.
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            // convert null values to arrays or objects before continuing
+            if (ptr->is_null())
+            {
+                // check if reference token is a number
+                const bool nums =
+                    std::all_of(reference_token.begin(), reference_token.end(),
+                                [](const unsigned char x)
+                {
+                    return std::isdigit(x);
+                });
+
+                // change value to array for numbers or "-" or to object otherwise
+                *ptr = (nums || reference_token == "-")
+                       ? detail::value_t::array
+                       : detail::value_t::object;
+            }
+
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (reference_token == "-")
+                    {
+                        // explicitly treat "-" as index beyond the end
+                        ptr = &ptr->operator[](ptr->m_value.array->size());
+                    }
+                    else
+                    {
+                        // convert array index to number; unchecked access
+                        ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    }
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_checked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @brief return a const reference to the pointed to value
+
+    @param[in] ptr  a JSON value
+
+    @return const reference to the JSON value pointed to by the JSON
+    pointer
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" cannot be used for const access
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr));
+                    }
+
+                    // use unchecked array access
+                    ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    */
+    template<typename BasicJsonType>
+    bool contains(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    if (!ptr->contains(reference_token))
+                    {
+                        // we did not find the key in the object
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
+                    {
+                        // invalid char
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
+                        {
+                            // first char should be between '1' and '9'
+                            return false;
+                        }
+                        for (std::size_t i = 1; i < reference_token.size(); i++)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
+                            {
+                                // other char should be between '0' and '9'
+                                return false;
+                            }
+                        }
+                    }
+
+                    const auto idx = array_index<BasicJsonType>(reference_token);
+                    if (idx >= ptr->size())
+                    {
+                        // index out of range
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](idx);
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                {
+                    // we do not expect primitive values if there is still a
+                    // reference token to process
+                    return false;
+                }
+            }
+        }
+
+        // no reference token left means we found a primitive value
+        return true;
+    }
+
+    /*!
+    @brief split the string input to reference tokens
+
+    @note This function is only called by the json_pointer constructor.
+          All exceptions below are documented there.
+
+    @throw parse_error.107  if the pointer is not empty or begins with '/'
+    @throw parse_error.108  if character '~' is not followed by '0' or '1'
+    */
+    static std::vector<string_t> split(const string_t& reference_string)
+    {
+        std::vector<string_t> result;
+
+        // special case: empty reference string -> no reference tokens
+        if (reference_string.empty())
+        {
+            return result;
+        }
+
+        // check if nonempty reference string begins with slash
+        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
+        {
+            JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
+        }
+
+        // extract the reference tokens:
+        // - slash: position of the last read slash (or end of string)
+        // - start: position after the previous slash
+        for (
+            // search for the first slash after the first character
+            std::size_t slash = reference_string.find_first_of('/', 1),
+            // set the beginning of the first reference token
+            start = 1;
+            // we can stop if start == 0 (if slash == string_t::npos)
+            start != 0;
+            // set the beginning of the next reference token
+            // (will eventually be 0 if slash == string_t::npos)
+            start = (slash == string_t::npos) ? 0 : slash + 1,
+            // find next slash
+            slash = reference_string.find_first_of('/', start))
+        {
+            // use the text between the beginning of the reference token
+            // (start) and the last slash (slash).
+            auto reference_token = reference_string.substr(start, slash - start);
+
+            // check reference tokens are properly escaped
+            for (std::size_t pos = reference_token.find_first_of('~');
+                    pos != string_t::npos;
+                    pos = reference_token.find_first_of('~', pos + 1))
+            {
+                JSON_ASSERT(reference_token[pos] == '~');
+
+                // ~ must be followed by 0 or 1
+                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
+                                         (reference_token[pos + 1] != '0' &&
+                                          reference_token[pos + 1] != '1')))
+                {
+                    JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
+                }
+            }
+
+            // finally, store the reference token
+            detail::unescape(reference_token);
+            result.push_back(reference_token);
+        }
+
+        return result;
+    }
+
+  private:
+    /*!
+    @param[in] reference_string  the reference string to the current value
+    @param[in] value             the value to consider
+    @param[in,out] result        the result object to insert values to
+
+    @note Empty objects or arrays are flattened to `null`.
+    */
+    template<typename BasicJsonType>
+    static void flatten(const string_t& reference_string,
+                        const BasicJsonType& value,
+                        BasicJsonType& result)
+    {
+        switch (value.type())
+        {
+            case detail::value_t::array:
+            {
+                if (value.m_value.array->empty())
+                {
+                    // flatten empty array as null
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate array and use index as reference string
+                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+                    {
+                        flatten(detail::concat(reference_string, '/', std::to_string(i)),
+                                value.m_value.array->operator[](i), result);
+                    }
+                }
+                break;
+            }
+
+            case detail::value_t::object:
+            {
+                if (value.m_value.object->empty())
+                {
+                    // flatten empty object as null
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate object and use keys as reference string
+                    for (const auto& element : *value.m_value.object)
+                    {
+                        flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
+                    }
+                }
+                break;
+            }
+
+            case detail::value_t::null:
+            case detail::value_t::string:
+            case detail::value_t::boolean:
+            case detail::value_t::number_integer:
+            case detail::value_t::number_unsigned:
+            case detail::value_t::number_float:
+            case detail::value_t::binary:
+            case detail::value_t::discarded:
+            default:
+            {
+                // add primitive value with its reference string
+                result[reference_string] = value;
+                break;
+            }
+        }
+    }
+
+    /*!
+    @param[in] value  flattened JSON
+
+    @return unflattened JSON
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.314  if value is not an object
+    @throw type_error.315  if object values are not primitive
+    @throw type_error.313  if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    static BasicJsonType
+    unflatten(const BasicJsonType& value)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
+        {
+            JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
+        }
+
+        BasicJsonType result;
+
+        // iterate the JSON object values
+        for (const auto& element : *value.m_value.object)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
+            {
+                JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
+            }
+
+            // assign value to reference pointed to by JSON pointer; Note that if
+            // the JSON pointer is "" (i.e., points to the whole value), function
+            // get_and_create returns a reference to result itself. An assignment
+            // will then create a primitive value.
+            json_pointer(element.first).get_and_create(result) = element.second;
+        }
+
+        return result;
+    }
+
+    // can't use conversion operator because of ambiguity
+    json_pointer<string_t> convert() const&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = reference_tokens;
+        return result;
+    }
+
+    json_pointer<string_t> convert()&&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = std::move(reference_tokens);
+        return result;
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs>
+    bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
+    {
+        return reference_tokens == rhs.reference_tokens;
+    }
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
+    bool operator==(const string_t& rhs) const
+    {
+        return *this == json_pointer(rhs);
+    }
+
+    /// @brief 3-way compares two JSON pointers
+    template<typename RefStringTypeRhs>
+    std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
+    {
+        return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*
+    }
+#else
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointers for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointer for less-than
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                          const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+#endif
+
+  private:
+    /// the reference tokens
+    std::vector<string_t> reference_tokens;
+};
+
+#if !JSON_HAS_THREE_WAY_COMPARISON
+// functions cannot be defined inside class due to ODR violations
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens == rhs.reference_tokens;
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return lhs == json_pointer<RefStringTypeLhs>(rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return json_pointer<RefStringTypeRhs>(lhs) == rhs;
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                      const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens < rhs.reference_tokens;
+}
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_ref.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <initializer_list>
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+class json_ref
+{
+  public:
+    using value_type = BasicJsonType;
+
+    json_ref(value_type&& value)
+        : owned_value(std::move(value))
+    {}
+
+    json_ref(const value_type& value)
+        : value_ref(&value)
+    {}
+
+    json_ref(std::initializer_list<json_ref> init)
+        : owned_value(init)
+    {}
+
+    template <
+        class... Args,
+        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
+    json_ref(Args && ... args)
+        : owned_value(std::forward<Args>(args)...)
+    {}
+
+    // class should be movable only
+    json_ref(json_ref&&) noexcept = default;
+    json_ref(const json_ref&) = delete;
+    json_ref& operator=(const json_ref&) = delete;
+    json_ref& operator=(json_ref&&) = delete;
+    ~json_ref() = default;
+
+    value_type moved_or_copied() const
+    {
+        if (value_ref == nullptr)
+        {
+            return std::move(owned_value);
+        }
+        return *value_ref;
+    }
+
+    value_type const& operator*() const
+    {
+        return value_ref ? *value_ref : owned_value;
+    }
+
+    value_type const* operator->() const
+    {
+        return &** this;
+    }
+
+  private:
+    mutable value_type owned_value = nullptr;
+    value_type const* value_ref = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <map> // map
+#include <cmath> // isnan, isinf
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <string> // basic_string
+#include <vector> // vector
+
+#ifndef JSON_NO_IO
+    #include <ios>      // streamsize
+    #include <ostream>  // basic_ostream
+#endif  // JSON_NO_IO
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+    virtual void write_character(CharType c) = 0;
+    virtual void write_characters(const CharType* s, std::size_t length) = 0;
+    virtual ~output_adapter_protocol() = default;
+
+    output_adapter_protocol() = default;
+    output_adapter_protocol(const output_adapter_protocol&) = default;
+    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
+    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
+    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType, typename AllocatorType = std::allocator<CharType>>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
+        : v(vec)
+    {}
+
+    void write_character(CharType c) override
+    {
+        v.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        v.insert(v.end(), s, s + length);
+    }
+
+  private:
+    std::vector<CharType, AllocatorType>& v;
+};
+
+#ifndef JSON_NO_IO
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
+        : stream(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        stream.put(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        stream.write(s, static_cast<std::streamsize>(length));
+    }
+
+  private:
+    std::basic_ostream<CharType>& stream;
+};
+#endif  // JSON_NO_IO
+
+/// output adapter for basic_string
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_string_adapter(StringType& s) noexcept
+        : str(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        str.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        str.append(s, length);
+    }
+
+  private:
+    StringType& str;
+};
+
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_adapter
+{
+  public:
+    template<typename AllocatorType = std::allocator<CharType>>
+    output_adapter(std::vector<CharType, AllocatorType>& vec)
+        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
+
+#ifndef JSON_NO_IO
+    output_adapter(std::basic_ostream<CharType>& s)
+        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+#endif  // JSON_NO_IO
+
+    output_adapter(StringType& s)
+        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+
+    operator output_adapter_t<CharType>()
+    {
+        return oa;
+    }
+
+  private:
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// binary writer //
+///////////////////
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+  public:
+    /*!
+    @brief create a binary writer
+
+    @param[in] adapter  output adapter to write to
+    */
+    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
+    {
+        JSON_ASSERT(oa);
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    @pre       j.type() == value_t::object
+    */
+    void write_bson(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+            {
+                write_bson_object(*j.m_value.object);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::array:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
+            }
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_cbor(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                oa->write_character(to_char_type(0xF6));
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                oa->write_character(j.m_value.boolean
+                                    ? to_char_type(0xF5)
+                                    : to_char_type(0xF4));
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                if (j.m_value.number_integer >= 0)
+                {
+                    // CBOR does not differentiate between positive signed
+                    // integers and unsigned integers. Therefore, we used the
+                    // code from the value_t::number_unsigned case here.
+                    if (j.m_value.number_integer <= 0x17)
+                    {
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x18));
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x19));
+                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x1A));
+                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x1B));
+                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+                    }
+                }
+                else
+                {
+                    // The conversions below encode the sign in the first
+                    // byte, and the value is converted to a positive number.
+                    const auto positive_number = -1 - j.m_value.number_integer;
+                    if (j.m_value.number_integer >= -24)
+                    {
+                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x38));
+                        write_number(static_cast<std::uint8_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x39));
+                        write_number(static_cast<std::uint16_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x3A));
+                        write_number(static_cast<std::uint32_t>(positive_number));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x3B));
+                        write_number(static_cast<std::uint64_t>(positive_number));
+                    }
+                }
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_value.number_unsigned <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x18));
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x19));
+                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x1A));
+                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));
+                }
+                else
+                {
+                    oa->write_character(to_char_type(0x1B));
+                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                if (std::isnan(j.m_value.number_float))
+                {
+                    // NaN is 0xf97e00 in CBOR
+                    oa->write_character(to_char_type(0xF9));
+                    oa->write_character(to_char_type(0x7E));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else if (std::isinf(j.m_value.number_float))
+                {
+                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00
+                    oa->write_character(to_char_type(0xf9));
+                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else
+                {
+                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);
+                }
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_value.string->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x60 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x78));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x79));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                // step 1: write control byte and the array size
+                const auto N = j.m_value.array->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x80 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x98));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x99));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_cbor(el);
+                }
+                break;
+            }
+
+            case value_t::binary:
+            {
+                if (j.m_value.binary->has_subtype())
+                {
+                    if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd8));
+                        write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd9));
+                        write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xda));
+                        write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xdb));
+                        write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));
+                    }
+                }
+
+                // step 1: write control byte and the binary array size
+                const auto N = j.m_value.binary->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x40 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x58));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x59));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                    N);
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // step 1: write control byte and the object size
+                const auto N = j.m_value.object->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0xA0 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB8));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB9));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBA));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBB));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_cbor(el.first);
+                    write_cbor(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_msgpack(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null: // nil
+            {
+                oa->write_character(to_char_type(0xC0));
+                break;
+            }
+
+            case value_t::boolean: // true and false
+            {
+                oa->write_character(j.m_value.boolean
+                                    ? to_char_type(0xC3)
+                                    : to_char_type(0xC2));
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                if (j.m_value.number_integer >= 0)
+                {
+                    // MessagePack does not differentiate between positive
+                    // signed integers and unsigned integers. Therefore, we used
+                    // the code from the value_t::number_unsigned case here.
+                    if (j.m_value.number_unsigned < 128)
+                    {
+                        // positive fixnum
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        // uint 8
+                        oa->write_character(to_char_type(0xCC));
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        // uint 16
+                        oa->write_character(to_char_type(0xCD));
+                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        // uint 32
+                        oa->write_character(to_char_type(0xCE));
+                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        // uint 64
+                        oa->write_character(to_char_type(0xCF));
+                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+                    }
+                }
+                else
+                {
+                    if (j.m_value.number_integer >= -32)
+                    {
+                        // negative fixnum
+                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                    {
+                        // int 8
+                        oa->write_character(to_char_type(0xD0));
+                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                    {
+                        // int 16
+                        oa->write_character(to_char_type(0xD1));
+                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                    {
+                        // int 32
+                        oa->write_character(to_char_type(0xD2));
+                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                    {
+                        // int 64
+                        oa->write_character(to_char_type(0xD3));
+                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));
+                    }
+                }
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_value.number_unsigned < 128)
+                {
+                    // positive fixnum
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // uint 8
+                    oa->write_character(to_char_type(0xCC));
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // uint 16
+                    oa->write_character(to_char_type(0xCD));
+                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // uint 32
+                    oa->write_character(to_char_type(0xCE));
+                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    // uint 64
+                    oa->write_character(to_char_type(0xCF));
+                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_value.string->size();
+                if (N <= 31)
+                {
+                    // fixstr
+                    write_number(static_cast<std::uint8_t>(0xA0 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // str 8
+                    oa->write_character(to_char_type(0xD9));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // str 16
+                    oa->write_character(to_char_type(0xDA));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // str 32
+                    oa->write_character(to_char_type(0xDB));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                // step 1: write control byte and the array size
+                const auto N = j.m_value.array->size();
+                if (N <= 15)
+                {
+                    // fixarray
+                    write_number(static_cast<std::uint8_t>(0x90 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // array 16
+                    oa->write_character(to_char_type(0xDC));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // array 32
+                    oa->write_character(to_char_type(0xDD));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_msgpack(el);
+                }
+                break;
+            }
+
+            case value_t::binary:
+            {
+                // step 0: determine if the binary type has a set subtype to
+                // determine whether or not to use the ext or fixext types
+                const bool use_ext = j.m_value.binary->has_subtype();
+
+                // step 1: write control byte and the byte string length
+                const auto N = j.m_value.binary->size();
+                if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    std::uint8_t output_type{};
+                    bool fixed = true;
+                    if (use_ext)
+                    {
+                        switch (N)
+                        {
+                            case 1:
+                                output_type = 0xD4; // fixext 1
+                                break;
+                            case 2:
+                                output_type = 0xD5; // fixext 2
+                                break;
+                            case 4:
+                                output_type = 0xD6; // fixext 4
+                                break;
+                            case 8:
+                                output_type = 0xD7; // fixext 8
+                                break;
+                            case 16:
+                                output_type = 0xD8; // fixext 16
+                                break;
+                            default:
+                                output_type = 0xC7; // ext 8
+                                fixed = false;
+                                break;
+                        }
+
+                    }
+                    else
+                    {
+                        output_type = 0xC4; // bin 8
+                        fixed = false;
+                    }
+
+                    oa->write_character(to_char_type(output_type));
+                    if (!fixed)
+                    {
+                        write_number(static_cast<std::uint8_t>(N));
+                    }
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    std::uint8_t output_type = use_ext
+                                               ? 0xC8 // ext 16
+                                               : 0xC5; // bin 16
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    std::uint8_t output_type = use_ext
+                                               ? 0xC9 // ext 32
+                                               : 0xC6; // bin 32
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 1.5: if this is an ext type, write the subtype
+                if (use_ext)
+                {
+                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));
+                }
+
+                // step 2: write the byte string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                    N);
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // step 1: write control byte and the object size
+                const auto N = j.m_value.object->size();
+                if (N <= 15)
+                {
+                    // fixmap
+                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // map 16
+                    oa->write_character(to_char_type(0xDE));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // map 32
+                    oa->write_character(to_char_type(0xDF));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_msgpack(el.first);
+                    write_msgpack(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    @param[in] use_count   whether to use '#' prefixes (optimized format)
+    @param[in] use_type    whether to use '$' prefixes (optimized format)
+    @param[in] add_prefix  whether prefixes need to be used for this value
+    @param[in] use_bjdata  whether write in BJData format, default is false
+    */
+    void write_ubjson(const BasicJsonType& j, const bool use_count,
+                      const bool use_type, const bool add_prefix = true,
+                      const bool use_bjdata = false)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('Z'));
+                }
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(j.m_value.boolean
+                                        ? to_char_type('T')
+                                        : to_char_type('F'));
+                }
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::string:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('S'));
+                }
+                write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata);
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_value.array->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::binary:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                if (use_type && !j.m_value.binary->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    oa->write_character(to_char_type('$'));
+                    oa->write_character('U');
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata);
+                }
+
+                if (use_type)
+                {
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                        j.m_value.binary->size());
+                }
+                else
+                {
+                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)
+                    {
+                        oa->write_character(to_char_type('U'));
+                        oa->write_character(j.m_value.binary->data()[i]);
+                    }
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end())
+                {
+                    if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type))  // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
+                    {
+                        break;
+                    }
+                }
+
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('{'));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_value.object->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin(), j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(el.first.c_str()),
+                        el.first.size());
+                    write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type('}'));
+                }
+
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @return The size of a BSON document entry header, including the id marker
+            and the entry name size (and its null-terminator).
+    */
+    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
+    {
+        const auto it = name.find(static_cast<typename string_t::value_type>(0));
+        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
+        {
+            JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
+            static_cast<void>(j);
+        }
+
+        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
+    }
+
+    /*!
+    @brief Writes the given @a element_type and @a name to the output adapter
+    */
+    void write_bson_entry_header(const string_t& name,
+                                 const std::uint8_t element_type)
+    {
+        oa->write_character(to_char_type(element_type)); // boolean
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(name.c_str()),
+            name.size() + 1u);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and boolean value @a value
+    */
+    void write_bson_boolean(const string_t& name,
+                            const bool value)
+    {
+        write_bson_entry_header(name, 0x08);
+        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and double value @a value
+    */
+    void write_bson_double(const string_t& name,
+                           const double value)
+    {
+        write_bson_entry_header(name, 0x01);
+        write_number<double>(value, true);
+    }
+
+    /*!
+    @return The size of the BSON-encoded string in @a value
+    */
+    static std::size_t calc_bson_string_size(const string_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and string value @a value
+    */
+    void write_bson_string(const string_t& name,
+                           const string_t& value)
+    {
+        write_bson_entry_header(name, 0x02);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(value.c_str()),
+            value.size() + 1);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and null value
+    */
+    void write_bson_null(const string_t& name)
+    {
+        write_bson_entry_header(name, 0x0A);
+    }
+
+    /*!
+    @return The size of the BSON-encoded integer @a value
+    */
+    static std::size_t calc_bson_integer_size(const std::int64_t value)
+    {
+        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and integer @a value
+    */
+    void write_bson_integer(const string_t& name,
+                            const std::int64_t value)
+    {
+        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            write_bson_entry_header(name, 0x10); // int32
+            write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
+        }
+        else
+        {
+            write_bson_entry_header(name, 0x12); // int64
+            write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
+        }
+    }
+
+    /*!
+    @return The size of the BSON-encoded unsigned integer in @a j
+    */
+    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
+    {
+        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and unsigned @a value
+    */
+    void write_bson_unsigned(const string_t& name,
+                             const BasicJsonType& j)
+    {
+        if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x10 /* int32 */);
+            write_number<std::int32_t>(static_cast<std::int32_t>(j.m_value.number_unsigned), true);
+        }
+        else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x12 /* int64 */);
+            write_number<std::int64_t>(static_cast<std::int64_t>(j.m_value.number_unsigned), true);
+        }
+        else
+        {
+            JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
+        }
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and object @a value
+    */
+    void write_bson_object_entry(const string_t& name,
+                                 const typename BasicJsonType::object_t& value)
+    {
+        write_bson_entry_header(name, 0x03); // object
+        write_bson_object(value);
+    }
+
+    /*!
+    @return The size of the BSON-encoded array @a value
+    */
+    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
+    {
+        std::size_t array_index = 0ul;
+
+        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
+        {
+            return result + calc_bson_element_size(std::to_string(array_index++), el);
+        });
+
+        return sizeof(std::int32_t) + embedded_document_size + 1ul;
+    }
+
+    /*!
+    @return The size of the BSON-encoded binary array @a value
+    */
+    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and array @a value
+    */
+    void write_bson_array(const string_t& name,
+                          const typename BasicJsonType::array_t& value)
+    {
+        write_bson_entry_header(name, 0x04); // array
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
+
+        std::size_t array_index = 0ul;
+
+        for (const auto& el : value)
+        {
+            write_bson_element(std::to_string(array_index++), el);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and binary value @a value
+    */
+    void write_bson_binary(const string_t& name,
+                           const binary_t& value)
+    {
+        write_bson_entry_header(name, 0x05);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
+        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
+
+        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
+    }
+
+    /*!
+    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
+    @return The calculated size for the BSON document entry for @a j with the given @a name.
+    */
+    static std::size_t calc_bson_element_size(const string_t& name,
+            const BasicJsonType& j)
+    {
+        const auto header_size = calc_bson_entry_header_size(name, j);
+        switch (j.type())
+        {
+            case value_t::object:
+                return header_size + calc_bson_object_size(*j.m_value.object);
+
+            case value_t::array:
+                return header_size + calc_bson_array_size(*j.m_value.array);
+
+            case value_t::binary:
+                return header_size + calc_bson_binary_size(*j.m_value.binary);
+
+            case value_t::boolean:
+                return header_size + 1ul;
+
+            case value_t::number_float:
+                return header_size + 8ul;
+
+            case value_t::number_integer:
+                return header_size + calc_bson_integer_size(j.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
+
+            case value_t::string:
+                return header_size + calc_bson_string_size(*j.m_value.string);
+
+            case value_t::null:
+                return header_size + 0ul;
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return 0ul;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Serializes the JSON value @a j to BSON and associates it with the
+           key @a name.
+    @param name The name to associate with the JSON entity @a j within the
+                current BSON document
+    */
+    void write_bson_element(const string_t& name,
+                            const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+                return write_bson_object_entry(name, *j.m_value.object);
+
+            case value_t::array:
+                return write_bson_array(name, *j.m_value.array);
+
+            case value_t::binary:
+                return write_bson_binary(name, *j.m_value.binary);
+
+            case value_t::boolean:
+                return write_bson_boolean(name, j.m_value.boolean);
+
+            case value_t::number_float:
+                return write_bson_double(name, j.m_value.number_float);
+
+            case value_t::number_integer:
+                return write_bson_integer(name, j.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return write_bson_unsigned(name, j);
+
+            case value_t::string:
+                return write_bson_string(name, *j.m_value.string);
+
+            case value_t::null:
+                return write_bson_null(name);
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Calculates the size of the BSON serialization of the given
+           JSON-object @a j.
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
+    {
+        std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
+                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)
+        {
+            return result += calc_bson_element_size(el.first, el.second);
+        });
+
+        return sizeof(std::int32_t) + document_size + 1ul;
+    }
+
+    /*!
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    void write_bson_object(const typename BasicJsonType::object_t& value)
+    {
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
+
+        for (const auto& el : value)
+        {
+            write_bson_element(el.first, el.second);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    static constexpr CharType get_cbor_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xFA);  // Single-Precision Float
+    }
+
+    static constexpr CharType get_cbor_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xFB);  // Double-Precision Float
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xCA);  // float 32
+    }
+
+    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xCB);  // float 64
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    // UBJSON: write number (floating point)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_floating_point<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if (add_prefix)
+        {
+            oa->write_character(get_ubjson_float_prefix(n));
+        }
+        write_number(n, use_bjdata);
+    }
+
+    // UBJSON: write number (unsigned integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_unsigned<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= (std::numeric_limits<std::uint8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<std::uint16_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<std::uint32_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('M'));  // uint64 - bjdata only
+            }
+            write_number(static_cast<std::uint64_t>(n), use_bjdata);
+        }
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+    }
+
+    // UBJSON: write number (signed integer)
+    template < typename NumberType, typename std::enable_if <
+                   std::is_signed<NumberType>::value&&
+                   !std::is_floating_point<NumberType>::value, int >::type = 0 >
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::int8_t>(n), use_bjdata);
+        }
+        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<uint16_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<uint32_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        // LCOV_EXCL_START
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+        // LCOV_EXCL_STOP
+    }
+
+    /*!
+    @brief determine the type prefix of container values
+    */
+    CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+                return 'Z';
+
+            case value_t::boolean:
+                return j.m_value.boolean ? 'T' : 'F';
+
+            case value_t::number_integer:
+            {
+                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                {
+                    return 'i';
+                }
+                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    return 'U';
+                }
+                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                {
+                    return 'I';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                {
+                    return 'l';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                {
+                    return 'L';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+                {
+                    return 'i';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
+                {
+                    return 'U';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+                {
+                    return 'I';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+                {
+                    return 'l';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+                {
+                    return 'L';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    return 'M';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            case value_t::number_float:
+                return get_ubjson_float_prefix(j.m_value.number_float);
+
+            case value_t::string:
+                return 'S';
+
+            case value_t::array: // fallthrough
+            case value_t::binary:
+                return '[';
+
+            case value_t::object:
+                return '{';
+
+            case value_t::discarded:
+            default:  // discarded values
+                return 'N';
+        }
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
+    {
+        return 'd';  // float 32
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
+    {
+        return 'D';  // float 64
+    }
+
+    /*!
+    @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
+    */
+    bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
+    {
+        std::map<string_t, CharType> bjdtype = {{"uint8", 'U'},  {"int8", 'i'},  {"uint16", 'u'}, {"int16", 'I'},
+            {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'}
+        };
+
+        string_t key = "_ArrayType_";
+        auto it = bjdtype.find(static_cast<string_t>(value.at(key)));
+        if (it == bjdtype.end())
+        {
+            return true;
+        }
+        CharType dtype = it->second;
+
+        key = "_ArraySize_";
+        std::size_t len = (value.at(key).empty() ? 0 : 1);
+        for (const auto& el : value.at(key))
+        {
+            len *= static_cast<std::size_t>(el.m_value.number_unsigned);
+        }
+
+        key = "_ArrayData_";
+        if (value.at(key).size() != len)
+        {
+            return true;
+        }
+
+        oa->write_character('[');
+        oa->write_character('$');
+        oa->write_character(dtype);
+        oa->write_character('#');
+
+        key = "_ArraySize_";
+        write_ubjson(value.at(key), use_count, use_type, true,  true);
+
+        key = "_ArrayData_";
+        if (dtype == 'U' || dtype == 'C')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint8_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'i')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int8_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'u')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint16_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'I')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int16_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'm')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint32_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'l')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int32_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'M')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint64_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'L')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int64_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'd')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<float>(el.m_value.number_float), true);
+            }
+        }
+        else if (dtype == 'D')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<double>(el.m_value.number_float), true);
+            }
+        }
+        return false;
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*
+    @brief write a number to output input
+    @param[in] n number of type @a NumberType
+    @param[in] OutputIsLittleEndian Set to true if output data is
+                                 required to be little endian
+    @tparam NumberType the type of the number
+
+    @note This function needs to respect the system's endianness, because bytes
+          in CBOR, MessagePack, and UBJSON are stored in network order (big
+          endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType>
+    void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
+    {
+        // step 1: write number to array of length NumberType
+        std::array<CharType, sizeof(NumberType)> vec{};
+        std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+        // step 2: write array to output (with possible reordering)
+        if (is_little_endian != OutputIsLittleEndian)
+        {
+            // reverse byte order prior to conversion if necessary
+            std::reverse(vec.begin(), vec.end());
+        }
+
+        oa->write_characters(vec.data(), sizeof(NumberType));
+    }
+
+    void write_compact_float(const number_float_t n, detail::input_format_t format)
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(static_cast<float>(n))
+                                : get_msgpack_float_prefix(static_cast<float>(n)));
+            write_number(static_cast<float>(n));
+        }
+        else
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(n)
+                                : get_msgpack_float_prefix(n));
+            write_number(n);
+        }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+  public:
+    // The following to_char_type functions are implement the conversion
+    // between uint8_t and CharType. In case CharType is not unsigned,
+    // such a conversion is required to allow values greater than 128.
+    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return *reinterpret_cast<char*>(&x);
+    }
+
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
+    static CharType to_char_type(std::uint8_t x) noexcept
+    {
+        static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
+        static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
+        CharType result;
+        std::memcpy(&result, &x, sizeof(x));
+        return result;
+    }
+
+    template<typename C = CharType,
+             enable_if_t<std::is_unsigned<C>::value>* = nullptr>
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return x;
+    }
+
+    template < typename InputCharType, typename C = CharType,
+               enable_if_t <
+                   std::is_signed<C>::value &&
+                   std::is_signed<char>::value &&
+                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
+                   > * = nullptr >
+    static constexpr CharType to_char_type(InputCharType x) noexcept
+    {
+        return x;
+    }
+
+  private:
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// the output
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/output/serializer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <limits> // numeric_limits
+#include <string> // string, char_traits
+#include <iomanip> // setfill, setw
+#include <type_traits> // is_same
+#include <utility> // move
+
+// #include <nlohmann/detail/conversions/to_chars.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cmath>   // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+#include <limits> // numeric_limits
+#include <type_traits> // conditional
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+    Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+    Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+    Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template<typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+    static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+    Target target;
+    std::memcpy(&target, &source, sizeof(Source));
+    return target;
+}
+
+struct diyfp // f * 2^e
+{
+    static constexpr int kPrecision = 64; // = q
+
+    std::uint64_t f = 0;
+    int e = 0;
+
+    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+    /*!
+    @brief returns x - y
+    @pre x.e == y.e and x.f >= y.f
+    */
+    static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+    {
+        JSON_ASSERT(x.e == y.e);
+        JSON_ASSERT(x.f >= y.f);
+
+        return {x.f - y.f, x.e};
+    }
+
+    /*!
+    @brief returns x * y
+    @note The result is rounded. (Only the upper q bits are returned.)
+    */
+    static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+    {
+        static_assert(kPrecision == 64, "internal error");
+
+        // Computes:
+        //  f = round((x.f * y.f) / 2^q)
+        //  e = x.e + y.e + q
+
+        // Emulate the 64-bit * 64-bit multiplication:
+        //
+        // p = u * v
+        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )
+        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )
+        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )
+        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)
+        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )
+        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )
+        //
+        // (Since Q might be larger than 2^32 - 1)
+        //
+        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+        //
+        // (Q_hi + H does not overflow a 64-bit int)
+        //
+        //   = p_lo + 2^64 p_hi
+
+        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+        const std::uint64_t u_hi = x.f >> 32u;
+        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+        const std::uint64_t v_hi = y.f >> 32u;
+
+        const std::uint64_t p0 = u_lo * v_lo;
+        const std::uint64_t p1 = u_lo * v_hi;
+        const std::uint64_t p2 = u_hi * v_lo;
+        const std::uint64_t p3 = u_hi * v_hi;
+
+        const std::uint64_t p0_hi = p0 >> 32u;
+        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+        const std::uint64_t p1_hi = p1 >> 32u;
+        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+        const std::uint64_t p2_hi = p2 >> 32u;
+
+        std::uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+        // The full product might now be computed as
+        //
+        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+        // p_lo = p0_lo + (Q << 32)
+        //
+        // But in this particular case here, the full p_lo is not required.
+        // Effectively we only need to add the highest bit in p_lo to p_hi (and
+        // Q_hi + 1 does not overflow).
+
+        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+        return {h, x.e + y.e + 64};
+    }
+
+    /*!
+    @brief normalize x such that the significand is >= 2^(q-1)
+    @pre x.f != 0
+    */
+    static diyfp normalize(diyfp x) noexcept
+    {
+        JSON_ASSERT(x.f != 0);
+
+        while ((x.f >> 63u) == 0)
+        {
+            x.f <<= 1u;
+            x.e--;
+        }
+
+        return x;
+    }
+
+    /*!
+    @brief normalize x such that the result has the exponent E
+    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+    */
+    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+    {
+        const int delta = x.e - target_exponent;
+
+        JSON_ASSERT(delta >= 0);
+        JSON_ASSERT(((x.f << delta) >> delta) == x.f);
+
+        return {x.f << delta, target_exponent};
+    }
+};
+
+struct boundaries
+{
+    diyfp w;
+    diyfp minus;
+    diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template<typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
+
+    // Convert the IEEE representation into a diyfp.
+    //
+    // If v is denormal:
+    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))
+    // If v is normalized:
+    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+    static_assert(std::numeric_limits<FloatType>::is_iec559,
+                  "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+    constexpr int      kMinExp    = 1 - kBias;
+    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
+
+    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
+    const std::uint64_t E = bits >> (kPrecision - 1);
+    const std::uint64_t F = bits & (kHiddenBit - 1);
+
+    const bool is_denormal = E == 0;
+    const diyfp v = is_denormal
+                    ? diyfp(F, kMinExp)
+                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+    // Compute the boundaries m- and m+ of the floating-point value
+    // v = f * 2^e.
+    //
+    // Determine v- and v+, the floating-point predecessor and successor if v,
+    // respectively.
+    //
+    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)
+    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)
+    //
+    //      v+ = v + 2^e
+    //
+    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+    // between m- and m+ round to v, regardless of how the input rounding
+    // algorithm breaks ties.
+    //
+    //      ---+-------------+-------------+-------------+-------------+---  (A)
+    //         v-            m-            v             m+            v+
+    //
+    //      -----------------+------+------+-------------+-------------+---  (B)
+    //                       v-     m-     v             m+            v+
+
+    const bool lower_boundary_is_closer = F == 0 && E > 1;
+    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+    const diyfp m_minus = lower_boundary_is_closer
+                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)
+                          : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+    // Determine the normalized w+ = m+.
+    const diyfp w_plus = diyfp::normalize(m_plus);
+
+    // Determine w- = m- such that e_(w-) = e_(w+).
+    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+    return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+//      alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+//                          <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+//              = (f div 2^-e) + (f mod 2^-e) * 2^e
+//              = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+//      -e >= 32   or   e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+//      (10 * p2) div 2^-e = d[-1]
+//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+//      -e <= 60   or   e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+    std::uint64_t f;
+    int e;
+    int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+     alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+    // Now
+    //
+    //      alpha <= e_c + e + q <= gamma                                    (1)
+    //      ==> f_c * 2^alpha <= c * 2^e * 2^q
+    //
+    // and since the c's are normalized, 2^(q-1) <= f_c,
+    //
+    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+    //      ==> 2^(alpha - e - 1) <= c
+    //
+    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as
+    //
+    //      k = ceil( log_10( 2^(alpha - e - 1) ) )
+    //        = ceil( (alpha - e - 1) * log_10(2) )
+    //
+    // From the paper:
+    // "In theory the result of the procedure could be wrong since c is rounded,
+    //  and the computation itself is approximated [...]. In practice, however,
+    //  this simple function is sufficient."
+    //
+    // For IEEE double precision floating-point numbers converted into
+    // normalized diyfp's w = f * 2^e, with q = 64,
+    //
+    //      e >= -1022      (min IEEE exponent)
+    //           -52        (p - 1)
+    //           -52        (p - 1, possibly normalize denormal IEEE numbers)
+    //           -11        (normalize the diyfp)
+    //         = -1137
+    //
+    // and
+    //
+    //      e <= +1023      (max IEEE exponent)
+    //           -52        (p - 1)
+    //           -11        (normalize the diyfp)
+    //         = 960
+    //
+    // This binary exponent range [-1137,960] results in a decimal exponent
+    // range [-307,324]. One does not need to store a cached power for each
+    // k in this range. For each such k it suffices to find a cached power
+    // such that the exponent of the product lies in [alpha,gamma].
+    // This implies that the difference of the decimal exponents of adjacent
+    // table entries must be less than or equal to
+    //
+    //      floor( (gamma - alpha) * log_10(2) ) = 8.
+    //
+    // (A smaller distance gamma-alpha would require a larger table.)
+
+    // NB:
+    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+    constexpr int kCachedPowersMinDecExp = -300;
+    constexpr int kCachedPowersDecStep = 8;
+
+    static constexpr std::array<cached_power, 79> kCachedPowers =
+    {
+        {
+            { 0xAB70FE17C79AC6CA, -1060, -300 },
+            { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+            { 0xBE5691EF416BD60C, -1007, -284 },
+            { 0x8DD01FAD907FFC3C,  -980, -276 },
+            { 0xD3515C2831559A83,  -954, -268 },
+            { 0x9D71AC8FADA6C9B5,  -927, -260 },
+            { 0xEA9C227723EE8BCB,  -901, -252 },
+            { 0xAECC49914078536D,  -874, -244 },
+            { 0x823C12795DB6CE57,  -847, -236 },
+            { 0xC21094364DFB5637,  -821, -228 },
+            { 0x9096EA6F3848984F,  -794, -220 },
+            { 0xD77485CB25823AC7,  -768, -212 },
+            { 0xA086CFCD97BF97F4,  -741, -204 },
+            { 0xEF340A98172AACE5,  -715, -196 },
+            { 0xB23867FB2A35B28E,  -688, -188 },
+            { 0x84C8D4DFD2C63F3B,  -661, -180 },
+            { 0xC5DD44271AD3CDBA,  -635, -172 },
+            { 0x936B9FCEBB25C996,  -608, -164 },
+            { 0xDBAC6C247D62A584,  -582, -156 },
+            { 0xA3AB66580D5FDAF6,  -555, -148 },
+            { 0xF3E2F893DEC3F126,  -529, -140 },
+            { 0xB5B5ADA8AAFF80B8,  -502, -132 },
+            { 0x87625F056C7C4A8B,  -475, -124 },
+            { 0xC9BCFF6034C13053,  -449, -116 },
+            { 0x964E858C91BA2655,  -422, -108 },
+            { 0xDFF9772470297EBD,  -396, -100 },
+            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },
+            { 0xF8A95FCF88747D94,  -343,  -84 },
+            { 0xB94470938FA89BCF,  -316,  -76 },
+            { 0x8A08F0F8BF0F156B,  -289,  -68 },
+            { 0xCDB02555653131B6,  -263,  -60 },
+            { 0x993FE2C6D07B7FAC,  -236,  -52 },
+            { 0xE45C10C42A2B3B06,  -210,  -44 },
+            { 0xAA242499697392D3,  -183,  -36 },
+            { 0xFD87B5F28300CA0E,  -157,  -28 },
+            { 0xBCE5086492111AEB,  -130,  -20 },
+            { 0x8CBCCC096F5088CC,  -103,  -12 },
+            { 0xD1B71758E219652C,   -77,   -4 },
+            { 0x9C40000000000000,   -50,    4 },
+            { 0xE8D4A51000000000,   -24,   12 },
+            { 0xAD78EBC5AC620000,     3,   20 },
+            { 0x813F3978F8940984,    30,   28 },
+            { 0xC097CE7BC90715B3,    56,   36 },
+            { 0x8F7E32CE7BEA5C70,    83,   44 },
+            { 0xD5D238A4ABE98068,   109,   52 },
+            { 0x9F4F2726179A2245,   136,   60 },
+            { 0xED63A231D4C4FB27,   162,   68 },
+            { 0xB0DE65388CC8ADA8,   189,   76 },
+            { 0x83C7088E1AAB65DB,   216,   84 },
+            { 0xC45D1DF942711D9A,   242,   92 },
+            { 0x924D692CA61BE758,   269,  100 },
+            { 0xDA01EE641A708DEA,   295,  108 },
+            { 0xA26DA3999AEF774A,   322,  116 },
+            { 0xF209787BB47D6B85,   348,  124 },
+            { 0xB454E4A179DD1877,   375,  132 },
+            { 0x865B86925B9BC5C2,   402,  140 },
+            { 0xC83553C5C8965D3D,   428,  148 },
+            { 0x952AB45CFA97A0B3,   455,  156 },
+            { 0xDE469FBD99A05FE3,   481,  164 },
+            { 0xA59BC234DB398C25,   508,  172 },
+            { 0xF6C69A72A3989F5C,   534,  180 },
+            { 0xB7DCBF5354E9BECE,   561,  188 },
+            { 0x88FCF317F22241E2,   588,  196 },
+            { 0xCC20CE9BD35C78A5,   614,  204 },
+            { 0x98165AF37B2153DF,   641,  212 },
+            { 0xE2A0B5DC971F303A,   667,  220 },
+            { 0xA8D9D1535CE3B396,   694,  228 },
+            { 0xFB9B7CD9A4A7443C,   720,  236 },
+            { 0xBB764C4CA7A44410,   747,  244 },
+            { 0x8BAB8EEFB6409C1A,   774,  252 },
+            { 0xD01FEF10A657842C,   800,  260 },
+            { 0x9B10A4E5E9913129,   827,  268 },
+            { 0xE7109BFBA19C0C9D,   853,  276 },
+            { 0xAC2820D9623BF429,   880,  284 },
+            { 0x80444B5E7AA7CF85,   907,  292 },
+            { 0xBF21E44003ACDD2D,   933,  300 },
+            { 0x8E679C2F5E44FF8F,   960,  308 },
+            { 0xD433179D9C8CB841,   986,  316 },
+            { 0x9E19DB92B4E31BA9,  1013,  324 },
+        }
+    };
+
+    // This computation gives exactly the same results for k as
+    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+    // for |e| <= 1500, but doesn't require floating-point operations.
+    // NB: log_10(2) ~= 78913 / 2^18
+    JSON_ASSERT(e >= -1500);
+    JSON_ASSERT(e <=  1500);
+    const int f = kAlpha - e - 1;
+    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
+
+    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+    JSON_ASSERT(index >= 0);
+    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
+
+    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+    JSON_ASSERT(kAlpha <= cached.e + e + 64);
+    JSON_ASSERT(kGamma >= cached.e + e + 64);
+
+    return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
+{
+    // LCOV_EXCL_START
+    if (n >= 1000000000)
+    {
+        pow10 = 1000000000;
+        return 10;
+    }
+    // LCOV_EXCL_STOP
+    if (n >= 100000000)
+    {
+        pow10 = 100000000;
+        return  9;
+    }
+    if (n >= 10000000)
+    {
+        pow10 = 10000000;
+        return  8;
+    }
+    if (n >= 1000000)
+    {
+        pow10 = 1000000;
+        return  7;
+    }
+    if (n >= 100000)
+    {
+        pow10 = 100000;
+        return  6;
+    }
+    if (n >= 10000)
+    {
+        pow10 = 10000;
+        return  5;
+    }
+    if (n >= 1000)
+    {
+        pow10 = 1000;
+        return  4;
+    }
+    if (n >= 100)
+    {
+        pow10 = 100;
+        return  3;
+    }
+    if (n >= 10)
+    {
+        pow10 = 10;
+        return  2;
+    }
+
+    pow10 = 1;
+    return 1;
+}
+
+inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
+                         std::uint64_t rest, std::uint64_t ten_k)
+{
+    JSON_ASSERT(len >= 1);
+    JSON_ASSERT(dist <= delta);
+    JSON_ASSERT(rest <= delta);
+    JSON_ASSERT(ten_k > 0);
+
+    //               <--------------------------- delta ---->
+    //                                  <---- dist --------->
+    // --------------[------------------+-------------------]--------------
+    //               M-                 w                   M+
+    //
+    //                                  ten_k
+    //                                <------>
+    //                                       <---- rest ---->
+    // --------------[------------------+----+--------------]--------------
+    //                                  w    V
+    //                                       = buf * 10^k
+    //
+    // ten_k represents a unit-in-the-last-place in the decimal representation
+    // stored in buf.
+    // Decrement buf by ten_k while this takes buf closer to w.
+
+    // The tests are written in this order to avoid overflow in unsigned
+    // integer arithmetic.
+
+    while (rest < dist
+            && delta - rest >= ten_k
+            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
+    {
+        JSON_ASSERT(buf[len - 1] != '0');
+        buf[len - 1]--;
+        rest += ten_k;
+    }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+                             diyfp M_minus, diyfp w, diyfp M_plus)
+{
+    static_assert(kAlpha >= -60, "internal error");
+    static_assert(kGamma <= -32, "internal error");
+
+    // Generates the digits (and the exponent) of a decimal floating-point
+    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+    //
+    //               <--------------------------- delta ---->
+    //                                  <---- dist --------->
+    // --------------[------------------+-------------------]--------------
+    //               M-                 w                   M+
+    //
+    // Grisu2 generates the digits of M+ from left to right and stops as soon as
+    // V is in [M-,M+].
+
+    JSON_ASSERT(M_plus.e >= kAlpha);
+    JSON_ASSERT(M_plus.e <= kGamma);
+
+    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+    //
+    //      M+ = f * 2^e
+    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+    //         = ((p1        ) * 2^-e + (p2        )) * 2^e
+    //         = p1 + p2 * 2^e
+
+    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e
+
+    // 1)
+    //
+    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+    JSON_ASSERT(p1 > 0);
+
+    std::uint32_t pow10{};
+    const int k = find_largest_pow10(p1, pow10);
+
+    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+    //
+    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))
+    //
+    //      M+ = p1                                             + p2 * 2^e
+    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e
+    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e
+    //
+    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+    //
+    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+    //
+    // but stop as soon as
+    //
+    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+    int n = k;
+    while (n > 0)
+    {
+        // Invariants:
+        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)
+        //      pow10 = 10^(n-1) <= p1 < 10^n
+        //
+        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)
+        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)
+        //
+        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+        //
+        JSON_ASSERT(d <= 9);
+        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+        //
+        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+        //
+        p1 = r;
+        n--;
+        //
+        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)
+        //      pow10 = 10^n
+        //
+
+        // Now check if enough digits have been generated.
+        // Compute
+        //
+        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+        //
+        // Note:
+        // Since rest and delta share the same exponent e, it suffices to
+        // compare the significands.
+        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;
+        if (rest <= delta)
+        {
+            // V = buffer * 10^n, with M- <= V <= M+.
+
+            decimal_exponent += n;
+
+            // We may now just stop. But instead look if the buffer could be
+            // decremented to bring V closer to w.
+            //
+            // pow10 = 10^n is now 1 ulp in the decimal representation V.
+            // The rounding procedure works with diyfp's with an implicit
+            // exponent of e.
+            //
+            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+            //
+            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;
+            grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+            return;
+        }
+
+        pow10 /= 10;
+        //
+        //      pow10 = 10^(n-1) <= p1 < 10^n
+        // Invariants restored.
+    }
+
+    // 2)
+    //
+    // The digits of the integral part have been generated:
+    //
+    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+    //         = buffer            + p2 * 2^e
+    //
+    // Now generate the digits of the fractional part p2 * 2^e.
+    //
+    // Note:
+    // No decimal point is generated: the exponent is adjusted instead.
+    //
+    // p2 actually represents the fraction
+    //
+    //      p2 * 2^e
+    //          = p2 / 2^-e
+    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...
+    //
+    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+    //
+    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+    //
+    // using
+    //
+    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+    //                = (                   d) * 2^-e + (                   r)
+    //
+    // or
+    //      10^m * p2 * 2^e = d + r * 2^e
+    //
+    // i.e.
+    //
+    //      M+ = buffer + p2 * 2^e
+    //         = buffer + 10^-m * (d + r * 2^e)
+    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+    //
+    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+    JSON_ASSERT(p2 > delta);
+
+    int m = 0;
+    for (;;)
+    {
+        // Invariant:
+        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+        //
+        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+        p2 *= 10;
+        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
+        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+        //
+        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+        //
+        JSON_ASSERT(d <= 9);
+        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+        //
+        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+        //
+        p2 = r;
+        m++;
+        //
+        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+        // Invariant restored.
+
+        // Check if enough digits have been generated.
+        //
+        //      10^-m * p2 * 2^e <= delta * 2^e
+        //              p2 * 2^e <= 10^m * delta * 2^e
+        //                    p2 <= 10^m * delta
+        delta *= 10;
+        dist  *= 10;
+        if (p2 <= delta)
+        {
+            break;
+        }
+    }
+
+    // V = buffer * 10^-m, with M- <= V <= M+.
+
+    decimal_exponent -= m;
+
+    // 1 ulp in the decimal representation is now 10^-m.
+    // Since delta and dist are now scaled by 10^m, we need to do the
+    // same with ulp in order to keep the units in sync.
+    //
+    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+    //
+    const std::uint64_t ten_m = one.f;
+    grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+    // By construction this algorithm generates the shortest possible decimal
+    // number (Loitsch, Theorem 6.2) which rounds back to w.
+    // For an input number of precision p, at least
+    //
+    //      N = 1 + ceil(p * log_10(2))
+    //
+    // decimal digits are sufficient to identify all binary floating-point
+    // numbers (Matula, "In-and-Out conversions").
+    // This implies that the algorithm does not produce more than N decimal
+    // digits.
+    //
+    //      N = 17 for p = 53 (IEEE double precision)
+    //      N = 9  for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+JSON_HEDLEY_NON_NULL(1)
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+                   diyfp m_minus, diyfp v, diyfp m_plus)
+{
+    JSON_ASSERT(m_plus.e == m_minus.e);
+    JSON_ASSERT(m_plus.e == v.e);
+
+    //  --------(-----------------------+-----------------------)--------    (A)
+    //          m-                      v                       m+
+    //
+    //  --------------------(-----------+-----------------------)--------    (B)
+    //                      m-          v                       m+
+    //
+    // First scale v (and m- and m+) such that the exponent is in the range
+    // [alpha, gamma].
+
+    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+    const diyfp w       = diyfp::mul(v,       c_minus_k);
+    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);
+
+    //  ----(---+---)---------------(---+---)---------------(---+---)----
+    //          w-                      w                       w+
+    //          = c*m-                  = c*v                   = c*m+
+    //
+    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+    // w+ are now off by a small amount.
+    // In fact:
+    //
+    //      w - v * 10^k < 1 ulp
+    //
+    // To account for this inaccuracy, add resp. subtract 1 ulp.
+    //
+    //  --------+---[---------------(---+---)---------------]---+--------
+    //          w-  M-                  w                   M+  w+
+    //
+    // Now any number in [M-, M+] (bounds included) will round to w when input,
+    // regardless of how the input rounding algorithm breaks ties.
+    //
+    // And digit_gen generates the shortest possible such number in [M-, M+].
+    // Note that this does not mean that Grisu2 always generates the shortest
+    // possible number in the interval (m-, m+).
+    const diyfp M_minus(w_minus.f + 1, w_minus.e);
+    const diyfp M_plus (w_plus.f  - 1, w_plus.e );
+
+    decimal_exponent = -cached.k; // = -(-k) = k
+
+    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1)
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+                  "internal error: not enough precision");
+
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
+
+    // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+    // decimal representations are not exactly "short".
+    //
+    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)
+    // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+    // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'
+    // does.
+    // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+    // representation using the corresponding std::from_chars function recovers value exactly". That
+    // indicates that single precision floating-point numbers should be recovered using
+    // 'std::strtof'.
+    //
+    // NB: If the neighbors are computed for single-precision numbers, there is a single float
+    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+    //     value is off by 1 ulp.
+#if 0
+    const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+    const boundaries w = compute_boundaries(value);
+#endif
+
+    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* append_exponent(char* buf, int e)
+{
+    JSON_ASSERT(e > -1000);
+    JSON_ASSERT(e <  1000);
+
+    if (e < 0)
+    {
+        e = -e;
+        *buf++ = '-';
+    }
+    else
+    {
+        *buf++ = '+';
+    }
+
+    auto k = static_cast<std::uint32_t>(e);
+    if (k < 10)
+    {
+        // Always print at least two digits in the exponent.
+        // This is for compatibility with printf("%g").
+        *buf++ = '0';
+        *buf++ = static_cast<char>('0' + k);
+    }
+    else if (k < 100)
+    {
+        *buf++ = static_cast<char>('0' + k / 10);
+        k %= 10;
+        *buf++ = static_cast<char>('0' + k);
+    }
+    else
+    {
+        *buf++ = static_cast<char>('0' + k / 100);
+        k %= 100;
+        *buf++ = static_cast<char>('0' + k / 10);
+        k %= 10;
+        *buf++ = static_cast<char>('0' + k);
+    }
+
+    return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+                           int min_exp, int max_exp)
+{
+    JSON_ASSERT(min_exp < 0);
+    JSON_ASSERT(max_exp > 0);
+
+    const int k = len;
+    const int n = len + decimal_exponent;
+
+    // v = buf * 10^(n-k)
+    // k is the length of the buffer (number of decimal digits)
+    // n is the position of the decimal point relative to the start of the buffer.
+
+    if (k <= n && n <= max_exp)
+    {
+        // digits[000]
+        // len <= max_exp + 2
+
+        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+        // Make it look like a floating-point number (#362, #378)
+        buf[n + 0] = '.';
+        buf[n + 1] = '0';
+        return buf + (static_cast<size_t>(n) + 2);
+    }
+
+    if (0 < n && n <= max_exp)
+    {
+        // dig.its
+        // len <= max_digits10 + 1
+
+        JSON_ASSERT(k > n);
+
+        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
+        buf[n] = '.';
+        return buf + (static_cast<size_t>(k) + 1U);
+    }
+
+    if (min_exp < n && n <= 0)
+    {
+        // 0.[000]digits
+        // len <= 2 + (-min_exp - 1) + max_digits10
+
+        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));
+        buf[0] = '0';
+        buf[1] = '.';
+        std::memset(buf + 2, '0', static_cast<size_t>(-n));
+        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
+    }
+
+    if (k == 1)
+    {
+        // dE+123
+        // len <= 1 + 5
+
+        buf += 1;
+    }
+    else
+    {
+        // d.igitsE+123
+        // len <= max_digits10 + 1 + 5
+
+        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
+        buf[1] = '.';
+        buf += 1 + static_cast<size_t>(k);
+    }
+
+    *buf++ = 'e';
+    return append_exponent(buf, n - 1);
+}
+
+}  // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1, 2)
+JSON_HEDLEY_RETURNS_NON_NULL
+char* to_chars(char* first, const char* last, FloatType value)
+{
+    static_cast<void>(last); // maybe unused - fix warning
+    JSON_ASSERT(std::isfinite(value));
+
+    // Use signbit(value) instead of (value < 0) since signbit works for -0.
+    if (std::signbit(value))
+    {
+        value = -value;
+        *first++ = '-';
+    }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+    if (value == 0) // +-0
+    {
+        *first++ = '0';
+        // Make it look like a floating-point number (#362, #378)
+        *first++ = '.';
+        *first++ = '0';
+        return first;
+    }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+    // Compute v = buffer * 10^decimal_exponent.
+    // The decimal digits are stored in the buffer, which needs to be interpreted
+    // as an unsigned decimal integer.
+    // len is the length of the buffer, i.e. the number of decimal digits.
+    int len = 0;
+    int decimal_exponent = 0;
+    dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
+
+    // Format the buffer like printf("%.*g", prec, value)
+    constexpr int kMinExp = -4;
+    // Use digits10 here to increase compatibility with version 2.
+    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+    JSON_ASSERT(last - first >= kMaxExp + 2);
+    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// serialization //
+///////////////////
+
+/// how to treat decoding errors
+enum class error_handler_t
+{
+    strict,  ///< throw a type_error exception in case of invalid UTF-8
+    replace, ///< replace invalid UTF-8 sequences with U+FFFD
+    ignore   ///< ignore invalid UTF-8 sequences
+};
+
+template<typename BasicJsonType>
+class serializer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using binary_char_t = typename BasicJsonType::binary_t::value_type;
+    static constexpr std::uint8_t UTF8_ACCEPT = 0;
+    static constexpr std::uint8_t UTF8_REJECT = 1;
+
+  public:
+    /*!
+    @param[in] s  output stream to serialize to
+    @param[in] ichar  indentation character to use
+    @param[in] error_handler_  how to react on decoding errors
+    */
+    serializer(output_adapter_t<char> s, const char ichar,
+               error_handler_t error_handler_ = error_handler_t::strict)
+        : o(std::move(s))
+        , loc(std::localeconv())
+        , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+        , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+        , indent_char(ichar)
+        , indent_string(512, indent_char)
+        , error_handler(error_handler_)
+    {}
+
+    // delete because of pointer members
+    serializer(const serializer&) = delete;
+    serializer& operator=(const serializer&) = delete;
+    serializer(serializer&&) = delete;
+    serializer& operator=(serializer&&) = delete;
+    ~serializer() = default;
+
+    /*!
+    @brief internal implementation of the serialization function
+
+    This function is called by the public member function dump and organizes
+    the serialization internally. The indentation level is propagated as
+    additional parameter. In case of arrays and objects, the function is
+    called recursively.
+
+    - strings and object keys are escaped using `escape_string()`
+    - integer numbers are converted implicitly via `operator<<`
+    - floating-point numbers are converted to a string using `"%g"` format
+    - binary values are serialized as objects containing the subtype and the
+      byte array
+
+    @param[in] val               value to serialize
+    @param[in] pretty_print      whether the output shall be pretty-printed
+    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+    in the output are escaped with `\uXXXX` sequences, and the result consists
+    of ASCII characters only.
+    @param[in] indent_step       the indent level
+    @param[in] current_indent    the current indent level (only used internally)
+    */
+    void dump(const BasicJsonType& val,
+              const bool pretty_print,
+              const bool ensure_ascii,
+              const unsigned int indent_step,
+              const unsigned int current_indent = 0)
+    {
+        switch (val.m_type)
+        {
+            case value_t::object:
+            {
+                if (val.m_value.object->empty())
+                {
+                    o->write_characters("{}", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    auto i = val.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\": ", 3);
+                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\": ", 3);
+                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_character('{');
+
+                    // first n-1 elements
+                    auto i = val.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\":", 2);
+                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
+                        o->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\":", 2);
+                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character('}');
+                }
+
+                return;
+            }
+
+            case value_t::array:
+            {
+                if (val.m_value.array->empty())
+                {
+                    o->write_characters("[]", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("[\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    for (auto i = val.m_value.array->cbegin();
+                            i != val.m_value.array->cend() - 1; ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        dump(*i, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_value.array->empty());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character(']');
+                }
+                else
+                {
+                    o->write_character('[');
+
+                    // first n-1 elements
+                    for (auto i = val.m_value.array->cbegin();
+                            i != val.m_value.array->cend() - 1; ++i)
+                    {
+                        dump(*i, false, ensure_ascii, indent_step, current_indent);
+                        o->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_value.array->empty());
+                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character(']');
+                }
+
+                return;
+            }
+
+            case value_t::string:
+            {
+                o->write_character('\"');
+                dump_escaped(*val.m_value.string, ensure_ascii);
+                o->write_character('\"');
+                return;
+            }
+
+            case value_t::binary:
+            {
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"bytes\": [", 10);
+
+                    if (!val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_characters(", ", 2);
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\n", 3);
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"subtype\": ", 11);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                    }
+                    else
+                    {
+                        o->write_characters("null", 4);
+                    }
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_characters("{\"bytes\":[", 10);
+
+                    if (!val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_character(',');
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\"subtype\":", 12);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                        o->write_character('}');
+                    }
+                    else
+                    {
+                        o->write_characters("null}", 5);
+                    }
+                }
+                return;
+            }
+
+            case value_t::boolean:
+            {
+                if (val.m_value.boolean)
+                {
+                    o->write_characters("true", 4);
+                }
+                else
+                {
+                    o->write_characters("false", 5);
+                }
+                return;
+            }
+
+            case value_t::number_integer:
+            {
+                dump_integer(val.m_value.number_integer);
+                return;
+            }
+
+            case value_t::number_unsigned:
+            {
+                dump_integer(val.m_value.number_unsigned);
+                return;
+            }
+
+            case value_t::number_float:
+            {
+                dump_float(val.m_value.number_float);
+                return;
+            }
+
+            case value_t::discarded:
+            {
+                o->write_characters("<discarded>", 11);
+                return;
+            }
+
+            case value_t::null:
+            {
+                o->write_characters("null", 4);
+                return;
+            }
+
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief dump escaped string
+
+    Escape a string by replacing certain special characters by a sequence of an
+    escape character (backslash) and another character and other control
+    characters by a sequence of "\u" followed by a four-digit hex
+    representation. The escaped string is written to output stream @a o.
+
+    @param[in] s  the string to escape
+    @param[in] ensure_ascii  whether to escape non-ASCII characters with
+                             \uXXXX sequences
+
+    @complexity Linear in the length of string @a s.
+    */
+    void dump_escaped(const string_t& s, const bool ensure_ascii)
+    {
+        std::uint32_t codepoint{};
+        std::uint8_t state = UTF8_ACCEPT;
+        std::size_t bytes = 0;  // number of bytes written to string_buffer
+
+        // number of bytes written at the point of the last valid byte
+        std::size_t bytes_after_last_accept = 0;
+        std::size_t undumped_chars = 0;
+
+        for (std::size_t i = 0; i < s.size(); ++i)
+        {
+            const auto byte = static_cast<std::uint8_t>(s[i]);
+
+            switch (decode(state, codepoint, byte))
+            {
+                case UTF8_ACCEPT:  // decode found a new code point
+                {
+                    switch (codepoint)
+                    {
+                        case 0x08: // backspace
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'b';
+                            break;
+                        }
+
+                        case 0x09: // horizontal tab
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 't';
+                            break;
+                        }
+
+                        case 0x0A: // newline
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'n';
+                            break;
+                        }
+
+                        case 0x0C: // formfeed
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'f';
+                            break;
+                        }
+
+                        case 0x0D: // carriage return
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'r';
+                            break;
+                        }
+
+                        case 0x22: // quotation mark
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\"';
+                            break;
+                        }
+
+                        case 0x5C: // reverse solidus
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\\';
+                            break;
+                        }
+
+                        default:
+                        {
+                            // escape control characters (0x00..0x1F) or, if
+                            // ensure_ascii parameter is used, non-ASCII characters
+                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
+                            {
+                                if (codepoint <= 0xFFFF)
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
+                                                                      static_cast<std::uint16_t>(codepoint)));
+                                    bytes += 6;
+                                }
+                                else
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+                                                                      static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
+                                                                      static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
+                                    bytes += 12;
+                                }
+                            }
+                            else
+                            {
+                                // copy byte to buffer (all previous bytes
+                                // been copied have in default case above)
+                                string_buffer[bytes++] = s[i];
+                            }
+                            break;
+                        }
+                    }
+
+                    // write buffer and reset index; there must be 13 bytes
+                    // left, as this is the maximal number of bytes to be
+                    // written ("\uxxxx\uxxxx\0") for one code point
+                    if (string_buffer.size() - bytes < 13)
+                    {
+                        o->write_characters(string_buffer.data(), bytes);
+                        bytes = 0;
+                    }
+
+                    // remember the byte position of this accept
+                    bytes_after_last_accept = bytes;
+                    undumped_chars = 0;
+                    break;
+                }
+
+                case UTF8_REJECT:  // decode found invalid UTF-8 byte
+                {
+                    switch (error_handler)
+                    {
+                        case error_handler_t::strict:
+                        {
+                            JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
+                        }
+
+                        case error_handler_t::ignore:
+                        case error_handler_t::replace:
+                        {
+                            // in case we saw this character the first time, we
+                            // would like to read it again, because the byte
+                            // may be OK for itself, but just not OK for the
+                            // previous sequence
+                            if (undumped_chars > 0)
+                            {
+                                --i;
+                            }
+
+                            // reset length buffer to the last accepted index;
+                            // thus removing/ignoring the invalid characters
+                            bytes = bytes_after_last_accept;
+
+                            if (error_handler == error_handler_t::replace)
+                            {
+                                // add a replacement character
+                                if (ensure_ascii)
+                                {
+                                    string_buffer[bytes++] = '\\';
+                                    string_buffer[bytes++] = 'u';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'd';
+                                }
+                                else
+                                {
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
+                                }
+
+                                // write buffer and reset index; there must be 13 bytes
+                                // left, as this is the maximal number of bytes to be
+                                // written ("\uxxxx\uxxxx\0") for one code point
+                                if (string_buffer.size() - bytes < 13)
+                                {
+                                    o->write_characters(string_buffer.data(), bytes);
+                                    bytes = 0;
+                                }
+
+                                bytes_after_last_accept = bytes;
+                            }
+
+                            undumped_chars = 0;
+
+                            // continue processing the string
+                            state = UTF8_ACCEPT;
+                            break;
+                        }
+
+                        default:            // LCOV_EXCL_LINE
+                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+
+                default:  // decode found yet incomplete multi-byte code point
+                {
+                    if (!ensure_ascii)
+                    {
+                        // code point will not be escaped - copy byte to buffer
+                        string_buffer[bytes++] = s[i];
+                    }
+                    ++undumped_chars;
+                    break;
+                }
+            }
+        }
+
+        // we finished processing the string
+        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
+        {
+            // write buffer
+            if (bytes > 0)
+            {
+                o->write_characters(string_buffer.data(), bytes);
+            }
+        }
+        else
+        {
+            // we finish reading, but do not accept: string was incomplete
+            switch (error_handler)
+            {
+                case error_handler_t::strict:
+                {
+                    JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
+                }
+
+                case error_handler_t::ignore:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    break;
+                }
+
+                case error_handler_t::replace:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    // add a replacement character
+                    if (ensure_ascii)
+                    {
+                        o->write_characters("\\ufffd", 6);
+                    }
+                    else
+                    {
+                        o->write_characters("\xEF\xBF\xBD", 3);
+                    }
+                    break;
+                }
+
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        }
+    }
+
+  private:
+    /*!
+    @brief count digits
+
+    Count the number of decimal (base 10) digits for an input unsigned integer.
+
+    @param[in] x  unsigned integer number to count its digits
+    @return    number of decimal digits
+    */
+    inline unsigned int count_digits(number_unsigned_t x) noexcept
+    {
+        unsigned int n_digits = 1;
+        for (;;)
+        {
+            if (x < 10)
+            {
+                return n_digits;
+            }
+            if (x < 100)
+            {
+                return n_digits + 1;
+            }
+            if (x < 1000)
+            {
+                return n_digits + 2;
+            }
+            if (x < 10000)
+            {
+                return n_digits + 3;
+            }
+            x = x / 10000u;
+            n_digits += 4;
+        }
+    }
+
+    /*!
+     * @brief convert a byte to a uppercase hex representation
+     * @param[in] byte byte to represent
+     * @return representation ("00".."FF")
+     */
+    static std::string hex_bytes(std::uint8_t byte)
+    {
+        std::string result = "FF";
+        constexpr const char* nibble_to_hex = "0123456789ABCDEF";
+        result[0] = nibble_to_hex[byte / 16];
+        result[1] = nibble_to_hex[byte % 16];
+        return result;
+    }
+
+    // templates to avoid warnings about useless casts
+    template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
+    bool is_negative_number(NumberType x)
+    {
+        return x < 0;
+    }
+
+    template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
+    bool is_negative_number(NumberType /*unused*/)
+    {
+        return false;
+    }
+
+    /*!
+    @brief dump an integer
+
+    Dump a given integer to output stream @a o. Works internally with
+    @a number_buffer.
+
+    @param[in] x  integer number (signed or unsigned) to dump
+    @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+    */
+    template < typename NumberType, detail::enable_if_t <
+                   std::is_integral<NumberType>::value ||
+                   std::is_same<NumberType, number_unsigned_t>::value ||
+                   std::is_same<NumberType, number_integer_t>::value ||
+                   std::is_same<NumberType, binary_char_t>::value,
+                   int > = 0 >
+    void dump_integer(NumberType x)
+    {
+        static constexpr std::array<std::array<char, 2>, 100> digits_to_99
+        {
+            {
+                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
+                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
+                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
+                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
+                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
+                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
+                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
+                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
+                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
+                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
+            }
+        };
+
+        // special case for "0"
+        if (x == 0)
+        {
+            o->write_character('0');
+            return;
+        }
+
+        // use a pointer to fill the buffer
+        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+
+        number_unsigned_t abs_value;
+
+        unsigned int n_chars{};
+
+        if (is_negative_number(x))
+        {
+            *buffer_ptr = '-';
+            abs_value = remove_sign(static_cast<number_integer_t>(x));
+
+            // account one more byte for the minus sign
+            n_chars = 1 + count_digits(abs_value);
+        }
+        else
+        {
+            abs_value = static_cast<number_unsigned_t>(x);
+            n_chars = count_digits(abs_value);
+        }
+
+        // spare 1 byte for '\0'
+        JSON_ASSERT(n_chars < number_buffer.size() - 1);
+
+        // jump to the end to generate the string from backward,
+        // so we later avoid reversing the result
+        buffer_ptr += n_chars;
+
+        // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
+        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
+        while (abs_value >= 100)
+        {
+            const auto digits_index = static_cast<unsigned>((abs_value % 100));
+            abs_value /= 100;
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+
+        if (abs_value >= 10)
+        {
+            const auto digits_index = static_cast<unsigned>(abs_value);
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+        else
+        {
+            *(--buffer_ptr) = static_cast<char>('0' + abs_value);
+        }
+
+        o->write_characters(number_buffer.data(), n_chars);
+    }
+
+    /*!
+    @brief dump a floating-point number
+
+    Dump a given floating-point number to output stream @a o. Works internally
+    with @a number_buffer.
+
+    @param[in] x  floating-point number to dump
+    */
+    void dump_float(number_float_t x)
+    {
+        // NaN / inf
+        if (!std::isfinite(x))
+        {
+            o->write_characters("null", 4);
+            return;
+        }
+
+        // If number_float_t is an IEEE-754 single or double precision number,
+        // use the Grisu2 algorithm to produce short numbers which are
+        // guaranteed to round-trip, using strtof and strtod, resp.
+        //
+        // NB: The test below works if <long double> == <double>.
+        static constexpr bool is_ieee_single_or_double
+            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
+              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+    }
+
+    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+    {
+        auto* begin = number_buffer.data();
+        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+        o->write_characters(begin, static_cast<size_t>(end - begin));
+    }
+
+    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+    {
+        // get number of digits for a float -> text -> float round-trip
+        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
+
+        // the actual conversion
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+        // negative value indicates an error
+        JSON_ASSERT(len > 0);
+        // check if buffer was large enough
+        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
+
+        // erase thousands separator
+        if (thousands_sep != '\0')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
+            std::fill(end, number_buffer.end(), '\0');
+            JSON_ASSERT((end - number_buffer.begin()) <= len);
+            len = (end - number_buffer.begin());
+        }
+
+        // convert decimal point to '.'
+        if (decimal_point != '\0' && decimal_point != '.')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+            if (dec_pos != number_buffer.end())
+            {
+                *dec_pos = '.';
+            }
+        }
+
+        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+        // determine if we need to append ".0"
+        const bool value_is_int_like =
+            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+                         [](char c)
+        {
+            return c == '.' || c == 'e';
+        });
+
+        if (value_is_int_like)
+        {
+            o->write_characters(".0", 2);
+        }
+    }
+
+    /*!
+    @brief check whether a string is UTF-8 encoded
+
+    The function checks each byte of a string whether it is UTF-8 encoded. The
+    result of the check is stored in the @a state parameter. The function must
+    be called initially with state 0 (accept). State 1 means the string must
+    be rejected, because the current byte is not allowed. If the string is
+    completely processed, but the state is non-zero, the string ended
+    prematurely; that is, the last byte indicated more bytes should have
+    followed.
+
+    @param[in,out] state  the state of the decoding
+    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)
+    @param[in] byte       next byte to decode
+    @return               new state
+
+    @note The function has been edited: a std::array is used.
+
+    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+    */
+    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
+    {
+        static const std::array<std::uint8_t, 400> utf8d =
+        {
+            {
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+            }
+        };
+
+        JSON_ASSERT(byte < utf8d.size());
+        const std::uint8_t type = utf8d[byte];
+
+        codep = (state != UTF8_ACCEPT)
+                ? (byte & 0x3fu) | (codep << 6u)
+                : (0xFFu >> type) & (byte);
+
+        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
+        JSON_ASSERT(index < 400);
+        state = utf8d[index];
+        return state;
+    }
+
+    /*
+     * Overload to make the compiler happy while it is instantiating
+     * dump_integer for number_unsigned_t.
+     * Must never be called.
+     */
+    number_unsigned_t remove_sign(number_unsigned_t x)
+    {
+        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        return x; // LCOV_EXCL_LINE
+    }
+
+    /*
+     * Helper function for dump_integer
+     *
+     * This function takes a negative signed integer and returns its absolute
+     * value as unsigned integer. The plus/minus shuffling is necessary as we can
+     * not directly remove the sign of an arbitrary signed integer as the
+     * absolute values of INT_MIN and INT_MAX are usually not the same. See
+     * #1708 for details.
+     */
+    inline number_unsigned_t remove_sign(number_integer_t x) noexcept
+    {
+        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
+        return static_cast<number_unsigned_t>(-(x + 1)) + 1;
+    }
+
+  private:
+    /// the output of the serializer
+    output_adapter_t<char> o = nullptr;
+
+    /// a (hopefully) large enough character buffer
+    std::array<char, 64> number_buffer{{}};
+
+    /// the locale
+    const std::lconv* loc = nullptr;
+    /// the locale's thousand separator character
+    const char thousands_sep = '\0';
+    /// the locale's decimal point character
+    const char decimal_point = '\0';
+
+    /// string buffer
+    std::array<char, 512> string_buffer{{}};
+
+    /// the indentation character
+    const char indent_char;
+    /// the indentation string
+    string_t indent_string;
+
+    /// error_handler how to react on decoding errors
+    const error_handler_t error_handler;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/value_t.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+
+// #include <nlohmann/ordered_map.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <functional> // equal_to, less
+#include <initializer_list> // initializer_list
+#include <iterator> // input_iterator_tag, iterator_traits
+#include <memory> // allocator
+#include <stdexcept> // for out_of_range
+#include <type_traits> // enable_if, is_convertible
+#include <utility> // pair
+#include <vector> // vector
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// ordered_map: a minimal map-like container that preserves insertion order
+/// for use within nlohmann::basic_json<ordered_map>
+template <class Key, class T, class IgnoredLess = std::less<Key>,
+          class Allocator = std::allocator<std::pair<const Key, T>>>
+                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+    using key_type = Key;
+    using mapped_type = T;
+    using Container = std::vector<std::pair<const Key, T>, Allocator>;
+    using iterator = typename Container::iterator;
+    using const_iterator = typename Container::const_iterator;
+    using size_type = typename Container::size_type;
+    using value_type = typename Container::value_type;
+#ifdef JSON_HAS_CPP_14
+    using key_compare = std::equal_to<>;
+#else
+    using key_compare = std::equal_to<Key>;
+#endif
+
+    // Explicit constructors instead of `using Container::Container`
+    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+    ordered_map() noexcept(noexcept(Container())) : Container{} {}
+    explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
+    template <class It>
+    ordered_map(It first, It last, const Allocator& alloc = Allocator())
+        : Container{first, last, alloc} {}
+    ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
+        : Container{init, alloc} {}
+
+    std::pair<iterator, bool> emplace(const key_type& key, T&& t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(key, std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    std::pair<iterator, bool> emplace(KeyType && key, T && t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    T& operator[](const key_type& key)
+    {
+        return emplace(key, T{}).first->second;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & operator[](KeyType && key)
+    {
+        return emplace(std::forward<KeyType>(key), T{}).first->second;
+    }
+
+    const T& operator[](const key_type& key) const
+    {
+        return at(key);
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & operator[](KeyType && key) const
+    {
+        return at(std::forward<KeyType>(key));
+    }
+
+    T& at(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & at(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    const T& at(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & at(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    size_type erase(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator erase(iterator pos)
+    {
+        return erase(pos, std::next(pos));
+    }
+
+    iterator erase(iterator first, iterator last)
+    {
+        if (first == last)
+        {
+            return first;
+        }
+
+        const auto elements_affected = std::distance(first, last);
+        const auto offset = std::distance(Container::begin(), first);
+
+        // This is the start situation. We need to delete elements_affected
+        // elements (3 in this example: e, f, g), and need to return an
+        // iterator past the last deleted element (h in this example).
+        // Note that offset is the distance from the start of the vector
+        // to first. We will need this later.
+
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // Since we cannot move const Keys, we re-construct them in place.
+        // We start at first and re-construct (viz. copy) the elements from
+        // the back of the vector. Example for first iteration:
+
+        //               ,--------.
+        //               v        |   destroy e and re-construct with h
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //               it       it + elements_affected
+
+        for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
+        {
+            it->~value_type(); // destroy but keep allocation
+            new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
+        }
+
+        // [ a, b, c, d, h, i, j, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // remove the unneeded elements at the end of the vector
+        Container::resize(this->size() - static_cast<size_type>(elements_affected));
+
+        // [ a, b, c, d, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // first is now pointing past the last deleted element, but we cannot
+        // use this iterator, because it may have been invalidated by the
+        // resize call. Instead, we can return begin() + offset.
+        return Container::begin() + offset;
+    }
+
+    size_type count(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator find(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    const_iterator find(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    std::pair<iterator, bool> insert( value_type&& value )
+    {
+        return emplace(value.first, std::move(value.second));
+    }
+
+    std::pair<iterator, bool> insert( const value_type& value )
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, value.first))
+            {
+                return {it, false};
+            }
+        }
+        Container::push_back(value);
+        return {--this->end(), true};
+    }
+
+    template<typename InputIt>
+    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
+            std::input_iterator_tag>::value>::type;
+
+    template<typename InputIt, typename = require_input_iter<InputIt>>
+    void insert(InputIt first, InputIt last)
+    {
+        for (auto it = first; it != last; ++it)
+        {
+            insert(*it);
+        }
+    }
+
+private:
+    JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+
+#if defined(JSON_HAS_CPP_17)
+    #include <any>
+    #include <string_view>
+#endif
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief a class to store JSON values
+
+@internal
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
+@note ObjectType trick from https://stackoverflow.com/a/9860911
+@endinternal
+
+@since version 1.0.0
+
+@nosubgrouping
+*/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+  private:
+    template<detail::value_t> friend struct detail::external_constructor;
+
+    template<typename>
+    friend class ::nlohmann::json_pointer;
+    // can be restored when json_pointer backwards compatibility is removed
+    // friend ::nlohmann::json_pointer<StringType>;
+
+    template<typename BasicJsonType, typename InputType>
+    friend class ::nlohmann::detail::parser;
+    friend ::nlohmann::detail::serializer<basic_json>;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::iter_impl;
+    template<typename BasicJsonType, typename CharType>
+    friend class ::nlohmann::detail::binary_writer;
+    template<typename BasicJsonType, typename InputType, typename SAX>
+    friend class ::nlohmann::detail::binary_reader;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::json_sax_dom_parser;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+    friend class ::nlohmann::detail::exception;
+
+    /// workaround type for MSVC
+    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // convenience aliases for types residing in namespace detail;
+    using lexer = ::nlohmann::detail::lexer_base<basic_json>;
+
+    template<typename InputAdapterType>
+    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(
+        InputAdapterType adapter,
+        detail::parser_callback_t<basic_json>cb = nullptr,
+        const bool allow_exceptions = true,
+        const bool ignore_comments = false
+                                 )
+    {
+        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+                std::move(cb), allow_exceptions, ignore_comments);
+    }
+
+  private:
+    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+    template<typename BasicJsonType>
+    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+    template<typename BasicJsonType>
+    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+    template<typename Iterator>
+    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+    template<typename CharType>
+    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+    template<typename InputType>
+    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
+    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    using serializer = ::nlohmann::detail::serializer<basic_json>;
+
+  public:
+    using value_t = detail::value_t;
+    /// JSON Pointer, see @ref nlohmann::json_pointer
+    using json_pointer = ::nlohmann::json_pointer<StringType>;
+    template<typename T, typename SFINAE>
+    using json_serializer = JSONSerializer<T, SFINAE>;
+    /// how to treat decoding errors
+    using error_handler_t = detail::error_handler_t;
+    /// how to treat CBOR tags
+    using cbor_tag_handler_t = detail::cbor_tag_handler_t;
+    /// helper type for initializer lists of basic_json values
+    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+    using input_format_t = detail::input_format_t;
+    /// SAX interface type, see @ref nlohmann::json_sax
+    using json_sax_t = json_sax<basic_json>;
+
+    ////////////////
+    // exceptions //
+    ////////////////
+
+    /// @name exceptions
+    /// Classes to implement user-defined exceptions.
+    /// @{
+
+    using exception = detail::exception;
+    using parse_error = detail::parse_error;
+    using invalid_iterator = detail::invalid_iterator;
+    using type_error = detail::type_error;
+    using out_of_range = detail::out_of_range;
+    using other_error = detail::other_error;
+
+    /// @}
+
+
+    /////////////////////
+    // container types //
+    /////////////////////
+
+    /// @name container types
+    /// The canonic container types to use @ref basic_json like any other STL
+    /// container.
+    /// @{
+
+    /// the type of elements in a basic_json container
+    using value_type = basic_json;
+
+    /// the type of an element reference
+    using reference = value_type&;
+    /// the type of an element const reference
+    using const_reference = const value_type&;
+
+    /// a type to represent differences between iterators
+    using difference_type = std::ptrdiff_t;
+    /// a type to represent container sizes
+    using size_type = std::size_t;
+
+    /// the allocator type
+    using allocator_type = AllocatorType<basic_json>;
+
+    /// the type of an element pointer
+    using pointer = typename std::allocator_traits<allocator_type>::pointer;
+    /// the type of an element const pointer
+    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
+
+    /// an iterator for a basic_json container
+    using iterator = iter_impl<basic_json>;
+    /// a const iterator for a basic_json container
+    using const_iterator = iter_impl<const basic_json>;
+    /// a reverse iterator for a basic_json container
+    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+    /// a const reverse iterator for a basic_json container
+    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
+
+    /// @}
+
+
+    /// @brief returns the allocator associated with the container
+    /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/
+    static allocator_type get_allocator()
+    {
+        return allocator_type();
+    }
+
+    /// @brief returns version information on the library
+    /// @sa https://json.nlohmann.me/api/basic_json/meta/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json meta()
+    {
+        basic_json result;
+
+        result["copyright"] = "(C) 2013-2022 Niels Lohmann";
+        result["name"] = "JSON for Modern C++";
+        result["url"] = "https://github.com/nlohmann/json";
+        result["version"]["string"] =
+            detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
+                           std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
+                           std::to_string(NLOHMANN_JSON_VERSION_PATCH));
+        result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+        result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+        result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+        result["platform"] = "win32";
+#elif defined __linux__
+        result["platform"] = "linux";
+#elif defined __APPLE__
+        result["platform"] = "apple";
+#elif defined __unix__
+        result["platform"] = "unix";
+#else
+        result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+        result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+        result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
+                    std::to_string(__GNUC__), '.',
+                    std::to_string(__GNUC_MINOR__), '.',
+                    std::to_string(__GNUC_PATCHLEVEL__))
+            }
+        };
+#elif defined(__HP_cc) || defined(__HP_aCC)
+        result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+
+#if defined(_MSVC_LANG)
+        result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
+#elif defined(__cplusplus)
+        result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+        result["compiler"]["c++"] = "unknown";
+#endif
+        return result;
+    }
+
+
+    ///////////////////////////
+    // JSON value data types //
+    ///////////////////////////
+
+    /// @name JSON value data types
+    /// The data types to store a JSON value. These types are derived from
+    /// the template arguments passed to class @ref basic_json.
+    /// @{
+
+    /// @brief default object key comparator type
+    /// The actual object key comparator type (@ref object_comparator_t) may be
+    /// different.
+    /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
+#if defined(JSON_HAS_CPP_14)
+    // use of transparent comparator avoids unnecessary repeated construction of temporaries
+    // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
+    using default_object_comparator_t = std::less<>;
+#else
+    using default_object_comparator_t = std::less<StringType>;
+#endif
+
+    /// @brief a type for an object
+    /// @sa https://json.nlohmann.me/api/basic_json/object_t/
+    using object_t = ObjectType<StringType,
+          basic_json,
+          default_object_comparator_t,
+          AllocatorType<std::pair<const StringType,
+          basic_json>>>;
+
+    /// @brief a type for an array
+    /// @sa https://json.nlohmann.me/api/basic_json/array_t/
+    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+
+    /// @brief a type for a string
+    /// @sa https://json.nlohmann.me/api/basic_json/string_t/
+    using string_t = StringType;
+
+    /// @brief a type for a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/
+    using boolean_t = BooleanType;
+
+    /// @brief a type for a number (integer)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/
+    using number_integer_t = NumberIntegerType;
+
+    /// @brief a type for a number (unsigned)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/
+    using number_unsigned_t = NumberUnsignedType;
+
+    /// @brief a type for a number (floating-point)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/
+    using number_float_t = NumberFloatType;
+
+    /// @brief a type for a packed binary type
+    /// @sa https://json.nlohmann.me/api/basic_json/binary_t/
+    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+
+    /// @brief object key comparator type
+    /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/
+    using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
+
+    /// @}
+
+  private:
+
+    /// helper for exception-safe object creation
+    template<typename T, typename... Args>
+    JSON_HEDLEY_RETURNS_NON_NULL
+    static T* create(Args&& ... args)
+    {
+        AllocatorType<T> alloc;
+        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
+        auto deleter = [&](T * obj)
+        {
+            AllocatorTraits::deallocate(alloc, obj, 1);
+        };
+        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+        JSON_ASSERT(obj != nullptr);
+        return obj.release();
+    }
+
+    ////////////////////////
+    // JSON value storage //
+    ////////////////////////
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief a JSON value
+
+    The actual storage for a JSON value of the @ref basic_json class. This
+    union combines the different storage types for the JSON value types
+    defined in @ref value_t.
+
+    JSON type | value_t type    | used type
+    --------- | --------------- | ------------------------
+    object    | object          | pointer to @ref object_t
+    array     | array           | pointer to @ref array_t
+    string    | string          | pointer to @ref string_t
+    boolean   | boolean         | @ref boolean_t
+    number    | number_integer  | @ref number_integer_t
+    number    | number_unsigned | @ref number_unsigned_t
+    number    | number_float    | @ref number_float_t
+    binary    | binary          | pointer to @ref binary_t
+    null      | null            | *no value is stored*
+
+    @note Variable-length types (objects, arrays, and strings) are stored as
+    pointers. The size of the union should not exceed 64 bits if the default
+    value types are used.
+
+    @since version 1.0.0
+    */
+    union json_value
+    {
+        /// object (stored with pointer to save storage)
+        object_t* object;
+        /// array (stored with pointer to save storage)
+        array_t* array;
+        /// string (stored with pointer to save storage)
+        string_t* string;
+        /// binary (stored with pointer to save storage)
+        binary_t* binary;
+        /// boolean
+        boolean_t boolean;
+        /// number (integer)
+        number_integer_t number_integer;
+        /// number (unsigned integer)
+        number_unsigned_t number_unsigned;
+        /// number (floating-point)
+        number_float_t number_float;
+
+        /// default constructor (for null values)
+        json_value() = default;
+        /// constructor for booleans
+        json_value(boolean_t v) noexcept : boolean(v) {}
+        /// constructor for numbers (integer)
+        json_value(number_integer_t v) noexcept : number_integer(v) {}
+        /// constructor for numbers (unsigned)
+        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
+        /// constructor for numbers (floating-point)
+        json_value(number_float_t v) noexcept : number_float(v) {}
+        /// constructor for empty values of a given type
+        json_value(value_t t)
+        {
+            switch (t)
+            {
+                case value_t::object:
+                {
+                    object = create<object_t>();
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    array = create<array_t>();
+                    break;
+                }
+
+                case value_t::string:
+                {
+                    string = create<string_t>("");
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    binary = create<binary_t>();
+                    break;
+                }
+
+                case value_t::boolean:
+                {
+                    boolean = static_cast<boolean_t>(false);
+                    break;
+                }
+
+                case value_t::number_integer:
+                {
+                    number_integer = static_cast<number_integer_t>(0);
+                    break;
+                }
+
+                case value_t::number_unsigned:
+                {
+                    number_unsigned = static_cast<number_unsigned_t>(0);
+                    break;
+                }
+
+                case value_t::number_float:
+                {
+                    number_float = static_cast<number_float_t>(0.0);
+                    break;
+                }
+
+                case value_t::null:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    break;
+                }
+
+                case value_t::discarded:
+                default:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
+                    {
+                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.2", nullptr)); // LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+            }
+        }
+
+        /// constructor for strings
+        json_value(const string_t& value) : string(create<string_t>(value)) {}
+
+        /// constructor for rvalue strings
+        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
+
+        /// constructor for objects
+        json_value(const object_t& value) : object(create<object_t>(value)) {}
+
+        /// constructor for rvalue objects
+        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
+
+        /// constructor for arrays
+        json_value(const array_t& value) : array(create<array_t>(value)) {}
+
+        /// constructor for rvalue arrays
+        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
+
+        /// constructor for binary arrays
+        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays
+        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        /// constructor for binary arrays (internal type)
+        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays (internal type)
+        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        void destroy(value_t t)
+        {
+            if (t == value_t::array || t == value_t::object)
+            {
+                // flatten the current json_value to a heap-allocated stack
+                std::vector<basic_json> stack;
+
+                // move the top-level items to stack
+                if (t == value_t::array)
+                {
+                    stack.reserve(array->size());
+                    std::move(array->begin(), array->end(), std::back_inserter(stack));
+                }
+                else
+                {
+                    stack.reserve(object->size());
+                    for (auto&& it : *object)
+                    {
+                        stack.push_back(std::move(it.second));
+                    }
+                }
+
+                while (!stack.empty())
+                {
+                    // move the last item to local variable to be processed
+                    basic_json current_item(std::move(stack.back()));
+                    stack.pop_back();
+
+                    // if current_item is array/object, move
+                    // its children to the stack to be processed later
+                    if (current_item.is_array())
+                    {
+                        std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
+
+                        current_item.m_value.array->clear();
+                    }
+                    else if (current_item.is_object())
+                    {
+                        for (auto&& it : *current_item.m_value.object)
+                        {
+                            stack.push_back(std::move(it.second));
+                        }
+
+                        current_item.m_value.object->clear();
+                    }
+
+                    // it's now safe that current_item get destructed
+                    // since it doesn't have any children
+                }
+            }
+
+            switch (t)
+            {
+                case value_t::object:
+                {
+                    AllocatorType<object_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    AllocatorType<array_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+                    break;
+                }
+
+                case value_t::string:
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
+                    break;
+                }
+
+                case value_t::null:
+                case value_t::boolean:
+                case value_t::number_integer:
+                case value_t::number_unsigned:
+                case value_t::number_float:
+                case value_t::discarded:
+                default:
+                {
+                    break;
+                }
+            }
+        }
+    };
+
+  private:
+    /*!
+    @brief checks the class invariants
+
+    This function asserts the class invariants. It needs to be called at the
+    end of every constructor to make sure that created objects respect the
+    invariant. Furthermore, it has to be called each time the type of a JSON
+    value is changed, because the invariant expresses a relationship between
+    @a m_type and @a m_value.
+
+    Furthermore, the parent relation is checked for arrays and objects: If
+    @a check_parents true and the value is an array or object, then the
+    container's elements must have the current value as parent.
+
+    @param[in] check_parents  whether the parent relation should be checked.
+               The value is true by default and should only be set to false
+               during destruction of objects when the invariant does not
+               need to hold.
+    */
+    void assert_invariant(bool check_parents = true) const noexcept
+    {
+        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
+        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
+        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
+        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+        JSON_TRY
+        {
+            // cppcheck-suppress assertWithSideEffect
+            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+            {
+                return j.m_parent == this;
+            }));
+        }
+        JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+        static_cast<void>(check_parents);
+    }
+
+    void set_parents()
+    {
+#if JSON_DIAGNOSTICS
+        switch (m_type)
+        {
+            case value_t::array:
+            {
+                for (auto& element : *m_value.array)
+                {
+                    element.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::object:
+            {
+                for (auto& element : *m_value.object)
+                {
+                    element.second.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                break;
+        }
+#endif
+    }
+
+    iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)
+    {
+#if JSON_DIAGNOSTICS
+        for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)
+        {
+            (it + i)->m_parent = this;
+        }
+#else
+        static_cast<void>(count_set_parents);
+#endif
+        return it;
+    }
+
+    reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1))
+    {
+#if JSON_DIAGNOSTICS
+        if (old_capacity != static_cast<std::size_t>(-1))
+        {
+            // see https://github.com/nlohmann/json/issues/2838
+            JSON_ASSERT(type() == value_t::array);
+            if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+            {
+                // capacity has changed: update all parents
+                set_parents();
+                return j;
+            }
+        }
+
+        // ordered_json uses a vector internally, so pointers could have
+        // been invalidated; see https://github.com/nlohmann/json/issues/2962
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning(push )
+#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
+#endif
+        if (detail::is_ordered_map<object_t>::value)
+        {
+            set_parents();
+            return j;
+        }
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning( pop )
+#endif
+
+        j.m_parent = this;
+#else
+        static_cast<void>(j);
+        static_cast<void>(old_capacity);
+#endif
+        return j;
+    }
+
+  public:
+    //////////////////////////
+    // JSON parser callback //
+    //////////////////////////
+
+    /// @brief parser event types
+    /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/
+    using parse_event_t = detail::parse_event_t;
+
+    /// @brief per-element parser callback type
+    /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/
+    using parser_callback_t = detail::parser_callback_t<basic_json>;
+
+    //////////////////
+    // constructors //
+    //////////////////
+
+    /// @name constructors and destructors
+    /// Constructors of class @ref basic_json, copy/move constructor, copy
+    /// assignment, static functions creating objects, and the destructor.
+    /// @{
+
+    /// @brief create an empty value with a given type
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const value_t v)
+        : m_type(v), m_value(v)
+    {
+        assert_invariant();
+    }
+
+    /// @brief create a null object
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
+        : basic_json(value_t::null)
+    {
+        assert_invariant();
+    }
+
+    /// @brief create a JSON value from compatible types
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename CompatibleType,
+               typename U = detail::uncvref_t<CompatibleType>,
+               detail::enable_if_t <
+                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
+                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
+                                           std::forward<CompatibleType>(val))))
+    {
+        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief create a JSON value from an existing one
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
+    basic_json(const BasicJsonType& val)
+    {
+        using other_boolean_t = typename BasicJsonType::boolean_t;
+        using other_number_float_t = typename BasicJsonType::number_float_t;
+        using other_number_integer_t = typename BasicJsonType::number_integer_t;
+        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+        using other_string_t = typename BasicJsonType::string_t;
+        using other_object_t = typename BasicJsonType::object_t;
+        using other_array_t = typename BasicJsonType::array_t;
+        using other_binary_t = typename BasicJsonType::binary_t;
+
+        switch (val.type())
+        {
+            case value_t::boolean:
+                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+                break;
+            case value_t::number_float:
+                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+                break;
+            case value_t::number_integer:
+                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+                break;
+            case value_t::number_unsigned:
+                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+                break;
+            case value_t::string:
+                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+                break;
+            case value_t::object:
+                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+                break;
+            case value_t::array:
+                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+                break;
+            case value_t::binary:
+                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
+                break;
+            case value_t::null:
+                *this = nullptr;
+                break;
+            case value_t::discarded:
+                m_type = value_t::discarded;
+                break;
+            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();
+    }
+
+    /// @brief create a container (array or object) from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(initializer_list_t init,
+               bool type_deduction = true,
+               value_t manual_type = value_t::array)
+    {
+        // check if each element is an array with two elements whose first
+        // element is a string
+        bool is_an_object = std::all_of(init.begin(), init.end(),
+                                        [](const detail::json_ref<basic_json>& element_ref)
+        {
+            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
+        });
+
+        // adjust type if type deduction is not wanted
+        if (!type_deduction)
+        {
+            // if array is wanted, do not create an object though possible
+            if (manual_type == value_t::array)
+            {
+                is_an_object = false;
+            }
+
+            // if object is wanted but impossible, throw an exception
+            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
+            {
+                JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
+            }
+        }
+
+        if (is_an_object)
+        {
+            // the initializer list is a list of pairs -> create object
+            m_type = value_t::object;
+            m_value = value_t::object;
+
+            for (auto& element_ref : init)
+            {
+                auto element = element_ref.moved_or_copied();
+                m_value.object->emplace(
+                    std::move(*((*element.m_value.array)[0].m_value.string)),
+                    std::move((*element.m_value.array)[1]));
+            }
+        }
+        else
+        {
+            // the initializer list describes an array -> create array
+            m_type = value_t::array;
+            m_value.array = create<array_t>(init.begin(), init.end());
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief explicitly create a binary array (without subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = init;
+        return res;
+    }
+
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = binary_t(init, subtype);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = std::move(init);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = binary_t(std::move(init), subtype);
+        return res;
+    }
+
+    /// @brief explicitly create an array from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/array/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json array(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::array);
+    }
+
+    /// @brief explicitly create an object from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/object/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json object(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::object);
+    }
+
+    /// @brief construct an array with count copies of given value
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(size_type cnt, const basic_json& val)
+        : m_type(value_t::array)
+    {
+        m_value.array = create<array_t>(cnt, val);
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief construct a JSON container given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < class InputIT, typename std::enable_if <
+                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
+    basic_json(InputIT first, InputIT last)
+    {
+        JSON_ASSERT(first.m_object != nullptr);
+        JSON_ASSERT(last.m_object != nullptr);
+
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
+        }
+
+        // copy type from first iterator
+        m_type = first.m_object->m_type;
+
+        // check if iterator range is complete for primitive values
+        switch (m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            {
+                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+                                         || !last.m_it.primitive_iterator.is_end()))
+                {
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
+                }
+                break;
+            }
+
+            case value_t::null:
+            case value_t::object:
+            case value_t::array:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                break;
+        }
+
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            {
+                m_value.number_integer = first.m_object->m_value.number_integer;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_value.number_unsigned = first.m_object->m_value.number_unsigned;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_value.number_float = first.m_object->m_value.number_float;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_value.boolean = first.m_object->m_value.boolean;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_value = *first.m_object->m_value.string;
+                break;
+            }
+
+            case value_t::object:
+            {
+                m_value.object = create<object_t>(first.m_it.object_iterator,
+                                                  last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_value.array = create<array_t>(first.m_it.array_iterator,
+                                                last.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_value = *first.m_object->m_value.binary;
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+
+    ///////////////////////////////////////
+    // other constructors and destructor //
+    ///////////////////////////////////////
+
+    template<typename JsonRef,
+             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
+
+    /// @brief copy constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const basic_json& other)
+        : m_type(other.m_type)
+    {
+        // check of passed value is valid
+        other.assert_invariant();
+
+        switch (m_type)
+        {
+            case value_t::object:
+            {
+                m_value = *other.m_value.object;
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_value = *other.m_value.array;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_value = *other.m_value.string;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_value = other.m_value.boolean;
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                m_value = other.m_value.number_integer;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_value = other.m_value.number_unsigned;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_value = other.m_value.number_float;
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_value = *other.m_value.binary;
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief move constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(basic_json&& other) noexcept
+        : m_type(std::move(other.m_type)),
+          m_value(std::move(other.m_value))
+    {
+        // check that passed value is valid
+        other.assert_invariant(false);
+
+        // invalidate payload
+        other.m_type = value_t::null;
+        other.m_value = {};
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief copy assignment
+    /// @sa https://json.nlohmann.me/api/basic_json/operator=/
+    basic_json& operator=(basic_json other) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        // check that passed value is valid
+        other.assert_invariant();
+
+        using std::swap;
+        swap(m_type, other.m_type);
+        swap(m_value, other.m_value);
+
+        set_parents();
+        assert_invariant();
+        return *this;
+    }
+
+    /// @brief destructor
+    /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/
+    ~basic_json() noexcept
+    {
+        assert_invariant(false);
+        m_value.destroy(m_type);
+    }
+
+    /// @}
+
+  public:
+    ///////////////////////
+    // object inspection //
+    ///////////////////////
+
+    /// @name object inspection
+    /// Functions to inspect the type of a JSON value.
+    /// @{
+
+    /// @brief serialization
+    /// @sa https://json.nlohmann.me/api/basic_json/dump/
+    string_t dump(const int indent = -1,
+                  const char indent_char = ' ',
+                  const bool ensure_ascii = false,
+                  const error_handler_t error_handler = error_handler_t::strict) const
+    {
+        string_t result;
+        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
+
+        if (indent >= 0)
+        {
+            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+        }
+        else
+        {
+            s.dump(*this, false, ensure_ascii, 0);
+        }
+
+        return result;
+    }
+
+    /// @brief return the type of the JSON value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/type/
+    constexpr value_t type() const noexcept
+    {
+        return m_type;
+    }
+
+    /// @brief return whether type is primitive
+    /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/
+    constexpr bool is_primitive() const noexcept
+    {
+        return is_null() || is_string() || is_boolean() || is_number() || is_binary();
+    }
+
+    /// @brief return whether type is structured
+    /// @sa https://json.nlohmann.me/api/basic_json/is_structured/
+    constexpr bool is_structured() const noexcept
+    {
+        return is_array() || is_object();
+    }
+
+    /// @brief return whether value is null
+    /// @sa https://json.nlohmann.me/api/basic_json/is_null/
+    constexpr bool is_null() const noexcept
+    {
+        return m_type == value_t::null;
+    }
+
+    /// @brief return whether value is a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/
+    constexpr bool is_boolean() const noexcept
+    {
+        return m_type == value_t::boolean;
+    }
+
+    /// @brief return whether value is a number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number/
+    constexpr bool is_number() const noexcept
+    {
+        return is_number_integer() || is_number_float();
+    }
+
+    /// @brief return whether value is an integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/
+    constexpr bool is_number_integer() const noexcept
+    {
+        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
+    }
+
+    /// @brief return whether value is an unsigned integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/
+    constexpr bool is_number_unsigned() const noexcept
+    {
+        return m_type == value_t::number_unsigned;
+    }
+
+    /// @brief return whether value is a floating-point number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/
+    constexpr bool is_number_float() const noexcept
+    {
+        return m_type == value_t::number_float;
+    }
+
+    /// @brief return whether value is an object
+    /// @sa https://json.nlohmann.me/api/basic_json/is_object/
+    constexpr bool is_object() const noexcept
+    {
+        return m_type == value_t::object;
+    }
+
+    /// @brief return whether value is an array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_array/
+    constexpr bool is_array() const noexcept
+    {
+        return m_type == value_t::array;
+    }
+
+    /// @brief return whether value is a string
+    /// @sa https://json.nlohmann.me/api/basic_json/is_string/
+    constexpr bool is_string() const noexcept
+    {
+        return m_type == value_t::string;
+    }
+
+    /// @brief return whether value is a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_binary/
+    constexpr bool is_binary() const noexcept
+    {
+        return m_type == value_t::binary;
+    }
+
+    /// @brief return whether value is discarded
+    /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/
+    constexpr bool is_discarded() const noexcept
+    {
+        return m_type == value_t::discarded;
+    }
+
+    /// @brief return the type of the JSON value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/
+    constexpr operator value_t() const noexcept
+    {
+        return m_type;
+    }
+
+    /// @}
+
+  private:
+    //////////////////
+    // value access //
+    //////////////////
+
+    /// get a boolean (explicit)
+    boolean_t get_impl(boolean_t* /*unused*/) const
+    {
+        if (JSON_HEDLEY_LIKELY(is_boolean()))
+        {
+            return m_value.boolean;
+        }
+
+        JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
+    }
+
+    /// get a pointer to the value (object)
+    object_t* get_impl_ptr(object_t* /*unused*/) noexcept
+    {
+        return is_object() ? m_value.object : nullptr;
+    }
+
+    /// get a pointer to the value (object)
+    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
+    {
+        return is_object() ? m_value.object : nullptr;
+    }
+
+    /// get a pointer to the value (array)
+    array_t* get_impl_ptr(array_t* /*unused*/) noexcept
+    {
+        return is_array() ? m_value.array : nullptr;
+    }
+
+    /// get a pointer to the value (array)
+    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
+    {
+        return is_array() ? m_value.array : nullptr;
+    }
+
+    /// get a pointer to the value (string)
+    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
+    {
+        return is_string() ? m_value.string : nullptr;
+    }
+
+    /// get a pointer to the value (string)
+    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
+    {
+        return is_string() ? m_value.string : nullptr;
+    }
+
+    /// get a pointer to the value (boolean)
+    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
+    {
+        return is_boolean() ? &m_value.boolean : nullptr;
+    }
+
+    /// get a pointer to the value (boolean)
+    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
+    {
+        return is_boolean() ? &m_value.boolean : nullptr;
+    }
+
+    /// get a pointer to the value (integer number)
+    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
+    {
+        return is_number_integer() ? &m_value.number_integer : nullptr;
+    }
+
+    /// get a pointer to the value (integer number)
+    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
+    {
+        return is_number_integer() ? &m_value.number_integer : nullptr;
+    }
+
+    /// get a pointer to the value (unsigned number)
+    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
+    {
+        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
+    }
+
+    /// get a pointer to the value (unsigned number)
+    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
+    {
+        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
+    }
+
+    /// get a pointer to the value (floating-point number)
+    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
+    {
+        return is_number_float() ? &m_value.number_float : nullptr;
+    }
+
+    /// get a pointer to the value (floating-point number)
+    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
+    {
+        return is_number_float() ? &m_value.number_float : nullptr;
+    }
+
+    /// get a pointer to the value (binary)
+    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
+    {
+        return is_binary() ? m_value.binary : nullptr;
+    }
+
+    /// get a pointer to the value (binary)
+    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
+    {
+        return is_binary() ? m_value.binary : nullptr;
+    }
+
+    /*!
+    @brief helper function to implement get_ref()
+
+    This function helps to implement get_ref() without code duplication for
+    const and non-const overloads
+
+    @tparam ThisType will be deduced as `basic_json` or `const basic_json`
+
+    @throw type_error.303 if ReferenceType does not match underlying value
+    type of the current JSON
+    */
+    template<typename ReferenceType, typename ThisType>
+    static ReferenceType get_ref_impl(ThisType& obj)
+    {
+        // delegate the call to get_ptr<>()
+        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+
+        if (JSON_HEDLEY_LIKELY(ptr != nullptr))
+        {
+            return *ptr;
+        }
+
+        JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
+    }
+
+  public:
+    /// @name value access
+    /// Direct access to the stored value of a JSON value.
+    /// @{
+
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template<typename PointerType, typename std::enable_if<
+                 std::is_pointer<PointerType>::value, int>::type = 0>
+    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+    {
+        // delegate the call to get_impl_ptr<>()
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
+    }
+
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template < typename PointerType, typename std::enable_if <
+                   std::is_pointer<PointerType>::value&&
+                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+    {
+        // delegate the call to get_impl_ptr<>() const
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
+    }
+
+  private:
+    /*!
+    @brief get a value (explicit)
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    ValueType ret;
+    JSONSerializer<ValueType>::from_json(*this, ret);
+    return ret;
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json,
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `void from_json(const basic_json&, ValueType&)`, and
+    - @ref json_serializer<ValueType> does not have a `from_json()` method of
+      the form `ValueType from_json(const basic_json&)`
+
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @liveexample{The example below shows several conversions from JSON values
+    to other types. There a few things to note: (1) Floating-point numbers can
+    be converted to integers\, (2) A JSON array can be converted to a standard
+    `std::vector<short>`\, (3) A JSON object can be converted to C++
+    associative containers such as `std::unordered_map<std::string\,
+    json>`.,get__ValueType_const}
+
+    @since version 2.1.0
+    */
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::is_default_constructible<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+    {
+        auto ret = ValueType();
+        JSONSerializer<ValueType>::from_json(*this, ret);
+        return ret;
+    }
+
+    /*!
+    @brief get a value (explicit); special case
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    return JSONSerializer<ValueType>::from_json(*this);
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json and
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `ValueType from_json(const basic_json&)`
+
+    @note If @ref json_serializer<ValueType> has both overloads of
+    `from_json()`, this one is chosen.
+
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @since version 2.1.0
+    */
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
+    {
+        return JSONSerializer<ValueType>::from_json(*this);
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads converts the current @ref basic_json in a different
+    @ref basic_json type
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this, converted into @a BasicJsonType
+
+    @complexity Depending on the implementation of the called `from_json()`
+                method.
+
+    @since version 3.2.0
+    */
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value,
+                   int > = 0 >
+    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads avoids a lot of template boilerplate, it can be seen as the
+    identity method
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this
+
+    @complexity Constant.
+
+    @since version 2.1.0
+    */
+    template<typename BasicJsonType,
+             detail::enable_if_t<
+                 std::is_same<BasicJsonType, basic_json_t>::value,
+                 int> = 0>
+    basic_json get_impl(detail::priority_tag<3> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get a pointer value (explicit)
+    @copydoc get()
+    */
+    template<typename PointerType,
+             detail::enable_if_t<
+                 std::is_pointer<PointerType>::value,
+                 int> = 0>
+    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+    {
+        // delegate the call to get_ptr
+        return get_ptr<PointerType>();
+    }
+
+  public:
+    /*!
+    @brief get a (pointer) value (explicit)
+
+    Performs explicit type conversion between the JSON value and a compatible value if required.
+
+    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+    No copies are made.
+
+    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+    from the current @ref basic_json.
+
+    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+    method.
+
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @tparam ValueType if necessary
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
+
+    @since version 2.1.0
+    */
+    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+    constexpr
+#endif
+    auto get() const noexcept(
+    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
+    {
+        // we cannot static_assert on ValueTypeCV being non-const, because
+        // there is support for get<const basic_json_t>(), which is why we
+        // still need the uncvref
+        static_assert(!std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        return get_impl<ValueType>(detail::priority_tag<4> {});
+    }
+
+    /*!
+    @brief get a pointer value (explicit)
+
+    Explicit pointer access to the internally stored JSON value. No copies are
+    made.
+
+    @warning The pointer becomes invalid if the underlying JSON object
+    changes.
+
+    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
+    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+    @ref number_unsigned_t, or @ref number_float_t.
+
+    @return pointer to the internally stored JSON value if the requested
+    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
+
+    @complexity Constant.
+
+    @liveexample{The example below shows how pointers to internal values of a
+    JSON value can be requested. Note that no type conversions are made and a
+    `nullptr` is returned if the value and the requested pointer type does not
+    match.,get__PointerType}
+
+    @sa see @ref get_ptr() for explicit pointer-member access
+
+    @since version 1.0.0
+    */
+    template<typename PointerType, typename std::enable_if<
+                 std::is_pointer<PointerType>::value, int>::type = 0>
+    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
+    {
+        // delegate the call to get_ptr
+        return get_ptr<PointerType>();
+    }
+
+    /// @brief get a value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_to/
+    template < typename ValueType,
+               detail::enable_if_t <
+                   !detail::is_basic_json<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType & get_to(ValueType& v) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
+    {
+        JSONSerializer<ValueType>::from_json(*this, v);
+        return v;
+    }
+
+    // specialization to allow calling get_to with a basic_json value
+    // see https://github.com/nlohmann/json/issues/2175
+    template<typename ValueType,
+             detail::enable_if_t <
+                 detail::is_basic_json<ValueType>::value,
+                 int> = 0>
+    ValueType & get_to(ValueType& v) const
+    {
+        v = *this;
+        return v;
+    }
+
+    template <
+        typename T, std::size_t N,
+        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+        detail::enable_if_t <
+            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    noexcept(noexcept(JSONSerializer<Array>::from_json(
+                          std::declval<const basic_json_t&>(), v)))
+    {
+        JSONSerializer<Array>::from_json(*this, v);
+        return v;
+    }
+
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+    template<typename ReferenceType, typename std::enable_if<
+                 std::is_reference<ReferenceType>::value, int>::type = 0>
+    ReferenceType get_ref()
+    {
+        // delegate call to get_ref_impl
+        return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+    template < typename ReferenceType, typename std::enable_if <
+                   std::is_reference<ReferenceType>::value&&
+                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
+    ReferenceType get_ref() const
+    {
+        // delegate call to get_ref_impl
+        return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /*!
+    @brief get a value (implicit)
+
+    Implicit type conversion between the JSON value and a compatible value.
+    The call is realized by calling @ref get() const.
+
+    @tparam ValueType non-pointer type compatible to the JSON value, for
+    instance `int` for JSON integer numbers, `bool` for JSON booleans, or
+    `std::vector` types for JSON arrays. The character type of @ref string_t
+    as well as an initializer list of this type is excluded to avoid
+    ambiguities as these types implicitly convert to `std::string`.
+
+    @return copy of the JSON value, converted to type @a ValueType
+
+    @throw type_error.302 in case passed type @a ValueType is incompatible
+    to the JSON value type (e.g., the JSON value is of type boolean, but a
+    string is requested); see example below
+
+    @complexity Linear in the size of the JSON value.
+
+    @liveexample{The example below shows several conversions from JSON values
+    to other types. There a few things to note: (1) Floating-point numbers can
+    be converted to integers\, (2) A JSON array can be converted to a standard
+    `std::vector<short>`\, (3) A JSON object can be converted to C++
+    associative containers such as `std::unordered_map<std::string\,
+    json>`.,operator__ValueType}
+
+    @since version 1.0.0
+    */
+    template < typename ValueType, typename std::enable_if <
+                   detail::conjunction <
+                       detail::negation<std::is_pointer<ValueType>>,
+                       detail::negation<std::is_same<ValueType, std::nullptr_t>>,
+                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,
+                                        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
+#if defined(JSON_HAS_CPP_17)
+                                                detail::negation<std::is_same<ValueType, std::any>>,
+#endif
+                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>
+                                                >::value, int >::type = 0 >
+                                        JSON_EXPLICIT operator ValueType() const
+    {
+        // delegate the call to get<>() const
+        return get<ValueType>();
+    }
+
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    binary_t& get_binary()
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<binary_t*>();
+    }
+
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    const binary_t& get_binary() const
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<const binary_t*>();
+    }
+
+    /// @}
+
+
+    ////////////////////
+    // element access //
+    ////////////////////
+
+    /// @name element access
+    /// Access to the JSON value.
+    /// @{
+
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(size_type idx)
+    {
+        // at only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            JSON_TRY
+            {
+                return set_parent(m_value.array->at(idx));
+            }
+            JSON_CATCH (std::out_of_range&)
+            {
+                // create better exception explanation
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(size_type idx) const
+    {
+        // at only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            JSON_TRY
+            {
+                return m_value.array->at(idx);
+            }
+            JSON_CATCH (std::out_of_range&)
+            {
+                // create better exception explanation
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(const typename object_t::key_type& key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    reference at(KeyType && key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(const typename object_t::key_type& key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_reference at(KeyType && key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](size_type idx)
+    {
+        // implicitly convert null value to an empty array
+        if (is_null())
+        {
+            m_type = value_t::array;
+            m_value.array = create<array_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // fill up array with null values if given idx is outside range
+            if (idx >= m_value.array->size())
+            {
+#if JSON_DIAGNOSTICS
+                // remember array size & capacity before resizing
+                const auto old_size = m_value.array->size();
+                const auto old_capacity = m_value.array->capacity();
+#endif
+                m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+                if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+                {
+                    // capacity has changed: update all parents
+                    set_parents();
+                }
+                else
+                {
+                    // set parent for values added above
+                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));
+                }
+#endif
+                assert_invariant();
+            }
+
+            return m_value.array->operator[](idx);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+    }
+
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](size_type idx) const
+    {
+        // const operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            return m_value.array->operator[](idx);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](typename object_t::key_type key)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_value.object->emplace(std::move(key), nullptr);
+            return set_parent(result.first->second);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](const typename object_t::key_type& key) const
+    {
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_value.object->find(key);
+            JSON_ASSERT(it != m_value.object->end());
+            return it->second;
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
+    // (they seemingly cannot be constrained to resolve the ambiguity)
+    template<typename T>
+    reference operator[](T* key)
+    {
+        return operator[](typename object_t::key_type(key));
+    }
+
+    template<typename T>
+    const_reference operator[](T* key) const
+    {
+        return operator[](typename object_t::key_type(key));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    reference operator[](KeyType && key)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_value.object->emplace(std::forward<KeyType>(key), nullptr);
+            return set_parent(result.first->second);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    const_reference operator[](KeyType && key) const
+    {
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_value.object->find(std::forward<KeyType>(key));
+            JSON_ASSERT(it != m_value.object->end());
+            return it->second;
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+  private:
+    template<typename KeyType>
+    using is_comparable_with_object_key = detail::is_comparable <
+        object_comparator_t, const typename object_t::key_type&, KeyType >;
+
+    template<typename ValueType>
+    using value_return_type = std::conditional <
+        detail::is_c_string_uncvref<ValueType>::value,
+        string_t, typename std::decay<ValueType>::type >;
+
+  public:
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(key);
+            if (it != end())
+            {
+                return it->template get<ValueType>();
+            }
+
+            return default_value;
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(key);
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
+
+            return std::forward<ValueType>(default_value);
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(KeyType && key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ValueType>();
+            }
+
+            return default_value;
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(KeyType && key, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
+
+            return std::forward<ValueType>(default_value);
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(const json_pointer& ptr, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if pointer resolves a value, return it or use default value
+            JSON_TRY
+            {
+                return ptr.get_checked(this).template get<ValueType>();
+            }
+            JSON_INTERNAL_CATCH (out_of_range&)
+            {
+                return default_value;
+            }
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const json_pointer& ptr, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if pointer resolves a value, return it or use default value
+            JSON_TRY
+            {
+                return ptr.get_checked(this).template get<ReturnType>();
+            }
+            JSON_INTERNAL_CATCH (out_of_range&)
+            {
+                return std::forward<ValueType>(default_value);
+            }
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    template < class ValueType, class BasicJsonType, detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
+    {
+        return value(ptr.convert(), default_value);
+    }
+
+    template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
+    {
+        return value(ptr.convert(), std::forward<ValueType>(default_value));
+    }
+
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
+    reference front()
+    {
+        return *begin();
+    }
+
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
+    const_reference front() const
+    {
+        return *cbegin();
+    }
+
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
+    reference back()
+    {
+        auto tmp = end();
+        --tmp;
+        return *tmp;
+    }
+
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
+    const_reference back() const
+    {
+        auto tmp = cend();
+        --tmp;
+        return *tmp;
+    }
+
+    /// @brief remove element given an iterator
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType pos)
+    {
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        IteratorType result = end();
+
+        switch (m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            case value_t::binary:
+            {
+                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
+                {
+                    JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
+                }
+
+                if (is_string())
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+                    m_value.string = nullptr;
+                }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+                    m_value.binary = nullptr;
+                }
+
+                m_type = value_t::null;
+                assert_invariant();
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return result;
+    }
+
+    /// @brief remove elements given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType first, IteratorType last)
+    {
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
+        }
+
+        IteratorType result = end();
+
+        switch (m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            case value_t::binary:
+            {
+                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+                                       || !last.m_it.primitive_iterator.is_end()))
+                {
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
+                }
+
+                if (is_string())
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+                    m_value.string = nullptr;
+                }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+                    m_value.binary = nullptr;
+                }
+
+                m_type = value_t::null;
+                assert_invariant();
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
+                                              last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
+                                             last.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return result;
+    }
+
+  private:
+    template < typename KeyType, detail::enable_if_t <
+                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return m_value.object->erase(std::forward<KeyType>(key));
+    }
+
+    template < typename KeyType, detail::enable_if_t <
+                   !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        const auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it != m_value.object->end())
+        {
+            m_value.object->erase(it);
+            return 1;
+        }
+        return 0;
+    }
+
+  public:
+
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    size_type erase(const typename object_t::key_type& key)
+    {
+        // the indirection via erase_internal() is added to avoid making this
+        // function a template and thus de-rank it during overload resolution
+        return erase_internal(key);
+    }
+
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        return erase_internal(std::forward<KeyType>(key));
+    }
+
+    /// @brief remove element from a JSON array given an index
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    void erase(const size_type idx)
+    {
+        // this erase only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            if (JSON_HEDLEY_UNLIKELY(idx >= size()))
+            {
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+
+            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
+        }
+        else
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+    }
+
+    /// @}
+
+
+    ////////////
+    // lookup //
+    ////////////
+
+    /// @name lookup
+    /// @{
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    iterator find(const typename object_t::key_type& key)
+    {
+        auto result = end();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(key);
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    const_iterator find(const typename object_t::key_type& key) const
+    {
+        auto result = cend();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(key);
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        auto result = end();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_iterator find(KeyType && key) const
+    {
+        auto result = cend();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+        }
+
+        return result;
+    }
+
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    size_type count(const typename object_t::key_type& key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_value.object->count(key) : 0;
+    }
+
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_value.object->count(std::forward<KeyType>(key)) : 0;
+    }
+
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const typename object_t::key_type& key) const
+    {
+        return is_object() && m_value.object->find(key) != m_value.object->end();
+    }
+
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    bool contains(KeyType && key) const
+    {
+        return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end();
+    }
+
+    /// @brief check the existence of an element in a JSON object given a JSON pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const json_pointer& ptr) const
+    {
+        return ptr.contains(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.contains(this);
+    }
+
+    /// @}
+
+
+    ///////////////
+    // iterators //
+    ///////////////
+
+    /// @name iterators
+    /// @{
+
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
+    iterator begin() noexcept
+    {
+        iterator result(this);
+        result.set_begin();
+        return result;
+    }
+
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
+    const_iterator begin() const noexcept
+    {
+        return cbegin();
+    }
+
+    /// @brief returns a const iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/cbegin/
+    const_iterator cbegin() const noexcept
+    {
+        const_iterator result(this);
+        result.set_begin();
+        return result;
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
+    iterator end() noexcept
+    {
+        iterator result(this);
+        result.set_end();
+        return result;
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
+    const_iterator end() const noexcept
+    {
+        return cend();
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/cend/
+    const_iterator cend() const noexcept
+    {
+        const_iterator result(this);
+        result.set_end();
+        return result;
+    }
+
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+    reverse_iterator rbegin() noexcept
+    {
+        return reverse_iterator(end());
+    }
+
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+    const_reverse_iterator rbegin() const noexcept
+    {
+        return crbegin();
+    }
+
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
+    reverse_iterator rend() noexcept
+    {
+        return reverse_iterator(begin());
+    }
+
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
+    const_reverse_iterator rend() const noexcept
+    {
+        return crend();
+    }
+
+    /// @brief returns a const reverse iterator to the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/crbegin/
+    const_reverse_iterator crbegin() const noexcept
+    {
+        return const_reverse_iterator(cend());
+    }
+
+    /// @brief returns a const reverse iterator to one before the first
+    /// @sa https://json.nlohmann.me/api/basic_json/crend/
+    const_reverse_iterator crend() const noexcept
+    {
+        return const_reverse_iterator(cbegin());
+    }
+
+  public:
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use @ref items() instead;
+    ///             that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
+    {
+        return ref.items();
+    }
+
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///         version 4.0.0 of the library. Please use @ref items() instead;
+    ///         that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
+    {
+        return ref.items();
+    }
+
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    iteration_proxy<iterator> items() noexcept
+    {
+        return iteration_proxy<iterator>(*this);
+    }
+
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    iteration_proxy<const_iterator> items() const noexcept
+    {
+        return iteration_proxy<const_iterator>(*this);
+    }
+
+    /// @}
+
+
+    //////////////
+    // capacity //
+    //////////////
+
+    /// @name capacity
+    /// @{
+
+    /// @brief checks whether the container is empty.
+    /// @sa https://json.nlohmann.me/api/basic_json/empty/
+    bool empty() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return true;
+            }
+
+            case value_t::array:
+            {
+                // delegate call to array_t::empty()
+                return m_value.array->empty();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::empty()
+                return m_value.object->empty();
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types are nonempty
+                return false;
+            }
+        }
+    }
+
+    /// @brief returns the number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/size/
+    size_type size() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return 0;
+            }
+
+            case value_t::array:
+            {
+                // delegate call to array_t::size()
+                return m_value.array->size();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::size()
+                return m_value.object->size();
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have size 1
+                return 1;
+            }
+        }
+    }
+
+    /// @brief returns the maximum possible number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/max_size/
+    size_type max_size() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::array:
+            {
+                // delegate call to array_t::max_size()
+                return m_value.array->max_size();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::max_size()
+                return m_value.object->max_size();
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have max_size() == size()
+                return size();
+            }
+        }
+    }
+
+    /// @}
+
+
+    ///////////////
+    // modifiers //
+    ///////////////
+
+    /// @name modifiers
+    /// @{
+
+    /// @brief clears the contents
+    /// @sa https://json.nlohmann.me/api/basic_json/clear/
+    void clear() noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            {
+                m_value.number_integer = 0;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_value.number_unsigned = 0;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_value.number_float = 0.0;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_value.boolean = false;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_value.string->clear();
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_value.binary->clear();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_value.array->clear();
+                break;
+            }
+
+            case value_t::object:
+            {
+                m_value.object->clear();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(basic_json&& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_type = value_t::array;
+            m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array (move semantics)
+        const auto old_capacity = m_value.array->capacity();
+        m_value.array->push_back(std::move(val));
+        set_parent(m_value.array->back(), old_capacity);
+        // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(basic_json&& val)
+    {
+        push_back(std::move(val));
+        return *this;
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const basic_json& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_type = value_t::array;
+            m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array
+        const auto old_capacity = m_value.array->capacity();
+        m_value.array->push_back(val);
+        set_parent(m_value.array->back(), old_capacity);
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const basic_json& val)
+    {
+        push_back(val);
+        return *this;
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const typename object_t::value_type& val)
+    {
+        // push_back only works for null objects or objects
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value = value_t::object;
+            assert_invariant();
+        }
+
+        // add element to object
+        auto res = m_value.object->insert(val);
+        set_parent(res.first->second);
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const typename object_t::value_type& val)
+    {
+        push_back(val);
+        return *this;
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(initializer_list_t init)
+    {
+        if (is_object() && init.size() == 2 && (*init.begin())->is_string())
+        {
+            basic_json&& key = init.begin()->moved_or_copied();
+            push_back(typename object_t::value_type(
+                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
+        }
+        else
+        {
+            push_back(basic_json(init));
+        }
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(initializer_list_t init)
+    {
+        push_back(init);
+        return *this;
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/
+    template<class... Args>
+    reference emplace_back(Args&& ... args)
+    {
+        // emplace_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_type = value_t::array;
+            m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array (perfect forwarding)
+        const auto old_capacity = m_value.array->capacity();
+        m_value.array->emplace_back(std::forward<Args>(args)...);
+        return set_parent(m_value.array->back(), old_capacity);
+    }
+
+    /// @brief add an object to an object if key does not exist
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace/
+    template<class... Args>
+    std::pair<iterator, bool> emplace(Args&& ... args)
+    {
+        // emplace only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+        {
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
+        }
+
+        // transform null object into an object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value = value_t::object;
+            assert_invariant();
+        }
+
+        // add element to array (perfect forwarding)
+        auto res = m_value.object->emplace(std::forward<Args>(args)...);
+        set_parent(res.first->second);
+
+        // create result iterator and set iterator to the result of emplace
+        auto it = begin();
+        it.m_it.object_iterator = res.first;
+
+        // return pair of iterator and boolean
+        return {it, res.second};
+    }
+
+    /// Helper for insertion of an iterator
+    /// @note: This uses std::distance to support GCC 4.8,
+    ///        see https://github.com/nlohmann/json/pull/1257
+    template<typename... Args>
+    iterator insert_iterator(const_iterator pos, Args&& ... args)
+    {
+        iterator result(this);
+        JSON_ASSERT(m_value.array != nullptr);
+
+        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
+        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
+        result.m_it.array_iterator = m_value.array->begin() + insert_pos;
+
+        // This could have been written as:
+        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+
+        set_parents();
+        return result;
+    }
+
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
+
+            // insert to array and return iterator
+            return insert_iterator(pos, val);
+        }
+
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
+
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, basic_json&& val)
+    {
+        return insert(pos, val);
+    }
+
+    /// @brief inserts copies of element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
+
+            // insert to array and return iterator
+            return insert_iterator(pos, cnt, val);
+        }
+
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
+
+    /// @brief inserts range of elements into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
+        {
+            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
+        }
+
+        // insert to array and return iterator
+        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+    }
+
+    /// @brief inserts elements from initializer list into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, initializer_list_t ilist)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        // insert to array and return iterator
+        return insert_iterator(pos, ilist.begin(), ilist.end());
+    }
+
+    /// @brief inserts range of elements into object
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    void insert(const_iterator first, const_iterator last)
+    {
+        // insert only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
+        }
+
+        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+    }
+
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_reference j, bool merge_objects = false)
+    {
+        update(j.begin(), j.end(), merge_objects);
+    }
+
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_iterator first, const_iterator last, bool merge_objects = false)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
+        }
+
+        for (auto it = first; it != last; ++it)
+        {
+            if (merge_objects && it.value().is_object())
+            {
+                auto it2 = m_value.object->find(it.key());
+                if (it2 != m_value.object->end())
+                {
+                    it2->second.update(it.value(), true);
+                    continue;
+                }
+            }
+            m_value.object->operator[](it.key()) = it.value();
+#if JSON_DIAGNOSTICS
+            m_value.object->operator[](it.key()).m_parent = this;
+#endif
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(reference other) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        std::swap(m_type, other.m_type);
+        std::swap(m_value, other.m_value);
+
+        set_parents();
+        other.set_parents();
+        assert_invariant();
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    friend void swap(reference left, reference right) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        left.swap(right);
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(array_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            using std::swap;
+            swap(*(m_value.array), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(object_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            using std::swap;
+            swap(*(m_value.object), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(string_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_string()))
+        {
+            using std::swap;
+            swap(*(m_value.string), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
+        }
+    }
+
+    /// @}
+
+    //////////////////////////////////////////
+    // lexicographical comparison operators //
+    //////////////////////////////////////////
+
+    /// @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
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        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, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs == basic_json(rhs);
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) == rhs;
+    }
+
+    /// @brief comparison: not equal
+    /// @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);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs != basic_json(rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) != rhs;
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    friend bool operator<(const_reference lhs, const_reference rhs) noexcept
+    {
+        // 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.
+        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs < basic_json(rhs);
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) < rhs;
+    }
+
+    /// @brief comparison: less than or equal
+    /// @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);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs <= basic_json(rhs);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) <= rhs;
+    }
+
+    /// @brief comparison: greater than
+    /// @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);
+    }
+
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs > basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) > rhs;
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @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);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs >= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) >= rhs;
+    }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
+
+    /// @}
+
+    ///////////////////
+    // serialization //
+    ///////////////////
+
+    /// @name serialization
+    /// @{
+#ifndef JSON_NO_IO
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
+    {
+        // read width member and use it as indentation parameter if nonzero
+        const bool pretty_print = o.width() > 0;
+        const auto indentation = pretty_print ? o.width() : 0;
+
+        // reset width to 0 for subsequent calls to this stream
+        o.width(0);
+
+        // do the actual serialization
+        serializer s(detail::output_adapter<char>(o), o.fill());
+        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
+        return o;
+    }
+
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    /// @deprecated This function is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator<<(std::ostream&, const basic_json&) instead; that is,
+    ///             replace calls like `j >> o;` with `o << j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))
+    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
+    {
+        return o << j;
+    }
+#endif  // JSON_NO_IO
+    /// @}
+
+
+    /////////////////////
+    // deserialization //
+    /////////////////////
+
+    /// @name deserialization
+    /// @{
+
+    /// @brief deserialize from a compatible input
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(InputType&& i,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    /// @brief deserialize from a pair of character iterators
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(IteratorType first,
+                            IteratorType last,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
+    static basic_json parse(detail::span_input_adapter&& i,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename InputType>
+    static bool accept(InputType&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
+    }
+
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename IteratorType>
+    static bool accept(IteratorType first, IteratorType last,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
+    static bool accept(detail::span_input_adapter&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(i.get(), nullptr, false, ignore_comments).accept(true);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template <typename InputType, typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(InputType&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template<class IteratorType, class SAX>
+    JSON_HEDLEY_NON_NULL(3)
+    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    /// @deprecated This function is deprecated since 3.8.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             sax_parse(ptr, ptr + len) instead.
+    template <typename SAX>
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = i.get();
+        return format == input_format_t::json
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+#ifndef JSON_NO_IO
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator>>(std::istream&, basic_json&) instead; that is,
+    ///             replace calls like `j << i;` with `i >> j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))
+    friend std::istream& operator<<(basic_json& j, std::istream& i)
+    {
+        return operator>>(i, j);
+    }
+
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    friend std::istream& operator>>(std::istream& i, basic_json& j)
+    {
+        parser(detail::input_adapter(i)).parse(false, j);
+        return i;
+    }
+#endif  // JSON_NO_IO
+    /// @}
+
+    ///////////////////////////
+    // convenience functions //
+    ///////////////////////////
+
+    /// @brief return the type as string
+    /// @sa https://json.nlohmann.me/api/basic_json/type_name/
+    JSON_HEDLEY_RETURNS_NON_NULL
+    const char* type_name() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+                return "null";
+            case value_t::object:
+                return "object";
+            case value_t::array:
+                return "array";
+            case value_t::string:
+                return "string";
+            case value_t::boolean:
+                return "boolean";
+            case value_t::binary:
+                return "binary";
+            case value_t::discarded:
+                return "discarded";
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            default:
+                return "number";
+        }
+    }
+
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    //////////////////////
+    // member variables //
+    //////////////////////
+
+    /// the type of the current element
+    value_t m_type = value_t::null;
+
+    /// the value of the current element
+    json_value m_value = {};
+
+#if JSON_DIAGNOSTICS
+    /// a pointer to a parent value (for debugging purposes)
+    basic_json* m_parent = nullptr;
+#endif
+
+    //////////////////////////////////////////
+    // binary serialization/deserialization //
+    //////////////////////////////////////////
+
+    /// @name binary serialization/deserialization support
+    /// @{
+
+  public:
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static std::vector<std::uint8_t> to_cbor(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_cbor(j, result);
+        return result;
+    }
+
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_cbor(j);
+    }
+
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_cbor(j);
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_msgpack(j, result);
+        return result;
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_msgpack(j);
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_msgpack(j);
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_ubjson(j, result, use_size, use_type);
+        return result;
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_bjdata(j, result, use_size, use_type);
+        return result;
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    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)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static std::vector<std::uint8_t> to_bson(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_bson(j, result);
+        return result;
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_bson(j);
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_bson(j);
+    }
+
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(InputType&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(IteratorType first, IteratorType last,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
+    }
+
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(detail::span_input_adapter&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        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::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(InputType&& 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 = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(IteratorType first, IteratorType last,
+                                   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 = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(const T* ptr, std::size_t len,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(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::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(InputType&& 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 = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(IteratorType first, IteratorType last,
+                                  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 = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(const T* ptr, std::size_t len,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(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::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+
+    /// @brief create a JSON value from an input in BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(InputType&& 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 = detail::input_adapter(std::forward<InputType>(i));
+        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 BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(IteratorType first, IteratorType last,
+                                  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 = detail::input_adapter(std::move(first), std::move(last));
+        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>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(InputType&& 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 = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &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 IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(IteratorType first, IteratorType last,
+                                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 = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        return from_bson(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(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::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+    /// @}
+
+    //////////////////////////
+    // JSON Pointer support //
+    //////////////////////////
+
+    /// @name JSON Pointer functions
+    /// @{
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](const json_pointer& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](const json_pointer& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(const json_pointer& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(const json_pointer& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    /// @brief return flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/flatten/
+    basic_json flatten() const
+    {
+        basic_json result(value_t::object);
+        json_pointer::flatten("", *this, result);
+        return result;
+    }
+
+    /// @brief unflatten a previously flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
+    basic_json unflatten() const
+    {
+        return json_pointer::unflatten(*this);
+    }
+
+    /// @}
+
+    //////////////////////////
+    // JSON Patch functions //
+    //////////////////////////
+
+    /// @name JSON Patch functions
+    /// @{
+
+    /// @brief applies a JSON patch in-place without copying the object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    void patch_inplace(const basic_json& json_patch)
+    {
+        basic_json& result = *this;
+        // the valid JSON Patch operations
+        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+        const auto get_op = [](const std::string & op)
+        {
+            if (op == "add")
+            {
+                return patch_operations::add;
+            }
+            if (op == "remove")
+            {
+                return patch_operations::remove;
+            }
+            if (op == "replace")
+            {
+                return patch_operations::replace;
+            }
+            if (op == "move")
+            {
+                return patch_operations::move;
+            }
+            if (op == "copy")
+            {
+                return patch_operations::copy;
+            }
+            if (op == "test")
+            {
+                return patch_operations::test;
+            }
+
+            return patch_operations::invalid;
+        };
+
+        // wrapper for "add" operation; add value at ptr
+        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+        {
+            // adding to the root of the target document means replacing it
+            if (ptr.empty())
+            {
+                result = val;
+                return;
+            }
+
+            // make sure the top element of the pointer exists
+            json_pointer top_pointer = ptr.top();
+            if (top_pointer != ptr)
+            {
+                result.at(top_pointer);
+            }
+
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            // parent must exist when performing patch add per RFC6902 specs
+            basic_json& parent = result.at(ptr);
+
+            switch (parent.m_type)
+            {
+                case value_t::null:
+                case value_t::object:
+                {
+                    // use operator[] to add value
+                    parent[last_path] = val;
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    if (last_path == "-")
+                    {
+                        // special case: append to back
+                        parent.push_back(val);
+                    }
+                    else
+                    {
+                        const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
+                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
+                        {
+                            // avoid undefined behavior
+                            JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
+                        }
+
+                        // default case: insert add offset
+                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                    }
+                    break;
+                }
+
+                // if there exists a parent it cannot be primitive
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        };
+
+        // wrapper for "remove" operation; remove value at ptr
+        const auto operation_remove = [this, &result](json_pointer & ptr)
+        {
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            basic_json& parent = result.at(ptr);
+
+            // remove child
+            if (parent.is_object())
+            {
+                // perform range check
+                auto it = parent.find(last_path);
+                if (JSON_HEDLEY_LIKELY(it != parent.end()))
+                {
+                    parent.erase(it);
+                }
+                else
+                {
+                    JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
+                }
+            }
+            else if (parent.is_array())
+            {
+                // note erase performs range check
+                parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
+            }
+        };
+
+        // type check: top level value must be an array
+        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
+        {
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
+        }
+
+        // iterate and apply the operations
+        for (const auto& val : json_patch)
+        {
+            // wrapper to get a value for an operation
+            const auto get_value = [&val](const std::string & op,
+                                          const std::string & member,
+                                          bool string_type) -> basic_json &
+            {
+                // find value
+                auto it = val.m_value.object->find(member);
+
+                // context-sensitive error message
+                const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\'');
+
+                // check if desired value is present
+                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
+                }
+
+                // check if result is of type string
+                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
+                }
+
+                // no error: return value
+                return it->second;
+            };
+
+            // type check: every element of the array must be an object
+            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
+            {
+                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
+            }
+
+            // collect mandatory members
+            const auto op = get_value("op", "op", true).template get<std::string>();
+            const auto path = get_value(op, "path", true).template get<std::string>();
+            json_pointer ptr(path);
+
+            switch (get_op(op))
+            {
+                case patch_operations::add:
+                {
+                    operation_add(ptr, get_value("add", "value", false));
+                    break;
+                }
+
+                case patch_operations::remove:
+                {
+                    operation_remove(ptr);
+                    break;
+                }
+
+                case patch_operations::replace:
+                {
+                    // the "path" location must exist - use at()
+                    result.at(ptr) = get_value("replace", "value", false);
+                    break;
+                }
+
+                case patch_operations::move:
+                {
+                    const auto from_path = get_value("move", "from", true).template get<std::string>();
+                    json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json v = result.at(from_ptr);
+
+                    // The move operation is functionally identical to a
+                    // "remove" operation on the "from" location, followed
+                    // immediately by an "add" operation at the target
+                    // location with the value that was just removed.
+                    operation_remove(from_ptr);
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::copy:
+                {
+                    const auto from_path = get_value("copy", "from", true).template get<std::string>();
+                    const json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json v = result.at(from_ptr);
+
+                    // The copy is functionally identical to an "add"
+                    // operation at the target location using the value
+                    // specified in the "from" member.
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::test:
+                {
+                    bool success = false;
+                    JSON_TRY
+                    {
+                        // check if "value" matches the one at "path"
+                        // the "path" location must exist - use at()
+                        success = (result.at(ptr) == get_value("test", "value", false));
+                    }
+                    JSON_INTERNAL_CATCH (out_of_range&)
+                    {
+                        // ignore out of range errors: success remains false
+                    }
+
+                    // throw an exception if test fails
+                    if (JSON_HEDLEY_UNLIKELY(!success))
+                    {
+                        JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
+                    }
+
+                    break;
+                }
+
+                case patch_operations::invalid:
+                default:
+                {
+                    // op must be "add", "remove", "replace", "move", "copy", or
+                    // "test"
+                    JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
+                }
+            }
+        }
+    }
+
+    /// @brief applies a JSON patch to a copy of the current object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    basic_json patch(const basic_json& json_patch) const
+    {
+        basic_json result = *this;
+        result.patch_inplace(json_patch);
+        return result;
+    }
+
+    /// @brief creates a diff as a JSON patch
+    /// @sa https://json.nlohmann.me/api/basic_json/diff/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json diff(const basic_json& source, const basic_json& target,
+                           const std::string& path = "")
+    {
+        // the patch
+        basic_json result(value_t::array);
+
+        // if the values are the same, return empty patch
+        if (source == target)
+        {
+            return result;
+        }
+
+        if (source.type() != target.type())
+        {
+            // different types: replace value
+            result.push_back(
+            {
+                {"op", "replace"}, {"path", path}, {"value", target}
+            });
+            return result;
+        }
+
+        switch (source.type())
+        {
+            case value_t::array:
+            {
+                // first pass: traverse common elements
+                std::size_t i = 0;
+                while (i < source.size() && i < target.size())
+                {
+                    // recursive call to compare array values at index i
+                    auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
+                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    ++i;
+                }
+
+                // We now reached the end of at least one array
+                // in a second pass, traverse the remaining elements
+
+                // remove my remaining elements
+                const auto end_index = static_cast<difference_type>(result.size());
+                while (i < source.size())
+                {
+                    // add operations in reverse order to avoid invalid
+                    // indices
+                    result.insert(result.begin() + end_index, object(
+                    {
+                        {"op", "remove"},
+                        {"path", detail::concat(path, '/', std::to_string(i))}
+                    }));
+                    ++i;
+                }
+
+                // add other remaining elements
+                while (i < target.size())
+                {
+                    result.push_back(
+                    {
+                        {"op", "add"},
+                        {"path", detail::concat(path, "/-")},
+                        {"value", target[i]}
+                    });
+                    ++i;
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // first pass: traverse this object's elements
+                for (auto it = source.cbegin(); it != source.cend(); ++it)
+                {
+                    // escape the key name to be used in a JSON patch
+                    const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+
+                    if (target.find(it.key()) != target.end())
+                    {
+                        // recursive call to compare object values at key it
+                        auto temp_diff = diff(it.value(), target[it.key()], path_key);
+                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    }
+                    else
+                    {
+                        // found a key that is not in o -> remove it
+                        result.push_back(object(
+                        {
+                            {"op", "remove"}, {"path", path_key}
+                        }));
+                    }
+                }
+
+                // second pass: traverse other object's elements
+                for (auto it = target.cbegin(); it != target.cend(); ++it)
+                {
+                    if (source.find(it.key()) == source.end())
+                    {
+                        // found a key that is not in this -> add it
+                        const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+                        result.push_back(
+                        {
+                            {"op", "add"}, {"path", path_key},
+                            {"value", it.value()}
+                        });
+                    }
+                }
+
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // both primitive type: replace value
+                result.push_back(
+                {
+                    {"op", "replace"}, {"path", path}, {"value", target}
+                });
+                break;
+            }
+        }
+
+        return result;
+    }
+    /// @}
+
+    ////////////////////////////////
+    // JSON Merge Patch functions //
+    ////////////////////////////////
+
+    /// @name JSON Merge Patch functions
+    /// @{
+
+    /// @brief applies a JSON Merge Patch
+    /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
+    void merge_patch(const basic_json& apply_patch)
+    {
+        if (apply_patch.is_object())
+        {
+            if (!is_object())
+            {
+                *this = object();
+            }
+            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
+            {
+                if (it.value().is_null())
+                {
+                    erase(it.key());
+                }
+                else
+                {
+                    operator[](it.key()).merge_patch(it.value());
+                }
+            }
+        }
+        else
+        {
+            *this = apply_patch;
+        }
+    }
+
+    /// @}
+};
+
+/// @brief user-defined to_string function for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/to_string/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
+{
+    return j.dump();
+}
+
+inline namespace literals
+{
+inline namespace json_literals
+{
+
+/// @brief user-defined string literal for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json operator "" _json(const char* s, std::size_t n)
+{
+    return nlohmann::json::parse(s, s + n);
+}
+
+/// @brief user-defined string literal for JSON pointer
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
+{
+    return nlohmann::json::json_pointer(std::string(s, n));
+}
+
+}  // namespace json_literals
+}  // namespace literals
+NLOHMANN_JSON_NAMESPACE_END
+
+///////////////////////
+// nonmember support //
+///////////////////////
+
+namespace std // NOLINT(cert-dcl58-cpp)
+{
+
+/// @brief hash value for JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL>
+{
+    std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
+    {
+        return nlohmann::detail::hash(j);
+    }
+};
+
+// specialization for std::less<value_t>
+template<>
+struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679
+{
+    /*!
+    @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
+    {
+#if JSON_HAS_THREE_WAY_COMPARISON
+        return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+        return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
+    }
+};
+
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
+
+/// @brief exchanges the values of two JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept(  // NOLINT(readability-inconsistent-declaration-parameter-name)
+    is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&&                          // NOLINT(misc-redundant-expression)
+    is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
+{
+    j1.swap(j2);
+}
+
+#endif
+
+}  // namespace std
+
+#if JSON_USE_GLOBAL_UDLS
+    using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+    using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+#endif
+
+// #include <nlohmann/detail/macro_unscope.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// restore clang diagnostic settings
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+
+// clean up
+#undef JSON_ASSERT
+#undef JSON_INTERNAL_CATCH
+#undef JSON_THROW
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#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
+#undef JSON_USE_GLOBAL_UDLS
+
+#ifndef JSON_TEST_KEEP_MACROS
+    #undef JSON_CATCH
+    #undef JSON_TRY
+    #undef JSON_HAS_CPP_11
+    #undef JSON_HAS_CPP_14
+    #undef JSON_HAS_CPP_17
+    #undef JSON_HAS_CPP_20
+    #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>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#undef JSON_HEDLEY_ALWAYS_INLINE
+#undef JSON_HEDLEY_ARM_VERSION
+#undef JSON_HEDLEY_ARM_VERSION_CHECK
+#undef JSON_HEDLEY_ARRAY_PARAM
+#undef JSON_HEDLEY_ASSUME
+#undef JSON_HEDLEY_BEGIN_C_DECLS
+#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#undef JSON_HEDLEY_CLANG_HAS_WARNING
+#undef JSON_HEDLEY_COMPCERT_VERSION
+#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#undef JSON_HEDLEY_CONCAT
+#undef JSON_HEDLEY_CONCAT3
+#undef JSON_HEDLEY_CONCAT3_EX
+#undef JSON_HEDLEY_CONCAT_EX
+#undef JSON_HEDLEY_CONST
+#undef JSON_HEDLEY_CONSTEXPR
+#undef JSON_HEDLEY_CONST_CAST
+#undef JSON_HEDLEY_CPP_CAST
+#undef JSON_HEDLEY_CRAY_VERSION
+#undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#undef JSON_HEDLEY_C_DECL
+#undef JSON_HEDLEY_DEPRECATED
+#undef JSON_HEDLEY_DEPRECATED_FOR
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#undef JSON_HEDLEY_DIAGNOSTIC_POP
+#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#undef JSON_HEDLEY_DMC_VERSION
+#undef JSON_HEDLEY_DMC_VERSION_CHECK
+#undef JSON_HEDLEY_EMPTY_BASES
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#undef JSON_HEDLEY_END_C_DECLS
+#undef JSON_HEDLEY_FLAGS
+#undef JSON_HEDLEY_FLAGS_CAST
+#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#undef JSON_HEDLEY_GCC_HAS_FEATURE
+#undef JSON_HEDLEY_GCC_HAS_WARNING
+#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#undef JSON_HEDLEY_GCC_VERSION
+#undef JSON_HEDLEY_GCC_VERSION_CHECK
+#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#undef JSON_HEDLEY_GNUC_HAS_WARNING
+#undef JSON_HEDLEY_GNUC_VERSION
+#undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#undef JSON_HEDLEY_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_BUILTIN
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_EXTENSION
+#undef JSON_HEDLEY_HAS_FEATURE
+#undef JSON_HEDLEY_HAS_WARNING
+#undef JSON_HEDLEY_IAR_VERSION
+#undef JSON_HEDLEY_IAR_VERSION_CHECK
+#undef JSON_HEDLEY_IBM_VERSION
+#undef JSON_HEDLEY_IBM_VERSION_CHECK
+#undef JSON_HEDLEY_IMPORT
+#undef JSON_HEDLEY_INLINE
+#undef JSON_HEDLEY_INTEL_CL_VERSION
+#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#undef JSON_HEDLEY_INTEL_VERSION
+#undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#undef JSON_HEDLEY_IS_CONSTANT
+#undef JSON_HEDLEY_IS_CONSTEXPR_
+#undef JSON_HEDLEY_LIKELY
+#undef JSON_HEDLEY_MALLOC
+#undef JSON_HEDLEY_MCST_LCC_VERSION
+#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#undef JSON_HEDLEY_MESSAGE
+#undef JSON_HEDLEY_MSVC_VERSION
+#undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#undef JSON_HEDLEY_NEVER_INLINE
+#undef JSON_HEDLEY_NON_NULL
+#undef JSON_HEDLEY_NO_ESCAPE
+#undef JSON_HEDLEY_NO_RETURN
+#undef JSON_HEDLEY_NO_THROW
+#undef JSON_HEDLEY_NULL
+#undef JSON_HEDLEY_PELLES_VERSION
+#undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#undef JSON_HEDLEY_PGI_VERSION
+#undef JSON_HEDLEY_PGI_VERSION_CHECK
+#undef JSON_HEDLEY_PREDICT
+#undef JSON_HEDLEY_PRINTF_FORMAT
+#undef JSON_HEDLEY_PRIVATE
+#undef JSON_HEDLEY_PUBLIC
+#undef JSON_HEDLEY_PURE
+#undef JSON_HEDLEY_REINTERPRET_CAST
+#undef JSON_HEDLEY_REQUIRE
+#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#undef JSON_HEDLEY_REQUIRE_MSG
+#undef JSON_HEDLEY_RESTRICT
+#undef JSON_HEDLEY_RETURNS_NON_NULL
+#undef JSON_HEDLEY_SENTINEL
+#undef JSON_HEDLEY_STATIC_ASSERT
+#undef JSON_HEDLEY_STATIC_CAST
+#undef JSON_HEDLEY_STRINGIFY
+#undef JSON_HEDLEY_STRINGIFY_EX
+#undef JSON_HEDLEY_SUNPRO_VERSION
+#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#undef JSON_HEDLEY_TINYC_VERSION
+#undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#undef JSON_HEDLEY_TI_ARMCL_VERSION
+#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL2000_VERSION
+#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL430_VERSION
+#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL6X_VERSION
+#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL7X_VERSION
+#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CLPRU_VERSION
+#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#undef JSON_HEDLEY_TI_VERSION
+#undef JSON_HEDLEY_TI_VERSION_CHECK
+#undef JSON_HEDLEY_UNAVAILABLE
+#undef JSON_HEDLEY_UNLIKELY
+#undef JSON_HEDLEY_UNPREDICTABLE
+#undef JSON_HEDLEY_UNREACHABLE
+#undef JSON_HEDLEY_UNREACHABLE_RETURN
+#undef JSON_HEDLEY_VERSION
+#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#undef JSON_HEDLEY_VERSION_ENCODE
+#undef JSON_HEDLEY_WARNING
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#undef JSON_HEDLEY_FALL_THROUGH
+
+
+
+#endif  // INCLUDE_NLOHMANN_JSON_HPP_
diff --git a/app/src/main/cpp/libcxx b/app/src/main/cpp/libcxx
deleted file mode 160000
index 12c8f4e..0000000
--- a/app/src/main/cpp/libcxx
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 12c8f4e93f196a700137e983dcceeac43cf807f2
diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp
index 23f0be1..e7b9a73 100644
--- a/app/src/main/cpp/main.cpp
+++ b/app/src/main/cpp/main.cpp
@@ -1,21 +1,22 @@
 #include <android/log.h>
 #include <sys/system_properties.h>
 #include <unistd.h>
-#include <string_view>
-#include <vector>
-#include <map>
-#include <fstream>
 
 #include "zygisk.hpp"
-#include "shadowhook.h"
+#include "dobby.h"
+#include "json.hpp"
 
 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__)
 
 #define DEX_FILE_PATH "/data/adb/modules/playintegrityfix/classes.dex"
 
-#define FIRST_API_LEVEL "23"
+#define PROP_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json"
 
-#define SECURITY_PATCH "2017-08-05"
+#define DEF_FIRST_API_LEVEL "23"
+
+#define DEF_SECURITY_PATCH "2017-08-05"
+
+static std::string SECURITY_PATCH, FIRST_API_LEVEL;
 
 typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
 
@@ -30,9 +31,17 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
     std::string_view prop(name);
 
     if (prop.ends_with("api_level")) {
-        value = FIRST_API_LEVEL;
+        if (FIRST_API_LEVEL.empty()) {
+            value = nullptr;
+        } else {
+            value = FIRST_API_LEVEL.c_str();
+        }
     } else if (prop.ends_with("security_patch")) {
-        value = SECURITY_PATCH;
+        if (SECURITY_PATCH.empty()) {
+            value = nullptr;
+        } else {
+            value = SECURITY_PATCH.c_str();
+        }
     }
 
     if (!prop.starts_with("cache") && !prop.starts_with("debug")) LOGD("[%s] -> %s", name, value);
@@ -52,18 +61,14 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void
 }
 
 static void doHook() {
-    shadowhook_init(SHADOWHOOK_MODE_UNIQUE, false);
-    void *handle = shadowhook_hook_sym_name(
-            "libc.so",
-            "__system_property_read_callback",
-            reinterpret_cast<void *>(my_system_property_read_callback),
-            reinterpret_cast<void **>(&o_system_property_read_callback)
-    );
+    void *handle = DobbySymbolResolver("libc.so", "__system_property_read_callback");
     if (handle == nullptr) {
         LOGD("Couldn't find '__system_property_read_callback' handle. Report to @chiteroman");
         return;
     }
     LOGD("Found '__system_property_read_callback' handle at %p", handle);
+    DobbyHook(handle, reinterpret_cast<void *>(my_system_property_read_callback),
+              reinterpret_cast<void **>(&o_system_property_read_callback));
 }
 
 class PlayIntegrityFix : public zygisk::ModuleBase {
@@ -74,10 +79,15 @@ public:
     }
 
     void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
+        bool isGms = false;
+        bool isGmsUnstable = false;
+
         auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
-        std::string_view process(rawProcess);
-        bool isGms = process.starts_with("com.google.android.gms");
-        bool isGmsUnstable = process.compare("com.google.android.gms.unstable") == 0;
+        if (rawProcess) {
+            std::string_view process(rawProcess);
+            isGms = process.starts_with("com.google.android.gms");
+            isGmsUnstable = process.compare("com.google.android.gms.unstable") == 0;
+        }
         env->ReleaseStringUTFChars(args->nice_name, rawProcess);
 
         if (!isGms) {
@@ -92,20 +102,41 @@ public:
             return;
         }
 
+        auto rawDir = env->GetStringUTFChars(args->app_data_dir, nullptr);
+        path = rawDir;
+        env->ReleaseStringUTFChars(args->app_data_dir, rawDir);
+
         int fd = api->connectCompanion();
 
-        char buffer[10000];
-        auto size = read(fd, buffer, sizeof(buffer));
+        write(fd, path.data(), path.size());
+
+        read(fd, nullptr, 0);
 
         close(fd);
-
-        moduleDex.insert(moduleDex.end(), buffer, buffer + size);
     }
 
     void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
-        if (moduleDex.empty()) return;
+        if (path.empty()) return;
 
-        inject();
+        std::string prop(path + "/cache/pif.json");
+        if (std::filesystem::exists(prop)) {
+            FILE *file = fopen(prop.c_str(), "r");
+            nlohmann::json json = nlohmann::json::parse(file, nullptr, false);
+            fclose(file);
+            LOGD("Read %d props from pif.json", static_cast<int>(json.size()));
+            SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
+            FIRST_API_LEVEL = json["FIRST_API_LEVEL"].get<std::string>();
+            json.clear();
+        } else {
+            LOGD("File pif.json doesn't exist, using default values");
+            SECURITY_PATCH = DEF_SECURITY_PATCH;
+            FIRST_API_LEVEL = DEF_FIRST_API_LEVEL;
+        }
+
+        std::string dex(path + "/cache/classes.dex");
+        if (std::filesystem::exists(dex)) {
+            inject();
+        }
 
         doHook();
     }
@@ -117,24 +148,22 @@ public:
 private:
     zygisk::Api *api = nullptr;
     JNIEnv *env = nullptr;
-    std::vector<char> moduleDex;
+    std::string path;
 
     void inject() {
-        LOGD("Preparing to inject %d bytes to the process", static_cast<int>(moduleDex.size()));
-
         LOGD("get system classloader");
         auto clClass = env->FindClass("java/lang/ClassLoader");
         auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader",
                                                            "()Ljava/lang/ClassLoader;");
         auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader);
 
-        LOGD("create buffer");
-        auto buf = env->NewDirectByteBuffer(moduleDex.data(), static_cast<jlong>(moduleDex.size()));
         LOGD("create class loader");
-        auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
-        auto dexClInit = env->GetMethodID(dexClClass, "<init>",
-                                          "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
-        auto dexCl = env->NewObject(dexClClass, dexClInit, buf, systemClassLoader);
+        auto pathClClass = env->FindClass("dalvik/system/PathClassLoader");
+        auto pathClInit = env->GetMethodID(pathClClass, "<init>",
+                                           "(Ljava/lang/String;Ljava/lang/ClassLoader;)V");
+        std::string dexPath(path + "/cache/classes.dex");
+        auto moduleDex = env->NewStringUTF(dexPath.c_str());
+        auto dexCl = env->NewObject(pathClClass, pathClInit, moduleDex, systemClassLoader);
 
         LOGD("load class");
         auto loadClass = env->GetMethodID(clClass, "loadClass",
@@ -144,28 +173,45 @@ private:
 
         auto entryClass = (jclass) entryClassObj;
 
+        LOGD("read props");
+        auto readProps = env->GetStaticMethodID(entryClass, "readProps", "(Ljava/lang/String;)V");
+        std::string prop(path + "/cache/pif.json");
+        auto javaPath = env->NewStringUTF(prop.c_str());
+        env->CallStaticVoidMethod(entryClass, readProps, javaPath);
+
         LOGD("call init");
         auto entryInit = env->GetStaticMethodID(entryClass, "init", "()V");
         env->CallStaticVoidMethod(entryClass, entryInit);
 
-        moduleDex.clear();
+        path.clear();
     }
 };
 
 static void companion(int fd) {
-    std::ifstream dex(DEX_FILE_PATH, std::ios::binary | std::ios::ate);
+    char buffer[256];
+    long size = read(fd, buffer, sizeof(buffer));
 
-    long size = dex.tellg();
-    dex.seekg(std::ios::beg);
+    std::string path(buffer, size);
+    LOGD("[ROOT] Got GMS dir: %s", path.c_str());
 
-    char buffer[size];
-    dex.read(buffer, size);
+    std::string dex(path + "/cache/classes.dex");
+    std::string prop(path + "/cache/pif.json");
 
-    dex.close();
+    if (std::filesystem::copy_file(DEX_FILE_PATH, dex,
+                                   std::filesystem::copy_options::overwrite_existing)) {
+        std::filesystem::permissions(dex, std::filesystem::perms::owner_read |
+                                          std::filesystem::perms::group_read |
+                                          std::filesystem::perms::others_read);
+    }
 
-    write(fd, buffer, size);
+    if (std::filesystem::copy_file(PROP_FILE_PATH, prop,
+                                   std::filesystem::copy_options::overwrite_existing)) {
+        std::filesystem::permissions(prop, std::filesystem::perms::owner_read |
+                                           std::filesystem::perms::group_read |
+                                           std::filesystem::perms::others_read);
+    }
 
-    close(fd);
+    write(fd, nullptr, 0);
 }
 
 REGISTER_ZYGISK_MODULE(PlayIntegrityFix)
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c
deleted file mode 100644
index d71e4f8..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_a32.h"
-
-#include <inttypes.h>
-#include <sh_util.h>
-#include <stdint.h>
-
-#include "sh_log.h"
-
-// https://developer.arm.com/documentation/ddi0406/latest
-// https://developer.arm.com/documentation/ddi0597/latest
-
-typedef enum {
-  IGNORED = 0,
-  B_A1,
-  BX_A1,
-  BL_IMM_A1,
-  BLX_IMM_A2,
-  ADD_REG_A1,
-  ADD_REG_PC_A1,
-  SUB_REG_A1,
-  SUB_REG_PC_A1,
-  ADR_A1,
-  ADR_A2,
-  MOV_REG_A1,
-  MOV_REG_PC_A1,
-  LDR_LIT_A1,
-  LDR_LIT_PC_A1,
-  LDRB_LIT_A1,
-  LDRD_LIT_A1,
-  LDRH_LIT_A1,
-  LDRSB_LIT_A1,
-  LDRSH_LIT_A1,
-  LDR_REG_A1,
-  LDR_REG_PC_A1,
-  LDRB_REG_A1,
-  LDRD_REG_A1,
-  LDRH_REG_A1,
-  LDRSB_REG_A1,
-  LDRSH_REG_A1
-} sh_a32_type_t;
-
-static sh_a32_type_t sh_a32_get_type(uint32_t inst) {
-  if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000))
-    return B_A1;
-  else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000))
-    return BX_A1;
-  else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000))
-    return BL_IMM_A1;
-  else if ((inst & 0xFE000000) == 0xFA000000)
-    return BLX_IMM_A2;
-  else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
-           (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
-    return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1;
-  else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
-           (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
-    return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1;
-  else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000))
-    return ADR_A1;
-  else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000))
-    return ADR_A2;
-  else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x0010F000u) != 0x0010F000) &&
-           (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000))))
-    return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1;
-  else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000))
-    return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1;
-  else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000))
-    return LDRB_LIT_A1;
-  else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000))
-    return LDRD_LIT_A1;
-  else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000))
-    return LDRH_LIT_A1;
-  else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000))
-    return LDRSB_LIT_A1;
-  else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000))
-    return LDRSH_LIT_A1;
-  else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1;
-  else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return LDRB_REG_A1;
-  else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return LDRD_REG_A1;
-  else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return LDRH_REG_A1;
-  else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return LDRSB_REG_A1;
-  else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) &&
-           ((inst & 0x01200000u) != 0x00200000))
-    return LDRSH_REG_A1;
-  else
-    return IGNORED;
-}
-
-size_t sh_a32_get_rewrite_inst_len(uint32_t inst) {
-  static uint8_t map[] = {
-      4,   // IGNORED
-      12,  // B_A1
-      12,  // BX_A1
-      16,  // BL_IMM_A1
-      16,  // BLX_IMM_A2
-      32,  // ADD_REG_A1
-      32,  // ADD_REG_PC_A1
-      32,  // SUB_REG_A1
-      32,  // SUB_REG_PC_A1
-      12,  // ADR_A1
-      12,  // ADR_A2
-      32,  // MOV_REG_A1
-      12,  // MOV_REG_PC_A1
-      24,  // LDR_LIT_A1
-      36,  // LDR_LIT_PC_A1
-      24,  // LDRB_LIT_A1
-      24,  // LDRD_LIT_A1
-      24,  // LDRH_LIT_A1
-      24,  // LDRSB_LIT_A1
-      24,  // LDRSH_LIT_A1
-      32,  // LDR_REG_A1
-      36,  // LDR_REG_PC_A1
-      32,  // LDRB_REG_A1
-      32,  // LDRD_REG_A1
-      32,  // LDRH_REG_A1
-      32,  // LDRSB_REG_A1
-      32   // LDRSH_REG_A1
-  };
-
-  return (size_t)(map[sh_a32_get_type(inst)]);
-}
-
-static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
-  return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr);
-}
-
-static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
-  if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) {
-    uintptr_t cursor_addr = rinfo->overwrite_start_addr;
-    size_t offset = 0;
-    for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) {
-      if (cursor_addr >= addr) break;
-      cursor_addr += 4;
-      offset += rinfo->rewrite_inst_lens[i];
-    }
-    uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset;
-    SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
-    return fixed_addr;
-  }
-
-  return addr;
-}
-
-static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
-                               sh_a32_rewrite_info_t *rinfo) {
-  uint32_t cond;
-  if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1)
-    cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  else
-    // type == BLX_IMM_A2
-    cond = 0xE;  // 1110 None (AL)
-
-  uint32_t addr;
-  if (type == B_A1 || type == BL_IMM_A1) {
-    uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u);
-    addr = pc + imm32;  // arm -> arm
-  } else if (type == BLX_IMM_A2) {
-    uint32_t h = SH_UTIL_GET_BIT_32(inst, 24);
-    uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u);
-    addr = SH_UTIL_SET_BIT0(pc + imm32);  // arm -> thumb
-  } else {
-    // type == BX_A1
-    // BX PC
-    // PC must be even, and the "arm" instruction must be at a 4-byte aligned address,
-    // so the instruction set must keep "arm" unchanged.
-    addr = pc;  // arm -> arm
-  }
-  addr = sh_a32_fix_addr(addr, rinfo);
-
-  size_t idx = 0;
-  if (type == BL_IMM_A1 || type == BLX_IMM_A2) {
-    buf[idx++] = 0x028FE008u | (cond << 28u);  // ADD<c> LR, PC, #8
-  }
-  buf[idx++] = 0x059FF000u | (cond << 28u);  // LDR<c> PC, [PC, #0]
-  buf[idx++] = 0xEA000000;                   // B #0
-  buf[idx++] = addr;
-  return idx * 4;  // 12 or 16
-}
-
-static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) {
-  // ADD{S}<c> <Rd>, <Rn>, PC{, <shift>}  or  ADD{S}<c> <Rd>, PC, <Rm>{, <shift>}
-  // SUB{S}<c> <Rd>, <Rn>, PC{, <shift>}  or  SUB{S}<c> <Rd>, PC, <Rm>{, <shift>}
-  uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16);
-  uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0);
-  uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
-
-  uint32_t rx;  // r0 - r3
-  for (rx = 3;; --rx)
-    if (rx != rn && rx != rm && rx != rd) break;
-
-  if (rd == 0xF)  // Rd == PC
-  {
-    uint32_t ry;  // r0 - r4
-    for (ry = 4;; --ry)
-      if (ry != rn && ry != rm && ry != rd && ry != rx) break;
-
-    buf[0] = 0x0A000000u | (cond << 28u);           // B<c> #0
-    buf[1] = 0xEA000005;                            // B #20
-    buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry);  // PUSH {Rx, Ry, PC}
-    buf[3] = 0xE59F0008 | (rx << 12u);              // LDR Rx, [PC, #8]
-    if (rn == 0xF)
-      // Rn == PC
-      buf[4] =
-          (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u);  // ADD/SUB Ry, Rx, Rm{, <shift>}
-    else
-      // Rm == PC
-      buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx;  // ADD/SUB Ry, Rn, Rx{, <shift>}
-    buf[5] = 0xE58D0008 | (ry << 12u);                                // STR Ry, [SP, #8]
-    buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry);                    // POP {Rx, Ry, PC}
-    buf[7] = pc;
-    return 32;
-  } else {
-    buf[0] = 0x0A000000u | (cond << 28u);  // B<c> #0
-    buf[1] = 0xEA000005;                   // B #20
-    buf[2] = 0xE52D0004 | (rx << 12u);     // PUSH {Rx}
-    buf[3] = 0xE59F0008 | (rx << 12u);     // LDR Rx, [PC, #8]
-    if (rn == 0xF)
-      // Rn == PC
-      buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u);  // ADD/SUB{S} Rd, Rx, Rm{, <shift>}
-    else
-      // Rm == PC
-      buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx;  // ADD/SUB{S} Rd, Rn, Rx{, <shift>}
-    buf[5] = 0xE49D0004 | (rx << 12u);                  // POP {Rx}
-    buf[6] = 0xEA000000;                                // B #0
-    buf[7] = pc;
-    return 32;
-  }
-}
-
-static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
-                                 sh_a32_rewrite_info_t *rinfo) {
-  uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);  // r0 - r15
-  uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0);
-  uint32_t imm32 = sh_util_arm_expand_imm(imm12);
-  uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
-  if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u);  // LDR<c> Rd, [PC, #0]
-  buf[1] = 0xEA000000;                                 // B #0
-  buf[2] = addr;
-  return 12;
-}
-
-static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) {
-  // MOV{S}<c> <Rd>, PC
-  uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
-  uint32_t rx = (rd == 0) ? 1 : 0;
-
-  if (rd == 0xF)  // Rd == PC (MOV PC, PC)
-  {
-    buf[0] = 0x059FF000u | (cond << 28u);  // LDR<c> PC, [PC, #0]
-    buf[1] = 0xEA000000;                   // B #0
-    buf[2] = pc;
-    return 12;
-  } else {
-    buf[0] = 0x0A000000u | (cond << 28u);             // B<c> #0
-    buf[1] = 0xEA000005;                              // B #20
-    buf[2] = 0xE52D0004 | (rx << 12u);                // PUSH {Rx}
-    buf[3] = 0xE59F0008 | (rx << 12u);                // LDR Rx, [PC, #8]
-    buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx;  // MOV{S} Rd, Rx{, <shift> #<amount>/RRX}
-    buf[5] = 0xE49D0004 | (rx << 12u);                // POP {Rx}
-    buf[6] = 0xEA000000;                              // B #0
-    buf[7] = pc;
-    return 32;
-  }
-}
-
-static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
-                                     sh_a32_rewrite_info_t *rinfo) {
-  uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  uint32_t u = SH_UTIL_GET_BIT_32(inst, 23);
-  uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12);
-
-  uint32_t imm32;
-  if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1)
-    imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0);
-  else
-    imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0);
-  uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
-  if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  if (type == LDR_LIT_PC_A1 && rt == 0xF) {
-    // Rt == PC
-    buf[0] = 0x0A000000u | (cond << 28u);  // B<c> #0
-    buf[1] = 0xEA000006;                   // B #24
-    buf[2] = 0xE92D0003;                   // PUSH {R0, R1}
-    buf[3] = 0xE59F0000;                   // LDR R0, [PC, #0]
-    buf[4] = 0xEA000000;                   // B #0
-    buf[5] = addr;                         //
-    buf[6] = 0xE5900000;                   // LDR R0, [R0]
-    buf[7] = 0xE58D0004;                   // STR R0, [SP, #4]
-    buf[8] = 0xE8BD8001;                   // POP {R0, PC}
-    return 36;
-  } else {
-    buf[0] = 0x0A000000u | (cond << 28u);  // B<c> #0
-    buf[1] = 0xEA000003;                   // B #12
-    buf[2] = 0xE59F0000 | (rt << 12u);     // LDR Rt, [PC, #0]
-    buf[3] = 0xEA000000;                   // B #0
-    buf[4] = addr;                         //
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wswitch"
-    switch (type) {
-      case LDR_LIT_A1:
-        buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u);  // LDR Rt, [Rt]
-        break;
-      case LDRB_LIT_A1:
-        buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u);  // LDRB Rt, [Rt]
-        break;
-      case LDRD_LIT_A1:
-        buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u);  // LDRD Rt, [Rt]
-        break;
-      case LDRH_LIT_A1:
-        buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u);  // LDRH Rt, [Rt]
-        break;
-      case LDRSB_LIT_A1:
-        buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u);  // LDRSB Rt, [Rt]
-        break;
-      case LDRSH_LIT_A1:
-        buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u);  // LDRSH Rt, [Rt]
-        break;
-    }
-#pragma clang diagnostic pop
-    return 24;
-  }
-}
-
-static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) {
-  // LDR<c> <Rt>, [PC,+/-<Rm>{, <shift>}]{!}
-  // ......
-  uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
-  uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12);
-  uint32_t rt2 = rt + 1;
-  uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0);
-  uint32_t rx;  // r0 - r3
-  for (rx = 3;; --rx)
-    if (rx != rt && rx != rt2 && rx != rm) break;
-
-  if (type == LDR_REG_PC_A1 && rt == 0xF) {
-    // Rt == PC
-    uint32_t ry;  // r0 - r4
-    for (ry = 4;; --ry)
-      if (ry != rt && ry != rt2 && ry != rm && ry != rx) break;
-
-    buf[0] = 0x0A000000u | (cond << 28u);           // B<c> #0
-    buf[1] = 0xEA000006;                            // B #24
-    buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry);  // PUSH {Rx, Ry, PC}
-    buf[3] = 0xE59F0000 | (rx << 12u);              // LDR Rx, [PC, #8]
-    buf[4] = 0xEA000000;                            // B #0
-    buf[5] = pc;
-    buf[6] =
-        (inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u);  // LDRxx Ry, [Rx],+/-Rm{, <shift>}
-    buf[7] = 0xE58D0008 | (ry << 12u);                                  // STR Ry, [SP, #8]
-    buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry);                      // POP {Rx, Ry, PC}
-    return 36;
-  } else {
-    buf[0] = 0x0A000000u | (cond << 28u);  // B<c> #0
-    buf[1] = 0xEA000005;                   // B #20
-    buf[2] = 0xE52D0004 | (rx << 12u);     // PUSH {Rx}
-    buf[3] = 0xE59F0000 | (rx << 12u);     // LDR Rx, [PC, #0]
-    buf[4] = 0xEA000000;                   // B #0
-    buf[5] = pc;
-    buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u);  // LDRxx Rt, [Rx],+/-Rm{, <shift>}
-    buf[7] = 0xE49D0004 | (rx << 12u);                         // POP {Rx}
-    return 32;
-  }
-}
-
-size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) {
-  sh_a32_type_t type = sh_a32_get_type(inst);
-  SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst);
-
-  // We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage.
-  // In this case, the add/sub/mov/ldr_reg instruction does not need to consider
-  // the problem of PC in the coverage area when rewriting.
-
-  if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2)
-    return sh_a32_rewrite_b(buf, inst, pc, type, rinfo);
-  else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1)
-    return sh_a32_rewrite_add_or_sub(buf, inst, pc);
-  else if (type == ADR_A1 || type == ADR_A2)
-    return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo);
-  else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1)
-    return sh_a32_rewrite_mov(buf, inst, pc);
-  else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 ||
-           type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1)
-    return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo);
-  else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 ||
-           type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1)
-    return sh_a32_rewrite_ldr_reg(buf, inst, pc, type);
-  else {
-    // IGNORED
-    buf[0] = inst;
-    return 4;
-  }
-}
-
-size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) {
-  buf[0] = 0xE51FF004;  // LDR PC, [PC, #-4]
-  buf[1] = addr;
-  return 8;
-}
-
-size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) {
-  buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u);  // B <label>
-  return 4;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h
deleted file mode 100644
index efe1592..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stddef.h>
-#include <stdint.h>
-
-typedef struct {
-  uintptr_t overwrite_start_addr;
-  uintptr_t overwrite_end_addr;
-  uint32_t *rewrite_buf;
-  size_t rewrite_buf_offset;
-  size_t rewrite_inst_lens[2];
-  size_t rewrite_inst_lens_cnt;
-} sh_a32_rewrite_info_t;
-
-size_t sh_a32_get_rewrite_inst_len(uint32_t inst);
-size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo);
-
-size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr);
-size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc);
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c
deleted file mode 100644
index 9ca310f..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c
+++ /dev/null
@@ -1,523 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_inst.h"
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#include "sh_a32.h"
-#include "sh_config.h"
-#include "sh_enter.h"
-#include "sh_exit.h"
-#include "sh_log.h"
-#include "sh_sig.h"
-#include "sh_t16.h"
-#include "sh_t32.h"
-#include "sh_txx.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-
-static void sh_inst_get_thumb_rewrite_info(sh_inst_t *self, uintptr_t target_addr,
-                                           sh_txx_rewrite_info_t *rinfo) {
-  memset(rinfo, 0, sizeof(sh_txx_rewrite_info_t));
-
-  size_t idx = 0;
-  uintptr_t target_addr_offset = 0;
-  uintptr_t pc = target_addr + 4;
-  size_t rewrite_len = 0;
-
-  while (rewrite_len < self->backup_len) {
-    // IT block
-    sh_t16_it_t it;
-    if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) {
-      rewrite_len += (2 + it.insts_len);
-
-      size_t it_block_idx = idx++;
-      size_t it_block_len = 4 + 4;  // IT-else + IT-then
-      for (size_t i = 0, j = 0; i < it.insts_cnt; i++) {
-        bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j])));
-        if (is_thumb32) {
-          it_block_len += sh_t32_get_rewrite_inst_len(it.insts[j], it.insts[j + 1]);
-          rinfo->inst_lens[idx++] = 0;
-          rinfo->inst_lens[idx++] = 0;
-          j += 2;
-        } else {
-          it_block_len += sh_t16_get_rewrite_inst_len(it.insts[j]);
-          rinfo->inst_lens[idx++] = 0;
-          j += 1;
-        }
-      }
-      rinfo->inst_lens[it_block_idx] = it_block_len;
-
-      target_addr_offset += (2 + it.insts_len);
-      pc += (2 + it.insts_len);
-    }
-    // not IT block
-    else {
-      bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset);
-      size_t inst_len = (is_thumb32 ? 4 : 2);
-      rewrite_len += inst_len;
-
-      if (is_thumb32) {
-        rinfo->inst_lens[idx++] =
-            sh_t32_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset)),
-                                        *((uint16_t *)(target_addr + target_addr_offset + 2)));
-        rinfo->inst_lens[idx++] = 0;
-      } else
-        rinfo->inst_lens[idx++] =
-            sh_t16_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset)));
-
-      target_addr_offset += inst_len;
-      pc += inst_len;
-    }
-  }
-
-  rinfo->start_addr = target_addr;
-  rinfo->end_addr = target_addr + rewrite_len;
-  rinfo->buf = (uint16_t *)self->enter_addr;
-  rinfo->buf_offset = 0;
-  rinfo->inst_lens_cnt = idx;
-}
-
-static int sh_inst_hook_thumb_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
-                                      uintptr_t *orig_addr2, size_t *rewrite_len) {
-  // backup original instructions (length: 4 or 8 or 10)
-  memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
-
-  // package the information passed to rewrite
-  sh_txx_rewrite_info_t rinfo;
-  sh_inst_get_thumb_rewrite_info(self, target_addr, &rinfo);
-
-  // backup and rewrite original instructions
-  uintptr_t target_addr_offset = 0;
-  uintptr_t pc = target_addr + 4;
-  *rewrite_len = 0;
-  while (*rewrite_len < self->backup_len) {
-    // IT block
-    sh_t16_it_t it;
-    if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) {
-      *rewrite_len += (2 + it.insts_len);
-
-      // save space holder point of IT-else B instruction
-      uintptr_t enter_inst_else_p = self->enter_addr + rinfo.buf_offset;
-      rinfo.buf_offset += 2;  // B<c> <label>
-      rinfo.buf_offset += 2;  // NOP
-
-      // rewrite IT block
-      size_t enter_inst_else_len = 4;  // B<c> + NOP + B + NOP
-      size_t enter_inst_then_len = 0;  // B + NOP
-      uintptr_t enter_inst_then_p = 0;
-      for (size_t i = 0, j = 0; i < it.insts_cnt; i++) {
-        if (i == it.insts_else_cnt) {
-          // save space holder point of IT-then (for B instruction)
-          enter_inst_then_p = self->enter_addr + rinfo.buf_offset;
-          rinfo.buf_offset += 2;  // B <label>
-          rinfo.buf_offset += 2;  // NOP
-
-          // fill IT-else B instruction
-          sh_t16_rewrite_it_else((uint16_t *)enter_inst_else_p, (uint16_t)enter_inst_else_len, &it);
-        }
-
-        // rewrite instructions in IT block
-        bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j])));
-        size_t len;
-        if (is_thumb32)
-          len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j],
-                               it.insts[j + 1], it.pcs[i], &rinfo);
-        else
-          len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j], it.pcs[i],
-                               &rinfo);
-        if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
-        rinfo.buf_offset += len;
-        j += (is_thumb32 ? 2 : 1);
-
-        // save the total offset for ELSE/THEN in enter
-        if (i < it.insts_else_cnt)
-          enter_inst_else_len += len;
-        else
-          enter_inst_then_len += len;
-
-        if (i == it.insts_cnt - 1) {
-          // fill IT-then B instruction
-          sh_t16_rewrite_it_then((uint16_t *)enter_inst_then_p, (uint16_t)enter_inst_then_len);
-        }
-      }
-
-      target_addr_offset += (2 + it.insts_len);
-      pc += (2 + it.insts_len);
-    }
-    // not IT block
-    else {
-      bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset);
-      size_t inst_len = (is_thumb32 ? 4 : 2);
-      *rewrite_len += inst_len;
-
-      // rewrite original instructions (fill in enter)
-      SH_LOG_INFO("thumb rewrite: offset %zu, pc %" PRIxPTR, rinfo.buf_offset, pc);
-      size_t len;
-      if (is_thumb32)
-        len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset),
-                             *((uint16_t *)(target_addr + target_addr_offset)),
-                             *((uint16_t *)(target_addr + target_addr_offset + 2)), pc, &rinfo);
-      else
-        len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset),
-                             *((uint16_t *)(target_addr + target_addr_offset)), pc, &rinfo);
-      if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
-      rinfo.buf_offset += len;
-
-      target_addr_offset += inst_len;
-      pc += inst_len;
-    }
-  }
-  SH_LOG_INFO("thumb rewrite: len %zu to %zu", *rewrite_len, rinfo.buf_offset);
-
-  // absolute jump back to remaining original instructions (fill in enter)
-  rinfo.buf_offset += sh_t32_absolute_jump((uint16_t *)(self->enter_addr + rinfo.buf_offset), true,
-                                           SH_UTIL_SET_BIT0(target_addr + *rewrite_len));
-  sh_util_clear_cache(self->enter_addr, rinfo.buf_offset);
-
-  // save original function address
-  if (NULL != orig_addr) __atomic_store_n(orig_addr, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST);
-  if (NULL != orig_addr2) __atomic_store_n(orig_addr2, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST);
-  return 0;
-}
-
-#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
-static bool sh_inst_thumb_is_long_enough(uintptr_t target_addr, size_t overwrite_len, xdl_info_t *dlinfo) {
-  if (overwrite_len <= dlinfo->dli_ssize) return true;
-
-  // check align-4 in the end of symbol
-  if ((overwrite_len == dlinfo->dli_ssize + 2) && ((target_addr + dlinfo->dli_ssize) % 4 == 2)) {
-    uintptr_t sym_end = target_addr + dlinfo->dli_ssize;
-    if (0 != sh_util_mprotect(sym_end, 2, PROT_READ | PROT_WRITE | PROT_EXEC)) return false;
-
-    // should be zero-ed
-    if (0 != *((uint16_t *)sym_end)) return false;
-
-    // should not belong to any symbol
-    void *dlcache = NULL;
-    xdl_info_t dlinfo2;
-    if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-      xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache);
-    } else {
-      SH_SIG_TRY(SIGSEGV, SIGBUS) {
-        xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache);
-      }
-      SH_SIG_CATCH() {
-        memset(&dlinfo2, 0, sizeof(dlinfo2));
-        SH_LOG_WARN("thumb detect tail aligned: crashed");
-      }
-      SH_SIG_EXIT
-    }
-    xdl_addr_clean(&dlcache);
-    if (NULL != dlinfo2.dli_sname) return false;
-
-    // trust here is useless alignment data
-    return true;
-  }
-
-  return false;
-}
-#endif
-
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-
-// B T4: [-16M, +16M - 2]
-#define SH_INST_T32_B_RANGE_LOW  (16777216)
-#define SH_INST_T32_B_RANGE_HIGH (16777214)
-
-static int sh_inst_hook_thumb_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                        uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
-  uintptr_t pc = target_addr + 4;
-  self->backup_len = 4;
-
-#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
-  if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo))
-    return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-#else
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-#endif
-
-  // alloc an exit for absolute jump
-  sh_t32_absolute_jump((uint16_t *)self->exit, true, new_addr);
-  if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
-                              sizeof(self->exit), SH_INST_T32_B_RANGE_LOW, SH_INST_T32_B_RANGE_HIGH)))
-    return r;
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC)) {
-    r = SHADOWHOOK_ERRNO_MPROT;
-    goto err;
-  }
-  size_t rewrite_len = 0;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len);
-  }
-  SH_SIG_CATCH() {
-    r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-    goto err;
-  }
-  SH_SIG_EXIT
-  if (0 != r) goto err;
-
-  // relative jump to the exit by overwriting the head of original function
-  sh_t32_relative_jump((uint16_t *)self->trampo, self->exit_addr, pc);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
-
-  SH_LOG_INFO("thumb: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
-              " -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
-              target_addr, self->exit_addr, new_addr, self->enter_addr,
-              SH_UTIL_SET_BIT0(target_addr + rewrite_len));
-  return 0;
-
-err:
-  sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
-  self->exit_addr = 0;  // this is a flag for with-exit or without-exit
-  return r;
-}
-#endif
-
-static int sh_inst_hook_thumb_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                           uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
-  bool is_align4 = (0 == (target_addr % 4));
-  self->backup_len = (is_align4 ? 8 : 10);
-
-#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
-  if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo))
-    return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-#else
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-#endif
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC))
-    return SHADOWHOOK_ERRNO_MPROT;
-  size_t rewrite_len = 0;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-  }
-  SH_SIG_EXIT
-  if (0 != r) return r;
-
-  // absolute jump to new function address by overwriting the head of original function
-  sh_t32_absolute_jump((uint16_t *)self->trampo, is_align4, new_addr);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
-
-  SH_LOG_INFO("thumb: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
-              " -> remaining %" PRIxPTR,
-              target_addr, new_addr, self->enter_addr, SH_UTIL_SET_BIT0(target_addr + rewrite_len));
-  return 0;
-}
-
-static int sh_inst_hook_arm_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
-                                    uintptr_t *orig_addr2) {
-  // backup original instructions (length: 4 or 8)
-  memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
-
-  // package the information passed to rewrite
-  sh_a32_rewrite_info_t rinfo;
-  rinfo.overwrite_start_addr = target_addr;
-  rinfo.overwrite_end_addr = target_addr + self->backup_len;
-  rinfo.rewrite_buf = (uint32_t *)self->enter_addr;
-  rinfo.rewrite_buf_offset = 0;
-  rinfo.rewrite_inst_lens_cnt = self->backup_len / 4;
-  for (uintptr_t i = 0; i < self->backup_len; i += 4)
-    rinfo.rewrite_inst_lens[i / 4] = sh_a32_get_rewrite_inst_len(*((uint32_t *)(target_addr + i)));
-
-  // rewrite original instructions (fill in enter)
-  uintptr_t pc = target_addr + 8;
-  for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) {
-    size_t offset = sh_a32_rewrite((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset),
-                                   *((uint32_t *)(target_addr + i)), pc, &rinfo);
-    if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
-    rinfo.rewrite_buf_offset += offset;
-  }
-
-  // absolute jump back to remaining original instructions (fill in enter)
-  rinfo.rewrite_buf_offset += sh_a32_absolute_jump((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset),
-                                                   target_addr + self->backup_len);
-  sh_util_clear_cache(self->enter_addr, rinfo.rewrite_buf_offset);
-
-  // save original function address
-  if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST);
-  if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST);
-  return 0;
-}
-
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-
-// B A1: [-32M, +32M - 4]
-#define SH_INST_A32_B_RANGE_LOW  (33554432)
-#define SH_INST_A32_B_RANGE_HIGH (33554428)
-
-static int sh_inst_hook_arm_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                      uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  uintptr_t pc = target_addr + 8;
-  self->backup_len = 4;
-
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-
-  // alloc an exit for absolute jump
-  sh_a32_absolute_jump(self->exit, new_addr);
-  if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
-                              sizeof(self->exit), SH_INST_A32_B_RANGE_LOW, SH_INST_A32_B_RANGE_HIGH)))
-    return r;
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
-    r = SHADOWHOOK_ERRNO_MPROT;
-    goto err;
-  }
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2);
-  }
-  SH_SIG_CATCH() {
-    r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-    goto err;
-  }
-  SH_SIG_EXIT
-  if (0 != r) goto err;
-
-  // relative jump to the exit by overwriting the head of original function
-  sh_a32_relative_jump(self->trampo, self->exit_addr, pc);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
-
-  SH_LOG_INFO("a32: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
-              " -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
-              target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
-  return 0;
-
-err:
-  sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
-  self->exit_addr = 0;  // this is a flag for with-exit or without-exit
-  return r;
-}
-
-#endif
-
-static int sh_inst_hook_arm_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                         uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  self->backup_len = 8;
-
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC))
-    return SHADOWHOOK_ERRNO_MPROT;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-  }
-  SH_SIG_EXIT
-  if (0 != r) return r;
-
-  // absolute jump to new function address by overwriting the head of original function
-  sh_a32_absolute_jump(self->trampo, new_addr);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
-
-  SH_LOG_INFO("a32: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
-              " -> remaining %" PRIxPTR,
-              target_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
-  return 0;
-}
-
-int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
-                 uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  self->enter_addr = sh_enter_alloc();
-  if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER;
-
-  int r;
-  if (SH_UTIL_IS_THUMB(target_addr)) {
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-    if (0 == (r = sh_inst_hook_thumb_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
-      return r;
-#endif
-    if (0 ==
-        (r = sh_inst_hook_thumb_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
-      return r;
-  } else {
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-    if (0 == (r = sh_inst_hook_arm_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
-      return r;
-#endif
-    if (0 == (r = sh_inst_hook_arm_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
-      return r;
-  }
-
-  // hook failed
-  if (NULL != orig_addr) *orig_addr = 0;
-  if (NULL != orig_addr2) *orig_addr2 = 0;
-  sh_enter_free(self->enter_addr);
-  return r;
-}
-
-int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) {
-  int r;
-  bool is_thumb = SH_UTIL_IS_THUMB(target_addr);
-
-  if (is_thumb) target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
-
-  // restore the instructions at the target address
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = memcmp((void *)target_addr, self->trampo, self->backup_len);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH;
-  }
-  SH_SIG_EXIT
-  if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH;
-  if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r;
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-
-  // free memory space for exit
-  if (0 != self->exit_addr)
-    if (0 !=
-        (r = sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit))))
-      return r;
-
-  // free memory space for enter
-  sh_enter_free(self->enter_addr);
-
-  SH_LOG_INFO("%s: unhook OK. target %" PRIxPTR, is_thumb ? "thumb" : "a32", target_addr);
-  return 0;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h
deleted file mode 100644
index ed38ab7..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdint.h>
-
-#include "xdl.h"
-
-typedef struct {
-  uint32_t trampo[4];   // align 16 // length == backup_len
-  uint8_t backup[16];   // align 16
-  uint16_t backup_len;  // == 4 or 8 or 10
-  uint16_t exit_type;
-  uintptr_t exit_addr;
-  uint32_t exit[2];
-  uintptr_t enter_addr;
-} sh_inst_t;
-
-int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
-                 uintptr_t *orig_addr, uintptr_t *orig_addr2);
-int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c
deleted file mode 100644
index d250526..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11.
-
-#include "sh_t16.h"
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "sh_log.h"
-#include "sh_util.h"
-
-// https://developer.arm.com/documentation/ddi0406/latest
-// https://developer.arm.com/documentation/ddi0597/latest
-
-typedef enum {
-  IGNORED = 0,
-  IT_T1,
-  B_T1,
-  B_T2,
-  BX_T1,
-  ADD_REG_T2,
-  MOV_REG_T1,
-  ADR_T1,
-  LDR_LIT_T1,
-  CBZ_T1,
-  CBNZ_T1
-} sh_t16_type_t;
-
-static sh_t16_type_t sh_t16_get_type(uint16_t inst) {
-  if (((inst & 0xFF00u) == 0xBF00) && ((inst & 0x000Fu) != 0x0000) && ((inst & 0x00F0u) != 0x00F0))
-    return IT_T1;
-  else if (((inst & 0xF000u) == 0xD000) && ((inst & 0x0F00u) != 0x0F00) && ((inst & 0x0F00u) != 0x0E00))
-    return B_T1;
-  else if ((inst & 0xF800u) == 0xE000)
-    return B_T2;
-  else if ((inst & 0xFFF8u) == 0x4778)
-    return BX_T1;
-  else if (((inst & 0xFF78u) == 0x4478) && ((inst & 0x0087u) != 0x0085))
-    return ADD_REG_T2;
-  else if ((inst & 0xFF78u) == 0x4678)
-    return MOV_REG_T1;
-  else if ((inst & 0xF800u) == 0xA000)
-    return ADR_T1;
-  else if ((inst & 0xF800u) == 0x4800)
-    return LDR_LIT_T1;
-  else if ((inst & 0xFD00u) == 0xB100)
-    return CBZ_T1;
-  else if ((inst & 0xFD00u) == 0xB900)
-    return CBNZ_T1;
-  else
-    return IGNORED;
-}
-
-size_t sh_t16_get_rewrite_inst_len(uint16_t inst) {
-  static uint8_t map[] = {
-      4,   // IGNORED
-      0,   // IT_T1
-      12,  // B_T1
-      8,   // B_T2
-      8,   // BX_T1
-      16,  // ADD_REG_T2
-      12,  // MOV_REG_T1
-      8,   // ADR_T1
-      12,  // LDR_LIT_T1
-      12,  // CBZ_T1
-      12   // CBNZ_T1
-  };
-
-  return (size_t)(map[sh_t16_get_type(inst)]);
-}
-
-static size_t sh_t16_rewrite_b(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_t16_type_t type,
-                               sh_txx_rewrite_info_t *rinfo) {
-  uint32_t addr;
-  if (type == B_T1) {
-    uint32_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
-    addr = pc + SH_UTIL_SIGN_EXTEND_32(imm8 << 1u, 9u);
-    addr = SH_UTIL_SET_BIT0(addr);  // thumb -> thumb
-  } else if (type == B_T2) {
-    uint32_t imm11 = SH_UTIL_GET_BITS_16(inst, 10, 0);
-    addr = pc + SH_UTIL_SIGN_EXTEND_32(imm11 << 1u, 12u);
-    addr = SH_UTIL_SET_BIT0(addr);  // thumb -> thumb
-  } else {
-    // type == BX_T1
-    // BX PC
-    // PC must be even, and the "BX PC" instruction must be at a 4-byte aligned address,
-    // so the instruction set must be exchanged from "thumb" to "arm".
-    addr = pc;  // thumb -> arm
-  }
-  addr = sh_txx_fix_addr(addr, rinfo);
-
-  size_t idx = 0;
-  if (type == B_T1) {
-    buf[idx++] = inst & 0xFF00u;  // B<c> #0
-    buf[idx++] = 0xE003;          // B PC, #6
-  }
-  buf[idx++] = 0xF8DF;  // LDR.W PC, [PC]
-  buf[idx++] = 0xF000;  // ...
-  buf[idx++] = addr & 0xFFFFu;
-  buf[idx++] = addr >> 16u;
-  return idx * 2;  // 8 or 12
-}
-
-static size_t sh_t16_rewrite_add(uint16_t *buf, uint16_t inst, uintptr_t pc) {
-  // ADD<c> <Rdn>, PC
-  uint16_t dn = SH_UTIL_GET_BIT_16(inst, 7);
-  uint16_t rdn = SH_UTIL_GET_BITS_16(inst, 2, 0);
-  uint16_t rd = (uint16_t)(dn << 3u) | rdn;
-  uint16_t rx = (rd == 0) ? 1 : 0;  // r0 - r1
-
-  buf[0] = (uint16_t)(0xB400u | (1u << rx));         // PUSH {Rx}
-  buf[1] = 0x4802u | (uint16_t)(rx << 8u);           // LDR Rx, [PC, #8]
-  buf[2] = (inst & 0xFF87u) | (uint16_t)(rx << 3u);  // ADD Rd, Rx
-  buf[3] = (uint16_t)(0xBC00u | (1u << rx));         // POP {Rx}
-  buf[4] = 0xE002;                                   // B #4
-  buf[5] = 0xBF00;
-  buf[6] = pc & 0xFFFFu;
-  buf[7] = pc >> 16u;
-  return 16;
-}
-
-static size_t sh_t16_rewrite_mov(uint16_t *buf, uint16_t inst, uintptr_t pc) {
-  // MOV<c> <Rd>, PC
-  uint16_t D = SH_UTIL_GET_BIT_16(inst, 7);
-  uint16_t rd = SH_UTIL_GET_BITS_16(inst, 2, 0);
-  uint16_t d = (uint16_t)(D << 3u) | rd;  // r0 - r15
-
-  buf[0] = 0xF8DF;                     // LDR.W Rd, [PC, #4]
-  buf[1] = (uint16_t)(d << 12u) + 4u;  // ...
-  buf[2] = 0xE002;                     // B #4
-  buf[3] = 0xBF00;                     // NOP
-  buf[4] = pc & 0xFFFFu;
-  buf[5] = pc >> 16u;
-  return 12;
-}
-
-static size_t sh_t16_rewrite_adr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
-  // ADR<c> <Rd>, <label>
-  uint16_t rd = SH_UTIL_GET_BITS_16(inst, 10, 8);  // r0 - r7
-  uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
-  uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u);
-  if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0x4800u | (uint16_t)(rd << 8u);  // LDR Rd, [PC]
-  buf[1] = 0xE001;                          // B #2
-  buf[2] = addr & 0xFFFFu;
-  buf[3] = addr >> 16u;
-  return 8;
-}
-
-static size_t sh_t16_rewrite_ldr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
-  // LDR<c> <Rt>, <label>
-  uint16_t rt = SH_UTIL_GET_BITS_16(inst, 10, 8);  // r0 - r7
-  uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
-  uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u);
-  if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0x4800u | (uint16_t)(rt << 8u);  // LDR Rt, [PC]
-  buf[1] = 0xE001;                          // B #2
-  buf[2] = addr & 0xFFFFu;
-  buf[3] = addr >> 16u;
-  buf[4] = 0x6800u | (uint16_t)(rt << 3u) | rt;  // LDR Rt, [Rt]
-  buf[5] = 0xBF00;                               // NOP
-  return 12;
-}
-
-static size_t sh_t16_rewrite_cb(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
-  // CB{N}Z <Rn>, <label>
-  uint16_t i = SH_UTIL_GET_BIT_16(inst, 9);
-  uint16_t imm5 = SH_UTIL_GET_BITS_16(inst, 7, 3);
-  uint32_t imm32 = (uint32_t)(i << 6u) | (uint32_t)(imm5 << 1u);
-  uint32_t addr = SH_UTIL_SET_BIT0(pc + imm32);  // thumb -> thumb
-  addr = sh_txx_fix_addr(addr, rinfo);
-
-  buf[0] = inst & 0xFD07u;  // CB(N)Z Rn, #0
-  buf[1] = 0xE003;          // B PC, #6
-  buf[2] = 0xF8DF;          // LDR.W PC, [PC]
-  buf[3] = 0xF000;          // ...
-  buf[4] = addr & 0xFFFFu;
-  buf[5] = addr >> 16u;
-  return 12;
-}
-
-size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
-  sh_t16_type_t type = sh_t16_get_type(inst);
-  SH_LOG_INFO("t16 rewrite: type %d, inst %" PRIx16, type, inst);
-
-  if (type == B_T1 || type == B_T2 || type == BX_T1)
-    return sh_t16_rewrite_b(buf, inst, pc, type, rinfo);
-  else if (type == ADD_REG_T2)
-    return sh_t16_rewrite_add(buf, inst, pc);
-  else if (type == MOV_REG_T1)
-    return sh_t16_rewrite_mov(buf, inst, pc);
-  else if (type == ADR_T1)
-    return sh_t16_rewrite_adr(buf, inst, pc, rinfo);
-  else if (type == LDR_LIT_T1)
-    return sh_t16_rewrite_ldr(buf, inst, pc, rinfo);
-  else if (type == CBZ_T1 || type == CBNZ_T1)
-    return sh_t16_rewrite_cb(buf, inst, pc, rinfo);
-  else {
-    // IGNORED
-    buf[0] = inst;
-    buf[1] = 0xBF00;  // NOP
-    return 4;
-  }
-}
-
-static size_t sh_t16_get_it_insts_count(uint16_t inst) {
-  if ((inst & 0x1u) != 0) return 4;
-  if ((inst & 0x2u) != 0) return 3;
-  if ((inst & 0x4u) != 0) return 2;
-  return 1;
-}
-
-bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc) {
-  if (IT_T1 != sh_t16_get_type(inst)) return false;
-  SH_LOG_INFO("t16 rewrite: type IT, inst %" PRIx16, inst);
-
-  // address of the first inst in the IT block, skip the IT inst itself (2 bytes)
-  uintptr_t target_addr = pc - 4 + 2;
-
-  it->firstcond = (uint8_t)(inst >> 4u);
-  uint8_t firstcond_0 = it->firstcond & 1u;
-
-  memset(it, 0, sizeof(sh_t16_it_t));
-  it->insts_cnt = sh_t16_get_it_insts_count(inst);
-
-  size_t insts_idx = 0, pcs_idx = 0;
-  for (int parse_else = 1; parse_else >= 0; parse_else--)  // round 0: parse ELSE, round 1: THEN
-  {
-    uintptr_t target_offset = 0;
-    for (size_t i = 0; i < it->insts_cnt; i++) {
-      bool is_thumb32 = sh_util_is_thumb32(target_addr + target_offset);
-      uint8_t mask_x = (uint8_t)(inst >> (uint16_t)(4 - i)) & 1u;
-
-      if ((parse_else && mask_x != firstcond_0) ||  // parse ELSE or
-          (!parse_else && mask_x == firstcond_0))   // parse THEN
-      {
-        it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset));
-        if (is_thumb32) it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset + 2));
-
-        it->pcs[pcs_idx++] = target_addr + target_offset + 4;
-        if (parse_else) it->insts_else_cnt++;
-      }
-
-      target_offset += (is_thumb32 ? 4 : 2);
-    }
-  }
-  it->insts_len = insts_idx * 2;
-
-  return true;
-}
-
-void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it) {
-  buf[0] = 0xD000u | (uint16_t)(it->firstcond << 8u) | (uint16_t)(imm9 >> 1u);  // B<c> <label>
-  buf[1] = 0xBF00;                                                              // NOP
-}
-
-void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12) {
-  buf[0] = 0xE000u | (uint16_t)(imm12 >> 1u);  // B <label>
-  buf[1] = 0xBF00;                             // NOP
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h
deleted file mode 100644
index 9d68d79..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "sh_txx.h"
-
-typedef struct {
-  uint16_t insts[8];
-  size_t insts_len;       // 2 - 16 (bytes)
-  size_t insts_cnt;       // 1 - 4
-  size_t insts_else_cnt;  // 0 - 3
-  uintptr_t pcs[4];
-  uint8_t firstcond;
-  uint8_t padding[3];
-} sh_t16_it_t;
-
-bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc);
-void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it);
-void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12);
-
-size_t sh_t16_get_rewrite_inst_len(uint16_t inst);
-size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo);
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c
deleted file mode 100644
index bb9e859..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c
+++ /dev/null
@@ -1,408 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_t32.h"
-
-#include <inttypes.h>
-#include <stdint.h>
-
-#include "sh_log.h"
-#include "sh_util.h"
-
-// https://developer.arm.com/documentation/ddi0406/latest
-// https://developer.arm.com/documentation/ddi0597/latest
-
-typedef enum {
-  IGNORED = 0,
-  B_T3,
-  B_T4,
-  BL_IMM_T1,
-  BLX_IMM_T2,
-  ADR_T2,
-  ADR_T3,
-  LDR_LIT_T2,
-  LDR_LIT_PC_T2,
-  LDRB_LIT_T1,
-  LDRD_LIT_T1,
-  LDRH_LIT_T1,
-  LDRSB_LIT_T1,
-  LDRSH_LIT_T1,
-  PLD_LIT_T1,
-  PLI_LIT_T3,
-  TBB_T1,
-  TBH_T1,
-  VLDR_LIT_T1
-} sh_t32_type_t;
-
-static sh_t32_type_t sh_t32_get_type(uint32_t inst) {
-  if (((inst & 0xF800D000) == 0xF0008000) && ((inst & 0x03800000u) != 0x03800000u))
-    return B_T3;
-  else if ((inst & 0xF800D000) == 0xF0009000)
-    return B_T4;
-  else if ((inst & 0xF800D000) == 0xF000D000)
-    return BL_IMM_T1;
-  else if ((inst & 0xF800D000) == 0xF000C000)
-    return BLX_IMM_T2;
-  else if ((inst & 0xFBFF8000) == 0xF2AF0000)
-    return ADR_T2;
-  else if ((inst & 0xFBFF8000) == 0xF20F0000)
-    return ADR_T3;
-  else if ((inst & 0xFF7F0000) == 0xF85F0000)
-    return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_T2 : LDR_LIT_T2;
-  else if (((inst & 0xFF7F0000) == 0xF81F0000) && ((inst & 0xF000u) != 0xF000u))
-    return LDRB_LIT_T1;
-  else if ((inst & 0xFF7F0000) == 0xE95F0000)
-    return LDRD_LIT_T1;
-  else if (((inst & 0xFF7F0000) == 0xF83F0000) && ((inst & 0xF000u) != 0xF000u))
-    return LDRH_LIT_T1;
-  else if (((inst & 0xFF7F0000) == 0xF91F0000) && ((inst & 0xF000u) != 0xF000u))
-    return LDRSB_LIT_T1;
-  else if (((inst & 0xFF7F0000) == 0xF93F0000) && ((inst & 0xF000u) != 0xF000u))
-    return LDRSH_LIT_T1;
-  else if ((inst & 0xFF7FF000) == 0xF81FF000)
-    return PLD_LIT_T1;
-  else if ((inst & 0xFF7FF000) == 0xF91FF000)
-    return PLI_LIT_T3;
-  else if ((inst & 0xFFF0FFF0) == 0xE8D0F000)
-    return TBB_T1;
-  else if ((inst & 0xFFF0FFF0) == 0xE8D0F010)
-    return TBH_T1;
-  else if ((inst & 0xFF3F0C00) == 0xED1F0800)
-    return VLDR_LIT_T1;
-  else
-    return IGNORED;
-}
-
-size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst) {
-  static uint8_t map[] = {
-      4,   // IGNORED
-      12,  // B_T3
-      8,   // B_T4
-      12,  // BL_IMM_T1
-      12,  // BLX_IMM_T2
-      12,  // ADR_T2
-      12,  // ADR_T3
-      16,  // LDR_LIT_T2
-      24,  // LDR_LIT_PC_T2
-      16,  // LDRB_LIT_T1
-      16,  // LDRD_LIT_T1
-      16,  // LDRH_LIT_T1
-      16,  // LDRSB_LIT_T1
-      16,  // LDRSH_LIT_T1
-      20,  // PLD_LIT_T1
-      20,  // PLI_LIT_T3
-      32,  // TBB_T1
-      32,  // TBH_T1
-      24   // VLDR_LIT_T1
-  };
-
-  uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst;
-  return (size_t)(map[sh_t32_get_type(inst)]);
-}
-
-static size_t sh_t32_rewrite_b(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                               sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
-  uint32_t j1 = SH_UTIL_GET_BIT_16(low_inst, 13);
-  uint32_t j2 = SH_UTIL_GET_BIT_16(low_inst, 11);
-  uint32_t s = SH_UTIL_GET_BIT_16(high_inst, 10);
-  uint32_t i1 = !(j1 ^ s);
-  uint32_t i2 = !(j2 ^ s);
-
-  uint32_t addr;
-  if (type == B_T3) {
-    uint32_t x =
-        (s << 20u) | (j2 << 19u) | (j1 << 18u) | ((high_inst & 0x3Fu) << 12u) | ((low_inst & 0x7FFu) << 1u);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 21u);
-    addr = SH_UTIL_SET_BIT0(pc + imm32);  // thumb -> thumb
-  } else if (type == B_T4) {
-    uint32_t x =
-        (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
-    addr = SH_UTIL_SET_BIT0(pc + imm32);  // thumb -> thumb
-  } else if (type == BL_IMM_T1) {
-    uint32_t x =
-        (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
-    addr = SH_UTIL_SET_BIT0(pc + imm32);  // thumb -> thumb
-  } else                                  // type == BLX_IMM_T2
-  {
-    uint32_t x =
-        (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FEu) << 1u);
-    uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
-    // In BL and BLX instructions, only when the target instruction set is "arm",
-    // you need to do 4-byte alignment for PC.
-    addr = SH_UTIL_ALIGN_4(pc) + imm32;  // thumb -> arm, align4
-  }
-  addr = sh_txx_fix_addr(addr, rinfo);
-
-  size_t idx = 0;
-  if (type == B_T3) {
-    uint32_t cond = SH_UTIL_GET_BITS_16(high_inst, 9, 6);
-    buf[idx++] = 0xD000u | (uint16_t)(cond << 8u);  // B<c> #0
-    buf[idx++] = 0xE003;                            // B #6
-  } else if (type == BL_IMM_T1 || type == BLX_IMM_T2) {
-    buf[idx++] = 0xF20F;  // ADD LR, PC, #9
-    buf[idx++] = 0x0E09;  // ...
-  }
-  buf[idx++] = 0xF8DF;  // LDR.W PC, [PC]
-  buf[idx++] = 0xF000;  // ...
-  buf[idx++] = addr & 0xFFFFu;
-  buf[idx++] = addr >> 16u;
-  return idx * 2;  // 8 or 12
-}
-
-static size_t sh_t32_rewrite_adr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                                 sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
-  uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 11, 8);  // r0 - r14
-  uint32_t i = SH_UTIL_GET_BIT_16(high_inst, 10);
-  uint32_t imm3 = SH_UTIL_GET_BITS_16(low_inst, 14, 12);
-  uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
-  uint32_t imm32 = (i << 11u) | (imm3 << 8u) | imm8;
-  uint32_t addr = (type == ADR_T2 ? (SH_UTIL_ALIGN_4(pc) - imm32) : (SH_UTIL_ALIGN_4(pc) + imm32));
-  if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0xF8DF;                      // LDR.W Rt, [PC, #4]
-  buf[1] = (uint16_t)(rt << 12u) + 4u;  // ...
-  buf[2] = 0xE002;                      // B #4
-  buf[3] = 0xBF00;                      // NOP
-  buf[4] = addr & 0xFFFFu;
-  buf[5] = addr >> 16u;
-  return 12;
-}
-
-static size_t sh_t32_rewrite_ldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                                 sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
-  uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
-  uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 15, 12);  // r0 - r15
-  uint32_t rt2 = 0;                                     // r0 - r15
-  uint32_t addr;
-
-  if (type == LDRD_LIT_T1) {
-    rt2 = SH_UTIL_GET_BITS_16(low_inst, 11, 8);
-    uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
-    addr = (u ? SH_UTIL_ALIGN_4(pc) + (imm8 << 2u) : SH_UTIL_ALIGN_4(pc) - (imm8 << 2u));
-  } else {
-    uint32_t imm12 = (uint32_t)SH_UTIL_GET_BITS_16(low_inst, 11, 0);
-    addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12);
-  }
-  if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  if (type == LDR_LIT_PC_T2 && rt == 0xF)  // Rt == PC
-  {
-    buf[0] = 0xB403;          // PUSH {R0, R1}
-    buf[1] = 0xBF00;          // NOP
-    buf[2] = 0xF8DF;          // LDR.W R0, [PC, #4]
-    buf[3] = 0x0004;          // ...
-    buf[4] = 0xE002;          // B #4
-    buf[5] = 0xBF00;          // NOP
-    buf[6] = addr & 0xFFFFu;  //
-    buf[7] = addr >> 16u;     //
-    buf[8] = 0xF8D0;          // LDR.W R0, [R0]
-    buf[9] = 0x0000;          // ...
-    buf[10] = 0x9001;         // STR R0, [SP, #4]
-    buf[11] = 0xBD01;         // POP {R0, PC}
-    return 24;
-  } else {
-    buf[0] = 0xF8DF;                      // LDR.W Rt, [PC, #4]
-    buf[1] = (uint16_t)(rt << 12u) | 4u;  // ...
-    buf[2] = 0xE002;                      // B #4
-    buf[3] = 0xBF00;                      // NOP
-    buf[4] = addr & 0xFFFFu;
-    buf[5] = addr >> 16u;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wswitch"
-    switch (type) {
-      case LDR_LIT_T2:
-        buf[6] = (uint16_t)(0xF8D0 + rt);  // LDR.W Rt, [Rt]
-        buf[7] = (uint16_t)(rt << 12u);    // ...
-        break;
-      case LDRB_LIT_T1:
-        buf[6] = (uint16_t)(0xF890 + rt);  // LDRB.W Rt, [Rt]
-        buf[7] = (uint16_t)(rt << 12u);    // ...
-        break;
-      case LDRD_LIT_T1:
-        buf[6] = (uint16_t)(0xE9D0 + rt);                        // LDRD Rt, Rt2, [Rt]
-        buf[7] = (uint16_t)(rt << 12u) + (uint16_t)(rt2 << 8u);  // ...
-        break;
-      case LDRH_LIT_T1:
-        buf[6] = (uint16_t)(0xF8B0 + rt);  // LDRH.W Rt, [Rt]
-        buf[7] = (uint16_t)(rt << 12u);    // ...
-        break;
-      case LDRSB_LIT_T1:
-        buf[6] = (uint16_t)(0xF990 + rt);  // LDRSB.W Rt, [Rt]
-        buf[7] = (uint16_t)(rt << 12u);    // ...
-        break;
-      case LDRSH_LIT_T1:
-        buf[6] = (uint16_t)(0xF9B0 + rt);  // LDRSH.W Rt, [Rt]
-        buf[7] = (uint16_t)(rt << 12u);    // ...
-        break;
-    }
-#pragma clang diagnostic pop
-    return 16;
-  }
-}
-
-static size_t sh_t32_rewrite_pl(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                                sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
-  uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
-  uint32_t imm12 = SH_UTIL_GET_BITS_16(low_inst, 11, 0);
-  uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12);
-  addr = sh_txx_fix_addr(addr, rinfo);
-
-  buf[0] = 0xB401;  // PUSH {R0}
-  buf[1] = 0xBF00;  // NOP
-  buf[2] = 0xF8DF;  // LDR.W R0, [PC, #8]
-  buf[3] = 0x0008;  // ...
-  if (type == PLD_LIT_T1) {
-    buf[4] = 0xF890;  // PLD [R0]
-    buf[5] = 0xF000;  // ...
-  } else {
-    buf[4] = 0xF990;  // PLI [R0]
-    buf[5] = 0xF000;  // ...
-  }
-  buf[6] = 0xBC01;  // POP {R0}
-  buf[7] = 0xE001;  // B #2
-  buf[8] = addr & 0xFFFFu;
-  buf[9] = addr >> 16u;
-  return 20;
-}
-
-static size_t sh_t32_rewrite_tb(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                                sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
-  // If TBB/TBH is not the last instruction that needs to be rewritten,
-  // the rewriting can NOT be completed.
-  uintptr_t target_addr = SH_UTIL_CLEAR_BIT0(pc - 4);
-  if (target_addr + 4 != rinfo->end_addr) return 0;  // rewrite failed
-
-  uint32_t rn = SH_UTIL_GET_BITS_16(high_inst, 3, 0);
-  uint32_t rm = SH_UTIL_GET_BITS_16(low_inst, 3, 0);
-  uint32_t rx, ry;  // r0 - r7
-  for (rx = 7;; --rx)
-    if (rx != rn && rx != rm) break;
-  for (ry = 7;; --ry)
-    if (ry != rn && ry != rm && ry != rx) break;
-
-  buf[0] = (uint16_t)(0xB500u | (1u << rx) | (1u << ry));  // PUSH {Rx, Ry, LR}
-  buf[1] = 0xBF00;                                         // NOP
-  buf[2] = 0xF8DF;                                         // LDR.W Rx, [PC, #20]
-  buf[3] = (uint16_t)(rx << 12u) | 20u;                    // ...
-  if (type == TBB_T1) {
-    buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn));  // ADD.W Ry, Rx|Rn, Rm
-    buf[5] = (uint16_t)(0x0000u | (ry << 8u) | rm);        // ...
-    buf[6] = (uint16_t)(0x7800u | (ry << 3u) | ry);        // LDRB Ry, [Ry]
-    buf[7] = 0xBF00;                                       // NOP
-  } else {
-    buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn));  // ADD.W Ry, Rx|Rn, Rm, LSL #1
-    buf[5] = (uint16_t)(0x0040u | (ry << 8u) | rm);        // ...
-    buf[6] = (uint16_t)(0x8800u | (ry << 3u) | ry);        // LDRH Ry, [Ry]
-    buf[7] = 0xBF00;                                       // NOP
-  }
-  buf[8] = (uint16_t)(0xEB00u | rx);                        // ADD Rx, Rx, Ry, LSL #1
-  buf[9] = (uint16_t)(0x0040u | (rx << 8u) | ry);           // ...
-  buf[10] = (uint16_t)(0x3001u | (rx << 8u));               // ADD Rx, #1
-  buf[11] = (uint16_t)(0x9002u | (rx << 8u));               // STR Rx, [SP, #8]
-  buf[12] = (uint16_t)(0xBD00u | (1u << rx) | (1u << ry));  // POP {Rx, Ry, PC}
-  buf[13] = 0xBF00;                                         // NOP
-  buf[14] = pc & 0xFFFFu;
-  buf[15] = pc >> 16u;
-  return 32;
-}
-
-static size_t sh_t32_rewrite_vldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                                  sh_txx_rewrite_info_t *rinfo) {
-  uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
-  uint32_t D = SH_UTIL_GET_BIT_16(high_inst, 6);
-  uint32_t vd = SH_UTIL_GET_BITS_16(low_inst, 15, 12);
-  uint32_t size = SH_UTIL_GET_BITS_16(low_inst, 9, 8);
-  uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
-  uint32_t esize = (8u << size);
-  uint32_t imm32 = (esize == 16 ? imm8 << 1u : imm8 << 2u);
-  uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm32 : SH_UTIL_ALIGN_4(pc) - imm32);
-  if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0xB401;                                       // PUSH {R0}
-  buf[1] = 0xBF00;                                       // NOP
-  buf[2] = 0xF8DF;                                       // LDR.W R0, [PC, #4]
-  buf[3] = 0x0004;                                       // ...
-  buf[4] = 0xE002;                                       // B #4
-  buf[5] = 0xBF00;                                       // NOP
-  buf[6] = addr & 0xFFFFu;                               //
-  buf[7] = addr >> 16u;                                  //
-  buf[8] = (uint16_t)(0xED90u | D << 6u);                // VLDR Sd|Dd, [R0]
-  buf[9] = (uint16_t)(0x800u | vd << 12u | size << 8u);  // ...
-  buf[10] = 0xBC01;                                      // POP {R0}
-  buf[11] = 0xBF00;                                      // NOP
-  return 24;
-}
-
-size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                      sh_txx_rewrite_info_t *rinfo) {
-  uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst;
-  sh_t32_type_t type = sh_t32_get_type(inst);
-  SH_LOG_INFO("t32 rewrite: type %d, high inst %" PRIx16 ", low inst %" PRIx16, type, high_inst, low_inst);
-
-  if (type == B_T3 || type == B_T4 || type == BL_IMM_T1 || type == BLX_IMM_T2)
-    return sh_t32_rewrite_b(buf, high_inst, low_inst, pc, type, rinfo);
-  else if (type == ADR_T2 || type == ADR_T3)
-    return sh_t32_rewrite_adr(buf, high_inst, low_inst, pc, type, rinfo);
-  else if (type == LDR_LIT_T2 || type == LDR_LIT_PC_T2 || type == LDRB_LIT_T1 || type == LDRD_LIT_T1 ||
-           type == LDRH_LIT_T1 || type == LDRSB_LIT_T1 || type == LDRSH_LIT_T1)
-    return sh_t32_rewrite_ldr(buf, high_inst, low_inst, pc, type, rinfo);
-  else if (type == PLD_LIT_T1 || type == PLI_LIT_T3)
-    return sh_t32_rewrite_pl(buf, high_inst, low_inst, pc, type, rinfo);
-  else if (type == TBB_T1 || type == TBH_T1)
-    return sh_t32_rewrite_tb(buf, high_inst, low_inst, pc, type, rinfo);
-  else if (type == VLDR_LIT_T1)
-    return sh_t32_rewrite_vldr(buf, high_inst, low_inst, pc, rinfo);
-  else {
-    // IGNORED
-    buf[0] = high_inst;
-    buf[1] = low_inst;
-    return 4;
-  }
-}
-
-size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr) {
-  size_t i = 0;
-  if (!is_align4) buf[i++] = 0xBF00;  // NOP
-  buf[i++] = 0xF8DF;                  // LDR.W PC, [PC]
-  buf[i++] = 0xF000;                  // ...
-  buf[i++] = addr & 0xFFFFu;
-  buf[i++] = addr >> 16u;
-  return i * 2;
-}
-
-size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc) {
-  uint32_t imm32 = addr - pc;
-  uint32_t s = SH_UTIL_GET_BIT_32(imm32, 24);
-  uint32_t i1 = SH_UTIL_GET_BIT_32(imm32, 23);
-  uint32_t i2 = SH_UTIL_GET_BIT_32(imm32, 22);
-  uint32_t imm10 = SH_UTIL_GET_BITS_32(imm32, 21, 12);
-  uint32_t imm11 = SH_UTIL_GET_BITS_32(imm32, 11, 1);
-  uint32_t j1 = (!i1) ^ s;
-  uint32_t j2 = (!i2) ^ s;
-
-  buf[0] = (uint16_t)(0xF000u | (s << 10u) | imm10);
-  buf[1] = (uint16_t)(0x9000u | (j1 << 13u) | (j2 << 11u) | imm11);
-  return 4;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h
deleted file mode 100644
index 1dc1179..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "sh_txx.h"
-
-size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst);
-size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
-                      sh_txx_rewrite_info_t *rinfo);
-
-size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr);
-size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc);
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c
deleted file mode 100644
index 79cefdb..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_txx.h"
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "sh_log.h"
-#include "sh_util.h"
-
-bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) {
-  return (rinfo->start_addr <= addr && addr < rinfo->end_addr);
-}
-
-uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) {
-  bool is_thumb = SH_UTIL_IS_THUMB(addr);
-
-  if (is_thumb) addr = SH_UTIL_CLEAR_BIT0(addr);
-
-  if (rinfo->start_addr <= addr && addr < rinfo->end_addr) {
-    uintptr_t cursor_addr = rinfo->start_addr;
-    size_t offset = 0;
-    for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) {
-      if (cursor_addr >= addr) break;
-      cursor_addr += 2;
-      offset += rinfo->inst_lens[i];
-    }
-    uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset;
-    if (is_thumb) fixed_addr = SH_UTIL_SET_BIT0(fixed_addr);
-
-    SH_LOG_INFO("txx rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
-    return fixed_addr;
-  }
-
-  if (is_thumb) addr = SH_UTIL_SET_BIT0(addr);
-  return addr;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h
deleted file mode 100644
index f5b9fae..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-typedef struct {
-  uintptr_t start_addr;
-  uintptr_t end_addr;
-  uint16_t *buf;
-  size_t buf_offset;
-  size_t inst_lens[13];  // 26 / 2 = 13
-  size_t inst_lens_cnt;
-} sh_txx_rewrite_info_t;
-
-bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo);
-uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo);
diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c
deleted file mode 100644
index cd9546b..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_a64.h"
-
-#include <inttypes.h>
-#include <sh_util.h>
-#include <stdint.h>
-
-#include "sh_log.h"
-
-// https://developer.arm.com/documentation/ddi0487/latest
-// https://developer.arm.com/documentation/ddi0602/latest
-
-typedef enum {
-  IGNORED = 0,
-  B,
-  B_COND,
-  BL,
-  ADR,
-  ADRP,
-  LDR_LIT_32,
-  LDR_LIT_64,
-  LDRSW_LIT,
-  PRFM_LIT,
-  LDR_SIMD_LIT_32,
-  LDR_SIMD_LIT_64,
-  LDR_SIMD_LIT_128,
-  CBZ,
-  CBNZ,
-  TBZ,
-  TBNZ
-} sh_a64_type_t;
-
-static sh_a64_type_t sh_a64_get_type(uint32_t inst) {
-  if ((inst & 0xFC000000) == 0x14000000)
-    return B;
-  else if ((inst & 0xFF000010) == 0x54000000)
-    return B_COND;
-  else if ((inst & 0xFC000000) == 0x94000000)
-    return BL;
-  else if ((inst & 0x9F000000) == 0x10000000)
-    return ADR;
-  else if ((inst & 0x9F000000) == 0x90000000)
-    return ADRP;
-  else if ((inst & 0xFF000000) == 0x18000000)
-    return LDR_LIT_32;
-  else if ((inst & 0xFF000000) == 0x58000000)
-    return LDR_LIT_64;
-  else if ((inst & 0xFF000000) == 0x98000000)
-    return LDRSW_LIT;
-  else if ((inst & 0xFF000000) == 0xD8000000)
-    return PRFM_LIT;
-  else if ((inst & 0xFF000000) == 0x1C000000)
-    return LDR_SIMD_LIT_32;
-  else if ((inst & 0xFF000000) == 0x5C000000)
-    return LDR_SIMD_LIT_64;
-  else if ((inst & 0xFF000000) == 0x9C000000)
-    return LDR_SIMD_LIT_128;
-  else if ((inst & 0x7F000000u) == 0x34000000)
-    return CBZ;
-  else if ((inst & 0x7F000000u) == 0x35000000)
-    return CBNZ;
-  else if ((inst & 0x7F000000u) == 0x36000000)
-    return TBZ;
-  else if ((inst & 0x7F000000u) == 0x37000000)
-    return TBNZ;
-  else
-    return IGNORED;
-}
-
-size_t sh_a64_get_rewrite_inst_len(uint32_t inst) {
-  static uint8_t map[] = {
-      4,   // IGNORED
-      20,  // B
-      28,  // B_COND
-      20,  // BL
-      16,  // ADR
-      16,  // ADRP
-      20,  // LDR_LIT_32
-      20,  // LDR_LIT_64
-      20,  // LDRSW_LIT
-      28,  // PRFM_LIT
-      28,  // LDR_SIMD_LIT_32
-      28,  // LDR_SIMD_LIT_64
-      28,  // LDR_SIMD_LIT_128
-      24,  // CBZ
-      24,  // CBNZ
-      24,  // TBZ
-      24   // TBNZ
-  };
-
-  return (size_t)(map[sh_a64_get_type(inst)]);
-}
-
-static bool sh_a64_is_addr_need_fix(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) {
-  return (rinfo->start_addr <= addr && addr < rinfo->end_addr);
-}
-
-static uintptr_t sh_a64_fix_addr(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) {
-  if (rinfo->start_addr <= addr && addr < rinfo->end_addr) {
-    uintptr_t cursor_addr = rinfo->start_addr;
-    size_t offset = 0;
-    for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) {
-      if (cursor_addr >= addr) break;
-      cursor_addr += 4;
-      offset += rinfo->inst_lens[i];
-    }
-    uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset;
-    SH_LOG_INFO("a64 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
-    return fixed_addr;
-  }
-
-  return addr;
-}
-
-static size_t sh_a64_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
-                               sh_a64_rewrite_info_t *rinfo) {
-  uint64_t imm64;
-  if (type == B_COND) {
-    uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
-    imm64 = SH_UTIL_SIGN_EXTEND_64(imm19 << 2u, 21u);
-  } else {
-    uint64_t imm26 = SH_UTIL_GET_BITS_32(inst, 25, 0);
-    imm64 = SH_UTIL_SIGN_EXTEND_64(imm26 << 2u, 28u);
-  }
-  uint64_t addr = pc + imm64;
-  addr = sh_a64_fix_addr(addr, rinfo);
-
-  size_t idx = 0;
-  if (type == B_COND) {
-    buf[idx++] = (inst & 0xFF00001F) | 0x40u;  // B.<cond> #8
-    buf[idx++] = 0x14000006;                   // B #24
-  }
-  buf[idx++] = 0x58000051;  // LDR X17, #8
-  buf[idx++] = 0x14000003;  // B #12
-  buf[idx++] = addr & 0xFFFFFFFF;
-  buf[idx++] = addr >> 32u;
-  if (type == BL)
-    buf[idx++] = 0xD63F0220;  // BLR X17
-  else
-    buf[idx++] = 0xD61F0220;  // BR X17
-  return idx * 4;             // 20 or 28
-}
-
-static size_t sh_a64_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
-                                 sh_a64_rewrite_info_t *rinfo) {
-  uint32_t xd = SH_UTIL_GET_BITS_32(inst, 4, 0);
-  uint64_t immlo = SH_UTIL_GET_BITS_32(inst, 30, 29);
-  uint64_t immhi = SH_UTIL_GET_BITS_32(inst, 23, 5);
-  uint64_t addr;
-  if (type == ADR)
-    addr = pc + SH_UTIL_SIGN_EXTEND_64((immhi << 2u) | immlo, 21u);
-  else  // ADRP
-    addr = (pc & 0xFFFFFFFFFFFFF000) + SH_UTIL_SIGN_EXTEND_64((immhi << 14u) | (immlo << 12u), 33u);
-  if (sh_a64_is_addr_need_fix(addr, rinfo)) return 0;  // rewrite failed
-
-  buf[0] = 0x58000040u | xd;  // LDR Xd, #8
-  buf[1] = 0x14000003;        // B #12
-  buf[2] = addr & 0xFFFFFFFF;
-  buf[3] = addr >> 32u;
-  return 16;
-}
-
-static size_t sh_a64_rewrite_ldr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
-                                 sh_a64_rewrite_info_t *rinfo) {
-  uint32_t rt = SH_UTIL_GET_BITS_32(inst, 4, 0);
-  uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
-  uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u);
-  uint64_t addr = pc + offset;
-
-  if (sh_a64_is_addr_need_fix(addr, rinfo)) {
-    if (type != PRFM_LIT) return 0;  // rewrite failed
-    addr = sh_a64_fix_addr(addr, rinfo);
-  }
-
-  if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT) {
-    buf[0] = 0x58000060u | rt;  // LDR Xt, #12
-    if (type == LDR_LIT_32)
-      buf[1] = 0xB9400000 | rt | (rt << 5u);  // LDR Wt, [Xt]
-    else if (type == LDR_LIT_64)
-      buf[1] = 0xF9400000 | rt | (rt << 5u);  // LDR Xt, [Xt]
-    else
-      // LDRSW_LIT
-      buf[1] = 0xB9800000 | rt | (rt << 5u);  // LDRSW Xt, [Xt]
-    buf[2] = 0x14000003;                      // B #12
-    buf[3] = addr & 0xFFFFFFFF;
-    buf[4] = addr >> 32u;
-    return 20;
-  } else {
-    buf[0] = 0xA93F47F0;  // STP X16, X17, [SP, -0x10]
-    buf[1] = 0x58000091;  // LDR X17, #16
-    if (type == PRFM_LIT)
-      buf[2] = 0xF9800220 | rt;  // PRFM Rt, [X17]
-    else if (type == LDR_SIMD_LIT_32)
-      buf[2] = 0xBD400220 | rt;  // LDR St, [X17]
-    else if (type == LDR_SIMD_LIT_64)
-      buf[2] = 0xFD400220 | rt;  // LDR Dt, [X17]
-    else
-      // LDR_SIMD_LIT_128
-      buf[2] = 0x3DC00220u | rt;  // LDR Qt, [X17]
-    buf[3] = 0xF85F83F1;          // LDR X17, [SP, -0x8]
-    buf[4] = 0x14000003;          // B #12
-    buf[5] = addr & 0xFFFFFFFF;
-    buf[6] = addr >> 32u;
-    return 28;
-  }
-}
-
-static size_t sh_a64_rewrite_cb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
-  uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
-  uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u);
-  uint64_t addr = pc + offset;
-  addr = sh_a64_fix_addr(addr, rinfo);
-
-  buf[0] = (inst & 0xFF00001F) | 0x40u;  // CB(N)Z Rt, #8
-  buf[1] = 0x14000005;                   // B #20
-  buf[2] = 0x58000051;                   // LDR X17, #8
-  buf[3] = 0xd61f0220;                   // BR X17
-  buf[4] = addr & 0xFFFFFFFF;
-  buf[5] = addr >> 32u;
-  return 24;
-}
-
-static size_t sh_a64_rewrite_tb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
-  uint64_t imm14 = SH_UTIL_GET_BITS_32(inst, 18, 5);
-  uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm14 << 2u), 16u);
-  uint64_t addr = pc + offset;
-  addr = sh_a64_fix_addr(addr, rinfo);
-
-  buf[0] = (inst & 0xFFF8001F) | 0x40u;  // TB(N)Z Rt, #<imm>, #8
-  buf[1] = 0x14000005;                   // B #20
-  buf[2] = 0x58000051;                   // LDR X17, #8
-  buf[3] = 0xd61f0220;                   // BR X17
-  buf[4] = addr & 0xFFFFFFFF;
-  buf[5] = addr >> 32u;
-  return 24;
-}
-
-size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
-  sh_a64_type_t type = sh_a64_get_type(inst);
-  SH_LOG_INFO("a64 rewrite: type %d, inst %" PRIx32, type, inst);
-
-  if (type == B || type == B_COND || type == BL)
-    return sh_a64_rewrite_b(buf, inst, pc, type, rinfo);
-  else if (type == ADR || type == ADRP)
-    return sh_a64_rewrite_adr(buf, inst, pc, type, rinfo);
-  else if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT || type == PRFM_LIT ||
-           type == LDR_SIMD_LIT_32 || type == LDR_SIMD_LIT_64 || type == LDR_SIMD_LIT_128)
-    return sh_a64_rewrite_ldr(buf, inst, pc, type, rinfo);
-  else if (type == CBZ || type == CBNZ)
-    return sh_a64_rewrite_cb(buf, inst, pc, rinfo);
-  else if (type == TBZ || type == TBNZ)
-    return sh_a64_rewrite_tb(buf, inst, pc, rinfo);
-  else {
-    // IGNORED
-    buf[0] = inst;
-    return 4;
-  }
-}
-
-size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr) {
-  buf[0] = 0x58000051;  // LDR X17, #8
-  buf[1] = 0xd61f0220;  // BR X17
-  buf[2] = addr & 0xFFFFFFFF;
-  buf[3] = addr >> 32u;
-  return 16;
-}
-
-// Use RET instead of BR to bypass arm64 BTI.
-//
-// ref:
-// https://developer.arm.com/documentation/102433/0100/Jump-oriented-programming
-// https://developer.arm.com/documentation/ddi0602/2023-06/Base-Instructions/BTI--Branch-Target-Identification-?lang=en
-// https://github.com/torvalds/linux/commit/8ef8f360cf30be12382f89ff48a57fbbd9b31c14
-// https://android-review.googlesource.com/c/platform/bionic/+/1242754
-// https://developer.android.com/ndk/guides/abis#armv9_enabling_pac_and_bti_for_cc
-// https://developer.arm.com/documentation/100067/0612/armclang-Command-line-Options/-mbranch-protection
-size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr) {
-  buf[0] = 0x58000051;  // LDR X17, #8
-  buf[1] = 0xd65f0220;  // RET X17
-  buf[2] = addr & 0xFFFFFFFF;
-  buf[3] = addr >> 32u;
-  return 16;
-}
-
-size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) {
-  buf[0] = 0x14000000u | (((addr - pc) & 0x0FFFFFFFu) >> 2u);  // B <label>
-  return 4;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h
deleted file mode 100644
index a51cd2e..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stddef.h>
-#include <stdint.h>
-
-#include "sh_inst.h"
-
-typedef struct {
-  uintptr_t start_addr;
-  uintptr_t end_addr;
-  uint32_t *buf;
-  size_t buf_offset;
-  size_t inst_lens[4];
-  size_t inst_lens_cnt;
-} sh_a64_rewrite_info_t;
-
-size_t sh_a64_get_rewrite_inst_len(uint32_t inst);
-size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo);
-
-size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr);
-size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr);
-size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc);
diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c
deleted file mode 100644
index 7474a6c..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_inst.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#include "sh_a64.h"
-#include "sh_config.h"
-#include "sh_enter.h"
-#include "sh_exit.h"
-#include "sh_log.h"
-#include "sh_sig.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "xdl.h"
-
-static int sh_inst_hook_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
-                                uintptr_t *orig_addr2) {
-  // backup original instructions (length: 4 or 16)
-  memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
-
-  // package the information passed to rewrite
-  sh_a64_rewrite_info_t rinfo;
-  rinfo.start_addr = target_addr;
-  rinfo.end_addr = target_addr + self->backup_len;
-  rinfo.buf = (uint32_t *)self->enter_addr;
-  rinfo.buf_offset = 0;
-  rinfo.inst_lens_cnt = self->backup_len / 4;
-  for (uintptr_t i = 0; i < self->backup_len; i += 4)
-    rinfo.inst_lens[i / 4] = sh_a64_get_rewrite_inst_len(*((uint32_t *)(target_addr + i)));
-
-  // rewrite original instructions (fill in enter)
-  uintptr_t pc = target_addr;
-  for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) {
-    size_t offset = sh_a64_rewrite((uint32_t *)(self->enter_addr + rinfo.buf_offset),
-                                   *((uint32_t *)(target_addr + i)), pc, &rinfo);
-    if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
-    rinfo.buf_offset += offset;
-  }
-
-  // absolute jump back to remaining original instructions (fill in enter)
-  rinfo.buf_offset += sh_a64_absolute_jump_with_ret((uint32_t *)(self->enter_addr + rinfo.buf_offset),
-                                                    target_addr + self->backup_len);
-  sh_util_clear_cache(self->enter_addr, rinfo.buf_offset);
-
-  // save original function address
-  if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST);
-  if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST);
-  return 0;
-}
-
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-
-// B: [-128M, +128M - 4]
-#define SH_INST_A64_B_RANGE_LOW  (134217728)
-#define SH_INST_A64_B_RANGE_HIGH (134217724)
-
-static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                  uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  uintptr_t pc = target_addr;
-  self->backup_len = 4;
-
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-
-  // alloc an exit for absolute jump
-  sh_a64_absolute_jump_with_br(self->exit, new_addr);
-  if (0 !=
-      (r = sh_exit_alloc(&self->exit_addr, (uint16_t *)&self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
-                         sizeof(self->exit), SH_INST_A64_B_RANGE_LOW, SH_INST_A64_B_RANGE_HIGH)))
-    return r;
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
-    r = SHADOWHOOK_ERRNO_MPROT;
-    goto err;
-  }
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2);
-  }
-  SH_SIG_CATCH() {
-    r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-    goto err;
-  }
-  SH_SIG_EXIT
-  if (0 != r) goto err;
-
-  // relative jump to the exit by overwriting the head of original function
-  sh_a64_relative_jump(self->trampo, self->exit_addr, pc);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
-
-  SH_LOG_INFO("a64: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
-              " -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
-              target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
-  return 0;
-
-err:
-  sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
-  self->exit_addr = 0;  // this is a flag for with-exit or without-exit
-  return r;
-}
-#endif
-
-static int sh_inst_hook_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
-                                     uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  int r;
-  self->backup_len = 16;
-
-  if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-
-  // rewrite
-  if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC))
-    return SHADOWHOOK_ERRNO_MPROT;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
-  }
-  SH_SIG_EXIT
-  if (0 != r) return r;
-
-  // absolute jump to new function address by overwriting the head of original function
-  sh_a64_absolute_jump_with_br(self->trampo, new_addr);
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-  if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
-
-  SH_LOG_INFO("a64: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
-              " -> remaining %" PRIxPTR,
-              target_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
-  return 0;
-}
-
-int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
-                 uintptr_t *orig_addr, uintptr_t *orig_addr2) {
-  self->enter_addr = sh_enter_alloc();
-  if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER;
-
-  int r;
-#ifdef SH_CONFIG_TRY_WITH_EXIT
-  if (0 == (r = sh_inst_hook_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) return r;
-#endif
-  if (0 == (r = sh_inst_hook_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
-    return r;
-
-  // hook failed
-  if (NULL != orig_addr) *orig_addr = 0;
-  if (NULL != orig_addr2) *orig_addr2 = 0;
-  sh_enter_free(self->enter_addr);
-  return r;
-}
-
-int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) {
-  int r;
-
-  // restore the instructions at the target address
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    r = memcmp((void *)target_addr, self->trampo, self->backup_len);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH;
-  }
-  SH_SIG_EXIT
-  if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH;
-  if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r;
-  __atomic_thread_fence(__ATOMIC_SEQ_CST);
-
-  // free memory space for exit
-  if (0 != self->exit_addr)
-    if (0 != (r = sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit),
-                               sizeof(self->exit))))
-      return r;
-
-  // free memory space for enter
-  sh_enter_free(self->enter_addr);
-
-  SH_LOG_INFO("a64: unhook OK. target %" PRIxPTR, target_addr);
-  return 0;
-}
diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h
deleted file mode 100644
index 5988b68..0000000
--- a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "xdl.h"
-
-typedef struct {
-  uint32_t trampo[4];   // align 16 // length == backup_len
-  uint8_t backup[16];   // align 16
-  uint32_t backup_len;  // == 4 or 16
-  uint32_t exit_type;
-  uintptr_t exit_addr;
-  uint32_t exit[4];
-  uintptr_t enter_addr;
-} sh_inst_t;
-
-int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
-                 uintptr_t *orig_addr, uintptr_t *orig_addr2);
-int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);
diff --git a/app/src/main/cpp/shadowhook/common/bytesig.c b/app/src/main/cpp/shadowhook/common/bytesig.c
deleted file mode 100644
index 8568279..0000000
--- a/app/src/main/cpp/shadowhook/common/bytesig.c
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2021-2023 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-// version 1.0.4
-
-#include "bytesig.h"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <pthread.h>
-#include <setjmp.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#define BYTESIG_DEBUG 0
-
-#if BYTESIG_DEBUG
-#include <android/log.h>
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-#define BYTESIG_LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "bytesig_tag", fmt, ##__VA_ARGS__)
-#pragma clang diagnostic pop
-#else
-#define BYTESIG_LOG(fmt, ...)
-#endif
-
-typedef enum {
-  BYTESIG_STATUS_UNAVAILABLE = 0,
-  BYTESIG_STATUS_SIG32 = 1,  // use sigset_t
-  BYTESIG_STATUS_SIG64 = 2   // use sigset64_t
-} bytesig_status_t;
-static bytesig_status_t bytesig_status = BYTESIG_STATUS_UNAVAILABLE;
-
-extern __attribute((weak)) int sigfillset64(sigset64_t *);
-extern __attribute((weak)) int sigemptyset64(sigset64_t *);
-extern __attribute((weak)) int sigaddset64(sigset64_t *, int);
-extern __attribute((weak)) int sigismember64(const sigset64_t *, int);
-
-typedef int (*bytesig_sigaction64_t)(int, const struct sigaction64 *, struct sigaction64 *);
-typedef int (*bytesig_sigaction_t)(int, const struct sigaction *, struct sigaction *);
-typedef int (*bytesig_sigprocmask64_t)(int, const sigset64_t *, sigset64_t *);
-typedef int (*bytesig_sigprocmask_t)(int, const sigset_t *, sigset_t *);
-
-static void *bytesig_sigaction;    // point to libc's sigaction64() or libc's sigaction()
-static void *bytesig_sigprocmask;  // point to libc's sigprocmask() or libc's sigprocmask64()
-
-__attribute__((constructor)) static void bytesig_ctor(void) {
-  void *libc = dlopen("libc.so", RTLD_LOCAL);
-  if (__predict_false(NULL == libc)) return;
-
-  if (__predict_true(NULL != sigfillset64 && NULL != sigemptyset64 && NULL != sigaddset64 &&
-                     NULL != sigismember64)) {
-    if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction64")) &&
-                       NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask64")))) {
-      bytesig_status = BYTESIG_STATUS_SIG64;
-      goto end;
-    }
-  }
-
-  if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction")) &&
-                     NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask")))) {
-    bytesig_status = BYTESIG_STATUS_SIG32;
-  }
-
-end:
-  dlclose(libc);
-}
-
-#define BYTESIG_PROTECTED_THREADS_MAX 256
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct {
-  pid_t tids[BYTESIG_PROTECTED_THREADS_MAX];
-  sigjmp_buf *jbufs[BYTESIG_PROTECTED_THREADS_MAX];
-  union {
-    struct sigaction64 prev_action64;
-    struct sigaction prev_action;
-  };
-} bytesig_signal_t;
-#pragma clang diagnostic pop
-
-// array index is signal number, corresponds to signals 1 to 31, except 9 and 19
-static bytesig_signal_t *bytesig_signal_array[__SIGRTMIN];
-
-static void bytesig_sigorset64(sigset64_t *dest, sigset64_t *left, sigset64_t *right) {
-  sigemptyset64(dest);
-  for (size_t i = 1; i < sizeof(sigset64_t) * CHAR_BIT; i++)
-    if (sigismember64(left, (int)i) == 1 || sigismember64(right, (int)i) == 1) sigaddset64(dest, (int)i);
-}
-
-static void bytesig_sigorset(sigset_t *dest, sigset_t *left, sigset_t *right) {
-  sigemptyset(dest);
-  for (size_t i = 1; i < sizeof(sigset_t) * CHAR_BIT; i++)
-    if (sigismember(left, (int)i) == 1 || sigismember(right, (int)i) == 1) sigaddset(dest, (int)i);
-}
-
-__attribute__((noinline)) static void bytesig_handler_internal(int signum, siginfo_t *siginfo,
-                                                               void *context) {
-  bytesig_signal_t *sig = bytesig_signal_array[signum];
-
-  // check protect info & do siglongjmp
-  pid_t tid = gettid();
-  if (__predict_false(0 == tid)) tid = (pid_t)syscall(SYS_gettid);
-  for (size_t i = 0; i < BYTESIG_PROTECTED_THREADS_MAX; i++) {
-    if (__predict_false(tid == __atomic_load_n(&(sig->tids[i]), __ATOMIC_RELAXED))) {
-      BYTESIG_LOG("siglongjmp signal %d (code %d) at %zu", signum, siginfo->si_code, i);
-
-      unsigned int ret_signum = (((unsigned int)signum & 0xFFU) << 16U);
-      unsigned int ret_code = 0U;
-      if (siginfo->si_code > 0)
-        ret_code = (((unsigned int)(siginfo->si_code) & 0xFFU) << 8U);
-      else if (siginfo->si_code < 0)
-        ret_code = (unsigned int)(-(siginfo->si_code)) & 0xFFU;
-      int ret_val = (int)(ret_signum | ret_code);
-
-      siglongjmp(*(__atomic_load_n(&(sig->jbufs[i]), __ATOMIC_RELAXED)), ret_val);
-    }
-  }
-
-#define SET_THREAD_SIGNAL_MASK(suffix)                                                       \
-  do {                                                                                       \
-    sigset##suffix##_t prev_mask;                                                            \
-    bytesig_sigorset##suffix(&prev_mask, &(((ucontext_t *)context)->uc_sigmask##suffix),     \
-                             &(sig->prev_action##suffix.sa_mask));                           \
-    if (0 == ((unsigned int)(sig->prev_action##suffix.sa_flags) & (unsigned int)SA_NODEFER)) \
-      sigaddset##suffix(&prev_mask, signum);                                                 \
-    sigaddset##suffix(&prev_mask, SIGPIPE);                                                  \
-    sigaddset##suffix(&prev_mask, SIGUSR1);                                                  \
-    sigaddset##suffix(&prev_mask, SIGQUIT);                                                  \
-    ((bytesig_sigprocmask##suffix##_t)bytesig_sigprocmask)(SIG_SETMASK, &prev_mask, NULL);   \
-  } while (0)
-
-  // set thread signal mask
-  if (BYTESIG_STATUS_SIG64 == bytesig_status)
-    SET_THREAD_SIGNAL_MASK(64);
-  else
-    SET_THREAD_SIGNAL_MASK();
-}
-
-// https://llvm.org/docs/CodeGenerator.html#tail-call-optimization
-// https://clang.llvm.org/docs/AttributeReference.html#disable-tail-calls
-//__attribute__((disable_tail_calls))
-static void bytesig_handler(int signum, siginfo_t *siginfo, void *context) {
-  bytesig_handler_internal(signum, siginfo, context);
-
-#define CALL_PREVIOUS_SIGNAL_HANDLER(suffix)                            \
-  do {                                                                  \
-    if (__predict_true(sig->prev_action##suffix.sa_flags & SA_SIGINFO)) \
-      sig->prev_action##suffix.sa_sigaction(signum, siginfo, context);  \
-    else if (SIG_DFL != sig->prev_action##suffix.sa_handler &&          \
-             SIG_IGN != sig->prev_action##suffix.sa_handler)            \
-      sig->prev_action##suffix.sa_handler(signum);                      \
-  } while (0)
-
-  // call previous signal handler
-  bytesig_signal_t *sig = bytesig_signal_array[signum];
-  if (BYTESIG_STATUS_SIG64 == bytesig_status)
-    CALL_PREVIOUS_SIGNAL_HANDLER(64);
-  else
-    CALL_PREVIOUS_SIGNAL_HANDLER();
-}
-
-int bytesig_init(int signum) {
-  if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
-    return -1;
-  if (__predict_false(BYTESIG_STATUS_UNAVAILABLE == bytesig_status)) return -1;
-  if (__predict_false(NULL != bytesig_signal_array[signum])) return -1;
-
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  pthread_mutex_lock(&lock);
-  int ret = -1;
-  if (__predict_false(NULL != bytesig_signal_array[signum])) goto end;
-
-  bytesig_signal_t *sig = calloc(1, sizeof(bytesig_signal_t));
-  if (__predict_false(NULL == sig)) goto end;
-
-#define SA_EXPOSE_TAGBITS 0x00000800
-
-#define REGISTER_SIGNAL_HANDLER(suffix)                                                                     \
-  do {                                                                                                      \
-    struct sigaction##suffix act;                                                                           \
-    memset(&act, 0, sizeof(struct sigaction##suffix));                                                      \
-    sigfillset##suffix(&act.sa_mask);                                                                       \
-    act.sa_sigaction = bytesig_handler;                                                                     \
-    act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART | SA_EXPOSE_TAGBITS;                                \
-    if (__predict_false(                                                                                    \
-            0 !=                                                                                            \
-            ((bytesig_sigaction##suffix##_t)bytesig_sigaction)(signum, &act, &sig->prev_action##suffix))) { \
-      free(sig);                                                                                            \
-      goto end;                                                                                             \
-    }                                                                                                       \
-  } while (0)
-
-  // register the signal handler, we start off with all signals blocked
-  if (BYTESIG_STATUS_SIG64 == bytesig_status)
-    REGISTER_SIGNAL_HANDLER(64);
-  else
-    REGISTER_SIGNAL_HANDLER();
-
-  bytesig_signal_array[signum] = sig;
-  ret = 0;  // OK
-
-end:
-  pthread_mutex_unlock(&lock);
-  return ret;
-}
-
-void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt) {
-  for (size_t i = 0; i < signums_cnt; i++) {
-    int signum = signums[i];
-    if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
-      continue;
-
-    bytesig_signal_t *sig = bytesig_signal_array[signum];
-    if (__predict_false(NULL == sig)) continue;
-
-    // check repeated thread
-    bool repeated = false;
-    for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) {
-      if (__predict_false(tid == sig->tids[j])) {
-        repeated = true;
-        break;
-      }
-    }
-    if (__predict_false(repeated)) continue;
-
-    // save thread-ID and jump buffer
-    size_t j = 0;
-    while (1) {
-      if (0 == sig->tids[j]) {
-        pid_t expected = 0;
-        if (__atomic_compare_exchange_n(&sig->tids[j], &expected, tid, false, __ATOMIC_ACQUIRE,
-                                        __ATOMIC_RELAXED)) {
-          sig->jbufs[j] = jbuf;
-          BYTESIG_LOG("protect_start signal %d at %zu", signum, j);
-          break;  // finished
-        }
-      }
-
-      j++;
-      if (__predict_false(BYTESIG_PROTECTED_THREADS_MAX == j)) j = 0;
-    }
-  }
-}
-
-void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt) {
-  for (size_t i = 0; i < signums_cnt; i++) {
-    int signum = signums[i];
-    if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
-      continue;
-
-    bytesig_signal_t *sig = bytesig_signal_array[signum];
-    if (__predict_false(NULL == sig)) continue;
-
-    // free thread-ID and jump buffer
-    for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) {
-      if (tid == sig->tids[j]) {
-        sig->jbufs[j] = NULL;
-        __atomic_store_n(&sig->tids[j], 0, __ATOMIC_RELEASE);
-        BYTESIG_LOG("protect_end signal %d at %zu", signum, j);
-        break;  // finished
-      }
-    }
-  }
-}
diff --git a/app/src/main/cpp/shadowhook/common/bytesig.h b/app/src/main/cpp/shadowhook/common/bytesig.h
deleted file mode 100644
index a7de526..0000000
--- a/app/src/main/cpp/shadowhook/common/bytesig.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2021-2023 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-// version 1.0.4
-
-/*
- * #include "bytesig.h"
- *
- * void init_once(void)
- * {
- *     bytesig_init(SIGSEGV);
- *     bytesig_init(SIGBUS);
- *     bytesig_init(SIGILL);
- *     bytesig_init(SIGABRT);
- *     // ......
- * }
- *
- * void unstable_func(void)
- * {
- *     int *p = NULL;
- *
- *     //
- *     // usage 1
- *     //
- *     BYTESIG_TRY(SIGSEGV, SIGBUS)
- *     {
- *         *p = 1;
- *     }
- *     BYTESIG_CATCH(signum, code)
- *     {
- *         LOG("signum %d (code %d)", signum, code);
- *     }
- *     BYTESIG_EXIT
- *
- *     //
- *     // usage 2
- *     //
- *     BYTESIG_TRY(SIGSEGV, SIGBUS)
- *     {
- *         *p = 2;
- *     }
- *     BYTESIG_CATCH(signum)
- *     {
- *         LOG("signum %d", signum);
- *     }
- *     BYTESIG_EXIT
- *
- *     //
- *     // usage 3
- *     //
- *     BYTESIG_TRY(SIGILL)
- *     {
- *         func_maybe_illed();
- *     }
- *     BYTESIG_CATCH()
- *     {
- *         do_something();
- *     }
- *     BYTESIG_EXIT
- *
- *     //
- *     // usage 4
- *     //
- *     BYTESIG_TRY(SIGABRT)
- *     {
- *         func_maybe_aborted();
- *     }
- *     BYTESIG_EXIT
- * }
- */
-
-#ifndef BYTEDANCE_BYTESIG
-#define BYTEDANCE_BYTESIG
-
-#include <setjmp.h>
-#include <signal.h>
-#include <stddef.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#define BYTESIG_TRY(...)                                                                                   \
-  do {                                                                                                     \
-    pid_t _bytesig_tid_ = gettid();                                                                        \
-    if (0 == _bytesig_tid_) _bytesig_tid_ = (pid_t)syscall(SYS_gettid);                                    \
-    sigjmp_buf _bytesig_jbuf_;                                                                             \
-    int _bytesig_sigs_[] = {__VA_ARGS__};                                                                  \
-    bytesig_protect(_bytesig_tid_, &_bytesig_jbuf_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
-    int _bytesig_protected_ = 1;                                                                           \
-    int _bytesig_ex_ = sigsetjmp(_bytesig_jbuf_, 1);                                                       \
-    if (0 == _bytesig_ex_) {
-#define BYTESIG_CATCH_2(signum_, code_)                                                     \
-  }                                                                                         \
-  else {                                                                                    \
-    bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
-    _bytesig_protected_ = 0;                                                                \
-    int signum_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF0000U) >> 16U);                   \
-    int code_ = 0;                                                                          \
-    if (((unsigned int)_bytesig_ex_ & 0xFF00U) > 0)                                         \
-      code_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF00U) >> 8U);                          \
-    else if (((unsigned int)_bytesig_ex_ & 0xFFU) > 0)                                      \
-      code_ = -((int)((unsigned int)_bytesig_ex_ & 0xFFU));                                 \
-    (void)signum_;                                                                          \
-    (void)code_;
-
-#define BYTESIG_CATCH_1(signum_) BYTESIG_CATCH_2(signum_, _bytesig_code_)
-#define BYTESIG_CATCH_0()        BYTESIG_CATCH_1(_bytesig_signum_)
-
-#define FUNC_CHOOSER(_f1, _f2, _f3, ...)     _f3
-#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
-#define CHOOSE_FROM_ARG_COUNT(...)           FUNC_RECOMPOSER((__VA_ARGS__, BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))
-#define NO_ARG_EXPANDER()                    , , BYTESIG_CATCH_0
-#define MACRO_CHOOSER(...)                   CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__())
-
-#define BYTESIG_CATCH(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
-
-#define BYTESIG_EXIT                                                                        \
-  }                                                                                         \
-  if (1 == _bytesig_protected_)                                                             \
-    bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
-  }                                                                                         \
-  while (0)                                                                                 \
-    ;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int bytesig_init(int signum);
-
-void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt);
-void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/common/sh_config.h b/app/src/main/cpp/shadowhook/common/sh_config.h
deleted file mode 100644
index 464dea9..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_config.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-
-// Global debugging.
-//
-// Note that in some cases these logs themselves can cause a crash.
-//
-//#define SH_CONFIG_DEBUG
-
-// Operation record of hook and unhook.
-//
-// Disabling it can reduce hook/unhook latency, memory footprint and file size.
-//
-#define SH_CONFIG_OPERATION_RECORDS
-
-// Crash signal protection.
-//
-// Do not disable it in a production environment.
-//
-#define SH_CONFIG_SIG_PROT
-
-// When hooking, try to find an exit for the relative jump for the dynamic library.
-//
-// Do not disable it in a production environment.
-//
-#define SH_CONFIG_TRY_WITH_EXIT
-
-// When hooking the function of the thumb instruction, if the function is too short,
-// try to use the gap aligned at the end of the function.
-//
-// Do not disable it in a production environment.
-//
-#define SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
diff --git a/app/src/main/cpp/shadowhook/common/sh_errno.c b/app/src/main/cpp/shadowhook/common/sh_errno.c
deleted file mode 100644
index e55891e..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_errno.c
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_errno.h"
-
-#include <pthread.h>
-#include <stdbool.h>
-
-#include "shadowhook.h"
-
-static int sh_errno_global = SHADOWHOOK_ERRNO_UNINIT;
-static pthread_key_t sh_errno_tls_key;
-
-int sh_errno_init(void) {
-  if (__predict_false(0 != pthread_key_create(&sh_errno_tls_key, NULL))) {
-    sh_errno_global = SHADOWHOOK_ERRNO_INIT_ERRNO;
-    return -1;
-  }
-  sh_errno_global = SHADOWHOOK_ERRNO_OK;
-  return 0;
-}
-
-void sh_errno_reset(void) {
-  sh_errno_set(0);
-}
-
-void sh_errno_set(int error_number) {
-  if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO) return;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"
-  pthread_setspecific(sh_errno_tls_key, (void *)error_number);
-#pragma clang diagnostic pop
-}
-
-int sh_errno_get(void) {
-  if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO)
-    return sh_errno_global;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wvoid-pointer-to-int-cast"
-  return (int)(pthread_getspecific(sh_errno_tls_key));
-#pragma clang diagnostic pop
-}
-
-const char *sh_errno_to_errmsg(int error_number) {
-  static const char *msg[] = {/* 0  */ "OK",
-                              /* 1  */ "Pending task",
-                              /* 2  */ "Not initialized",
-                              /* 3  */ "Invalid argument",
-                              /* 4  */ "Out of memory",
-                              /* 5  */ "MProtect failed",
-                              /* 6  */ "Write to arbitrary address crashed",
-                              /* 7  */ "Init errno mod failed",
-                              /* 8  */ "Init bytesig SIGSEGV mod failed",
-                              /* 9  */ "Init bytesig SIGBUS mod failed",
-                              /* 10 */ "Init enter mod failed",
-                              /* 11 */ "Init safe mod failed",
-                              /* 12 */ "Init linker mod failed",
-                              /* 13 */ "Init hub mod failed",
-                              /* 14 */ "Create hub failed",
-                              /* 15 */ "Monitor dlopen failed",
-                              /* 16 */ "Create monitor thread failed",
-                              /* 17 */ "Open ELF crashed",
-                              /* 18 */ "Find symbol in ELF failed",
-                              /* 19 */ "Find symbol in ELF crashed",
-                              /* 20 */ "Duplicate hook",
-                              /* 21 */ "Dladdr crashed",
-                              /* 22 */ "Find dlinfo failed",
-                              /* 23 */ "Symbol size too small",
-                              /* 24 */ "Alloc enter failed",
-                              /* 25 */ "Instruction rewrite crashed",
-                              /* 26 */ "Instruction rewrite failed",
-                              /* 27 */ "Switch not found",
-                              /* 28 */ "Verify original instruction crashed",
-                              /* 29 */ "Verify original instruction failed",
-                              /* 30 */ "Exit instruction mismatch",
-                              /* 31 */ "Free exit crashed",
-                              /* 32 */ "Unhook on an error status task",
-                              /* 33 */ "Unhook on an unfinished task",
-                              /* 34 */ "ELF with an unsupported architecture",
-                              /* 35 */ "Linker with an unsupported architecture"};
-
-  if (error_number < 0 || error_number >= (int)(sizeof(msg) / sizeof(msg[0]))) return "Unknown error number";
-
-  return msg[error_number];
-}
diff --git a/app/src/main/cpp/shadowhook/common/sh_errno.h b/app/src/main/cpp/shadowhook/common/sh_errno.h
deleted file mode 100644
index 10f017b..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_errno.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include "sh_log.h"
-
-#define SH_ERRNO_SET_RET_ERRNUM(errnum) SH_ERRNO_SET_RET((errnum), (errnum))
-#define SH_ERRNO_SET_RET_FAIL(errnum)   SH_ERRNO_SET_RET((errnum), -1)
-#define SH_ERRNO_SET_RET_NULL(errnum)   SH_ERRNO_SET_RET((errnum), NULL)
-#define SH_ERRNO_SET_RET(errnum, ret) \
-  do {                                \
-    sh_errno_set((errnum));           \
-    return (ret);                     \
-  } while (0)
-
-int sh_errno_init(void);
-void sh_errno_reset(void);
-void sh_errno_set(int error_number);
-int sh_errno_get(void);
-const char *sh_errno_to_errmsg(int error_number);
diff --git a/app/src/main/cpp/shadowhook/common/sh_log.c b/app/src/main/cpp/shadowhook/common/sh_log.c
deleted file mode 100644
index 2632723..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_log.c
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_log.h"
-
-#include <android/log.h>
-#include <stdbool.h>
-
-#include "sh_config.h"
-
-android_LogPriority sh_log_priority =
-#ifdef SH_CONFIG_DEBUG
-    ANDROID_LOG_INFO
-#else
-    ANDROID_LOG_SILENT
-#endif
-    ;
-
-bool sh_log_get_debuggable(void) {
-  return sh_log_priority <= ANDROID_LOG_INFO;
-}
-
-void sh_log_set_debuggable(bool debuggable) {
-#ifdef SH_CONFIG_DEBUG
-  (void)debuggable;
-  sh_log_priority = ANDROID_LOG_INFO;
-#else
-  if (__predict_false(debuggable))
-    sh_log_priority = ANDROID_LOG_INFO;
-  else
-    sh_log_priority = ANDROID_LOG_SILENT;
-#endif
-}
diff --git a/app/src/main/cpp/shadowhook/common/sh_log.h b/app/src/main/cpp/shadowhook/common/sh_log.h
deleted file mode 100644
index fff786f..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_log.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <android/log.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-extern android_LogPriority sh_log_priority;
-
-#define SH_LOG_TAG "shadowhook_tag"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-// Notice:
-// We don't use ANDROID_LOG_DEBUG, because some Android devices will filter out ANDROID_LOG_DEBUG.
-#ifdef SH_CONFIG_DEBUG
-#define SH_LOG_DEBUG(fmt, ...)                                               \
-  do {                                                                       \
-    if (sh_log_priority <= ANDROID_LOG_INFO)                                 \
-      __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
-  } while (0)
-#else
-#define SH_LOG_DEBUG(fmt, ...)
-#endif
-
-#define SH_LOG_INFO(fmt, ...)                                                \
-  do {                                                                       \
-    if (__predict_false(sh_log_priority <= ANDROID_LOG_INFO))                \
-      __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
-  } while (0)
-#define SH_LOG_WARN(fmt, ...)                                                \
-  do {                                                                       \
-    if (__predict_false(sh_log_priority <= ANDROID_LOG_WARN))                \
-      __android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
-  } while (0)
-#define SH_LOG_ERROR(fmt, ...)                                                \
-  do {                                                                        \
-    if (__predict_false(sh_log_priority <= ANDROID_LOG_ERROR))                \
-      __android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
-  } while (0)
-#define SH_LOG_ALWAYS_SHOW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__)
-
-#pragma clang diagnostic pop
-
-bool sh_log_get_debuggable(void);
-void sh_log_set_debuggable(bool debuggable);
diff --git a/app/src/main/cpp/shadowhook/common/sh_sig.h b/app/src/main/cpp/shadowhook/common/sh_sig.h
deleted file mode 100644
index 5b926ae..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_sig.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-
-#include "sh_config.h"
-
-#ifdef SH_CONFIG_SIG_PROT
-
-#include "bytesig.h"
-#define SH_SIG_TRY   BYTESIG_TRY
-#define SH_SIG_CATCH BYTESIG_CATCH
-#define SH_SIG_EXIT  BYTESIG_EXIT
-
-#else
-
-#define SH_SIG_TRY(...) \
-  do {                  \
-    if (0 == 0) {
-#define SH_SIG_CATCH(...) \
-  }                       \
-  else {
-#define SH_SIG_EXIT \
-  }                 \
-  }                 \
-  while (0)         \
-    ;
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/common/sh_trampo.c b/app/src/main/cpp/shadowhook/common/sh_trampo.c
deleted file mode 100644
index 894273c..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_trampo.c
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_trampo.h"
-
-#include <pthread.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/time.h>
-
-#include "queue.h"
-#include "sh_util.h"
-
-#define SH_TRAMPO_PAGE_SZ 4096
-#define SH_TRAMPO_ALIGN   4
-
-void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size,
-                        time_t delay_sec) {
-  SLIST_INIT(&mem_mgr->pages);
-  pthread_mutex_init(&mem_mgr->pages_lock, NULL);
-  mem_mgr->page_name = page_name;
-  mem_mgr->trampo_size = SH_UTIL_ALIGN_END(trampo_size, SH_TRAMPO_ALIGN);
-  mem_mgr->delay_sec = delay_sec;
-}
-
-uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t range_low,
-                          uintptr_t range_high) {
-  uintptr_t mem = 0;
-  uintptr_t new_ptr;
-  uintptr_t new_ptr_prctl = (uintptr_t)MAP_FAILED;
-  size_t count = SH_TRAMPO_PAGE_SZ / mem_mgr->trampo_size;
-
-  if (range_low > hint) range_low = hint;
-  if (range_high > UINTPTR_MAX - hint) range_high = UINTPTR_MAX - hint;
-
-  struct timeval now;
-  if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL);
-
-  pthread_mutex_lock(&mem_mgr->pages_lock);
-
-  // try to find an unused mem
-  sh_trampo_page_t *page;
-  SLIST_FOREACH(page, &mem_mgr->pages, link) {
-    // check hit range
-    uintptr_t page_trampo_start = page->ptr;
-    uintptr_t page_trampo_end = page->ptr + SH_TRAMPO_PAGE_SZ - mem_mgr->trampo_size;
-    if (hint > 0 && ((page_trampo_end < hint - range_low) || (hint + range_high < page_trampo_start)))
-      continue;
-
-    for (uintptr_t i = 0; i < count; i++) {
-      size_t flags_idx = i / 32;
-      uint32_t mask = (uint32_t)1 << (i % 32);
-      if (0 == (page->flags[flags_idx] & mask))  // check flag
-      {
-        // check timestamp
-        if (mem_mgr->delay_sec > 0 &&
-            (now.tv_sec <= page->timestamps[i] || now.tv_sec - page->timestamps[i] <= mem_mgr->delay_sec))
-          continue;
-
-        // check hit range
-        uintptr_t cur = page->ptr + (mem_mgr->trampo_size * i);
-        if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue;
-
-        // OK
-        page->flags[flags_idx] |= mask;
-        mem = cur;
-        memset((void *)mem, 0, mem_mgr->trampo_size);
-        goto end;
-      }
-    }
-  }
-
-  // alloc a new mem page
-  new_ptr = (uintptr_t)(mmap(hint > 0 ? (void *)(hint - range_low) : NULL, SH_TRAMPO_PAGE_SZ,
-                             PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
-  if ((uintptr_t)MAP_FAILED == new_ptr) goto err;
-  new_ptr_prctl = new_ptr;
-
-  // check hit range
-  if (hint > 0 && ((hint - range_low >= new_ptr + SH_TRAMPO_PAGE_SZ - mem_mgr->trampo_size) ||
-                   (hint + range_high < new_ptr)))
-    goto err;
-
-  // create a new trampo-page info
-  if (NULL == (page = calloc(1, sizeof(sh_trampo_page_t)))) goto err;
-  memset((void *)new_ptr, 0, SH_TRAMPO_PAGE_SZ);
-  page->ptr = new_ptr;
-  new_ptr = (uintptr_t)MAP_FAILED;
-  if (NULL == (page->flags = calloc(1, SH_UTIL_ALIGN_END(count, 32) / 8))) goto err;
-  page->timestamps = NULL;
-  if (mem_mgr->delay_sec > 0) {
-    if (NULL == (page->timestamps = calloc(1, count * sizeof(time_t)))) goto err;
-  }
-  SLIST_INSERT_HEAD(&mem_mgr->pages, page, link);
-
-  // alloc mem from the new mem page
-  for (uintptr_t i = 0; i < count; i++) {
-    size_t flags_idx = i / 32;
-    uint32_t mask = (uint32_t)1 << (i % 32);
-
-    // check hit range
-    uintptr_t find = page->ptr + (mem_mgr->trampo_size * i);
-    if (hint > 0 && ((find < hint - range_low) || (hint + range_high < find))) continue;
-
-    // OK
-    page->flags[flags_idx] |= mask;
-    mem = find;
-    break;
-  }
-  if (0 == mem) abort();
-
-end:
-  pthread_mutex_unlock(&mem_mgr->pages_lock);
-  if ((uintptr_t)MAP_FAILED != new_ptr_prctl)
-    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_ptr_prctl, SH_TRAMPO_PAGE_SZ, mem_mgr->page_name);
-  return mem;
-
-err:
-  pthread_mutex_unlock(&mem_mgr->pages_lock);
-  if (NULL != page) {
-    if (0 != page->ptr) munmap((void *)page->ptr, SH_TRAMPO_PAGE_SZ);
-    if (NULL != page->flags) free(page->flags);
-    if (NULL != page->timestamps) free(page->timestamps);
-    free(page);
-  }
-  if ((uintptr_t)MAP_FAILED != new_ptr) munmap((void *)new_ptr, SH_TRAMPO_PAGE_SZ);
-  return 0;
-}
-
-void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem) {
-  struct timeval now;
-  if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL);
-
-  pthread_mutex_lock(&mem_mgr->pages_lock);
-
-  sh_trampo_page_t *page;
-  SLIST_FOREACH(page, &mem_mgr->pages, link) {
-    if (page->ptr <= mem && mem < page->ptr + SH_TRAMPO_PAGE_SZ) {
-      uintptr_t i = (mem - page->ptr) / mem_mgr->trampo_size;
-      size_t flags_idx = i / 32;
-      uint32_t mask = (uint32_t)1 << (i % 32);
-      if (mem_mgr->delay_sec > 0) page->timestamps[i] = now.tv_sec;
-      page->flags[flags_idx] &= ~mask;
-      break;
-    }
-  }
-
-  pthread_mutex_unlock(&mem_mgr->pages_lock);
-}
diff --git a/app/src/main/cpp/shadowhook/common/sh_trampo.h b/app/src/main/cpp/shadowhook/common/sh_trampo.h
deleted file mode 100644
index 0a20964..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_trampo.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/time.h>
-
-#include "queue.h"
-
-typedef struct sh_trampo_page {
-  uintptr_t ptr;
-  uint32_t *flags;
-  time_t *timestamps;
-  SLIST_ENTRY(sh_trampo_page, ) link;
-} sh_trampo_page_t;
-typedef SLIST_HEAD(sh_trampo_page_list, sh_trampo_page, ) sh_trampo_page_list_t;
-
-typedef struct sh_trampo_mgr {
-  sh_trampo_page_list_t pages;
-  pthread_mutex_t pages_lock;
-  const char *page_name;
-  size_t trampo_size;
-  time_t delay_sec;
-} sh_trampo_mgr_t;
-
-void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size,
-                        time_t delay_sec);
-
-uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t low_offset,
-                          uintptr_t high_offset);
-void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem);
diff --git a/app/src/main/cpp/shadowhook/common/sh_util.c b/app/src/main/cpp/shadowhook/common/sh_util.c
deleted file mode 100644
index b8604c8..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_util.c
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_util.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "sh_log.h"
-#include "sh_sig.h"
-#include "shadowhook.h"
-
-int sh_util_mprotect(uintptr_t addr, size_t len, int prot) {
-  uintptr_t start = SH_UTIL_PAGE_START(addr);
-  uintptr_t end = SH_UTIL_PAGE_END(addr + len - 1);
-
-  return mprotect((void *)start, end - start, prot);
-}
-
-void sh_util_clear_cache(uintptr_t addr, size_t len) {
-  __builtin___clear_cache((char *)addr, (char *)(addr + len));
-}
-
-bool sh_util_is_thumb32(uintptr_t target_addr) {
-  uint16_t opcode = *((uint16_t *)target_addr);
-  int tmp = opcode >> 11u;
-  return (tmp == 0x1d) || (tmp == 0x1e) || (tmp == 0x1f);
-}
-
-static uint32_t sh_util_ror(uint32_t val, uint32_t n, uint32_t shift) {
-  uint32_t m = shift % n;
-  return (val >> m) | (val << (n - m));
-}
-
-uint32_t sh_util_arm_expand_imm(uint32_t opcode) {
-  uint32_t imm = SH_UTIL_GET_BITS_32(opcode, 7, 0);
-  uint32_t amt = 2 * SH_UTIL_GET_BITS_32(opcode, 11, 8);
-
-  return amt == 0 ? imm : sh_util_ror(imm, 32, amt);
-}
-
-int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len) {
-  if (0 != sh_util_mprotect(target_addr, inst_len, PROT_READ | PROT_WRITE | PROT_EXEC))
-    return SHADOWHOOK_ERRNO_MPROT;
-
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    if ((4 == inst_len) && (0 == target_addr % 4))
-      __atomic_store_n((uint32_t *)target_addr, *((uint32_t *)inst), __ATOMIC_SEQ_CST);
-    else if ((8 == inst_len) && (0 == target_addr % 8))
-      __atomic_store_n((uint64_t *)target_addr, *((uint64_t *)inst), __ATOMIC_SEQ_CST);
-#ifdef __LP64__
-    else if ((16 == inst_len) && (0 == target_addr % 16))
-      __atomic_store_n((__int128 *)target_addr, *((__int128 *)inst), __ATOMIC_SEQ_CST);
-#endif
-    else
-      memcpy((void *)target_addr, inst, inst_len);
-
-    sh_util_clear_cache(target_addr, inst_len);
-  }
-  SH_SIG_CATCH() {
-    return SHADOWHOOK_ERRNO_WRITE_CRASH;
-  }
-  SH_SIG_EXIT
-
-  return 0;  // OK
-}
-
-static bool sh_util_starts_with(const char *str, const char *start) {
-  while (*str && *str == *start) {
-    str++;
-    start++;
-  }
-
-  return '\0' == *start;
-}
-
-static int sh_util_get_api_level_from_build_prop(void) {
-  char buf[128];
-  int api_level = -1;
-
-  FILE *fp = fopen("/system/build.prop", "r");
-  if (__predict_false(NULL == fp)) goto end;
-
-  while (fgets(buf, sizeof(buf), fp)) {
-    if (__predict_false(sh_util_starts_with(buf, "ro.build.version.sdk="))) {
-      api_level = atoi(buf + 21);
-      break;
-    }
-  }
-  fclose(fp);
-
-end:
-  return (api_level > 0) ? api_level : -1;
-}
-
-int sh_util_get_api_level(void) {
-  static int xdl_util_api_level = -1;
-
-  if (__predict_false(xdl_util_api_level < 0)) {
-    int api_level = android_get_device_api_level();
-    if (__predict_false(api_level < 0))
-      api_level = sh_util_get_api_level_from_build_prop();  // compatible with unusual models
-    if (__predict_false(api_level < __ANDROID_API_J__)) api_level = __ANDROID_API_J__;
-
-    __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST);
-  }
-
-  return xdl_util_api_level;
-}
-
-int sh_util_write(int fd, const char *buf, size_t buf_len) {
-  if (fd < 0) return -1;
-
-  const char *ptr = buf;
-  size_t nleft = buf_len;
-
-  while (nleft > 0) {
-    errno = 0;
-    ssize_t nwritten = write(fd, ptr, nleft);
-    if (nwritten <= 0) {
-      if (nwritten < 0 && errno == EINTR)
-        nwritten = 0;  // call write() again
-      else
-        return -1;  // error
-    }
-    nleft -= (size_t)nwritten;
-    ptr += nwritten;
-  }
-
-  return 0;
-}
-
-/* Nonzero if YEAR is a leap year (every 4 years,
-   except every 100th isn't, and every 400th is).  */
-#define SH_UTIL_ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
-#define SH_UTIL_SECS_PER_HOUR        (60 * 60)
-#define SH_UTIL_SECS_PER_DAY         (SH_UTIL_SECS_PER_HOUR * 24)
-#define SH_UTIL_DIV(a, b)            ((a) / (b) - ((a) % (b) < 0))
-#define SH_UTIL_LEAPS_THRU_END_OF(y) (SH_UTIL_DIV(y, 4) - SH_UTIL_DIV(y, 100) + SH_UTIL_DIV(y, 400))
-
-static const unsigned short int sh_util_mon_yday[2][13] = {
-    /* Normal years.  */
-    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
-    /* Leap years.  */
-    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
-
-/* Compute the `struct tm' representation of *T,
-   offset GMTOFF seconds east of UTC,
-   and store year, yday, mon, mday, wday, hour, min, sec into *RESULT.
-   Return RESULT if successful.  */
-struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result) {
-  time_t days, rem, y;
-  const unsigned short int *ip;
-
-  if (NULL == result) return NULL;
-
-  result->tm_gmtoff = gmtoff;
-
-  days = ((*timep) / SH_UTIL_SECS_PER_DAY);
-  rem = ((*timep) % SH_UTIL_SECS_PER_DAY);
-  rem += gmtoff;
-  while (rem < 0) {
-    rem += SH_UTIL_SECS_PER_DAY;
-    --days;
-  }
-  while (rem >= SH_UTIL_SECS_PER_DAY) {
-    rem -= SH_UTIL_SECS_PER_DAY;
-    ++days;
-  }
-  result->tm_hour = (int)(rem / SH_UTIL_SECS_PER_HOUR);
-  rem %= SH_UTIL_SECS_PER_HOUR;
-  result->tm_min = (int)(rem / 60L);
-  result->tm_sec = (int)(rem % 60L);
-  /* January 1, 1970 was a Thursday.  */
-  result->tm_wday = (int)(4 + days) % 7;
-  if (result->tm_wday < 0) result->tm_wday += 7;
-  y = 1970;
-
-  while (days < 0 || days >= (SH_UTIL_ISLEAP(y) ? 366 : 365)) {
-    /* Guess a corrected year, assuming 365 days per year.  */
-    time_t yg = y + days / 365 - (days % 365 < 0);
-
-    /* Adjust DAYS and Y to match the guessed year.  */
-    days -= ((yg - y) * 365 + SH_UTIL_LEAPS_THRU_END_OF(yg - 1) - SH_UTIL_LEAPS_THRU_END_OF(y - 1));
-
-    y = yg;
-  }
-  result->tm_year = (int)(y - 1900);
-  if (result->tm_year != y - 1900) {
-    /* The year cannot be represented due to overflow.  */
-    errno = EOVERFLOW;
-    return NULL;
-  }
-  result->tm_yday = (int)days;
-  ip = sh_util_mon_yday[SH_UTIL_ISLEAP(y)];
-  for (y = 11; days < (long int)ip[y]; --y) continue;
-  days -= ip[y];
-  result->tm_mon = (int)y;
-  result->tm_mday = (int)(days + 1);
-  return result;
-}
-
-static unsigned sh_util_parse_decimal(const char *format, int *ppos) {
-  const char *p = format + *ppos;
-  unsigned result = 0;
-  for (;;) {
-    int ch = *p;
-    unsigned d = (unsigned)(ch - '0');
-    if (d >= 10U) {
-      break;
-    }
-    result = result * 10 + d;
-    p++;
-  }
-  *ppos = (int)(p - format);
-  return result;
-}
-
-static void sh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, int base, int caps) {
-  char *p = buf;
-  char *end = buf + buf_size - 1;
-
-  // Generate digit string in reverse order.
-  while (value) {
-    unsigned d = (unsigned)(value % (uint64_t)base);
-    value /= (uint64_t)base;
-    if (p != end) {
-      char ch;
-      if (d < 10) {
-        ch = '0' + (char)d;
-      } else {
-        ch = (caps ? 'A' : 'a') + (char)(d - 10);
-      }
-      *p++ = ch;
-    }
-  }
-
-  // Special case for 0.
-  if (p == buf) {
-    if (p != end) {
-      *p++ = '0';
-    }
-  }
-  *p = '\0';
-
-  // Reverse digit string in-place.
-  size_t length = (size_t)(p - buf);
-  for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
-    char ch = buf[i];
-    buf[i] = buf[j];
-    buf[j] = ch;
-  }
-}
-
-static void sh_util_format_integer(char *buf, size_t buf_size, uint64_t value, char conversion) {
-  // Decode the conversion specifier.
-  int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
-  int base = 10;
-  if (conversion == 'x' || conversion == 'X') {
-    base = 16;
-  } else if (conversion == 'o') {
-    base = 8;
-  }
-  int caps = (conversion == 'X');
-  if (is_signed && (int64_t)(value) < 0) {
-    buf[0] = '-';
-    buf += 1;
-    buf_size -= 1;
-    value = (uint64_t)(-(int64_t)(value));
-  }
-  sh_util_format_unsigned(buf, buf_size, value, base, caps);
-}
-
-// format stream
-typedef struct {
-  size_t total;
-  char *pos;
-  size_t avail;
-} sh_util_stream_t;
-
-static void sh_util_stream_init(sh_util_stream_t *self, char *buffer, size_t buffer_size) {
-  self->total = 0;
-  self->pos = buffer;
-  self->avail = buffer_size;
-
-  if (self->avail > 0) self->pos[0] = '\0';
-}
-
-static size_t sh_util_stream_total(sh_util_stream_t *self) {
-  return self->total;
-}
-
-static void sh_util_stream_send(sh_util_stream_t *self, const char *data, int len) {
-  if (len < 0) {
-    len = (int)strlen(data);
-  }
-  self->total += (size_t)len;
-
-  if (self->avail <= 1) {
-    // no space to put anything else
-    return;
-  }
-
-  if ((size_t)len >= self->avail) {
-    len = (int)(self->avail - 1);
-  }
-
-  memcpy(self->pos, data, (size_t)len);
-  self->pos += len;
-  self->pos[0] = '\0';
-  self->avail -= (size_t)len;
-}
-
-static void sh_util_stream_send_repeat(sh_util_stream_t *self, char ch, int count) {
-  char pad[8];
-  memset(pad, ch, sizeof(pad));
-
-  const int pad_size = (int)(sizeof(pad));
-  while (count > 0) {
-    int avail = count;
-    if (avail > pad_size) {
-      avail = pad_size;
-    }
-    sh_util_stream_send(self, pad, avail);
-    count -= avail;
-  }
-}
-
-static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, va_list args) {
-  int nn = 0;
-
-  for (;;) {
-    int mm;
-    int padZero = 0;
-    int padLeft = 0;
-    char sign = '\0';
-    int width = -1;
-    int prec = -1;
-    size_t bytelen = sizeof(int);
-    int slen;
-    char buffer[32];  // temporary buffer used to format numbers
-    char c;
-
-    // first, find all characters that are not 0 or '%', then send them to the output directly
-    mm = nn;
-    do {
-      c = format[mm];
-      if (c == '\0' || c == '%') break;
-      mm++;
-    } while (1);
-    if (mm > nn) {
-      sh_util_stream_send(self, format + nn, mm - nn);
-      nn = mm;
-    }
-
-    // is this it ? then exit
-    if (c == '\0') break;
-
-    // nope, we are at a '%' modifier
-    nn++;  // skip it
-
-    // parse flags
-    for (;;) {
-      c = format[nn++];
-      if (c == '\0') {
-        // single trailing '%' ?
-        c = '%';
-        sh_util_stream_send(self, &c, 1);
-        return;
-      } else if (c == '0') {
-        padZero = 1;
-        continue;
-      } else if (c == '-') {
-        padLeft = 1;
-        continue;
-      } else if (c == ' ' || c == '+') {
-        sign = c;
-        continue;
-      }
-      break;
-    }
-
-    // parse field width
-    if ((c >= '0' && c <= '9')) {
-      nn--;
-      width = (int)(sh_util_parse_decimal(format, &nn));
-      c = format[nn++];
-    }
-
-    // parse precision
-    if (c == '.') {
-      prec = (int)(sh_util_parse_decimal(format, &nn));
-      c = format[nn++];
-    }
-
-    // length modifier
-    switch (c) {
-      case 'h':
-        bytelen = sizeof(short);
-        if (format[nn] == 'h') {
-          bytelen = sizeof(char);
-          nn += 1;
-        }
-        c = format[nn++];
-        break;
-      case 'l':
-        bytelen = sizeof(long);
-        if (format[nn] == 'l') {
-          bytelen = sizeof(long long);
-          nn += 1;
-        }
-        c = format[nn++];
-        break;
-      case 'z':
-        bytelen = sizeof(size_t);
-        c = format[nn++];
-        break;
-      case 't':
-        bytelen = sizeof(ptrdiff_t);
-        c = format[nn++];
-        break;
-      default:;
-    }
-
-    // conversion specifier
-    const char *str = buffer;
-    if (c == 's') {
-      // string
-      str = va_arg(args, const char *);
-      if (str == NULL) {
-        str = "(null)";
-      }
-    } else if (c == 'c') {
-      // character
-      // NOTE: char is promoted to int when passed through the stack
-      buffer[0] = (char)(va_arg(args, int));
-      buffer[1] = '\0';
-    } else if (c == 'p') {
-      uint64_t value = (uintptr_t)(va_arg(args, void *));
-      buffer[0] = '0';
-      buffer[1] = 'x';
-      sh_util_format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
-    } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
-      // integers - first read value from stack
-      uint64_t value;
-      int is_signed = (c == 'd' || c == 'i' || c == 'o');
-      // NOTE: int8_t and int16_t are promoted to int when passed through the stack
-      switch (bytelen) {
-        case 1:
-          value = (uint8_t)(va_arg(args, int));
-          break;
-        case 2:
-          value = (uint16_t)(va_arg(args, int));
-          break;
-        case 4:
-          value = va_arg(args, uint32_t);
-          break;
-        case 8:
-          value = va_arg(args, uint64_t);
-          break;
-        default:
-          return;  // should not happen
-      }
-      // sign extension, if needed
-      if (is_signed) {
-        int shift = (int)(64 - 8 * bytelen);
-        value = (uint64_t)(((int64_t)(value << shift)) >> shift);
-      }
-      // format the number properly into our buffer
-      sh_util_format_integer(buffer, sizeof(buffer), value, c);
-    } else if (c == '%') {
-      buffer[0] = '%';
-      buffer[1] = '\0';
-    } else {
-      //__assert(__FILE__, __LINE__, "conversion specifier unsupported");
-      return;
-    }
-
-    // if we are here, 'str' points to the content that must be outputted.
-    // handle padding and alignment now
-    slen = (int)strlen(str);
-    if (sign != '\0' || prec != -1) {
-      //__assert(__FILE__, __LINE__, "sign/precision unsupported");
-      return;
-    }
-    if (slen < width && !padLeft) {
-      char padChar = padZero ? '0' : ' ';
-      sh_util_stream_send_repeat(self, padChar, width - slen);
-    }
-    sh_util_stream_send(self, str, slen);
-    if (slen < width && padLeft) {
-      char padChar = padZero ? '0' : ' ';
-      sh_util_stream_send_repeat(self, padChar, width - slen);
-    }
-  }
-}
-
-size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args) {
-  sh_util_stream_t stream;
-  sh_util_stream_init(&stream, buffer, buffer_size);
-  sh_util_stream_vformat(&stream, format, args);
-  return sh_util_stream_total(&stream);
-}
-
-size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...) {
-  va_list args;
-  va_start(args, format);
-  size_t buffer_len = sh_util_vsnprintf(buffer, buffer_size, format, args);
-  va_end(args);
-  return buffer_len;
-}
diff --git a/app/src/main/cpp/shadowhook/common/sh_util.h b/app/src/main/cpp/shadowhook/common/sh_util.h
deleted file mode 100644
index c8d0515..0000000
--- a/app/src/main/cpp/shadowhook/common/sh_util.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <android/api-level.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <time.h>
-
-#define SH_UTIL_ALIGN_START(x, align) ((uintptr_t)(x) & ~((uintptr_t)(align)-1))
-#define SH_UTIL_ALIGN_END(x, align)   (((uintptr_t)(x) + (uintptr_t)(align)-1) & ~((uintptr_t)(align)-1))
-
-#define SH_UTIL_PAGE_START(x) SH_UTIL_ALIGN_START(x, 0x1000)
-#define SH_UTIL_PAGE_END(x)   SH_UTIL_ALIGN_END(x, 0x1000)
-
-#define SH_UTIL_IS_THUMB(addr)   ((addr)&1u)
-#define SH_UTIL_CLEAR_BIT0(addr) ((addr)&0xFFFFFFFE)
-#define SH_UTIL_SET_BIT0(addr)   ((addr) | 1u)
-
-#define SH_UTIL_ALIGN_4(pc) ((pc)&0xFFFFFFFC)
-#define SH_UTIL_SIGN_EXTEND_32(n, len) \
-  ((SH_UTIL_GET_BIT_32(n, len - 1) > 0) ? ((n) | (0xFFFFFFFF << (len))) : n)
-#define SH_UTIL_SIGN_EXTEND_64(n, len) \
-  ((SH_UTIL_GET_BIT_64(n, len - 1) > 0) ? ((n) | (0xFFFFFFFFFFFFFFFF << (len))) : n)
-
-#define SH_UTIL_GET_BIT_16(n, idx)        ((uint16_t)((n) << (15u - (idx))) >> 15u)
-#define SH_UTIL_GET_BITS_16(n, high, low) ((uint16_t)((n) << (15u - (high))) >> (15u - (high) + (low)))
-#define SH_UTIL_GET_BIT_32(n, idx)        ((uint32_t)((n) << (31u - (idx))) >> 31u)
-#define SH_UTIL_GET_BITS_32(n, high, low) ((uint32_t)((n) << (31u - (high))) >> (31u - (high) + (low)))
-#define SH_UTIL_GET_BIT_64(n, idx)        ((uint64_t)((n) << (63u - (idx))) >> 63u)
-#define SH_UTIL_GET_BITS_64(n, high, low) ((uint64_t)((n) << (63u - (high))) >> (63u - (high) + (low)))
-
-#define SH_UTIL_TEMP_FAILURE_RETRY(exp)    \
-  ({                                       \
-    __typeof__(exp) _rc;                   \
-    do {                                   \
-      errno = 0;                           \
-      _rc = (exp);                         \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc;                                   \
-  })
-
-#define SH_UTIL_MAX(a, b)   \
-  ({                        \
-    __typeof__(a) _a = (a); \
-    __typeof__(b) _b = (b); \
-    _a > _b ? _a : _b;      \
-  })
-
-#define SH_UTIL_MIN(a, b)   \
-  ({                        \
-    __typeof__(a) _a = (a); \
-    __typeof__(b) _b = (b); \
-    _a < _b ? _a : _b;      \
-  })
-
-int sh_util_mprotect(uintptr_t addr, size_t len, int prot);
-void sh_util_clear_cache(uintptr_t addr, size_t len);
-
-bool sh_util_is_thumb32(uintptr_t target_addr);
-
-uint32_t sh_util_arm_expand_imm(uint32_t opcode);
-
-int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len);
-
-int sh_util_get_api_level(void);
-
-int sh_util_write(int fd, const char *buf, size_t buf_len);
-
-struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result);
-
-size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args);
-size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...);
diff --git a/app/src/main/cpp/shadowhook/include/shadowhook.h b/app/src/main/cpp/shadowhook/include/shadowhook.h
deleted file mode 100644
index 0aa7f1a..0000000
--- a/app/src/main/cpp/shadowhook/include/shadowhook.h
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// shadowhook is now credited to all its contributors.
-//
-// Before shadowhook was open sourced in February 2022, it was developed by
-// the following developers from ByteDance's App Health Team:
-//
-// Kelun Cai (caikelun@bytedance.com)
-// Pengying Xu (xupengying@bytedance.com)
-//
-
-#ifndef BYTEDANCE_SHADOWHOOK_H
-#define BYTEDANCE_SHADOWHOOK_H
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#define SHADOWHOOK_VERSION "1.0.8"
-
-#define SHADOWHOOK_ERRNO_OK                     0
-#define SHADOWHOOK_ERRNO_PENDING                1
-#define SHADOWHOOK_ERRNO_UNINIT                 2
-#define SHADOWHOOK_ERRNO_INVALID_ARG            3
-#define SHADOWHOOK_ERRNO_OOM                    4
-#define SHADOWHOOK_ERRNO_MPROT                  5
-#define SHADOWHOOK_ERRNO_WRITE_CRASH            6
-#define SHADOWHOOK_ERRNO_INIT_ERRNO             7
-#define SHADOWHOOK_ERRNO_INIT_SIGSEGV           8
-#define SHADOWHOOK_ERRNO_INIT_SIGBUS            9
-#define SHADOWHOOK_ERRNO_INIT_ENTER             10
-#define SHADOWHOOK_ERRNO_INIT_SAFE              11
-#define SHADOWHOOK_ERRNO_INIT_LINKER            12
-#define SHADOWHOOK_ERRNO_INIT_HUB               13
-#define SHADOWHOOK_ERRNO_HUB_CREAT              14
-#define SHADOWHOOK_ERRNO_MONITOR_DLOPEN         15
-#define SHADOWHOOK_ERRNO_MONITOR_THREAD         16
-#define SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH      17
-#define SHADOWHOOK_ERRNO_HOOK_DLSYM             18
-#define SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH       19
-#define SHADOWHOOK_ERRNO_HOOK_DUP               20
-#define SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH      21
-#define SHADOWHOOK_ERRNO_HOOK_DLINFO            22
-#define SHADOWHOOK_ERRNO_HOOK_SYMSZ             23
-#define SHADOWHOOK_ERRNO_HOOK_ENTER             24
-#define SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH     25
-#define SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED    26
-#define SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND        27
-#define SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH       28
-#define SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH 29
-#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH   30
-#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH      31
-#define SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR        32
-#define SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED   33
-#define SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH      34
-#define SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH   35
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-const char *shadowhook_get_version(void);
-
-// init
-typedef enum {
-  SHADOWHOOK_MODE_SHARED = 0,  // a function can be hooked multiple times
-  SHADOWHOOK_MODE_UNIQUE = 1   // a function can only be hooked once, and hooking again will report an error
-} shadowhook_mode_t;
-int shadowhook_init(shadowhook_mode_t mode, bool debuggable);
-int shadowhook_get_init_errno(void);
-
-// get and set attributes
-#define SHADOWHOOK_IS_SHARED_MODE (SHADOWHOOK_MODE_SHARED == shadowhook_get_mode())
-#define SHADOWHOOK_IS_UNIQUE_MODE (SHADOWHOOK_MODE_UNIQUE == shadowhook_get_mode())
-shadowhook_mode_t shadowhook_get_mode(void);
-bool shadowhook_get_debuggable(void);
-void shadowhook_set_debuggable(bool debuggable);
-bool shadowhook_get_recordable(void);
-void shadowhook_set_recordable(bool recordable);
-
-// get error-number and error message
-int shadowhook_get_errno(void);
-const char *shadowhook_to_errmsg(int error_number);
-
-// hook and unhook
-typedef void (*shadowhook_hooked_t)(int error_number, const char *lib_name, const char *sym_name,
-                                    void *sym_addr, void *new_addr, void *orig_addr, void *arg);
-void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr);
-void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr);
-void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr);
-void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr,
-                                        void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg);
-int shadowhook_unhook(void *stub);
-
-// get operation records
-#define SHADOWHOOK_RECORD_ITEM_ALL             0x3FF  // 0b1111111111
-#define SHADOWHOOK_RECORD_ITEM_TIMESTAMP       (1 << 0)
-#define SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1)
-#define SHADOWHOOK_RECORD_ITEM_OP              (1 << 2)
-#define SHADOWHOOK_RECORD_ITEM_LIB_NAME        (1 << 3)
-#define SHADOWHOOK_RECORD_ITEM_SYM_NAME        (1 << 4)
-#define SHADOWHOOK_RECORD_ITEM_SYM_ADDR        (1 << 5)
-#define SHADOWHOOK_RECORD_ITEM_NEW_ADDR        (1 << 6)
-#define SHADOWHOOK_RECORD_ITEM_BACKUP_LEN      (1 << 7)
-#define SHADOWHOOK_RECORD_ITEM_ERRNO           (1 << 8)
-#define SHADOWHOOK_RECORD_ITEM_STUB            (1 << 9)
-char *shadowhook_get_records(uint32_t item_flags);
-void shadowhook_dump_records(int fd, uint32_t item_flags);
-
-// helper functions for "get symbol-address from library-name and symbol-name"
-void *shadowhook_dlopen(const char *lib_name);
-void shadowhook_dlclose(void *handle);
-void *shadowhook_dlsym(void *handle, const char *sym_name);
-void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name);
-void *shadowhook_dlsym_symtab(void *handle, const char *sym_name);
-
-// for internal use
-void *shadowhook_get_prev_func(void *func);
-void shadowhook_pop_stack(void *return_address);
-void shadowhook_allow_reentrant(void *return_address);
-void shadowhook_disallow_reentrant(void *return_address);
-void *shadowhook_get_return_address(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-// call previous function in proxy-function
-#ifdef __cplusplus
-#define SHADOWHOOK_CALL_PREV(func, ...) \
-  ((decltype(&(func)))shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
-#else
-#define SHADOWHOOK_CALL_PREV(func, func_sig, ...) \
-  ((func_sig)shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
-#endif
-// pop stack in proxy-function (for C/C++)
-#define SHADOWHOOK_POP_STACK()                                                        \
-  do {                                                                                \
-    if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(__builtin_return_address(0)); \
-  } while (0)
-
-// pop stack in proxy-function (for C++ only)
-#ifdef __cplusplus
-class ShadowhookStackScope {
- public:
-  ShadowhookStackScope(void *return_address) : return_address_(return_address) {}
-  ~ShadowhookStackScope() {
-    if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(return_address_);
-  }
-
- private:
-  void *return_address_;
-};
-#define SHADOWHOOK_STACK_SCOPE() ShadowhookStackScope shadowhook_stack_scope_obj(__builtin_return_address(0))
-#endif
-
-// allow reentrant of the current proxy-function
-#define SHADOWHOOK_ALLOW_REENTRANT()                                                        \
-  do {                                                                                      \
-    if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_allow_reentrant(__builtin_return_address(0)); \
-  } while (0)
-
-// disallow reentrant of the current proxy-function
-#define SHADOWHOOK_DISALLOW_REENTRANT()                                                        \
-  do {                                                                                         \
-    if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_disallow_reentrant(__builtin_return_address(0)); \
-  } while (0)
-
-// get return address in proxy-function
-#define SHADOWHOOK_RETURN_ADDRESS() \
-  ((void *)(SHADOWHOOK_IS_SHARED_MODE ? shadowhook_get_return_address() : __builtin_return_address(0)))
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/sh_enter.c b/app/src/main/cpp/shadowhook/sh_enter.c
deleted file mode 100644
index f30b811..0000000
--- a/app/src/main/cpp/shadowhook/sh_enter.c
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_enter.h"
-
-#include <stdint.h>
-
-#include "sh_trampo.h"
-
-#define SH_ENTER_PAGE_NAME "shadowhook-enter"
-#define SH_ENTER_SZ        256
-#define SH_ENTER_DELAY_SEC 10
-
-static sh_trampo_mgr_t sh_enter_trampo_mgr;
-
-int sh_enter_init(void) {
-  sh_trampo_init_mgr(&sh_enter_trampo_mgr, SH_ENTER_PAGE_NAME, SH_ENTER_SZ, SH_ENTER_DELAY_SEC);
-  return 0;
-}
-
-uintptr_t sh_enter_alloc(void) {
-  return sh_trampo_alloc(&sh_enter_trampo_mgr, 0, 0, 0);
-}
-
-void sh_enter_free(uintptr_t enter) {
-  sh_trampo_free(&sh_enter_trampo_mgr, enter);
-}
diff --git a/app/src/main/cpp/shadowhook/sh_enter.h b/app/src/main/cpp/shadowhook/sh_enter.h
deleted file mode 100644
index 89ba8e5..0000000
--- a/app/src/main/cpp/shadowhook/sh_enter.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdint.h>
-
-int sh_enter_init(void);
-
-uintptr_t sh_enter_alloc(void);
-void sh_enter_free(uintptr_t enter);
diff --git a/app/src/main/cpp/shadowhook/sh_exit.c b/app/src/main/cpp/shadowhook/sh_exit.c
deleted file mode 100644
index 2ae905d..0000000
--- a/app/src/main/cpp/shadowhook/sh_exit.c
+++ /dev/null
@@ -1,420 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_exit.h"
-
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#include "sh_config.h"
-#include "sh_log.h"
-#include "sh_sig.h"
-#include "sh_trampo.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "xdl.h"
-
-#define SH_EXIT_PAGE_NAME "shadowhook-exit"
-#if defined(__arm__)
-#define SH_EXIT_SZ 8
-#elif defined(__aarch64__)
-#define SH_EXIT_SZ 16
-#endif
-#define SH_EXIT_DELAY_SEC 2
-#define SH_EXIT_GAPS_CAP  16
-
-// Used to identify whether the ELF gap has been zero-filled,
-// and also serves as a guard instruction.
-#if defined(__arm__)
-#define SH_EXIT_CLEAR_FLAG 0xE1200070BE00BE00  // BKPT #0(thumb) + BKPT #0(thumb) + BKPT #0(arm)
-#elif defined(__aarch64__)
-#define SH_EXIT_CLEAR_FLAG 0xD4200000D4200000  // BRK #0 + BRK #0
-#endif
-
-#define SH_EXIT_TYPE_OUT_LIBRARY 0
-#define SH_EXIT_TYPE_IN_LIBRARY  1
-
-extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
-
-static pthread_mutex_t sh_exit_lock = PTHREAD_MUTEX_INITIALIZER;
-static sh_trampo_mgr_t sh_exit_trampo_mgr;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct {
-  uintptr_t load_bias;
-  const ElfW(Phdr) *dlpi_phdr;
-  ElfW(Half) dlpi_phnum;
-} sh_exit_elfinfo_t;
-#pragma clang diagnostic pop
-
-static sh_exit_elfinfo_t sh_exit_app_process_info;
-static sh_exit_elfinfo_t sh_exit_linker_info;
-static sh_exit_elfinfo_t sh_exit_vdso_info;  // vdso may not exist
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-
-static void sh_exit_init_elfinfo(unsigned long type, sh_exit_elfinfo_t *info) {
-  if (__predict_false(NULL == getauxval)) goto err;
-
-  uintptr_t val = (uintptr_t)getauxval(type);
-  if (__predict_false(0 == val)) goto err;
-
-  // get base
-  uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val);
-  if (__predict_false(0 != memcmp((void *)base, ELFMAG, SELFMAG))) goto err;
-
-  // ELF info
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
-  const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
-  ElfW(Half) dlpi_phnum = ehdr->e_phnum;
-
-  // get load_bias
-  uintptr_t min_vaddr = UINTPTR_MAX;
-  for (size_t i = 0; i < dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type) {
-      if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
-    }
-  }
-  if (__predict_false(UINTPTR_MAX == min_vaddr || base < min_vaddr)) goto err;
-  uintptr_t load_bias = base - min_vaddr;
-
-  info->load_bias = load_bias;
-  info->dlpi_phdr = dlpi_phdr;
-  info->dlpi_phnum = dlpi_phnum;
-  return;
-
-err:
-  info->load_bias = 0;
-  info->dlpi_phdr = NULL;
-  info->dlpi_phnum = 0;
-}
-
-void sh_exit_init(void) {
-  // init for out-library mode
-  sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC);
-
-  // init for in-library mode
-  sh_exit_init_elfinfo(AT_PHDR, &sh_exit_app_process_info);
-  sh_exit_init_elfinfo(AT_BASE, &sh_exit_linker_info);
-  sh_exit_init_elfinfo(AT_SYSINFO_EHDR, &sh_exit_vdso_info);
-}
-
-// out-library mode:
-//
-// We store the shellcode for exit in mmaped memory near the PC.
-//
-
-static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
-                                     size_t exit_len, size_t range_low, size_t range_high) {
-  (void)dlinfo;
-
-  uintptr_t addr = sh_trampo_alloc(&sh_exit_trampo_mgr, pc, range_low, range_high);
-  if (0 == addr) return -1;
-
-  memcpy((void *)addr, exit, exit_len);
-  sh_util_clear_cache(addr, exit_len);
-  *exit_addr = addr;
-  return 0;
-}
-
-static void sh_exit_free_out_library(uintptr_t exit_addr) {
-  sh_trampo_free(&sh_exit_trampo_mgr, exit_addr);
-}
-
-// in-library mode:
-//
-// We store the shellcode for exit in the memory gaps in the ELF.
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct {
-  uintptr_t start;
-  uintptr_t end;
-  bool need_fill_zero;
-  bool readable;
-} sh_exit_gap_t;
-#pragma clang diagnostic pop
-
-static size_t sh_exit_get_gaps(xdl_info_t *dlinfo, sh_exit_gap_t *gaps, size_t gaps_cap,
-                               bool elf_loaded_by_kernel) {
-  size_t gaps_used = 0;
-
-  for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) {
-    // current LOAD segment
-    const ElfW(Phdr) *cur_phdr = &(dlinfo->dlpi_phdr[i]);
-    if (PT_LOAD != cur_phdr->p_type) continue;
-
-    // next LOAD segment
-    const ElfW(Phdr) *next_phdr = NULL;
-    if (!elf_loaded_by_kernel) {
-      for (size_t j = i + 1; j < dlinfo->dlpi_phnum; j++) {
-        if (PT_LOAD == dlinfo->dlpi_phdr[j].p_type) {
-          next_phdr = &(dlinfo->dlpi_phdr[j]);
-          break;
-        }
-      }
-    }
-
-    uintptr_t cur_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_memsz;
-    uintptr_t cur_page_end = SH_UTIL_PAGE_END(cur_end);
-    uintptr_t cur_file_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_filesz;
-    uintptr_t cur_file_page_end = SH_UTIL_PAGE_END(cur_file_end);
-    uintptr_t next_page_start =
-        (NULL == next_phdr ? cur_page_end
-                           : SH_UTIL_PAGE_START((uintptr_t)dlinfo->dli_fbase + next_phdr->p_vaddr));
-
-    sh_exit_gap_t gap = {0, 0, false, false};
-    if (cur_phdr->p_flags & PF_X) {
-      // From: last PF_X page's unused memory tail space.
-      // To: next page start.
-      gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10);
-      gap.end = next_page_start;
-      gap.need_fill_zero = true;
-      gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start);
-    } else if (cur_page_end > cur_file_page_end) {
-      // From: last .bss page(which must NOT be file backend)'s unused memory tail space.
-      // To: next page start.
-      gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10);
-      gap.end = next_page_start;
-      gap.need_fill_zero = false;
-      gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start);
-    } else if (next_page_start > cur_page_end) {
-      // Entire unused memory pages.
-      gap.start = cur_page_end;
-      gap.end = next_page_start;
-      gap.need_fill_zero = true;
-      gap.readable = false;
-    }
-
-    if ((gap.need_fill_zero && gap.end > gap.start + 0x10) || (!gap.need_fill_zero && gap.end > gap.start)) {
-      SH_LOG_INFO("exit: gap, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR " - %" PRIxPTR
-                  "), NFZ %d, READABLE %d",
-                  gap.start, gap.end, (uintptr_t)dlinfo->dli_fbase, gap.start - (uintptr_t)dlinfo->dli_fbase,
-                  gap.end - (uintptr_t)dlinfo->dli_fbase, gap.need_fill_zero ? 1 : 0, gap.readable ? 1 : 0);
-      gaps[gaps_used].start = gap.start;
-      gaps[gaps_used].end = gap.end;
-      gaps[gaps_used].need_fill_zero = gap.need_fill_zero;
-      gaps[gaps_used].readable = gap.readable;
-      gaps_used++;
-    }
-
-    if (gaps_used >= gaps_cap) break;
-  }
-
-  return gaps_used;
-}
-
-static bool sh_exit_is_in_elf_range(uintptr_t pc, sh_exit_elfinfo_t *info) {
-  if (pc < info->load_bias) return false;
-
-  uintptr_t vaddr = pc - info->load_bias;
-  for (size_t i = 0; i < info->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
-    if (PT_LOAD != phdr->p_type) continue;
-
-    if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true;
-  }
-
-  return false;
-}
-
-static bool sh_exit_is_elf_loaded_by_kernel(uintptr_t pc) {
-  if (NULL == sh_exit_app_process_info.dlpi_phdr) return true;
-  if (sh_exit_is_in_elf_range(pc, &sh_exit_app_process_info)) return true;
-
-  if (NULL == sh_exit_linker_info.dlpi_phdr) return true;
-  if (sh_exit_is_in_elf_range(pc, &sh_exit_linker_info)) return true;
-
-  // vdso may not exist
-  if (NULL != sh_exit_vdso_info.dlpi_phdr)
-    if (sh_exit_is_in_elf_range(pc, &sh_exit_vdso_info)) return true;
-
-  return false;
-}
-
-static bool sh_exit_is_zero(uintptr_t buf, size_t buf_len) {
-  for (uintptr_t i = buf; i < buf + buf_len; i += sizeof(uintptr_t))
-    if (*((uintptr_t *)i) != 0) return false;
-
-  return true;
-}
-
-static int sh_exit_fill_zero(uintptr_t start, uintptr_t end, bool readable, xdl_info_t *dlinfo) {
-  size_t size = end - start;
-  bool set_prot_rwx = false;
-
-  if (!readable) {
-    if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
-    set_prot_rwx = true;
-  }
-
-  if (*((uint64_t *)start) != SH_EXIT_CLEAR_FLAG) {
-    SH_LOG_INFO("exit: gap fill zero, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR
-                " - %" PRIxPTR "), READABLE %d",
-                start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase,
-                end - (uintptr_t)dlinfo->dli_fbase, readable ? 1 : 0);
-    if (!set_prot_rwx)
-      if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
-    memset((void *)start, 0, size);
-    *((uint64_t *)(start)) = SH_EXIT_CLEAR_FLAG;
-    sh_util_clear_cache(start, size);
-  }
-
-  return 0;
-}
-
-static int sh_exit_try_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
-                                        size_t exit_len, size_t range_low, size_t range_high, uintptr_t start,
-                                        uintptr_t end) {
-  if (pc >= range_low) start = SH_UTIL_MAX(start, pc - range_low);
-  start = SH_UTIL_ALIGN_END(start, exit_len);
-
-  if (range_high <= UINTPTR_MAX - pc) end = SH_UTIL_MIN(end - exit_len, pc + range_high);
-  end = SH_UTIL_ALIGN_START(end, exit_len);
-
-  if (end < start) return -1;
-  SH_LOG_INFO("exit: gap resize, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR
-              " - %" PRIxPTR ")",
-              start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase,
-              end - (uintptr_t)dlinfo->dli_fbase);
-
-  for (uintptr_t cur = start; cur <= end; cur += exit_len) {
-    // check if the current space has been used
-    if (!sh_exit_is_zero(cur, exit_len)) continue;
-
-    // write shellcode to the current location
-    if (0 != sh_util_mprotect(cur, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
-    memcpy((void *)cur, exit, exit_len);
-    sh_util_clear_cache(cur, exit_len);
-    *exit_addr = cur;
-    SH_LOG_INFO("exit: in-library alloc, at %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR "), len %zu",
-                cur, (uintptr_t)dlinfo->dli_fbase, cur - (uintptr_t)dlinfo->dli_fbase, exit_len);
-    return 0;
-  }
-  return -1;
-}
-
-static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
-                                    size_t exit_len, size_t range_low, size_t range_high) {
-  int r = -1;
-  *exit_addr = 0;
-
-  bool elf_loaded_by_kernel = sh_exit_is_elf_loaded_by_kernel(pc);
-
-  pthread_mutex_lock(&sh_exit_lock);
-
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    sh_exit_gap_t gaps[SH_EXIT_GAPS_CAP];
-    size_t gaps_used = sh_exit_get_gaps(dlinfo, gaps, SH_EXIT_GAPS_CAP, elf_loaded_by_kernel);
-    for (size_t i = 0; i < gaps_used; i++) {
-      // fill zero
-      if (gaps[i].need_fill_zero) {
-        if (0 != sh_exit_fill_zero(gaps[i].start, gaps[i].end, gaps[i].readable, dlinfo)) return -1;
-      }
-
-      if (0 == (r = sh_exit_try_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high,
-                                                 gaps[i].start, gaps[i].end)))
-        break;
-    }
-  }
-  SH_SIG_CATCH() {
-    r = -1;
-    *exit_addr = 0;
-    SH_LOG_WARN("exit: alloc crashed");
-  }
-  SH_SIG_EXIT
-
-  pthread_mutex_unlock(&sh_exit_lock);
-  return r;
-}
-
-static int sh_exit_free_in_library(uintptr_t exit_addr, uint8_t *exit, size_t exit_len) {
-  int r;
-
-  pthread_mutex_lock(&sh_exit_lock);
-
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    if (0 != memcmp((void *)exit_addr, exit, exit_len)) {
-      r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH;
-      goto err;
-    }
-    if (0 != sh_util_mprotect((uintptr_t)exit_addr, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
-      r = SHADOWHOOK_ERRNO_MPROT;
-      goto err;
-    }
-    memset((void *)exit_addr, 0, exit_len);
-    sh_util_clear_cache((uintptr_t)exit_addr, exit_len);
-    r = 0;
-  err:;
-  }
-  SH_SIG_CATCH() {
-    r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH;
-    SH_LOG_WARN("exit: free crashed");
-  }
-  SH_SIG_EXIT
-
-  pthread_mutex_unlock(&sh_exit_lock);
-  return r;
-}
-
-int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
-                  size_t exit_len, size_t range_low, size_t range_high) {
-  int r;
-
-  // (1) try out-library mode first. Because ELF gaps are a valuable non-renewable resource.
-  *exit_type = SH_EXIT_TYPE_OUT_LIBRARY;
-  r = sh_exit_alloc_out_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high);
-  if (0 == r) goto ok;
-
-  // (2) try in-library mode.
-  *exit_type = SH_EXIT_TYPE_IN_LIBRARY;
-  r = sh_exit_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high);
-  if (0 == r) goto ok;
-
-  return r;
-
-ok:
-  SH_LOG_INFO("exit: alloc %s library, exit %" PRIxPTR ", pc %" PRIxPTR ", distance %" PRIxPTR
-              ", range [-%zx, %zx]",
-              (*exit_type == SH_EXIT_TYPE_OUT_LIBRARY ? "out" : "in"), *exit_addr, pc,
-              (pc > *exit_addr ? pc - *exit_addr : *exit_addr - pc), range_low, range_high);
-  return 0;
-}
-
-int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len) {
-  if (SH_EXIT_TYPE_OUT_LIBRARY == exit_type) {
-    (void)exit, (void)exit_len;
-    sh_exit_free_out_library(exit_addr);
-    return 0;
-  } else
-    return sh_exit_free_in_library(exit_addr, exit, exit_len);
-}
-
-#pragma clang diagnostic pop
diff --git a/app/src/main/cpp/shadowhook/sh_exit.h b/app/src/main/cpp/shadowhook/sh_exit.h
deleted file mode 100644
index e0f46b6..0000000
--- a/app/src/main/cpp/shadowhook/sh_exit.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stddef.h>
-#include <stdint.h>
-
-#include "xdl.h"
-
-void sh_exit_init(void);
-
-int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
-                  size_t exit_len, size_t range_low, size_t range_high);
-int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len);
diff --git a/app/src/main/cpp/shadowhook/sh_hub.c b/app/src/main/cpp/shadowhook/sh_hub.c
deleted file mode 100644
index eef9a34..0000000
--- a/app/src/main/cpp/shadowhook/sh_hub.c
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_hub.h"
-
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-
-#include "queue.h"
-#include "sh_log.h"
-#include "sh_safe.h"
-#include "sh_sig.h"
-#include "sh_trampo.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "tree.h"
-
-#define SH_HUB_TRAMPO_PAGE_NAME "shadowhook-hub-trampo"
-#define SH_HUB_TRAMPO_DELAY_SEC 5
-#define SH_HUB_STACK_NAME       "shadowhook-hub-stack"
-#define SH_HUB_STACK_SIZE       4096
-#define SH_HUB_STACK_FRAME_MAX  127  // keep sizeof(sh_hub_stack_t) < 4K
-#define SH_HUB_THREAD_MAX       1024
-#define SH_HUB_DELAY_SEC        10
-
-#define SH_HUB_FRAME_FLAG_NONE            ((uintptr_t)0)
-#define SH_HUB_FRAME_FLAG_ALLOW_REENTRANT ((uintptr_t)(1 << 0))
-
-// proxy for each hook-task in the same target-address
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct sh_hub_proxy {
-  void *func;
-  bool enabled;
-  SLIST_ENTRY(sh_hub_proxy, ) link;
-} sh_hub_proxy_t;
-#pragma clang diagnostic pop
-
-// proxy list for each hub
-typedef SLIST_HEAD(sh_hub_proxy_list, sh_hub_proxy, ) sh_hub_proxy_list_t;
-
-// frame in the stack
-typedef struct {
-  sh_hub_proxy_list_t proxies;
-  uintptr_t orig_addr;
-  void *return_address;
-  uintptr_t flags;
-} sh_hub_frame_t;
-
-// stack for each thread
-typedef struct {
-  size_t frames_cnt;
-  sh_hub_frame_t frames[SH_HUB_STACK_FRAME_MAX];
-} sh_hub_stack_t;
-
-// hub for each target-address
-struct sh_hub {
-  sh_hub_proxy_list_t proxies;
-  pthread_mutex_t proxies_lock;
-  uintptr_t orig_addr;
-  uintptr_t trampo;
-  time_t destroy_ts;
-  LIST_ENTRY(sh_hub, ) link;
-};
-
-// hub list for delayed-destroy staging
-typedef LIST_HEAD(sh_hub_list, sh_hub, ) sh_hub_list_t;
-
-// global data for hub delayed-destroy staging
-static sh_hub_list_t sh_hub_delayed_destroy;
-static pthread_mutex_t sh_hub_delayed_destroy_lock;
-
-// global data for trampo
-static sh_trampo_mgr_t sh_hub_trampo_mgr;
-
-// global data for stack
-static pthread_key_t sh_hub_stack_tls_key;
-static sh_hub_stack_t *sh_hub_stack_cache;
-static uint8_t *sh_hub_stack_cache_used;
-
-// hub trampoline template
-extern void *sh_hub_trampo_template_data __attribute__((visibility("hidden")));
-__attribute__((naked)) static void sh_hub_trampo_template(void) {
-#if defined(__arm__)
-  __asm__(
-      // Save parameter registers, LR
-      "push  { r0 - r3, lr }     \n"
-
-      // Call sh_hub_push_stack()
-      "ldr   r0, hub_ptr         \n"
-      "mov   r1, lr              \n"
-      "ldr   ip, push_stack      \n"
-      "blx   ip                  \n"
-
-      // Save the hook function's address to IP register
-      "mov   ip, r0              \n"
-
-      // Restore parameter registers, LR
-      "pop   { r0 - r3, lr }     \n"
-
-      // Call hook function
-      "bx    ip                  \n"
-
-      "sh_hub_trampo_template_data:"
-      ".global sh_hub_trampo_template_data;"
-      "push_stack:"
-      ".word 0;"
-      "hub_ptr:"
-      ".word 0;");
-#elif defined(__aarch64__)
-  __asm__(
-      // Save parameter registers, XR(X8), LR
-      "stp   x0, x1, [sp, #-0xd0]!    \n"
-      "stp   x2, x3, [sp, #0x10]      \n"
-      "stp   x4, x5, [sp, #0x20]      \n"
-      "stp   x6, x7, [sp, #0x30]      \n"
-      "stp   x8, lr, [sp, #0x40]      \n"
-      "stp   q0, q1, [sp, #0x50]      \n"
-      "stp   q2, q3, [sp, #0x70]      \n"
-      "stp   q4, q5, [sp, #0x90]      \n"
-      "stp   q6, q7, [sp, #0xb0]      \n"
-
-      // Call sh_hub_push_stack()
-      "ldr   x0, hub_ptr              \n"
-      "mov   x1, lr                   \n"
-      "ldr   x16, push_stack          \n"
-      "blr   x16                      \n"
-
-      // Save the hook function's address to IP register
-      "mov   x16, x0                  \n"
-
-      // Restore parameter registers, XR(X8), LR
-      "ldp   q6, q7, [sp, #0xb0]      \n"
-      "ldp   q4, q5, [sp, #0x90]      \n"
-      "ldp   q2, q3, [sp, #0x70]      \n"
-      "ldp   q0, q1, [sp, #0x50]      \n"
-      "ldp   x8, lr, [sp, #0x40]      \n"
-      "ldp   x6, x7, [sp, #0x30]      \n"
-      "ldp   x4, x5, [sp, #0x20]      \n"
-      "ldp   x2, x3, [sp, #0x10]      \n"
-      "ldp   x0, x1, [sp], #0xd0      \n"
-
-      // Call hook function
-      "br    x16                      \n"
-
-      "sh_hub_trampo_template_data:"
-      ".global sh_hub_trampo_template_data;"
-      "push_stack:"
-      ".quad 0;"
-      "hub_ptr:"
-      ".quad 0;");
-#endif
-}
-
-static void *sh_hub_trampo_template_start(void) {
-#if defined(__arm__) && defined(__thumb__)
-  return (void *)((uintptr_t)&sh_hub_trampo_template - 1);
-#else
-  return (void *)&sh_hub_trampo_template;
-#endif
-}
-
-static sh_hub_stack_t *sh_hub_stack_create(void) {
-  // get stack from global cache
-  for (size_t i = 0; i < SH_HUB_THREAD_MAX; i++) {
-    uint8_t *used = &(sh_hub_stack_cache_used[i]);
-    if (0 == *used) {
-      uint8_t expected = 0;
-      if (__atomic_compare_exchange_n(used, &expected, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
-        sh_hub_stack_t *stack = &(sh_hub_stack_cache[i]);
-        stack->frames_cnt = 0;
-        SH_LOG_DEBUG("hub: get stack from global cache[%zu] %p", i, (void *)stack);
-        return stack;  // OK
-      }
-    }
-  }
-
-  // create new stack by mmap
-  int prot = PROT_READ | PROT_WRITE;
-  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
-  void *buf = sh_safe_mmap(NULL, SH_HUB_STACK_SIZE, prot, flags, -1, 0);
-  if (MAP_FAILED == buf) return NULL;  // failed
-  sh_safe_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)buf, SH_HUB_STACK_SIZE,
-                (unsigned long)SH_HUB_STACK_NAME);
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)buf;
-  stack->frames_cnt = 0;
-  return stack;  // OK
-}
-
-static void sh_hub_stack_destroy(void *buf) {
-  if (NULL == buf) return;
-
-  if ((uintptr_t)sh_hub_stack_cache <= (uintptr_t)buf &&
-      (uintptr_t)buf < ((uintptr_t)sh_hub_stack_cache + SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t))) {
-    // return stack to global cache
-    size_t i = ((uintptr_t)buf - (uintptr_t)sh_hub_stack_cache) / sizeof(sh_hub_stack_t);
-    uint8_t *used = &(sh_hub_stack_cache_used[i]);
-    if (1 != *used) abort();
-    __atomic_store_n(used, 0, __ATOMIC_RELEASE);
-    SH_LOG_DEBUG("hub: return stack to global cache[%zu] %p", i, buf);
-  } else {
-    // munmap stack
-    munmap(buf, SH_HUB_STACK_SIZE);
-  }
-}
-
-int sh_hub_init(void) {
-  LIST_INIT(&sh_hub_delayed_destroy);
-  pthread_mutex_init(&sh_hub_delayed_destroy_lock, NULL);
-
-  // init TLS key
-  if (__predict_false(0 != pthread_key_create(&sh_hub_stack_tls_key, sh_hub_stack_destroy))) return -1;
-
-  // init hub's stack cache
-  if (__predict_false(NULL == (sh_hub_stack_cache = malloc(SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t)))))
-    return -1;
-  if (__predict_false(NULL == (sh_hub_stack_cache_used = calloc(SH_HUB_THREAD_MAX, sizeof(uint8_t)))))
-    return -1;
-
-  // init hub's trampoline manager
-  size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start());
-  size_t data_size = sizeof(void *) + sizeof(void *);
-  sh_trampo_init_mgr(&sh_hub_trampo_mgr, SH_HUB_TRAMPO_PAGE_NAME, code_size + data_size,
-                     SH_HUB_TRAMPO_DELAY_SEC);
-
-  return 0;
-}
-
-static void *sh_hub_push_stack(sh_hub_t *self, void *return_address) {
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
-
-  // create stack, only once
-  if (__predict_false(NULL == stack)) {
-    if (__predict_false(NULL == (stack = sh_hub_stack_create()))) goto end;
-    sh_safe_pthread_setspecific(sh_hub_stack_tls_key, (void *)stack);
-  }
-
-  // check whether a recursive call occurred
-  bool recursive = false;
-  for (size_t i = stack->frames_cnt; i > 0; i--) {
-    sh_hub_frame_t *frame = &stack->frames[i - 1];
-    if (0 == (frame->flags & SH_HUB_FRAME_FLAG_ALLOW_REENTRANT) && (frame->orig_addr == self->orig_addr)) {
-      // recursive call found
-      recursive = true;
-      break;
-    }
-  }
-
-  // find and return the first enabled proxy's function in the proxy-list
-  // (does not include the original function)
-  if (!recursive) {
-    sh_hub_proxy_t *proxy;
-    SLIST_FOREACH(proxy, &self->proxies, link) {
-      if (proxy->enabled) {
-        // push a new frame for the current proxy
-        if (stack->frames_cnt >= SH_HUB_STACK_FRAME_MAX) goto end;
-        stack->frames_cnt++;
-        SH_LOG_DEBUG("hub: frames_cnt++ = %zu", stack->frames_cnt);
-        sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
-        frame->proxies = self->proxies;
-        frame->orig_addr = self->orig_addr;
-        frame->return_address = return_address;
-        frame->flags = SH_HUB_FRAME_FLAG_NONE;
-
-        // return the first enabled proxy's function
-        SH_LOG_DEBUG("hub: push_stack() return first enabled proxy %p", proxy->func);
-        return proxy->func;
-      }
-    }
-  }
-
-  // if not found enabled proxy in the proxy-list, or recursive call found,
-  // just return the original-function
-end:
-  SH_LOG_DEBUG("hub: push_stack() return orig_addr %p", (void *)self->orig_addr);
-  return (void *)self->orig_addr;
-}
-
-void sh_hub_pop_stack(void *return_address) {
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
-  if (0 == stack->frames_cnt) return;
-  sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
-
-  // only the first proxy will actually execute pop-stack()
-  if (frame->return_address == return_address) {
-    stack->frames_cnt--;
-    SH_LOG_DEBUG("hub: frames_cnt-- = %zu", stack->frames_cnt);
-  }
-}
-
-sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo) {
-  size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start());
-  size_t data_size = sizeof(void *) + sizeof(void *);
-
-  sh_hub_t *self = malloc(sizeof(sh_hub_t));
-  if (NULL == self) return NULL;
-  SLIST_INIT(&self->proxies);
-  pthread_mutex_init(&self->proxies_lock, NULL);
-  self->orig_addr = 0;
-
-  // alloc memory for trampoline
-  if (0 == (self->trampo = sh_trampo_alloc(&sh_hub_trampo_mgr, 0, 0, 0))) {
-    free(self);
-    return NULL;
-  }
-
-  // fill in code
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    memcpy((void *)self->trampo, sh_hub_trampo_template_start(), code_size);
-  }
-  SH_SIG_CATCH() {
-    sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
-    free(self);
-    SH_LOG_WARN("hub: fill in code crashed");
-    return NULL;
-  }
-  SH_SIG_EXIT
-
-  // file in data
-  void **data = (void **)(self->trampo + code_size);
-  *data++ = (void *)sh_hub_push_stack;
-  *data = (void *)self;
-
-  // clear CPU cache
-  sh_util_clear_cache(self->trampo, code_size + data_size);
-
-#if defined(__arm__) && defined(__thumb__)
-  *trampo = self->trampo + 1;
-#else
-  *trampo = self->trampo;
-#endif
-
-  SH_LOG_INFO("hub: create trampo for target_addr %" PRIxPTR " at %" PRIxPTR ", size %zu + %zu = %zu",
-              target_addr, *trampo, code_size, data_size, code_size + data_size);
-  return self;
-}
-
-static void sh_hub_destroy_inner(sh_hub_t *self) {
-  pthread_mutex_destroy(&self->proxies_lock);
-
-  if (0 != self->trampo) sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
-
-  while (!SLIST_EMPTY(&self->proxies)) {
-    sh_hub_proxy_t *proxy = SLIST_FIRST(&self->proxies);
-    SLIST_REMOVE_HEAD(&self->proxies, link);
-    free(proxy);
-  }
-
-  free(self);
-}
-
-void sh_hub_destroy(sh_hub_t *self, bool with_delay) {
-  if (SHADOWHOOK_IS_SHARED_MODE) {
-    struct timeval now;
-    gettimeofday(&now, NULL);
-
-    if (!LIST_EMPTY(&sh_hub_delayed_destroy)) {
-      pthread_mutex_lock(&sh_hub_delayed_destroy_lock);
-      sh_hub_t *hub, *hub_tmp;
-      LIST_FOREACH_SAFE(hub, &sh_hub_delayed_destroy, link, hub_tmp)
-      if (now.tv_sec - hub->destroy_ts > SH_HUB_DELAY_SEC) {
-        LIST_REMOVE(hub, link);
-        sh_hub_destroy_inner(hub);
-      }
-      pthread_mutex_unlock(&sh_hub_delayed_destroy_lock);
-    }
-
-    if (with_delay) {
-      self->destroy_ts = now.tv_sec;
-      sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
-      self->trampo = 0;
-
-      pthread_mutex_lock(&sh_hub_delayed_destroy_lock);
-      LIST_INSERT_HEAD(&sh_hub_delayed_destroy, self, link);
-      pthread_mutex_unlock(&sh_hub_delayed_destroy_lock);
-    } else
-      sh_hub_destroy_inner(self);
-  } else
-    sh_hub_destroy_inner(self);
-}
-
-uintptr_t sh_hub_get_orig_addr(sh_hub_t *self) {
-  return self->orig_addr;
-}
-
-uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self) {
-  return &self->orig_addr;
-}
-
-int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func) {
-  int r = SHADOWHOOK_ERRNO_OK;
-
-  pthread_mutex_lock(&self->proxies_lock);
-
-  // check repeated funcion
-  sh_hub_proxy_t *proxy;
-  SLIST_FOREACH(proxy, &self->proxies, link) {
-    if (proxy->enabled && proxy->func == (void *)func) {
-      r = SHADOWHOOK_ERRNO_HOOK_DUP;
-      goto end;
-    }
-  }
-
-  // try to re-enable an exists item
-  SLIST_FOREACH(proxy, &self->proxies, link) {
-    if (proxy->func == (void *)func) {
-      if (!proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, true, __ATOMIC_SEQ_CST);
-
-      SH_LOG_INFO("hub: add(re-enable) func %" PRIxPTR, func);
-      goto end;
-    }
-  }
-
-  // create new item
-  if (NULL == (proxy = malloc(sizeof(sh_hub_proxy_t)))) {
-    r = SHADOWHOOK_ERRNO_OOM;
-    goto end;
-  }
-  proxy->func = (void *)func;
-  proxy->enabled = true;
-
-  // insert to the head of the proxy-list
-  // equivalent to: SLIST_INSERT_HEAD(&self->proxies, proxy, link);
-  // but: __ATOMIC_RELEASE ensures readers see only fully-constructed item
-  SLIST_NEXT(proxy, link) = SLIST_FIRST(&self->proxies);
-  __atomic_store_n((uintptr_t *)(&SLIST_FIRST(&self->proxies)), (uintptr_t)proxy, __ATOMIC_RELEASE);
-  SH_LOG_INFO("hub: add(new) func %" PRIxPTR, func);
-
-end:
-  pthread_mutex_unlock(&self->proxies_lock);
-  return r;
-}
-
-int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy) {
-  *have_enabled_proxy = false;
-
-  pthread_mutex_lock(&self->proxies_lock);
-
-  sh_hub_proxy_t *proxy;
-  bool deleted = false;
-  SLIST_FOREACH(proxy, &self->proxies, link) {
-    if (proxy->func == (void *)func) {
-      if (proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, false, __ATOMIC_SEQ_CST);
-
-      deleted = true;
-      SH_LOG_INFO("hub: del func %" PRIxPTR, func);
-    }
-
-    if (proxy->enabled && !*have_enabled_proxy) *have_enabled_proxy = true;
-
-    if (deleted && *have_enabled_proxy) break;
-  }
-
-  pthread_mutex_unlock(&self->proxies_lock);
-
-  return deleted ? 0 : -1;
-}
-
-static sh_hub_frame_t *sh_hub_get_current_frame(void *return_address) {
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
-  if (0 == stack->frames_cnt) return NULL;
-  sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
-  return frame->return_address == return_address ? frame : NULL;
-}
-
-void sh_hub_allow_reentrant(void *return_address) {
-  sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address);
-  if (NULL != frame) {
-    frame->flags |= SH_HUB_FRAME_FLAG_ALLOW_REENTRANT;
-    SH_LOG_DEBUG("hub: allow reentrant frame %p", return_address);
-  }
-}
-
-void sh_hub_disallow_reentrant(void *return_address) {
-  sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address);
-  if (NULL != frame) {
-    frame->flags &= ~SH_HUB_FRAME_FLAG_ALLOW_REENTRANT;
-    SH_LOG_DEBUG("hub: disallow reentrant frame %p", return_address);
-  }
-}
-
-void *sh_hub_get_prev_func(void *func) {
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
-  if (0 == stack->frames_cnt) sh_safe_abort();  // called in a non-hook status?
-  sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
-
-  // find and return the next enabled hook-function in the hook-chain
-  bool found = false;
-  sh_hub_proxy_t *proxy;
-  SLIST_FOREACH(proxy, &(frame->proxies), link) {
-    if (!found) {
-      if (proxy->func == func) found = true;
-    } else {
-      if (proxy->enabled) break;
-    }
-  }
-  if (NULL != proxy) {
-    SH_LOG_DEBUG("hub: get_prev_func() return next enabled proxy %p", proxy->func);
-    return proxy->func;
-  }
-
-  SH_LOG_DEBUG("hub: get_prev_func() return orig_addr %p", (void *)frame->orig_addr);
-  // did not find, return the original-function
-  return (void *)frame->orig_addr;
-}
-
-void *sh_hub_get_return_address(void) {
-  sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
-  if (0 == stack->frames_cnt) sh_safe_abort();  // called in a non-hook status?
-  sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
-
-  return frame->return_address;
-}
diff --git a/app/src/main/cpp/shadowhook/sh_hub.h b/app/src/main/cpp/shadowhook/sh_hub.h
deleted file mode 100644
index accbfb5..0000000
--- a/app/src/main/cpp/shadowhook/sh_hub.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-int sh_hub_init(void);
-
-typedef struct sh_hub sh_hub_t;
-
-sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo);
-void sh_hub_destroy(sh_hub_t *self, bool with_delay);
-
-uintptr_t sh_hub_get_orig_addr(sh_hub_t *self);
-uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self);
-
-int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func);
-int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy);
-
-void *sh_hub_get_prev_func(void *func);
-void sh_hub_pop_stack(void *return_address);
-void sh_hub_allow_reentrant(void *return_address);
-void sh_hub_disallow_reentrant(void *return_address);
-void *sh_hub_get_return_address(void);
diff --git a/app/src/main/cpp/shadowhook/sh_jni.c b/app/src/main/cpp/shadowhook/sh_jni.c
deleted file mode 100644
index 051f6d9..0000000
--- a/app/src/main/cpp/shadowhook/sh_jni.c
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include <errno.h>
-#include <jni.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "sh_log.h"
-#include "shadowhook.h"
-
-#define SH_JNI_VERSION    JNI_VERSION_1_6
-#define SH_JNI_CLASS_NAME "com/bytedance/shadowhook/ShadowHook"
-
-static jstring sh_jni_get_version(JNIEnv *env, jobject thiz) {
-  (void)thiz;
-  return (*env)->NewStringUTF(env, shadowhook_get_version());
-}
-
-static jint sh_jni_init(JNIEnv *env, jobject thiz, jint mode, jboolean debuggable) {
-  (void)env, (void)thiz;
-
-  return shadowhook_init(0 == mode ? SHADOWHOOK_MODE_SHARED : SHADOWHOOK_MODE_UNIQUE, (bool)debuggable);
-}
-
-static jint sh_jni_get_init_errno(JNIEnv *env, jobject thiz) {
-  (void)env, (void)thiz;
-
-  return shadowhook_get_init_errno();
-}
-
-static jint sh_jni_get_mode(JNIEnv *env, jobject thiz) {
-  (void)env, (void)thiz;
-
-  return SHADOWHOOK_MODE_SHARED == shadowhook_get_mode() ? 0 : 1;
-}
-
-static jboolean sh_jni_get_debuggable(JNIEnv *env, jobject thiz) {
-  (void)env, (void)thiz;
-
-  return shadowhook_get_debuggable();
-}
-
-static void sh_jni_set_debuggable(JNIEnv *env, jobject thiz, jboolean debuggable) {
-  (void)env, (void)thiz;
-
-  shadowhook_set_debuggable((bool)debuggable);
-}
-
-static jboolean sh_jni_get_recordable(JNIEnv *env, jobject thiz) {
-  (void)env, (void)thiz;
-
-  return shadowhook_get_recordable();
-}
-
-static void sh_jni_set_recordable(JNIEnv *env, jobject thiz, jboolean recordable) {
-  (void)env, (void)thiz;
-
-  shadowhook_set_recordable((bool)recordable);
-}
-
-static jstring sh_jni_to_errmsg(JNIEnv *env, jobject thiz, jint error_number) {
-  (void)thiz;
-
-  return (*env)->NewStringUTF(env, shadowhook_to_errmsg(error_number));
-}
-
-static jstring sh_jni_get_records(JNIEnv *env, jobject thiz, jint item_flags) {
-  (void)thiz;
-
-  char *str = shadowhook_get_records((uint32_t)item_flags);
-  if (NULL == str) return NULL;
-
-  jstring jstr = (*env)->NewStringUTF(env, str);
-  free(str);
-  return jstr;
-}
-
-static jstring sh_jni_get_arch(JNIEnv *env, jobject thiz) {
-  (void)thiz;
-
-#if defined(__arm__)
-  char *arch = "arm";
-#elif defined(__aarch64__)
-  char *arch = "arm64";
-#else
-  char *arch = "unsupported";
-#endif
-
-  return (*env)->NewStringUTF(env, arch);
-}
-
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
-  (void)reserved;
-
-  if (__predict_false(NULL == vm)) return JNI_ERR;
-
-  JNIEnv *env;
-  if (__predict_false(JNI_OK != (*vm)->GetEnv(vm, (void **)&env, SH_JNI_VERSION))) return JNI_ERR;
-  if (__predict_false(NULL == env || NULL == *env)) return JNI_ERR;
-
-  jclass cls;
-  if (__predict_false(NULL == (cls = (*env)->FindClass(env, SH_JNI_CLASS_NAME)))) return JNI_ERR;
-
-  JNINativeMethod m[] = {{"nativeGetVersion", "()Ljava/lang/String;", (void *)sh_jni_get_version},
-                         {"nativeInit", "(IZ)I", (void *)sh_jni_init},
-                         {"nativeGetInitErrno", "()I", (void *)sh_jni_get_init_errno},
-                         {"nativeGetMode", "()I", (void *)sh_jni_get_mode},
-                         {"nativeGetDebuggable", "()Z", (void *)sh_jni_get_debuggable},
-                         {"nativeSetDebuggable", "(Z)V", (void *)sh_jni_set_debuggable},
-                         {"nativeGetRecordable", "()Z", (void *)sh_jni_get_recordable},
-                         {"nativeSetRecordable", "(Z)V", (void *)sh_jni_set_recordable},
-                         {"nativeToErrmsg", "(I)Ljava/lang/String;", (void *)sh_jni_to_errmsg},
-                         {"nativeGetRecords", "(I)Ljava/lang/String;", (void *)sh_jni_get_records},
-                         {"nativeGetArch", "()Ljava/lang/String;", (void *)sh_jni_get_arch}};
-  if (__predict_false(0 != (*env)->RegisterNatives(env, cls, m, sizeof(m) / sizeof(m[0])))) return JNI_ERR;
-
-  return SH_JNI_VERSION;
-}
diff --git a/app/src/main/cpp/shadowhook/sh_linker.c b/app/src/main/cpp/shadowhook/sh_linker.c
deleted file mode 100644
index 2cf3a2e..0000000
--- a/app/src/main/cpp/shadowhook/sh_linker.c
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_linker.h"
-
-#include <dlfcn.h>
-#include <pthread.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "sh_log.h"
-#include "sh_recorder.h"
-#include "sh_sig.h"
-#include "sh_switch.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "xdl.h"
-
-#ifndef __LP64__
-#define SH_LINKER_BASENAME "linker"
-#else
-#define SH_LINKER_BASENAME "linker64"
-#endif
-
-#define SH_LINKER_SYM_G_DL_MUTEX  "__dl__ZL10g_dl_mutex"
-#define SH_LINKER_SYM_DO_DLOPEN_L "__dl__Z9do_dlopenPKciPK17android_dlextinfo"
-#define SH_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv"
-#define SH_LINKER_SYM_DO_DLOPEN_O "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv"
-
-static bool sh_linker_dlopen_hooked = false;
-
-static sh_linker_post_dlopen_t sh_linker_post_dlopen;
-static void *sh_linker_post_dlopen_arg;
-
-static pthread_mutex_t *sh_linker_g_dl_mutex;
-static uintptr_t sh_linker_dlopen_addr;  // save address of dlopen(==4.x) or do_dlopen(>=5.0)
-static xdl_info_t sh_linker_dlopen_dlinfo;
-
-#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
-static uintptr_t sh_linker_dlfcn[6];
-static const char *sh_linker_dlfcn_name[6] = {"dlopen", "dlerror", "dlsym",
-                                              "dladdr", "dlclose", "dl_unwind_find_exidx"};
-#endif
-
-__attribute__((constructor)) static void sh_linker_ctor(void) {
-  sh_linker_dlopen_addr = (uintptr_t)dlopen;
-#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
-  sh_linker_dlfcn[0] = (uintptr_t)dlopen;
-  sh_linker_dlfcn[1] = (uintptr_t)dlerror;
-  sh_linker_dlfcn[2] = (uintptr_t)dlsym;
-  sh_linker_dlfcn[3] = (uintptr_t)dladdr;
-  sh_linker_dlfcn[4] = (uintptr_t)dlclose;
-  sh_linker_dlfcn[5] = (uintptr_t)dl_unwind_find_exidx;
-#endif
-}
-
-static void *sh_linker_get_base_addr(xdl_info_t *dlinfo) {
-  uintptr_t vaddr_min = UINTPTR_MAX;
-  for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(dlinfo->dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type && vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr;
-  }
-
-  if (UINTPTR_MAX == vaddr_min)
-    return dlinfo->dli_fbase;  // should not happen
-  else
-    return (void *)((uintptr_t)dlinfo->dli_fbase + SH_UTIL_PAGE_START(vaddr_min));
-}
-
-static bool sh_linker_check_arch(xdl_info_t *dlinfo) {
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)sh_linker_get_base_addr(dlinfo);
-
-#if defined(__LP64__)
-#define SH_LINKER_ELF_CLASS   ELFCLASS64
-#define SH_LINKER_ELF_MACHINE EM_AARCH64
-#else
-#define SH_LINKER_ELF_CLASS   ELFCLASS32
-#define SH_LINKER_ELF_MACHINE EM_ARM
-#endif
-
-  if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return false;
-  if (SH_LINKER_ELF_CLASS != ehdr->e_ident[EI_CLASS]) return false;
-  if (SH_LINKER_ELF_MACHINE != ehdr->e_machine) return false;
-
-  return true;
-}
-
-int sh_linker_init(void) {
-  memset(&sh_linker_dlopen_dlinfo, 0, sizeof(sh_linker_dlopen_dlinfo));
-
-  int api_level = sh_util_get_api_level();
-  if (__predict_true(api_level >= __ANDROID_API_L__)) {
-    sh_linker_dlopen_addr = 0;
-
-    void *handle = xdl_open(SH_LINKER_BASENAME, XDL_DEFAULT);
-    if (__predict_false(NULL == handle)) return -1;
-    xdl_info(handle, XDL_DI_DLINFO, (void *)&sh_linker_dlopen_dlinfo);
-    sh_linker_dlopen_dlinfo.dli_fname = SH_LINKER_BASENAME;
-
-    // get g_dl_mutex
-    sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX, NULL));
-
-    // get do_dlopen
-    if (api_level >= __ANDROID_API_O__)
-      sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_O;
-    else if (api_level >= __ANDROID_API_N__)
-      sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_N;
-    else
-      sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_L;
-    sh_linker_dlopen_dlinfo.dli_saddr =
-        xdl_dsym(handle, sh_linker_dlopen_dlinfo.dli_sname, &(sh_linker_dlopen_dlinfo.dli_ssize));
-    sh_linker_dlopen_addr = (uintptr_t)sh_linker_dlopen_dlinfo.dli_saddr;
-
-    xdl_close(handle);
-  }
-
-  return (0 != sh_linker_dlopen_addr && (0 != sh_linker_g_dl_mutex || api_level < __ANDROID_API_L__)) ? 0
-                                                                                                      : -1;
-}
-
-const char *sh_linker_match_dlfcn(uintptr_t target_addr) {
-#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
-  if (sh_util_get_api_level() < __ANDROID_API_L__)
-    for (size_t i = 0; i < sizeof(sh_linker_dlfcn) / sizeof(sh_linker_dlfcn[0]); i++)
-      if (sh_linker_dlfcn[i] == target_addr) return sh_linker_dlfcn_name[i];
-#else
-  (void)target_addr;
-#endif
-
-  return NULL;
-}
-
-bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr) {
-  return SHADOWHOOK_IS_UNIQUE_MODE && !sh_linker_dlopen_hooked && target_addr == sh_linker_dlopen_addr;
-}
-
-typedef void *(*sh_linker_proxy_dlopen_t)(const char *, int);
-static sh_linker_proxy_dlopen_t sh_linker_orig_dlopen;
-static void *sh_linker_proxy_dlopen(const char *filename, int flag) {
-  void *handle;
-  if (SHADOWHOOK_IS_SHARED_MODE)
-    handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_dlopen, sh_linker_proxy_dlopen_t, filename, flag);
-  else
-    handle = sh_linker_orig_dlopen(filename, flag);
-
-  if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
-
-  if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
-  return handle;
-}
-
-typedef void *(*sh_linker_proxy_do_dlopen_l_t)(const char *, int, const void *);
-static sh_linker_proxy_do_dlopen_l_t sh_linker_orig_do_dlopen_l;
-static void *sh_linker_proxy_do_dlopen_l(const char *name, int flags, const void *extinfo) {
-  void *handle;
-  if (SHADOWHOOK_IS_SHARED_MODE)
-    handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_l, sh_linker_proxy_do_dlopen_l_t, name, flags,
-                                  extinfo);
-  else
-    handle = sh_linker_orig_do_dlopen_l(name, flags, extinfo);
-
-  if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
-
-  if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
-  return handle;
-}
-
-typedef void *(*sh_linker_proxy_do_dlopen_n_t)(const char *, int, const void *, void *);
-static sh_linker_proxy_do_dlopen_n_t sh_linker_orig_do_dlopen_n;
-static void *sh_linker_proxy_do_dlopen_n(const char *name, int flags, const void *extinfo,
-                                         void *caller_addr) {
-  void *handle;
-  if (SHADOWHOOK_IS_SHARED_MODE)
-    handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_n, sh_linker_proxy_do_dlopen_n_t, name, flags,
-                                  extinfo, caller_addr);
-  else
-    handle = sh_linker_orig_do_dlopen_n(name, flags, extinfo, caller_addr);
-
-  if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
-
-  if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
-  return handle;
-}
-
-int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg) {
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  static int result = SHADOWHOOK_ERRNO_MONITOR_DLOPEN;
-
-  if (sh_linker_dlopen_hooked) return result;
-  pthread_mutex_lock(&lock);
-  if (sh_linker_dlopen_hooked) goto end;
-
-  // try hooking-dlopen only once
-  sh_linker_dlopen_hooked = true;
-
-  // do init for SHARED mode
-  if (SHADOWHOOK_IS_SHARED_MODE)
-    if (0 != sh_linker_init()) goto end;
-
-  // save post callback ptr before hooking
-  sh_linker_post_dlopen = post_dlopen;
-  sh_linker_post_dlopen_arg = post_dlopen_arg;
-
-  // hook for dlopen() or do_dlopen()
-  int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *) =
-      SHADOWHOOK_IS_SHARED_MODE ? sh_switch_hook : sh_switch_hook_invisible;
-  int api_level = sh_util_get_api_level();
-  size_t backup_len = 0;
-  int r;
-  if (api_level < __ANDROID_API_L__) {
-    // get & check dlinfo
-    r = sh_linker_get_dlinfo_by_addr((void *)sh_linker_dlopen_addr, &sh_linker_dlopen_dlinfo, NULL, 0, NULL,
-                                     0, false);
-    if (SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH == r) result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH;
-    if (0 != r) goto end;
-
-    // hook
-    r = hook(sh_linker_dlopen_addr, (uintptr_t)sh_linker_proxy_dlopen, (uintptr_t *)&sh_linker_orig_dlopen,
-             &backup_len, &sh_linker_dlopen_dlinfo);
-
-    // record
-    sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME, "dlopen",
-                         (uintptr_t)sh_linker_proxy_dlopen, backup_len, UINTPTR_MAX,
-                         (uintptr_t)(__builtin_return_address(0)));
-
-    if (0 != r) goto end;
-  } else {
-    // check dlinfo
-    if (!sh_linker_check_arch(&sh_linker_dlopen_dlinfo)) {
-      result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH;
-      goto end;
-    }
-
-    uintptr_t proxy;
-    uintptr_t *orig;
-    if (api_level >= __ANDROID_API_N__) {
-      proxy = (uintptr_t)sh_linker_proxy_do_dlopen_n;
-      orig = (uintptr_t *)&sh_linker_orig_do_dlopen_n;
-    } else {
-      proxy = (uintptr_t)sh_linker_proxy_do_dlopen_l;
-      orig = (uintptr_t *)&sh_linker_orig_do_dlopen_l;
-    }
-
-    // hook
-    pthread_mutex_lock(sh_linker_g_dl_mutex);
-    r = hook(sh_linker_dlopen_addr, proxy, orig, &backup_len, &sh_linker_dlopen_dlinfo);
-    pthread_mutex_unlock(sh_linker_g_dl_mutex);
-
-    // record
-    sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME,
-                         sh_linker_dlopen_dlinfo.dli_sname, proxy, backup_len, UINTPTR_MAX,
-                         (uintptr_t)(__builtin_return_address(0)));
-
-    if (0 != r) goto end;
-  }
-
-  // OK
-  result = 0;
-
-end:
-  pthread_mutex_unlock(&lock);
-  SH_LOG_INFO("linker: hook dlopen %s, return: %d", 0 == result ? "OK" : "FAILED", result);
-  return result;
-}
-
-int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
-                                 char *sym_name, size_t sym_name_sz, bool ignore_symbol_check) {
-  // dladdr()
-  bool crashed = false;
-  void *dlcache = NULL;
-  int r = 0;
-  if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-    r = xdl_addr((void *)addr, dlinfo, &dlcache);
-  } else {
-    SH_SIG_TRY(SIGSEGV, SIGBUS) {
-      r = xdl_addr((void *)addr, dlinfo, &dlcache);
-    }
-    SH_SIG_CATCH() {
-      crashed = true;
-    }
-    SH_SIG_EXIT
-  }
-  SH_LOG_INFO("task: get dlinfo by target addr: target_addr %p, sym_name %s, sym_sz %zu, load_bias %" PRIxPTR
-              ", pathname %s",
-              addr, NULL == dlinfo->dli_sname ? "(NULL)" : dlinfo->dli_sname, dlinfo->dli_ssize,
-              (uintptr_t)dlinfo->dli_fbase, NULL == dlinfo->dli_fname ? "(NULL)" : dlinfo->dli_fname);
-
-  // check error
-  if (crashed) {
-    r = SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH;
-    goto end;
-  }
-  if (0 == r || NULL == dlinfo->dli_fname) {
-    r = SHADOWHOOK_ERRNO_HOOK_DLINFO;
-    goto end;
-  }
-  if (!sh_linker_check_arch(dlinfo)) {
-    r = SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH;
-    goto end;
-  }
-
-  if (NULL == dlinfo->dli_sname) {
-    if (ignore_symbol_check) {
-      dlinfo->dli_saddr = addr;
-      dlinfo->dli_sname = "unknown";
-      dlinfo->dli_ssize = 1024;  // big enough
-    } else {
-      const char *matched_dlfcn_name = NULL;
-      if (NULL == (matched_dlfcn_name = sh_linker_match_dlfcn((uintptr_t)addr))) {
-        r = SHADOWHOOK_ERRNO_HOOK_DLINFO;
-        goto end;
-      } else {
-        dlinfo->dli_saddr = addr;
-        dlinfo->dli_sname = matched_dlfcn_name;
-        dlinfo->dli_ssize = 4;  // safe length, only relative jumps are allowed
-        SH_LOG_INFO("task: match dlfcn, target_addr %p, sym_name %s", addr, matched_dlfcn_name);
-      }
-    }
-  }
-  if (0 == dlinfo->dli_ssize) {
-    r = SHADOWHOOK_ERRNO_HOOK_SYMSZ;
-    goto end;
-  }
-
-  if (NULL != lib_name) strlcpy(lib_name, dlinfo->dli_fname, lib_name_sz);
-  if (NULL != sym_name) strlcpy(sym_name, dlinfo->dli_sname, sym_name_sz);
-  r = 0;
-
-end:
-  xdl_addr_clean(&dlcache);
-  return r;
-}
-
-int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo,
-                                     char *real_lib_name, size_t real_lib_name_sz) {
-  // open library
-  bool crashed = false;
-  void *handle = NULL;
-  if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-    handle = xdl_open(lib_name, XDL_DEFAULT);
-  } else {
-    SH_SIG_TRY(SIGSEGV, SIGBUS) {
-      handle = xdl_open(lib_name, XDL_DEFAULT);
-    }
-    SH_SIG_CATCH() {
-      crashed = true;
-    }
-    SH_SIG_EXIT
-  }
-  if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH;
-  if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING;
-
-  // get dlinfo
-  xdl_info(handle, XDL_DI_DLINFO, (void *)dlinfo);
-
-  // check error
-  if (!sh_linker_check_arch(dlinfo)) {
-    xdl_close(handle);
-    return SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH;
-  }
-
-  // lookup symbol address
-  crashed = false;
-  void *addr = NULL;
-  size_t sym_size = 0;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    // do xdl_sym() or xdl_dsym() in an dlclosed-ELF will cause a crash
-    addr = xdl_sym(handle, sym_name, &sym_size);
-    if (NULL == addr) addr = xdl_dsym(handle, sym_name, &sym_size);
-  }
-  SH_SIG_CATCH() {
-    crashed = true;
-  }
-  SH_SIG_EXIT
-
-  // close library
-  xdl_close(handle);
-
-  if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH;
-  if (NULL == addr) return SHADOWHOOK_ERRNO_HOOK_DLSYM;
-
-  dlinfo->dli_fname = lib_name;
-  dlinfo->dli_sname = sym_name;
-  dlinfo->dli_saddr = addr;
-  dlinfo->dli_ssize = sym_size;
-  if (NULL != real_lib_name) strlcpy(real_lib_name, dlinfo->dli_fname, real_lib_name_sz);
-  return 0;
-}
diff --git a/app/src/main/cpp/shadowhook/sh_linker.h b/app/src/main/cpp/shadowhook/sh_linker.h
deleted file mode 100644
index 749e0a6..0000000
--- a/app/src/main/cpp/shadowhook/sh_linker.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "xdl.h"
-
-int sh_linker_init(void);
-
-const char *sh_linker_match_dlfcn(uintptr_t target_addr);
-bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr);
-
-typedef void (*sh_linker_post_dlopen_t)(void *arg);
-int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg);
-
-int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
-                                 char *sym_name, size_t sym_name_sz, bool ignore_symbol_check);
-int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo,
-                                     char *real_lib_name, size_t real_lib_name_sz);
diff --git a/app/src/main/cpp/shadowhook/sh_recorder.c b/app/src/main/cpp/shadowhook/sh_recorder.c
deleted file mode 100644
index 22cf39d..0000000
--- a/app/src/main/cpp/shadowhook/sh_recorder.c
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_recorder.h"
-
-#include "sh_config.h"
-
-#ifdef SH_CONFIG_OPERATION_RECORDS
-
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "sh_sig.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "xdl.h"
-
-#define SH_RECORDER_OP_HOOK_SYM_ADDR 0
-#define SH_RECORDER_OP_HOOK_SYM_NAME 1
-#define SH_RECORDER_OP_UNHOOK        2
-
-#define SH_RECORDER_LIB_NAME_MAX 512
-#define SH_RECORDER_SYM_NAME_MAX 1024
-
-#define SH_RECORDER_STRINGS_BUF_EXPAND_STEP (1024 * 16)
-#define SH_RECORDER_STRINGS_BUF_MAX         (1024 * 128)
-#define SH_RECORDER_RECORDS_BUF_EXPAND_STEP (1024 * 32)
-#define SH_RECORDER_RECORDS_BUF_MAX         (1024 * 384)
-#define SH_RECORDER_OUTPUT_BUF_EXPAND_STEP  (1024 * 128)
-#define SH_RECORDER_OUTPUT_BUF_MAX          (1024 * 1024)
-
-static bool sh_recorder_recordable = false;
-
-bool sh_recorder_get_recordable(void) {
-  return sh_recorder_recordable;
-}
-
-void sh_recorder_set_recordable(bool recordable) {
-  sh_recorder_recordable = recordable;
-}
-
-typedef struct {
-  void *ptr;
-  size_t cap;
-  size_t sz;
-  pthread_mutex_t lock;
-} sh_recorder_buf_t;
-
-static int sh_recorder_buf_append(sh_recorder_buf_t *buf, size_t step, size_t max, const void *header,
-                                  size_t header_sz, const void *body, size_t body_sz) {
-  size_t needs = (header_sz + (NULL != body ? body_sz : 0));
-  if (needs > step) return -1;
-
-  if (buf->cap - buf->sz < needs) {
-    size_t new_cap = buf->cap + step;
-    if (new_cap > max) return -1;
-    void *new_ptr = realloc(buf->ptr, new_cap);
-    if (NULL == new_ptr) return -1;
-    buf->ptr = new_ptr;
-    buf->cap = new_cap;
-  }
-
-  memcpy((void *)((uintptr_t)buf->ptr + buf->sz), header, header_sz);
-  if (NULL != body) memcpy((void *)((uintptr_t)buf->ptr + buf->sz + header_sz), body, body_sz);
-  buf->sz += needs;
-  return 0;
-}
-
-static void sh_recorder_buf_free(sh_recorder_buf_t *buf) {
-  if (NULL != buf->ptr) {
-    free(buf->ptr);
-    buf->ptr = NULL;
-  }
-}
-
-static sh_recorder_buf_t sh_recorder_strings = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
-static sh_recorder_buf_t sh_recorder_records = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
-static bool sh_recorder_error = false;
-
-typedef struct {
-  uint16_t str_len;  // body length, in order to speed up the search
-} __attribute__((packed)) sh_recorder_str_header_t;
-// +body: string, including the terminating null byte ('\0')
-
-typedef struct {
-  uint64_t op : 8;
-  uint64_t error_number : 8;
-  uint64_t ts_ms : 48;
-  uintptr_t stub;
-  uint16_t caller_lib_name_idx;
-  uint8_t backup_len;
-  uint16_t lib_name_idx;
-  uint16_t sym_name_idx;
-  uintptr_t sym_addr;
-  uintptr_t new_addr;
-} __attribute__((packed)) sh_recorder_record_hook_header_t;
-// no body
-
-typedef struct {
-  uint64_t op : 8;
-  uint64_t error_number : 8;
-  uint64_t ts_ms : 48;
-  uintptr_t stub;
-  uint16_t caller_lib_name_idx;
-} __attribute__((packed)) sh_recorder_record_unhook_header_t;
-// no body
-
-static int sh_recorder_add_str(const char *str, size_t str_len, uint16_t *str_idx) {
-  uint16_t idx = 0;
-  bool ok = false;
-
-  pthread_mutex_lock(&sh_recorder_strings.lock);
-
-  // find in existing strings
-  size_t i = 0;
-  while (i < sh_recorder_strings.sz) {
-    sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
-    if (header->str_len == str_len) {
-      void *tmp = (void *)((uintptr_t)sh_recorder_strings.ptr + i + sizeof(header->str_len));
-      if (0 == memcmp(tmp, str, str_len)) {
-        *str_idx = idx;
-        ok = true;
-        break;  // OK
-      }
-    }
-    i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1);
-    idx++;
-    if (idx == UINT16_MAX) break;  // failed
-  }
-
-  // insert a new string
-  if (!ok && idx < UINT16_MAX) {
-    // append new string
-    sh_recorder_str_header_t header = {(uint16_t)str_len};
-    if (0 == sh_recorder_buf_append(&sh_recorder_strings, SH_RECORDER_STRINGS_BUF_EXPAND_STEP,
-                                    SH_RECORDER_STRINGS_BUF_MAX, &header, sizeof(header), str, str_len + 1)) {
-      *str_idx = idx;
-      ok = true;  // OK
-    }
-  }
-
-  pthread_mutex_unlock(&sh_recorder_strings.lock);
-
-  return ok ? 0 : -1;
-}
-
-static char *sh_recorder_find_str(uint16_t idx) {
-  uint16_t cur_idx = 0;
-
-  size_t i = 0;
-  while (i < sh_recorder_strings.sz && cur_idx < idx) {
-    sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
-    i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1);
-    cur_idx++;
-  }
-  if (cur_idx != idx) return "error";
-
-  sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
-  return (char *)((uintptr_t)header + sizeof(sh_recorder_str_header_t));
-}
-
-static long sh_recorder_tz = LONG_MAX;
-
-static uint64_t sh_recorder_get_timestamp_ms(void) {
-  struct timeval tv;
-  gettimeofday(&tv, NULL);
-
-  if (LONG_MAX == sh_recorder_tz) {
-    // localtime_r() will call getenv() without lock protection,
-    // and will crash when encountering concurrent setenv() calls.
-    // We really encountered.
-    sh_recorder_tz = 0;
-    //    struct tm tm;
-    //    if (NULL != localtime_r((time_t *)(&(tv.tv_sec)), &tm)) sh_recorder_tz = tm.tm_gmtoff;
-  }
-
-  return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
-}
-
-static size_t sh_recorder_format_timestamp_ms(uint64_t ts_ms, char *buf, size_t buf_len) {
-  time_t sec = (time_t)(ts_ms / 1000);
-  time_t msec = (time_t)(ts_ms % 1000);
-
-  struct tm tm;
-  sh_util_localtime_r(&sec, sh_recorder_tz, &tm);
-
-  return sh_util_snprintf(buf, buf_len, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%c%02ld:%02ld,",
-                          tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
-                          msec, sh_recorder_tz < 0 ? '-' : '+', labs(sh_recorder_tz / 3600),
-                          labs(sh_recorder_tz % 3600));
-}
-
-static const char *sh_recorder_get_base_name(const char *lib_name) {
-  const char *p = strrchr(lib_name, '/');
-  if (NULL != p && '\0' != *(p + 1))
-    return p + 1;
-  else
-    return lib_name;
-}
-
-static int sh_recorder_get_base_name_by_addr_iterator(struct dl_phdr_info *info, size_t size, void *arg) {
-  (void)size;
-
-  uintptr_t *pkg = (uintptr_t *)arg;
-  uintptr_t addr = *pkg++;
-  char *base_name = (char *)*pkg++;
-  size_t base_name_sz = (size_t)*pkg;
-
-  for (size_t i = 0; i < info->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
-    if (PT_LOAD != phdr->p_type) continue;
-    if (addr < (uintptr_t)(info->dlpi_addr + phdr->p_vaddr) ||
-        addr >= (uintptr_t)(info->dlpi_addr + phdr->p_vaddr + phdr->p_memsz))
-      continue;
-
-    // get lib_name from path_name
-    const char *p;
-    if (NULL == info->dlpi_name || '\0' == info->dlpi_name[0])
-      p = "unknown";
-    else {
-      p = strrchr(info->dlpi_name, '/');
-      if (NULL == p || '\0' == *(p + 1))
-        p = info->dlpi_name;
-      else
-        p++;
-    }
-
-    strlcpy(base_name, p, base_name_sz);
-    return 1;  // OK
-  }
-
-  return 0;  // continue
-}
-
-static void sh_recorder_get_base_name_by_addr(uintptr_t addr, char *base_name, size_t base_name_sz) {
-  base_name[0] = '\0';
-  uintptr_t pkg[3] = {addr, (uintptr_t)base_name, (uintptr_t)base_name_sz};
-
-  if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-    xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT);
-  } else {
-    SH_SIG_TRY(SIGSEGV, SIGBUS) {
-      xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT);
-    }
-    SH_SIG_EXIT
-  }
-
-  if ('\0' == base_name[0]) strlcpy(base_name, "unknown", base_name_sz);
-}
-
-int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
-                         const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
-                         uintptr_t caller_addr) {
-  if (!sh_recorder_recordable) return 0;
-  if (sh_recorder_error) return -1;
-
-  // lib_name
-  if (NULL == lib_name) return -1;
-  lib_name = sh_recorder_get_base_name(lib_name);
-  size_t lib_name_len = strlen(lib_name);
-  if (0 == lib_name_len || lib_name_len > SH_RECORDER_LIB_NAME_MAX) return -1;
-
-  // sym_name
-  if (NULL == sym_name) return -1;
-  size_t sym_name_len = strlen(sym_name);
-  if (0 == sym_name_len || sym_name_len > SH_RECORDER_SYM_NAME_MAX) return -1;
-
-  // caller_lib_name
-  char caller_lib_name[SH_RECORDER_LIB_NAME_MAX];
-  sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name));
-  size_t caller_lib_name_len = strlen(caller_lib_name);
-
-  // add strings to strings-pool
-  uint16_t lib_name_idx, sym_name_idx, caller_lib_name_idx;
-  if (0 != sh_recorder_add_str(lib_name, lib_name_len, &lib_name_idx)) goto err;
-  if (0 != sh_recorder_add_str(sym_name, sym_name_len, &sym_name_idx)) goto err;
-  if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err;
-
-  // append new hook record
-  sh_recorder_record_hook_header_t header = {
-      is_hook_sym_addr ? SH_RECORDER_OP_HOOK_SYM_ADDR : SH_RECORDER_OP_HOOK_SYM_NAME,
-      (uint8_t)error_number,
-      sh_recorder_get_timestamp_ms(),
-      stub,
-      caller_lib_name_idx,
-      (uint8_t)backup_len,
-      lib_name_idx,
-      sym_name_idx,
-      sym_addr,
-      new_addr};
-  pthread_mutex_lock(&sh_recorder_records.lock);
-  int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP,
-                                 SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0);
-  pthread_mutex_unlock(&sh_recorder_records.lock);
-  if (0 != r) goto err;
-
-  return 0;
-
-err:
-  sh_recorder_error = true;
-  return -1;
-}
-
-int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) {
-  if (!sh_recorder_recordable) return 0;
-  if (sh_recorder_error) return -1;
-
-  char caller_lib_name[SH_RECORDER_LIB_NAME_MAX];
-  sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name));
-  size_t caller_lib_name_len = strlen(caller_lib_name);
-
-  uint16_t caller_lib_name_idx;
-  if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err;
-
-  sh_recorder_record_unhook_header_t header = {SH_RECORDER_OP_UNHOOK, (uint8_t)error_number,
-                                               sh_recorder_get_timestamp_ms(), stub, caller_lib_name_idx};
-  pthread_mutex_lock(&sh_recorder_records.lock);
-  int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP,
-                                 SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0);
-  pthread_mutex_unlock(&sh_recorder_records.lock);
-  if (0 != r) goto err;
-
-  return 0;
-
-err:
-  sh_recorder_error = true;
-  return -1;
-}
-
-static const char *sh_recorder_get_op_name(uint8_t op) {
-  switch (op) {
-    case SH_RECORDER_OP_HOOK_SYM_ADDR:
-      return "hook_sym_addr";
-    case SH_RECORDER_OP_HOOK_SYM_NAME:
-      return "hook_sym_name";
-    case SH_RECORDER_OP_UNHOOK:
-      return "unhook";
-    default:
-      return "error";
-  }
-}
-
-static void sh_recorder_output(char **str, int fd, uint32_t item_flags) {
-  if (NULL == sh_recorder_records.ptr || 0 == sh_recorder_records.sz) return;
-
-  sh_recorder_buf_t output = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
-
-  pthread_mutex_lock(&sh_recorder_records.lock);
-  pthread_mutex_lock(&sh_recorder_strings.lock);
-
-  char line[SH_RECORDER_LIB_NAME_MAX * 2 + SH_RECORDER_SYM_NAME_MAX + 256];
-  size_t line_sz;
-  size_t i = 0;
-  while (i < sh_recorder_records.sz) {
-    line_sz = 0;
-    sh_recorder_record_hook_header_t *header =
-        (sh_recorder_record_hook_header_t *)((uintptr_t)sh_recorder_records.ptr + i);
-
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP)
-      line_sz += sh_recorder_format_timestamp_ms(header->ts_ms, line + line_sz, sizeof(line) - line_sz);
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
-                                  sh_recorder_find_str(header->caller_lib_name_idx));
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_OP)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
-                                  sh_recorder_get_op_name(header->op));
-    if ((item_flags & SHADOWHOOK_RECORD_ITEM_LIB_NAME) && header->op != SH_RECORDER_OP_UNHOOK)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
-                                  sh_recorder_find_str(header->lib_name_idx));
-    if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_NAME) && header->op != SH_RECORDER_OP_UNHOOK)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
-                                  sh_recorder_find_str(header->sym_name_idx));
-    if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_ADDR) && header->op != SH_RECORDER_OP_UNHOOK)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->sym_addr);
-    if ((item_flags & SHADOWHOOK_RECORD_ITEM_NEW_ADDR) && header->op != SH_RECORDER_OP_UNHOOK)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->new_addr);
-    if ((item_flags & SHADOWHOOK_RECORD_ITEM_BACKUP_LEN) && header->op != SH_RECORDER_OP_UNHOOK)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->backup_len);
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_ERRNO)
-      line_sz +=
-          sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->error_number);
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_STUB)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->stub);
-    line[line_sz - 1] = '\n';
-
-    if (NULL != str) {
-      // append to string
-      if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
-                                      line, line_sz, NULL, 0)) {
-        sh_recorder_buf_free(&output);
-        break;  // failed
-      }
-    } else {
-      // write to FD
-      if (0 != sh_util_write(fd, line, line_sz)) break;  // failed
-    }
-
-    i += (SH_RECORDER_OP_UNHOOK == header->op ? sizeof(sh_recorder_record_unhook_header_t)
-                                              : sizeof(sh_recorder_record_hook_header_t));
-  }
-
-  pthread_mutex_unlock(&sh_recorder_strings.lock);
-  pthread_mutex_unlock(&sh_recorder_records.lock);
-
-  // error message
-  if (sh_recorder_error) {
-    line_sz = 0;
-
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "9999-99-99T00:00:00.000+00:00,");
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
-    if (item_flags & SHADOWHOOK_RECORD_ITEM_OP)
-      line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
-
-    if (0 == line_sz) line_sz = sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
-
-    line[line_sz - 1] = '\n';
-
-    if (NULL != str) {
-      // append to string
-      if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
-                                      line, line_sz, NULL, 0)) {
-        sh_recorder_buf_free(&output);
-        return;  // failed
-      }
-    } else {
-      // write to FD
-      if (0 != sh_util_write(fd, line, line_sz)) return;  // failed
-    }
-  }
-
-  // return string
-  if (NULL != str) {
-    if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
-                                    "", 1, NULL, 0)) {
-      sh_recorder_buf_free(&output);
-      return;  // failed
-    }
-    *str = output.ptr;
-  }
-}
-
-char *sh_recorder_get(uint32_t item_flags) {
-  if (!sh_recorder_recordable) return NULL;
-  if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return NULL;
-
-  char *str = NULL;
-  sh_recorder_output(&str, -1, item_flags);
-  return str;
-}
-
-void sh_recorder_dump(int fd, uint32_t item_flags) {
-  if (!sh_recorder_recordable) return;
-  if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return;
-  if (fd < 0) return;
-  sh_recorder_output(NULL, fd, item_flags);
-}
-
-#else
-
-bool sh_recorder_get_recordable(void) {
-  return false;
-}
-
-void sh_recorder_set_recordable(bool recordable) {
-  (void)recordable;
-}
-
-int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
-                         const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
-                         uintptr_t caller_addr) {
-  (void)error_number, (void)is_hook_sym_addr, (void)sym_addr, (void)lib_name, (void)sym_name, (void)new_addr,
-      (void)backup_len, (void)stub, (void)caller_addr;
-  return 0;
-}
-
-int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) {
-  (void)error_number, (void)stub, (void)caller_addr;
-  return 0;
-}
-
-char *sh_recorder_get(uint32_t item_flags) {
-  (void)item_flags;
-  return NULL;
-}
-
-void sh_recorder_dump(int fd, uint32_t item_flags) {
-  (void)fd, (void)item_flags;
-}
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/sh_recorder.h b/app/src/main/cpp/shadowhook/sh_recorder.h
deleted file mode 100644
index 91ce843..0000000
--- a/app/src/main/cpp/shadowhook/sh_recorder.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-bool sh_recorder_get_recordable(void);
-void sh_recorder_set_recordable(bool recordable);
-
-int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
-                         const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
-                         uintptr_t caller_addr);
-int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr);
-
-char *sh_recorder_get(uint32_t item_flags);
-void sh_recorder_dump(int fd, uint32_t item_flags);
diff --git a/app/src/main/cpp/shadowhook/sh_safe.c b/app/src/main/cpp/shadowhook/sh_safe.c
deleted file mode 100644
index eacc14d..0000000
--- a/app/src/main/cpp/shadowhook/sh_safe.c
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_safe.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "sh_util.h"
-#include "xdl.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wreserved-id-macro"
-#pragma clang diagnostic ignored "-Wvariadic-macros"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-#pragma clang diagnostic ignored "-Wsign-conversion"
-#pragma clang diagnostic ignored "-Wpacked"
-#pragma clang diagnostic ignored "-Wshorten-64-to-32"
-#include "linux_syscall_support.h"
-#pragma clang diagnostic pop
-
-#define SH_SAFE_IDX_PTHREAD_GETSPECIFIC 0
-#define SH_SAFE_IDX_PTHREAD_SETSPECIFIC 1
-#define SH_SAFE_IDX_ABORT               2
-
-#define SH_SAFE_IDX_SZ 3
-typedef struct {
-  uintptr_t target_addr;
-  uintptr_t orig_addr;
-} sh_safe_addr_t;
-static sh_safe_addr_t sh_safe_addrs[SH_SAFE_IDX_SZ];
-static int sh_safe_api_level;
-
-static int sh_safe_init_func(void *handle, const char *symbol, size_t idx) {
-  sh_safe_addrs[idx].target_addr = (uintptr_t)(xdl_sym(handle, symbol, NULL));
-  if (__predict_false(0 == sh_safe_addrs[idx].target_addr)) return -1;
-  sh_safe_addrs[idx].orig_addr = 0;
-  return 0;
-}
-
-int sh_safe_init(void) {
-  sh_safe_api_level = sh_util_get_api_level();
-
-  void *handle = xdl_open("libc.so", XDL_DEFAULT);
-  if (NULL == handle) return -1;
-
-  int r = -1;
-  if (__predict_false(0 != sh_safe_init_func(handle, "pthread_getspecific", SH_SAFE_IDX_PTHREAD_GETSPECIFIC)))
-    goto end;
-  if (__predict_false(0 != sh_safe_init_func(handle, "pthread_setspecific", SH_SAFE_IDX_PTHREAD_SETSPECIFIC)))
-    goto end;
-  if (__predict_false(0 != sh_safe_init_func(handle, "abort", SH_SAFE_IDX_ABORT))) goto end;
-  r = 0;
-
-end:
-  xdl_close(handle);
-  return r;
-}
-
-uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr) {
-  for (size_t i = 0; i < SH_SAFE_IDX_SZ; i++) {
-    if (sh_safe_addrs[i].target_addr == target_addr) {
-      return &sh_safe_addrs[i].orig_addr;
-    }
-  }
-  return NULL;
-}
-
-static uintptr_t sh_safe_get_orig_addr(size_t idx) {
-  sh_safe_addr_t *addr = &sh_safe_addrs[idx];
-  return 0 != addr->orig_addr ? addr->orig_addr : addr->target_addr;
-}
-
-void *sh_safe_pthread_getspecific(pthread_key_t key) {
-  uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_GETSPECIFIC);
-  return ((void *(*)(pthread_key_t))addr)(key);
-}
-
-int sh_safe_pthread_setspecific(pthread_key_t key, const void *value) {
-  if (sh_safe_api_level >= __ANDROID_API_M__) {
-    uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_SETSPECIFIC);
-    return ((int (*)(pthread_key_t, const void *))addr)(key, value);
-  } else {
-    // Before Android M, pthread_setspecific() will call pthread_mutex_lock() and
-    // pthread_mutex_unlock(). So if we use pthread_setspecific() in hub's trampo,
-    // we will NOT be able to hook pthread_mutex_lock() and pthread_mutex_unlock().
-    void **tls;
-#if defined(__aarch64__)
-    __asm__("mrs %0, tpidr_el0" : "=r"(tls));
-#elif defined(__arm__)
-    __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(tls));
-#endif
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-    tls[key] = (void *)value;
-#pragma clang diagnostic pop
-    return 0;
-  }
-}
-
-void sh_safe_abort(void) {
-  uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_ABORT);
-  ((void (*)(void))addr)();
-}
-
-void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
-  return sys_mmap(addr, length, prot, flags, fd, offset);
-}
-
-int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,
-                  unsigned long arg5) {
-  return sys_prctl(option, arg2, arg3, arg4, arg5);
-}
diff --git a/app/src/main/cpp/shadowhook/sh_safe.h b/app/src/main/cpp/shadowhook/sh_safe.h
deleted file mode 100644
index 5f353bd..0000000
--- a/app/src/main/cpp/shadowhook/sh_safe.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-int sh_safe_init(void);
-uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr);
-
-void *sh_safe_pthread_getspecific(pthread_key_t key);
-int sh_safe_pthread_setspecific(pthread_key_t key, const void *value);
-void sh_safe_abort(void);
-
-void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
-int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
diff --git a/app/src/main/cpp/shadowhook/sh_switch.c b/app/src/main/cpp/shadowhook/sh_switch.c
deleted file mode 100644
index 0eb7ba9..0000000
--- a/app/src/main/cpp/shadowhook/sh_switch.c
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_switch.h"
-
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "sh_config.h"
-#include "sh_errno.h"
-#include "sh_hub.h"
-#include "sh_inst.h"
-#include "sh_linker.h"
-#include "sh_log.h"
-#include "sh_safe.h"
-#include "sh_sig.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "tree.h"
-#include "xdl.h"
-
-// switch
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct sh_switch {
-  sh_inst_t inst;  // align 16
-  uintptr_t target_addr;
-  sh_hub_t *hub;
-  RB_ENTRY(sh_switch) link;
-} sh_switch_t;
-#pragma clang diagnostic pop
-
-// switch tree
-static __inline__ int sh_switch_cmp(sh_switch_t *a, sh_switch_t *b) {
-  if (a->target_addr == b->target_addr)
-    return 0;
-  else
-    return a->target_addr > b->target_addr ? 1 : -1;
-}
-typedef RB_HEAD(sh_switch_tree, sh_switch) sh_switch_tree_t;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-RB_GENERATE_STATIC(sh_switch_tree, sh_switch, link, sh_switch_cmp)
-#pragma clang diagnostic pop
-
-// switch tree object
-static sh_switch_tree_t sh_switches = RB_INITIALIZER(&sh_switches);
-static pthread_rwlock_t sh_switches_lock = PTHREAD_RWLOCK_INITIALIZER;
-
-static sh_switch_t *sh_switch_find(uintptr_t target_addr) {
-  sh_switch_t key = {.target_addr = target_addr};
-
-  pthread_rwlock_rdlock(&sh_switches_lock);
-  sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
-  pthread_rwlock_unlock(&sh_switches_lock);
-
-  return self;
-}
-
-static int sh_switch_create(sh_switch_t **self, uintptr_t target_addr, uintptr_t *hub_trampo) {
-  *self = memalign(16, sizeof(sh_switch_t));
-  if (NULL == *self) return SHADOWHOOK_ERRNO_OOM;
-
-  memset(&(*self)->inst, 0, sizeof((*self)->inst));
-  (*self)->target_addr = target_addr;
-  (*self)->hub = NULL;
-
-  if (NULL != hub_trampo) {
-    if (NULL == ((*self)->hub = sh_hub_create(target_addr, hub_trampo))) {
-      free(self);
-      return SHADOWHOOK_ERRNO_HUB_CREAT;
-    }
-  }
-
-  return 0;
-}
-
-static void sh_switch_destroy(sh_switch_t *self, bool hub_with_delay) {
-  if (NULL != self->hub) sh_hub_destroy(self->hub, hub_with_delay);
-  free(self);
-}
-
-static void sh_switch_dump_enter(sh_switch_t *self) {
-#ifdef SH_CONFIG_DEBUG
-  char buf[1024];
-  size_t len = 0;
-  size_t zero = 0;
-  for (size_t i = 0; i < 128; i++) {
-    uint16_t inst = ((uint16_t *)(self->inst.enter_addr))[i];
-    zero = (0 == inst ? zero + 1 : 0);
-    if (zero > 4) break;
-    len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%04" PRIx16 " ", inst);
-  }
-  SH_LOG_DEBUG("switch: enter < %s>", buf);
-#else
-  (void)self;
-#endif
-}
-
-static int sh_switch_hook_unique(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                                 size_t *backup_len, xdl_info_t *dlinfo) {
-  sh_switch_t *self = sh_switch_find(target_addr);
-  if (NULL != self) return SHADOWHOOK_ERRNO_HOOK_DUP;
-
-  // alloc new switch
-  int r;
-  if (0 != (r = sh_switch_create(&self, target_addr, NULL))) return r;
-
-  sh_switch_t *useless = NULL;
-  pthread_rwlock_wrlock(&sh_switches_lock);  // SYNC - start
-
-  // insert new switch to switch-tree
-  if (NULL != RB_INSERT(sh_switch_tree, &sh_switches, self)) {
-    useless = self;
-    r = SHADOWHOOK_ERRNO_HOOK_DUP;
-    goto end;
-  }
-
-  // do hook
-  if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, NULL))) {
-    RB_REMOVE(sh_switch_tree, &sh_switches, self);
-    useless = self;
-    goto end;
-  }
-  *backup_len = self->inst.backup_len;
-  sh_switch_dump_enter(self);
-
-end:
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC - end
-  if (NULL != useless) sh_switch_destroy(useless, false);
-  return r;
-}
-
-static int sh_switch_hook_shared(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                                 size_t *backup_len, xdl_info_t *dlinfo) {
-  int r;
-
-  pthread_rwlock_rdlock(&sh_switches_lock);  // SYNC(read) - start
-  sh_switch_t key = {.target_addr = target_addr};
-  sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
-  if (NULL != self)  // already exists
-  {
-    // add an new proxy to hub
-    if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub);
-    r = sh_hub_add_proxy(self->hub, new_addr);
-    pthread_rwlock_unlock(&sh_switches_lock);  // SYNC(read) - end
-
-    *backup_len = self->inst.backup_len;
-    return r;
-  }
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC(read) - end
-
-  // first hook for this target_addr
-
-  // alloc new switch
-  uintptr_t hub_trampo;
-  if (0 != (r = sh_switch_create(&self, target_addr, &hub_trampo))) return r;
-
-  sh_switch_t *useless = NULL;
-  pthread_rwlock_wrlock(&sh_switches_lock);  // SYNC - start
-
-  // insert new switch to switch-tree
-  sh_switch_t *exists;
-  if (NULL != (exists = RB_INSERT(sh_switch_tree, &sh_switches, self))) {
-    // already exists
-    useless = self;
-    if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(exists->hub);
-    r = sh_hub_add_proxy(exists->hub, new_addr);
-    *backup_len = exists->inst.backup_len;
-  } else {
-    // do hook
-    uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr);
-    if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, hub_trampo,
-                               sh_hub_get_orig_addr_addr(self->hub), safe_orig_addr_addr))) {
-      RB_REMOVE(sh_switch_tree, &sh_switches, self);
-      useless = self;
-      goto end;
-    }
-    *backup_len = self->inst.backup_len;
-    sh_switch_dump_enter(self);
-
-    // return original-address
-    if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub);
-
-    // add proxy to hub
-    if (0 != (r = sh_hub_add_proxy(self->hub, new_addr))) {
-      sh_inst_unhook(&self->inst, target_addr);
-      *backup_len = 0;
-      RB_REMOVE(sh_switch_tree, &sh_switches, self);
-      useless = self;
-      goto end;
-    }
-  }
-
-end:
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC - end
-  if (NULL != useless) sh_switch_destroy(useless, false);
-
-  return r;
-}
-
-int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
-                   xdl_info_t *dlinfo) {
-  int r;
-  if (SHADOWHOOK_IS_UNIQUE_MODE)
-    r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo);
-  else
-    r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
-
-  if (0 == r)
-    SH_LOG_INFO("switch: hook in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
-                SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr);
-
-  return r;
-}
-
-static int sh_switch_hook_unique_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                                           size_t *backup_len, xdl_info_t *dlinfo) {
-  pthread_rwlock_wrlock(&sh_switches_lock);  // SYNC - start
-
-  // do hook
-  sh_inst_t inst;
-  int r = sh_inst_hook(&inst, target_addr, dlinfo, new_addr, orig_addr, NULL);
-
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC - end
-
-  *backup_len = inst.backup_len;
-  return r;
-}
-
-int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                             size_t *backup_len, xdl_info_t *dlinfo) {
-  int r;
-  if (SHADOWHOOK_IS_UNIQUE_MODE)
-    r = sh_switch_hook_unique_invisible(target_addr, new_addr, orig_addr, backup_len, dlinfo);
-  else
-    r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
-
-  if (0 == r)
-    SH_LOG_INFO("switch: hook(invisible) in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
-                SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr);
-  return r;
-}
-
-static int sh_switch_unhook_unique(uintptr_t target_addr) {
-  int r;
-  sh_switch_t *useless = NULL;
-
-  pthread_rwlock_wrlock(&sh_switches_lock);  // SYNC - start
-
-  sh_switch_t key = {.target_addr = target_addr};
-  sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
-  if (NULL == self) {
-    r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
-    goto end;
-  }
-  r = sh_inst_unhook(&self->inst, target_addr);
-  RB_REMOVE(sh_switch_tree, &sh_switches, self);
-  useless = self;
-
-end:
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC - end
-  if (NULL != useless) sh_switch_destroy(useless, false);
-  return r;
-}
-
-static int sh_switch_unhook_shared(uintptr_t target_addr, uintptr_t new_addr) {
-  int r;
-  sh_switch_t *useless = NULL;
-
-  pthread_rwlock_wrlock(&sh_switches_lock);  // SYNC - start
-
-  sh_switch_t key = {.target_addr = target_addr};
-  sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
-  if (NULL == self) {
-    r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
-    goto end;
-  }
-
-  // delete proxy in hub
-  bool have_enabled_proxy;
-  if (0 != sh_hub_del_proxy(self->hub, new_addr, &have_enabled_proxy)) {
-    r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
-    goto end;
-  }
-  r = 0;
-
-  // unhook inst, remove current switch from switch-tree
-  if (!have_enabled_proxy) {
-    r = sh_inst_unhook(&self->inst, target_addr);
-
-    uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr);
-    if (NULL != safe_orig_addr_addr) __atomic_store_n(safe_orig_addr_addr, 0, __ATOMIC_SEQ_CST);
-
-    RB_REMOVE(sh_switch_tree, &sh_switches, self);
-    useless = self;
-  }
-
-end:
-  pthread_rwlock_unlock(&sh_switches_lock);  // SYNC - end
-  if (NULL != useless) sh_switch_destroy(useless, true);
-  return r;
-}
-
-int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr) {
-  int r;
-  if (SHADOWHOOK_IS_UNIQUE_MODE) {
-    r = sh_switch_unhook_unique(target_addr);
-    if (0 == r) SH_LOG_INFO("switch: unhook in UNIQUE mode OK: target_addr %" PRIxPTR, target_addr);
-  } else {
-    r = sh_switch_unhook_shared(target_addr, new_addr);
-    if (0 == r)
-      SH_LOG_INFO("switch: unhook in SHARED mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
-                  target_addr, new_addr);
-  }
-
-  return r;
-}
diff --git a/app/src/main/cpp/shadowhook/sh_switch.h b/app/src/main/cpp/shadowhook/sh_switch.h
deleted file mode 100644
index 3899a93..0000000
--- a/app/src/main/cpp/shadowhook/sh_switch.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdint.h>
-
-#include "xdl.h"
-
-int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
-                   xdl_info_t *dlinfo);
-int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr);
-
-int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                             size_t *backup_len, xdl_info_t *dlinfo);
diff --git a/app/src/main/cpp/shadowhook/sh_task.c b/app/src/main/cpp/shadowhook/sh_task.c
deleted file mode 100644
index 49ad751..0000000
--- a/app/src/main/cpp/shadowhook/sh_task.c
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "sh_task.h"
-
-#include <inttypes.h>
-#include <malloc.h>
-#include <poll.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
-
-#include "queue.h"
-#include "sh_config.h"
-#include "sh_errno.h"
-#include "sh_linker.h"
-#include "sh_log.h"
-#include "sh_recorder.h"
-#include "sh_sig.h"
-#include "sh_switch.h"
-#include "sh_util.h"
-#include "shadowhook.h"
-#include "xdl.h"
-
-#define SH_TASK_THREAD_NAME "shadowhook-task"
-
-// task
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-struct sh_task {
-  char *lib_name;
-  char *sym_name;
-  uintptr_t target_addr;
-  uintptr_t new_addr;
-  uintptr_t *orig_addr;
-  shadowhook_hooked_t hooked;
-  void *hooked_arg;
-  uintptr_t caller_addr;
-  bool finished;
-  bool error;
-  bool ignore_symbol_check;
-  TAILQ_ENTRY(sh_task, ) link;
-};
-#pragma clang diagnostic pop
-
-// task queue
-typedef TAILQ_HEAD(sh_task_queue, sh_task, ) sh_task_queue_t;
-
-// task queue object
-static sh_task_queue_t sh_tasks = TAILQ_HEAD_INITIALIZER(sh_tasks);
-static pthread_rwlock_t sh_tasks_lock = PTHREAD_RWLOCK_INITIALIZER;
-static int sh_tasks_unfinished_cnt = 0;
-
-static int sh_task_eventfd;
-static int thread_started_result = SHADOWHOOK_ERRNO_MONITOR_THREAD;
-
-sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                                         bool ignore_symbol_check, uintptr_t caller_addr) {
-  sh_task_t *self = malloc(sizeof(sh_task_t));
-  if (NULL == self) return NULL;
-  self->lib_name = NULL;
-  self->sym_name = NULL;
-  self->target_addr = target_addr;
-  self->new_addr = new_addr;
-  self->orig_addr = orig_addr;
-  self->hooked = NULL;
-  self->hooked_arg = NULL;
-  self->caller_addr = caller_addr;
-  self->finished = false;
-  self->error = false;
-  self->ignore_symbol_check = ignore_symbol_check;
-
-  return self;
-}
-
-sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr,
-                                      uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
-                                      uintptr_t caller_addr) {
-  sh_task_t *self = malloc(sizeof(sh_task_t));
-  if (NULL == self) return NULL;
-
-  if (NULL == (self->lib_name = strdup(lib_name))) goto err;
-  if (NULL == (self->sym_name = strdup(sym_name))) goto err;
-  self->target_addr = 0;
-  self->new_addr = new_addr;
-  self->orig_addr = orig_addr;
-  self->hooked = hooked;
-  self->hooked_arg = hooked_arg;
-  self->caller_addr = caller_addr;
-  self->finished = false;
-  self->error = false;
-  self->ignore_symbol_check = false;
-
-  return self;
-
-err:
-  if (NULL != self->lib_name) free(self->lib_name);
-  if (NULL != self->sym_name) free(self->sym_name);
-  free(self);
-  return NULL;
-}
-
-void sh_task_destroy(sh_task_t *self) {
-  if (NULL != self->lib_name) free(self->lib_name);
-  if (NULL != self->sym_name) free(self->sym_name);
-  free(self);
-}
-
-static void sh_task_do_callback(sh_task_t *self, int error_number) {
-  if (NULL != self->hooked)
-    self->hooked(error_number, self->lib_name, self->sym_name, (void *)self->target_addr,
-                 (void *)self->new_addr, self->orig_addr, self->hooked_arg);
-}
-
-static int sh_task_hook_pending(struct dl_phdr_info *info, size_t size, void *arg) {
-  (void)size, (void)arg;
-
-  pthread_rwlock_rdlock(&sh_tasks_lock);
-
-  sh_task_t *task;
-  TAILQ_FOREACH(task, &sh_tasks, link) {
-    if (task->finished) continue;
-    if ('/' == info->dlpi_name[0] && NULL == strstr(info->dlpi_name, task->lib_name)) continue;
-    if ('/' != info->dlpi_name[0] && NULL == strstr(task->lib_name, info->dlpi_name)) continue;
-
-    xdl_info_t dlinfo;
-    char real_lib_name[512];
-    int r = sh_linker_get_dlinfo_by_sym_name(task->lib_name, task->sym_name, &dlinfo, real_lib_name,
-                                             sizeof(real_lib_name));
-    task->target_addr = (uintptr_t)dlinfo.dli_saddr;
-    if (SHADOWHOOK_ERRNO_PENDING != r) {
-      size_t backup_len = 0;
-      if (0 == r) {
-        r = sh_switch_hook(task->target_addr, task->new_addr, task->orig_addr, &backup_len, &dlinfo);
-        if (0 != r) task->error = true;
-      } else {
-        strlcpy(real_lib_name, task->lib_name, sizeof(real_lib_name));
-        task->error = true;
-      }
-      sh_recorder_add_hook(r, false, task->target_addr, real_lib_name, task->sym_name, task->new_addr,
-                           backup_len, (uintptr_t)task, task->caller_addr);
-      task->finished = true;
-      sh_task_do_callback(task, r);
-      if (0 == __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST)) break;
-    }
-  }
-
-  pthread_rwlock_unlock(&sh_tasks_lock);
-
-  return __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ? 0 : 1;
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-
-static void sh_task_post_dlopen_callback(void *arg) {
-  (void)arg;
-
-  if (0 == thread_started_result && __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0) {
-    uint64_t ev_val = 1;
-    SH_UTIL_TEMP_FAILURE_RETRY(write(sh_task_eventfd, &ev_val, sizeof(ev_val)));
-  }
-}
-
-__noreturn static void *sh_task_thread_func(void *arg) {
-  (void)arg;
-  pthread_t thread = pthread_self();
-  pthread_setname_np(thread, SH_TASK_THREAD_NAME);
-  pthread_detach(thread);
-
-  struct pollfd ev = {.fd = sh_task_eventfd, .events = POLLIN, .revents = 0};
-  while (1) {
-    int n = SH_UTIL_TEMP_FAILURE_RETRY(poll(&ev, 1, -1));
-    if (n < 0) {
-      sleep(1);
-      continue;
-    } else if (n > 0) {
-      uint64_t ev_val;
-      SH_UTIL_TEMP_FAILURE_RETRY(read(sh_task_eventfd, &ev_val, sizeof(ev_val)));
-
-      if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-        xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT);
-      } else {
-        SH_SIG_TRY(SIGSEGV, SIGBUS) {
-          xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT);
-        }
-        SH_SIG_CATCH() {
-          SH_LOG_WARN("task: dliterate crashed");
-        }
-        SH_SIG_EXIT
-      }
-    }
-  }
-}
-
-#pragma clang diagnostic pop
-
-static int sh_task_start_monitor(bool start_thread) {
-  static bool thread_started = false;
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  pthread_t thread;
-  int r;
-
-  // hook linker dlopen()
-  if (0 != (r = sh_linker_hook_dlopen(sh_task_post_dlopen_callback, NULL))) return r;
-
-  if (!start_thread) return 0;
-
-  // start thread
-  if (thread_started) return thread_started_result;
-  pthread_mutex_lock(&lock);
-  if (thread_started) goto end;
-
-  if (0 > (sh_task_eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC))) goto end;
-  if (0 != pthread_create(&thread, NULL, &sh_task_thread_func, NULL)) goto end;
-
-  // OK
-  thread_started_result = 0;
-
-end:
-  thread_started = true;
-  pthread_mutex_unlock(&lock);
-  SH_LOG_INFO("task: start monitor %s, return: %d", 0 == thread_started_result ? "OK" : "FAILED",
-              thread_started_result);
-  return thread_started_result;
-}
-
-int sh_task_hook(sh_task_t *self) {
-  int r;
-  bool is_hook_sym_addr = true;
-  char real_lib_name[512] = "unknown";
-  char real_sym_name[1024] = "unknown";
-  size_t backup_len = 0;
-
-  // find target-address by library-name and symbol-name
-  xdl_info_t dlinfo;
-  memset(&dlinfo, 0, sizeof(xdl_info_t));
-  if (0 == self->target_addr) {
-    is_hook_sym_addr = false;
-    strlcpy(real_lib_name, self->lib_name, sizeof(real_lib_name));
-    strlcpy(real_sym_name, self->sym_name, sizeof(real_sym_name));
-    r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name,
-                                         sizeof(real_lib_name));
-    if (SHADOWHOOK_ERRNO_PENDING == r) {
-      // we need to start monitor linker dlopen for handle the pending task
-      if (0 != (r = sh_task_start_monitor(true))) goto end;
-      r = SHADOWHOOK_ERRNO_PENDING;
-      goto end;
-    }
-    if (0 != r) goto end;                             // error
-    self->target_addr = (uintptr_t)dlinfo.dli_saddr;  // OK
-  } else {
-    r = sh_linker_get_dlinfo_by_addr((void *)self->target_addr, &dlinfo, real_lib_name, sizeof(real_lib_name),
-                                     real_sym_name, sizeof(real_sym_name), self->ignore_symbol_check);
-    if (0 != r) goto end;  // error
-  }
-
-  // In UNIQUE mode, if external users are hooking the linker dlopen() or do_dlopen(),
-  // we MUST hook this method with invisible for ourself first.
-  if (sh_linker_need_to_hook_dlopen(self->target_addr)) {
-    SH_LOG_INFO("task: hook dlopen/do_dlopen internal. target-address %" PRIxPTR, self->target_addr);
-    if (0 != (r = sh_task_start_monitor(false))) goto end;
-  }
-
-  // hook by target-address
-  r = sh_switch_hook(self->target_addr, self->new_addr, self->orig_addr, &backup_len, &dlinfo);
-  self->finished = true;
-
-end:
-  if (0 == r || SHADOWHOOK_ERRNO_PENDING == r)  // "PENDING" is NOT an error
-  {
-    pthread_rwlock_wrlock(&sh_tasks_lock);
-    TAILQ_INSERT_TAIL(&sh_tasks, self, link);
-    if (!self->finished) __atomic_add_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST);
-    pthread_rwlock_unlock(&sh_tasks_lock);
-  }
-
-  // record
-  sh_recorder_add_hook(r, is_hook_sym_addr, self->target_addr, real_lib_name, real_sym_name, self->new_addr,
-                       backup_len, (uintptr_t)self, self->caller_addr);
-
-  return r;
-}
-
-int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr) {
-  pthread_rwlock_wrlock(&sh_tasks_lock);
-  TAILQ_REMOVE(&sh_tasks, self, link);
-  if (!self->finished) __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST);
-  pthread_rwlock_unlock(&sh_tasks_lock);
-
-  // check task status
-  int r;
-  if (self->error) {
-    r = SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR;
-    goto end;
-  }
-  if (!self->finished) {
-    r = SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED;
-    goto end;
-  }
-
-  // do unhook
-  r = sh_switch_unhook(self->target_addr, self->new_addr);
-
-end:
-  // record
-  sh_recorder_add_unhook(r, (uintptr_t)self, caller_addr);
-  return r;
-}
diff --git a/app/src/main/cpp/shadowhook/sh_task.h b/app/src/main/cpp/shadowhook/sh_task.h
deleted file mode 100644
index e26aef9..0000000
--- a/app/src/main/cpp/shadowhook/sh_task.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "shadowhook.h"
-
-typedef struct sh_task sh_task_t;
-
-sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
-                                         bool ignore_symbol_check, uintptr_t caller_addr);
-sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr,
-                                      uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
-                                      uintptr_t caller_addr);
-void sh_task_destroy(sh_task_t *self);
-
-int sh_task_hook(sh_task_t *self);
-int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr);
diff --git a/app/src/main/cpp/shadowhook/shadowhook.c b/app/src/main/cpp/shadowhook/shadowhook.c
deleted file mode 100644
index 254cda1..0000000
--- a/app/src/main/cpp/shadowhook/shadowhook.c
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright (c) 2021-2022 ByteDance Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
-
-#include "shadowhook.h"
-
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-#include "bytesig.h"
-#include "sh_enter.h"
-#include "sh_errno.h"
-#include "sh_exit.h"
-#include "sh_hub.h"
-#include "sh_linker.h"
-#include "sh_log.h"
-#include "sh_recorder.h"
-#include "sh_safe.h"
-#include "sh_sig.h"
-#include "sh_switch.h"
-#include "sh_task.h"
-#include "sh_util.h"
-#include "xdl.h"
-
-#define GOTO_ERR(errnum) \
-  do {                   \
-    r = errnum;          \
-    goto err;            \
-  } while (0)
-
-static int shadowhook_init_errno = SHADOWHOOK_ERRNO_UNINIT;
-static shadowhook_mode_t shadowhook_mode = SHADOWHOOK_MODE_SHARED;
-
-const char *shadowhook_get_version(void) {
-  return "shadowhook version " SHADOWHOOK_VERSION;
-}
-
-int shadowhook_init(shadowhook_mode_t mode, bool debuggable) {
-  bool do_init = false;
-
-  if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) {
-    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-    pthread_mutex_lock(&lock);
-    if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) {
-      do_init = true;
-      shadowhook_mode = mode;
-      sh_log_set_debuggable(debuggable);
-
-#define GOTO_END(errnum)            \
-  do {                              \
-    shadowhook_init_errno = errnum; \
-    goto end;                       \
-  } while (0)
-
-      if (__predict_false(0 != sh_errno_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ERRNO);
-      if (__predict_false(0 != bytesig_init(SIGSEGV))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGSEGV);
-      if (__predict_false(0 != bytesig_init(SIGBUS))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGBUS);
-      if (__predict_false(0 != sh_enter_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ENTER);
-      sh_exit_init();
-      if (SHADOWHOOK_MODE_SHARED == shadowhook_mode) {
-        if (__predict_false(0 != sh_safe_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_SAFE);
-        if (__predict_false(0 != sh_hub_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_HUB);
-      } else {
-        if (__predict_false(0 != sh_linker_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_LINKER);
-      }
-
-#undef GOTO_END
-
-      shadowhook_init_errno = SHADOWHOOK_ERRNO_OK;
-    }
-  end:
-    pthread_mutex_unlock(&lock);
-  }
-
-  SH_LOG_ALWAYS_SHOW("%s: shadowhook init(mode: %s, debuggable: %s), return: %d, real-init: %s",
-                     shadowhook_get_version(), SHADOWHOOK_MODE_SHARED == mode ? "SHARED" : "UNIQUE",
-                     debuggable ? "true" : "false", shadowhook_init_errno, do_init ? "yes" : "no");
-  SH_ERRNO_SET_RET_ERRNUM(shadowhook_init_errno);
-}
-
-int shadowhook_get_init_errno(void) {
-  return shadowhook_init_errno;
-}
-
-shadowhook_mode_t shadowhook_get_mode(void) {
-  return shadowhook_mode;
-}
-
-bool shadowhook_get_debuggable(void) {
-  return sh_log_get_debuggable();
-}
-
-void shadowhook_set_debuggable(bool debuggable) {
-  sh_log_set_debuggable(debuggable);
-}
-
-bool shadowhook_get_recordable(void) {
-  return sh_recorder_get_recordable();
-}
-
-void shadowhook_set_recordable(bool recordable) {
-  sh_recorder_set_recordable(recordable);
-}
-
-int shadowhook_get_errno(void) {
-  return sh_errno_get();
-}
-
-const char *shadowhook_to_errmsg(int error_number) {
-  return sh_errno_to_errmsg(error_number);
-}
-
-static void *shadowhook_hook_addr_impl(void *sym_addr, void *new_addr, void **orig_addr,
-                                       bool ignore_symbol_check, uintptr_t caller_addr) {
-  SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) ...", ignore_symbol_check ? "func" : "sym", sym_addr,
-              new_addr);
-  sh_errno_reset();
-
-  int r;
-  if (NULL == sym_addr || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
-  if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
-
-  // create task
-  sh_task_t *task =
-      sh_task_create_by_target_addr((uintptr_t)sym_addr, (uintptr_t)new_addr, (uintptr_t *)orig_addr,
-                                    ignore_symbol_check, (uintptr_t)caller_addr);
-  if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM);
-
-  // do hook
-  r = sh_task_hook(task);
-  if (0 != r) {
-    sh_task_destroy(task);
-    GOTO_ERR(r);
-  }
-
-  // OK
-  SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) OK. return: %p", ignore_symbol_check ? "func" : "sym",
-              sym_addr, new_addr, (void *)task);
-  SH_ERRNO_SET_RET(SHADOWHOOK_ERRNO_OK, (void *)task);
-
-err:
-  SH_LOG_ERROR("shadowhook: hook_%s_addr(%p, %p) FAILED. %d - %s", ignore_symbol_check ? "func" : "sym",
-               sym_addr, new_addr, r, sh_errno_to_errmsg(r));
-  SH_ERRNO_SET_RET_NULL(r);
-}
-
-void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr) {
-  const void *caller_addr = __builtin_return_address(0);
-  return shadowhook_hook_addr_impl(func_addr, new_addr, orig_addr, true, (uintptr_t)caller_addr);
-}
-
-void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr) {
-  const void *caller_addr = __builtin_return_address(0);
-  return shadowhook_hook_addr_impl(sym_addr, new_addr, orig_addr, false, (uintptr_t)caller_addr);
-}
-
-static void *shadowhook_hook_sym_name_impl(const char *lib_name, const char *sym_name, void *new_addr,
-                                           void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
-                                           uintptr_t caller_addr) {
-  SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) ...", lib_name, sym_name, new_addr);
-  sh_errno_reset();
-
-  int r;
-  if (NULL == lib_name || NULL == sym_name || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
-  if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
-
-  // create task
-  sh_task_t *task =
-      sh_task_create_by_sym_name(lib_name, sym_name, (uintptr_t)new_addr, (uintptr_t *)orig_addr, hooked,
-                                 hooked_arg, (uintptr_t)caller_addr);
-  if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM);
-
-  // do hook
-  r = sh_task_hook(task);
-  if (0 != r && SHADOWHOOK_ERRNO_PENDING != r) {
-    sh_task_destroy(task);
-    GOTO_ERR(r);
-  }
-
-  // OK
-  SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) OK. return: %p. %d - %s", lib_name, sym_name, new_addr,
-              (void *)task, r, sh_errno_to_errmsg(r));
-  SH_ERRNO_SET_RET(r, (void *)task);
-
-err:
-  SH_LOG_ERROR("shadowhook: hook_sym_name(%s, %s, %p) FAILED. %d - %s", lib_name, sym_name, new_addr, r,
-               sh_errno_to_errmsg(r));
-  SH_ERRNO_SET_RET_NULL(r);
-}
-
-void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr) {
-  const void *caller_addr = __builtin_return_address(0);
-  return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, NULL, NULL,
-                                       (uintptr_t)caller_addr);
-}
-
-void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr,
-                                        void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg) {
-  const void *caller_addr = __builtin_return_address(0);
-  return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, hooked, hooked_arg,
-                                       (uintptr_t)caller_addr);
-}
-
-int shadowhook_unhook(void *stub) {
-  const void *caller_addr = __builtin_return_address(0);
-  SH_LOG_INFO("shadowhook: unhook(%p) ...", stub);
-  sh_errno_reset();
-
-  int r;
-  if (NULL == stub) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
-  if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
-
-  sh_task_t *task = (sh_task_t *)stub;
-  r = sh_task_unhook(task, (uintptr_t)caller_addr);
-  sh_task_destroy(task);
-  if (0 != r) GOTO_ERR(r);
-
-  // OK
-  SH_LOG_INFO("shadowhook: unhook(%p) OK", stub);
-  SH_ERRNO_SET_RET_ERRNUM(SHADOWHOOK_ERRNO_OK);
-
-err:
-  SH_LOG_ERROR("shadowhook: unhook(%p) FAILED. %d - %s", stub, r, sh_errno_to_errmsg(r));
-  SH_ERRNO_SET_RET_FAIL(r);
-}
-
-char *shadowhook_get_records(uint32_t item_flags) {
-  return sh_recorder_get(item_flags);
-}
-
-void shadowhook_dump_records(int fd, uint32_t item_flags) {
-  sh_recorder_dump(fd, item_flags);
-}
-
-void *shadowhook_dlopen(const char *lib_name) {
-  void *handle = NULL;
-  if (sh_util_get_api_level() >= __ANDROID_API_L__) {
-    handle = xdl_open(lib_name, XDL_DEFAULT);
-  } else {
-    SH_SIG_TRY(SIGSEGV, SIGBUS) {
-      handle = xdl_open(lib_name, XDL_DEFAULT);
-    }
-    SH_SIG_CATCH() {
-      SH_LOG_WARN("shadowhook: dlopen crashed - %s", lib_name);
-    }
-    SH_SIG_EXIT
-  }
-  return handle;
-}
-
-void shadowhook_dlclose(void *handle) {
-  xdl_close(handle);
-}
-
-void *shadowhook_dlsym(void *handle, const char *sym_name) {
-  void *addr = shadowhook_dlsym_dynsym(handle, sym_name);
-  if (NULL == addr) addr = shadowhook_dlsym_symtab(handle, sym_name);
-  return addr;
-}
-
-void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name) {
-  void *addr = NULL;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    addr = xdl_sym(handle, sym_name, NULL);
-  }
-  SH_SIG_CATCH() {
-    SH_LOG_WARN("shadowhook: dlsym_dynsym crashed - %p, %s", handle, sym_name);
-  }
-  SH_SIG_EXIT
-  return addr;
-}
-
-void *shadowhook_dlsym_symtab(void *handle, const char *sym_name) {
-  void *addr = NULL;
-  SH_SIG_TRY(SIGSEGV, SIGBUS) {
-    addr = xdl_dsym(handle, sym_name, NULL);
-  }
-  SH_SIG_CATCH() {
-    SH_LOG_WARN("shadowhook: dlsym_symtab crashed - %p, %s", handle, sym_name);
-  }
-  SH_SIG_EXIT
-  return addr;
-}
-
-void *shadowhook_get_prev_func(void *func) {
-  if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
-  return sh_hub_get_prev_func(func);
-}
-
-void shadowhook_pop_stack(void *return_address) {
-  if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
-  sh_hub_pop_stack(return_address);
-}
-
-void shadowhook_allow_reentrant(void *return_address) {
-  if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
-  sh_hub_allow_reentrant(return_address);
-}
-
-void shadowhook_disallow_reentrant(void *return_address) {
-  if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
-  sh_hub_disallow_reentrant(return_address);
-}
-
-void *shadowhook_get_return_address(void) {
-  if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
-  return sh_hub_get_return_address();
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/queue.h b/app/src/main/cpp/shadowhook/third_party/bsd/queue.h
deleted file mode 100644
index f550f6c..0000000
--- a/app/src/main/cpp/shadowhook/third_party/bsd/queue.h
+++ /dev/null
@@ -1,551 +0,0 @@
-/*-
- * Copyright (c) 1991, 1993
- *      The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef QUEUE_H
-#define QUEUE_H
-
-/* #include <sys/cdefs.h> */
-#define QUEUE_CONTAINER_OF(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))
-
-/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction.  Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may be traversed in either direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- *                              SLIST   LIST    STAILQ  TAILQ
- * _HEAD                        +       +       +       +
- * _HEAD_INITIALIZER            +       +       +       +
- * _ENTRY                       +       +       +       +
- * _INIT                        +       +       +       +
- * _EMPTY                       +       +       +       +
- * _FIRST                       +       +       +       +
- * _NEXT                        +       +       +       +
- * _PREV                        -       +       -       +
- * _LAST                        -       -       +       +
- * _FOREACH                     +       +       +       +
- * _FOREACH_FROM                +       +       +       +
- * _FOREACH_SAFE                +       +       +       +
- * _FOREACH_FROM_SAFE           +       +       +       +
- * _FOREACH_REVERSE             -       -       -       +
- * _FOREACH_REVERSE_FROM        -       -       -       +
- * _FOREACH_REVERSE_SAFE        -       -       -       +
- * _FOREACH_REVERSE_FROM_SAFE   -       -       -       +
- * _INSERT_HEAD                 +       +       +       +
- * _INSERT_BEFORE               -       +       -       +
- * _INSERT_AFTER                +       +       +       +
- * _INSERT_TAIL                 -       -       +       +
- * _CONCAT                      -       -       +       +
- * _REMOVE_AFTER                +       -       +       -
- * _REMOVE_HEAD                 +       -       +       -
- * _REMOVE                      +       +       +       +
- * _SWAP                        +       +       +       +
- *
- */
-
-/*
- * Singly-linked List declarations.
- */
-#define SLIST_HEAD(name, type, qual)                                    \
-    struct name {                                                       \
-        struct type *qual slh_first; /* first element */                \
-    }
-
-#define SLIST_HEAD_INITIALIZER(head)                                    \
-    { NULL }
-
-#define SLIST_ENTRY(type, qual)                                         \
-    struct {                                                            \
-        struct type *qual sle_next;  /* next element */                 \
-    }
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_INIT(head) do {                                           \
-        SLIST_FIRST((head)) = NULL;                                     \
-    } while (0)
-
-#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
-
-#define SLIST_FIRST(head) ((head)->slh_first)
-
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_FOREACH(var, head, field)                                 \
-    for ((var) = SLIST_FIRST((head));                                   \
-         (var);                                                         \
-         (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_FROM(var, head, field)                            \
-    for ((var) = ((var) ? (var) : SLIST_FIRST((head)));                 \
-         (var);                                                         \
-         (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
-    for ((var) = SLIST_FIRST((head));                                   \
-         (var) && ((tvar) = SLIST_NEXT((var), field), 1);               \
-         (var) = (tvar))
-
-#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
-    for ((var) = ((var) ? (var) : SLIST_FIRST((head)));                 \
-         (var) && ((tvar) = SLIST_NEXT((var), field), 1);               \
-         (var) = (tvar))
-
-#define SLIST_INSERT_HEAD(head, elm, field) do {                        \
-        SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
-        SLIST_FIRST((head)) = (elm);                                    \
-    } while (0)
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
-        SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
-        SLIST_NEXT((slistelm), field) = (elm);                          \
-    } while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do {                             \
-        SLIST_NEXT(elm, field) =                                        \
-            SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
-    } while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do {                             \
-        SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
-    } while (0)
-
-#define SLIST_REMOVE(head, elm, type, field) do {                       \
-        if (SLIST_FIRST((head)) == (elm)) {                             \
-            SLIST_REMOVE_HEAD((head), field);                           \
-        }                                                               \
-        else {                                                          \
-            struct type *curelm = SLIST_FIRST((head));                  \
-            while (SLIST_NEXT(curelm, field) != (elm))                  \
-                curelm = SLIST_NEXT(curelm, field);                     \
-            SLIST_REMOVE_AFTER(curelm, field);                          \
-        }                                                               \
-    } while (0)
-
-#define SLIST_SWAP(head1, head2, type) do {                             \
-        struct type *swap_first = SLIST_FIRST(head1);                   \
-        SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \
-        SLIST_FIRST(head2) = swap_first;                                \
-    } while (0)
-
-/*
- * List declarations.
- */
-#define LIST_HEAD(name, type, qual)                                     \
-    struct name {                                                       \
-        struct type *qual lh_first;  /* first element */                \
-    }
-
-#define LIST_HEAD_INITIALIZER(head)                                     \
-    { NULL }
-
-#define LIST_ENTRY(type, qual)                                          \
-    struct {                                                            \
-        struct type *qual le_next;   /* next element */                 \
-        struct type *qual *le_prev;  /* address of previous next element */ \
-    }
-
-/*
- * List functions.
- */
-#define LIST_INIT(head) do {                                            \
-        LIST_FIRST((head)) = NULL;                                      \
-    } while (0)
-
-#define LIST_EMPTY(head) ((head)->lh_first == NULL)
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_PREV(elm, head, type, field)                               \
-    ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :               \
-     QUEUE_CONTAINER_OF((elm)->field.le_prev, struct type, field.le_next))
-
-#define LIST_FOREACH(var, head, field)                                  \
-    for ((var) = LIST_FIRST((head));                                    \
-         (var);                                                         \
-         (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_FROM(var, head, field)                             \
-    for ((var) = ((var) ? (var) : LIST_FIRST((head)));                  \
-         (var);                                                         \
-         (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar)                       \
-    for ((var) = LIST_FIRST((head));                                    \
-         (var) && ((tvar) = LIST_NEXT((var), field), 1);                \
-         (var) = (tvar))
-
-#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar)                  \
-    for ((var) = ((var) ? (var) : LIST_FIRST((head)));                  \
-         (var) && ((tvar) = LIST_NEXT((var), field), 1);                \
-         (var) = (tvar))
-
-#define LIST_INSERT_HEAD(head, elm, field) do {                         \
-        if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
-            LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
-        LIST_FIRST((head)) = (elm);                                     \
-        (elm)->field.le_prev = &LIST_FIRST((head));                     \
-    } while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
-        (elm)->field.le_prev = (listelm)->field.le_prev;                \
-        LIST_NEXT((elm), field) = (listelm);                            \
-        *(listelm)->field.le_prev = (elm);                              \
-        (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
-    } while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do {                     \
-        if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
-            LIST_NEXT((listelm), field)->field.le_prev =                \
-                &LIST_NEXT((elm), field);                               \
-        LIST_NEXT((listelm), field) = (elm);                            \
-        (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
-    } while (0)
-
-#define LIST_REMOVE(elm, field) do {                                    \
-        if (LIST_NEXT((elm), field) != NULL)                            \
-            LIST_NEXT((elm), field)->field.le_prev =                    \
-                (elm)->field.le_prev;                                   \
-        *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
-    } while (0)
-
-#define LIST_SWAP(head1, head2, type, field) do {                       \
-        struct type *swap_tmp = LIST_FIRST((head1));                    \
-        LIST_FIRST((head1)) = LIST_FIRST((head2));                      \
-        LIST_FIRST((head2)) = swap_tmp;                                 \
-        if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \
-            swap_tmp->field.le_prev = &LIST_FIRST((head1));             \
-        if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \
-            swap_tmp->field.le_prev = &LIST_FIRST((head2));             \
-    } while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type, qual)                                   \
-    struct name {                                                       \
-        struct type *qual stqh_first;/* first element */                \
-        struct type *qual *stqh_last;/* addr of last next element */    \
-    }
-
-#define STAILQ_HEAD_INITIALIZER(head)                                   \
-    { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type, qual)                                        \
-    struct {                                                            \
-        struct type *qual stqe_next; /* next element */                 \
-    }
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_INIT(head) do {                                          \
-        STAILQ_FIRST((head)) = NULL;                                    \
-        (head)->stqh_last = &STAILQ_FIRST((head));                      \
-    } while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_LAST(head, type, field)                                  \
-    (STAILQ_EMPTY((head)) ? NULL :                                      \
-     QUEUE_CONTAINER_OF((head)->stqh_last, struct type, field.stqe_next))
-
-#define STAILQ_FOREACH(var, head, field)                                \
-    for((var) = STAILQ_FIRST((head));                                   \
-        (var);                                                          \
-        (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_FROM(var, head, field)                           \
-    for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));                \
-         (var);                                                         \
-         (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
-    for ((var) = STAILQ_FIRST((head));                                  \
-         (var) && ((tvar) = STAILQ_NEXT((var), field), 1);              \
-         (var) = (tvar))
-
-#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                \
-    for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));                \
-         (var) && ((tvar) = STAILQ_NEXT((var), field), 1);              \
-         (var) = (tvar))
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do {                       \
-        if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
-            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \
-        STAILQ_FIRST((head)) = (elm);                                   \
-    } while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
-        if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
-            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \
-        STAILQ_NEXT((tqelm), field) = (elm);                            \
-    } while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do {                       \
-        STAILQ_NEXT((elm), field) = NULL;                               \
-        *(head)->stqh_last = (elm);                                     \
-        (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
-    } while (0)
-
-#define STAILQ_CONCAT(head1, head2) do {                                \
-        if (!STAILQ_EMPTY((head2))) {                                   \
-            *(head1)->stqh_last = (head2)->stqh_first;                  \
-            (head1)->stqh_last = (head2)->stqh_last;                    \
-            STAILQ_INIT((head2));                                       \
-        }                                                               \
-    } while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do {                      \
-        if ((STAILQ_NEXT(elm, field) =                                  \
-             STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \
-            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \
-    } while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do {                            \
-        if ((STAILQ_FIRST((head)) =                                     \
-             STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
-            (head)->stqh_last = &STAILQ_FIRST((head));                  \
-    } while (0)
-
-#define STAILQ_REMOVE(head, elm, type, field) do {                      \
-        if (STAILQ_FIRST((head)) == (elm)) {                            \
-            STAILQ_REMOVE_HEAD((head), field);                          \
-        }                                                               \
-        else {                                                          \
-            struct type *curelm = STAILQ_FIRST((head));                 \
-            while (STAILQ_NEXT(curelm, field) != (elm))                 \
-                curelm = STAILQ_NEXT(curelm, field);                    \
-            STAILQ_REMOVE_AFTER(head, curelm, field);                   \
-        }                                                               \
-    } while (0)
-
-#define STAILQ_SWAP(head1, head2, type) do {                            \
-        struct type *swap_first = STAILQ_FIRST(head1);                  \
-        struct type **swap_last = (head1)->stqh_last;                   \
-        STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \
-        (head1)->stqh_last = (head2)->stqh_last;                        \
-        STAILQ_FIRST(head2) = swap_first;                               \
-        (head2)->stqh_last = swap_last;                                 \
-        if (STAILQ_EMPTY(head1))                                        \
-            (head1)->stqh_last = &STAILQ_FIRST(head1);                  \
-        if (STAILQ_EMPTY(head2))                                        \
-            (head2)->stqh_last = &STAILQ_FIRST(head2);                  \
-    } while (0)
-
-/*
- * Tail queue declarations.
- */
-#define TAILQ_HEAD(name, type, qual)                                    \
-    struct name {                                                       \
-        struct type *qual tqh_first; /* first element */                \
-        struct type *qual *tqh_last; /* addr of last next element */    \
-}
-
-#define TAILQ_HEAD_INITIALIZER(head)                                    \
-    { NULL, &(head).tqh_first }
-
-#define TAILQ_ENTRY(type, qual)                                         \
-    struct {                                                            \
-        struct type *qual tqe_next;  /* next element */                 \
-        struct type *qual *tqe_prev; /* address of previous next element */ \
-    }
-
-/*
- * Tail queue functions.
- */
-#define TAILQ_INIT(head) do {                                           \
-        TAILQ_FIRST((head)) = NULL;                                     \
-        (head)->tqh_last = &TAILQ_FIRST((head));                        \
-    } while (0)
-
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define TAILQ_PREV(elm, headname, field)                                \
-        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define TAILQ_LAST(head, headname)                                      \
-    (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define TAILQ_FOREACH(var, head, field)                                 \
-    for ((var) = TAILQ_FIRST((head));                                   \
-         (var);                                                         \
-         (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_FROM(var, head, field)                            \
-    for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));                 \
-         (var);                                                         \
-         (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
-    for ((var) = TAILQ_FIRST((head));                                   \
-         (var) && ((tvar) = TAILQ_NEXT((var), field), 1);               \
-         (var) = (tvar))
-
-#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
-    for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));                 \
-         (var) && ((tvar) = TAILQ_NEXT((var), field), 1);               \
-         (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
-    for ((var) = TAILQ_LAST((head), headname);                          \
-         (var);                                                         \
-         (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)          \
-    for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));        \
-         (var);                                                         \
-         (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
-    for ((var) = TAILQ_LAST((head), headname);                          \
-         (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);     \
-         (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
-    for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));        \
-         (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);     \
-         (var) = (tvar))
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do {                        \
-        if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
-            TAILQ_FIRST((head))->field.tqe_prev =                       \
-                &TAILQ_NEXT((elm), field);                              \
-        else                                                            \
-            (head)->tqh_last = &TAILQ_NEXT((elm), field);               \
-        TAILQ_FIRST((head)) = (elm);                                    \
-        (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
-    } while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
-        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
-        TAILQ_NEXT((elm), field) = (listelm);                           \
-        *(listelm)->field.tqe_prev = (elm);                             \
-        (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
-    } while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
-        if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
-            TAILQ_NEXT((elm), field)->field.tqe_prev =                  \
-                &TAILQ_NEXT((elm), field);                              \
-        else                                                            \
-            (head)->tqh_last = &TAILQ_NEXT((elm), field);               \
-        TAILQ_NEXT((listelm), field) = (elm);                           \
-        (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
-    } while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do {                        \
-        TAILQ_NEXT((elm), field) = NULL;                                \
-        (elm)->field.tqe_prev = (head)->tqh_last;                       \
-        *(head)->tqh_last = (elm);                                      \
-        (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
-    } while (0)
-
-#define TAILQ_CONCAT(head1, head2, field) do {                          \
-        if (!TAILQ_EMPTY(head2)) {                                      \
-            *(head1)->tqh_last = (head2)->tqh_first;                    \
-            (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;     \
-            (head1)->tqh_last = (head2)->tqh_last;                      \
-            TAILQ_INIT((head2));                                        \
-        }                                                               \
-    } while (0)
-
-#define TAILQ_REMOVE(head, elm, field) do {                             \
-        if ((TAILQ_NEXT((elm), field)) != NULL)                         \
-            TAILQ_NEXT((elm), field)->field.tqe_prev =                  \
-                (elm)->field.tqe_prev;                                  \
-        else                                                            \
-            (head)->tqh_last = (elm)->field.tqe_prev;                   \
-        *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
-    } while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) do {                      \
-        struct type *swap_first = (head1)->tqh_first;                   \
-        struct type **swap_last = (head1)->tqh_last;                    \
-        (head1)->tqh_first = (head2)->tqh_first;                        \
-        (head1)->tqh_last = (head2)->tqh_last;                          \
-        (head2)->tqh_first = swap_first;                                \
-        (head2)->tqh_last = swap_last;                                  \
-        if ((swap_first = (head1)->tqh_first) != NULL)                  \
-            swap_first->field.tqe_prev = &(head1)->tqh_first;           \
-        else                                                            \
-            (head1)->tqh_last = &(head1)->tqh_first;                    \
-        if ((swap_first = (head2)->tqh_first) != NULL)                  \
-            swap_first->field.tqe_prev = &(head2)->tqh_first;           \
-        else                                                            \
-            (head2)->tqh_last = &(head2)->tqh_first;                    \
-    } while (0)
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/tree.h b/app/src/main/cpp/shadowhook/third_party/bsd/tree.h
deleted file mode 100644
index e6db6a9..0000000
--- a/app/src/main/cpp/shadowhook/third_party/bsd/tree.h
+++ /dev/null
@@ -1,759 +0,0 @@
-/*-
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef TREE_H
-#define TREE_H
-
-/*
- * This file defines data structures for different types of trees:
- * splay trees and red-black trees.
- *
- * A splay tree is a self-organizing data structure.  Every operation
- * on the tree causes a splay to happen.  The splay moves the requested
- * node to the root of the tree and partly rebalances it.
- *
- * This has the benefit that request locality causes faster lookups as
- * the requested nodes move to the top of the tree.  On the other hand,
- * every lookup causes memory writes.
- *
- * The Balance Theorem bounds the total access time for m operations
- * and n inserts on an initially empty tree as O((m + n)lg n).  The
- * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
- *
- * A red-black tree is a binary search tree with the node color as an
- * extra attribute.  It fulfills a set of conditions:
- *      - every search path from the root to a leaf consists of the
- *        same number of black nodes,
- *      - each red node (except for the root) has a black parent,
- *      - each leaf node is black.
- *
- * Every operation on a red-black tree is bounded as O(lg n).
- * The maximum height of a red-black tree is 2lg (n+1).
- */
-
-#define SPLAY_HEAD(name, type)                                          \
-struct name {                                                           \
-        struct type *sph_root; /* root of the tree */                   \
-}
-
-#define SPLAY_INITIALIZER(root)                                         \
-        { NULL }
-
-#define SPLAY_INIT(root) do {                                           \
-        (root)->sph_root = NULL;                                        \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_ENTRY(type)                                               \
-struct {                                                                \
-        struct type *spe_left; /* left element */                       \
-        struct type *spe_right; /* right element */                     \
-}
-
-#define SPLAY_LEFT(elm, field)          (elm)->field.spe_left
-#define SPLAY_RIGHT(elm, field)         (elm)->field.spe_right
-#define SPLAY_ROOT(head)                (head)->sph_root
-#define SPLAY_EMPTY(head)               (SPLAY_ROOT(head) == NULL)
-
-/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
-#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                       \
-        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);  \
-        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
-        (head)->sph_root = tmp;                                         \
-} while (/*CONSTCOND*/ 0)
-        
-#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                        \
-        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);  \
-        SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
-        (head)->sph_root = tmp;                                         \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_LINKLEFT(head, tmp, field) do {                           \
-        SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
-        tmp = (head)->sph_root;                                         \
-        (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);         \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_LINKRIGHT(head, tmp, field) do {                          \
-        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
-        tmp = (head)->sph_root;                                         \
-        (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);        \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_ASSEMBLE(head, node, left, right, field) do {             \
-        SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
-        SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
-        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
-        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-
-#define SPLAY_PROTOTYPE(name, type, field, cmp)                         \
-void name##_SPLAY(struct name *, struct type *);                        \
-void name##_SPLAY_MINMAX(struct name *, int);                           \
-struct type *name##_SPLAY_INSERT(struct name *, struct type *);         \
-struct type *name##_SPLAY_REMOVE(struct name *, struct type *);         \
-                                                                        \
-/* Finds the node with the same key as elm */                           \
-static __inline struct type *                                           \
-name##_SPLAY_FIND(struct name *head, struct type *elm)                  \
-{                                                                       \
-        if (SPLAY_EMPTY(head))                                          \
-                return(NULL);                                           \
-        name##_SPLAY(head, elm);                                        \
-        if ((cmp)(elm, (head)->sph_root) == 0)                          \
-                return (head->sph_root);                                \
-        return (NULL);                                                  \
-}                                                                       \
-                                                                        \
-static __inline struct type *                                          \
-name##_SPLAY_NEXT(struct name *head, struct type *elm)                 \
-{                                                                       \
-        name##_SPLAY(head, elm);                                        \
-        if (SPLAY_RIGHT(elm, field) != NULL) {                          \
-                elm = SPLAY_RIGHT(elm, field);                          \
-                while (SPLAY_LEFT(elm, field) != NULL) {                \
-                        elm = SPLAY_LEFT(elm, field);                   \
-                }                                                       \
-        } else                                                          \
-                elm = NULL;                                             \
-        return (elm);                                                   \
-}                                                                       \
-                                                                        \
-static __inline struct type *                                           \
-name##_SPLAY_MIN_MAX(struct name *head, int val)                        \
-{                                                                       \
-        name##_SPLAY_MINMAX(head, val);                                 \
-        return (SPLAY_ROOT(head));                                      \
-}
-
-/* Main splay operation.
- * Moves node close to the key of elm to top
- */
-#define SPLAY_GENERATE(name, type, field, cmp)                          \
-struct type *                                                           \
-name##_SPLAY_INSERT(struct name *head, struct type *elm)                \
-{                                                                       \
-    if (SPLAY_EMPTY(head)) {                                            \
-            SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;    \
-    } else {                                                            \
-            int __comp;                                                 \
-            name##_SPLAY(head, elm);                                    \
-            __comp = (cmp)(elm, (head)->sph_root);                      \
-            if(__comp < 0) {                                            \
-                    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
-                    SPLAY_RIGHT(elm, field) = (head)->sph_root;         \
-                    SPLAY_LEFT((head)->sph_root, field) = NULL;         \
-            } else if (__comp > 0) {                                    \
-                    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
-                    SPLAY_LEFT(elm, field) = (head)->sph_root;          \
-                    SPLAY_RIGHT((head)->sph_root, field) = NULL;        \
-            } else                                                      \
-                    return ((head)->sph_root);                          \
-    }                                                                   \
-    (head)->sph_root = (elm);                                           \
-    return (NULL);                                                      \
-}                                                                       \
-                                                                        \
-struct type *                                                           \
-name##_SPLAY_REMOVE(struct name *head, struct type *elm)                \
-{                                                                       \
-        struct type *__tmp;                                             \
-        if (SPLAY_EMPTY(head))                                          \
-                return (NULL);                                          \
-        name##_SPLAY(head, elm);                                        \
-        if ((cmp)(elm, (head)->sph_root) == 0) {                        \
-                if (SPLAY_LEFT((head)->sph_root, field) == NULL) {      \
-                        (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
-                } else {                                                \
-                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                        (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
-                        name##_SPLAY(head, elm);                        \
-                        SPLAY_RIGHT((head)->sph_root, field) = __tmp;   \
-                }                                                       \
-                return (elm);                                           \
-        }                                                               \
-        return (NULL);                                                  \
-}                                                                       \
-                                                                        \
-void                                                                    \
-name##_SPLAY(struct name *head, struct type *elm)                       \
-{                                                                       \
-        struct type __node, *__left, *__right, *__tmp;                  \
-        int __comp;                                                     \
-\
-        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-        __left = __right = &__node;                                     \
-\
-        while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {          \
-                if (__comp < 0) {                                       \
-                        __tmp = SPLAY_LEFT((head)->sph_root, field);    \
-                        if (__tmp == NULL)                              \
-                                break;                                  \
-                        if ((cmp)(elm, __tmp) < 0){                     \
-                                SPLAY_ROTATE_RIGHT(head, __tmp, field); \
-                                if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-                                        break;                          \
-                        }                                               \
-                        SPLAY_LINKLEFT(head, __right, field);           \
-                } else if (__comp > 0) {                                \
-                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                        if (__tmp == NULL)                              \
-                                break;                                  \
-                        if ((cmp)(elm, __tmp) > 0){                     \
-                                SPLAY_ROTATE_LEFT(head, __tmp, field);  \
-                                if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-                                        break;                          \
-                        }                                               \
-                        SPLAY_LINKRIGHT(head, __left, field);           \
-                }                                                       \
-        }                                                               \
-        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
-}                                                                       \
-                                                                        \
-/* Splay with either the minimum or the maximum element                 \
- * Used to find minimum or maximum element in tree.                     \
- */                                                                     \
-void name##_SPLAY_MINMAX(struct name *head, int __comp) \
-{                                                                       \
-        struct type __node, *__left, *__right, *__tmp;                  \
-\
-        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-        __left = __right = &__node;                                     \
-\
-        while (1) {                                                     \
-                if (__comp < 0) {                                       \
-                        __tmp = SPLAY_LEFT((head)->sph_root, field);    \
-                        if (__tmp == NULL)                              \
-                                break;                                  \
-                        if (__comp < 0){                                \
-                                SPLAY_ROTATE_RIGHT(head, __tmp, field); \
-                                if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-                                        break;                          \
-                        }                                               \
-                        SPLAY_LINKLEFT(head, __right, field);           \
-                } else if (__comp > 0) {                                \
-                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                        if (__tmp == NULL)                              \
-                                break;                                  \
-                        if (__comp > 0) {                               \
-                                SPLAY_ROTATE_LEFT(head, __tmp, field);  \
-                                if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-                                        break;                          \
-                        }                                               \
-                        SPLAY_LINKRIGHT(head, __left, field);           \
-                }                                                       \
-        }                                                               \
-        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
-}
-
-#define SPLAY_NEGINF    -1
-#define SPLAY_INF       1
-
-#define SPLAY_INSERT(name, x, y)        name##_SPLAY_INSERT(x, y)
-#define SPLAY_REMOVE(name, x, y)        name##_SPLAY_REMOVE(x, y)
-#define SPLAY_FIND(name, x, y)          name##_SPLAY_FIND(x, y)
-#define SPLAY_NEXT(name, x, y)          name##_SPLAY_NEXT(x, y)
-#define SPLAY_MIN(name, x)              (SPLAY_EMPTY(x) ? NULL  \
-                                        : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
-#define SPLAY_MAX(name, x)              (SPLAY_EMPTY(x) ? NULL  \
-                                        : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
-
-#define SPLAY_FOREACH(x, name, head)                                    \
-        for ((x) = SPLAY_MIN(name, head);                               \
-             (x) != NULL;                                               \
-             (x) = SPLAY_NEXT(name, head, x))
-
-/* Macros that define a red-black tree */
-#define RB_HEAD(name, type)                                             \
-struct name {                                                           \
-        struct type *rbh_root; /* root of the tree */                   \
-}
-
-#define RB_INITIALIZER(root)                                            \
-        { NULL }
-
-#define RB_INIT(root) do {                                              \
-        (root)->rbh_root = NULL;                                        \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_BLACK        0
-#define RB_RED          1
-#define RB_ENTRY(type)                                                  \
-struct {                                                                \
-        struct type *rbe_left;          /* left element */              \
-        struct type *rbe_right;         /* right element */             \
-        struct type *rbe_parent;        /* parent element */            \
-        int rbe_color;                  /* node color */                \
-}
-
-#define RB_LEFT(elm, field)             (elm)->field.rbe_left
-#define RB_RIGHT(elm, field)            (elm)->field.rbe_right
-#define RB_PARENT(elm, field)           (elm)->field.rbe_parent
-#define RB_COLOR(elm, field)            (elm)->field.rbe_color
-#define RB_ROOT(head)                   (head)->rbh_root
-#define RB_EMPTY(head)                  (RB_ROOT(head) == NULL)
-
-#define RB_SET(elm, parent, field) do {                                 \
-        RB_PARENT(elm, field) = parent;                                 \
-        RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;              \
-        RB_COLOR(elm, field) = RB_RED;                                  \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_SET_BLACKRED(black, red, field) do {                         \
-        RB_COLOR(black, field) = RB_BLACK;                              \
-        RB_COLOR(red, field) = RB_RED;                                  \
-} while (/*CONSTCOND*/ 0)
-
-#ifndef RB_AUGMENT
-#define RB_AUGMENT(x)   do {} while (0)
-#endif
-
-#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                      \
-        (tmp) = RB_RIGHT(elm, field);                                   \
-        if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {     \
-                RB_PARENT(RB_LEFT(tmp, field), field) = (elm);          \
-        }                                                               \
-        RB_AUGMENT(elm);                                                \
-        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
-                if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
-                        RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
-                else                                                    \
-                        RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
-        } else                                                          \
-                (head)->rbh_root = (tmp);                               \
-        RB_LEFT(tmp, field) = (elm);                                    \
-        RB_PARENT(elm, field) = (tmp);                                  \
-        RB_AUGMENT(tmp);                                                \
-        if ((RB_PARENT(tmp, field)))                                    \
-                RB_AUGMENT(RB_PARENT(tmp, field));                      \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                     \
-        (tmp) = RB_LEFT(elm, field);                                    \
-        if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {     \
-                RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);         \
-        }                                                               \
-        RB_AUGMENT(elm);                                                \
-        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
-                if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
-                        RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
-                else                                                    \
-                        RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
-        } else                                                          \
-                (head)->rbh_root = (tmp);                               \
-        RB_RIGHT(tmp, field) = (elm);                                   \
-        RB_PARENT(elm, field) = (tmp);                                  \
-        RB_AUGMENT(tmp);                                                \
-        if ((RB_PARENT(tmp, field)))                                    \
-                RB_AUGMENT(RB_PARENT(tmp, field));                      \
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-#define RB_PROTOTYPE(name, type, field, cmp)                            \
-        RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
-#define RB_PROTOTYPE_STATIC(name, type, field, cmp)                     \
-        RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static)
-#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)             \
-attr void name##_RB_INSERT_COLOR(struct name *, struct type *);         \
-attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
-attr struct type *name##_RB_REMOVE(struct name *, struct type *);       \
-attr struct type *name##_RB_INSERT(struct name *, struct type *);       \
-attr struct type *name##_RB_FIND(struct name *, struct type *);         \
-attr struct type *name##_RB_NFIND(struct name *, struct type *);        \
-attr struct type *name##_RB_NEXT(struct type *);                        \
-attr struct type *name##_RB_PREV(struct type *);                        \
-attr struct type *name##_RB_MINMAX(struct name *, int);                 \
-                                                                        \
-
-/* Main rb operation.
- * Moves node close to the key of elm to top
- */
-#define RB_GENERATE(name, type, field, cmp)                             \
-        RB_GENERATE_INTERNAL(name, type, field, cmp,)
-#define RB_GENERATE_STATIC(name, type, field, cmp)                      \
-        RB_GENERATE_INTERNAL(name, type, field, cmp, static)
-#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)              \
-attr void                                                               \
-name##_RB_INSERT_COLOR(struct name *head, struct type *elm)             \
-{                                                                       \
-        struct type *parent, *gparent, *tmp;                            \
-        while ((parent = RB_PARENT(elm, field)) != NULL &&              \
-            RB_COLOR(parent, field) == RB_RED) {                        \
-                gparent = RB_PARENT(parent, field);                     \
-                if (parent == RB_LEFT(gparent, field)) {                \
-                        tmp = RB_RIGHT(gparent, field);                 \
-                        if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
-                                RB_COLOR(tmp, field) = RB_BLACK;        \
-                                RB_SET_BLACKRED(parent, gparent, field);\
-                                elm = gparent;                          \
-                                continue;                               \
-                        }                                               \
-                        if (RB_RIGHT(parent, field) == elm) {           \
-                                RB_ROTATE_LEFT(head, parent, tmp, field);\
-                                tmp = parent;                           \
-                                parent = elm;                           \
-                                elm = tmp;                              \
-                        }                                               \
-                        RB_SET_BLACKRED(parent, gparent, field);        \
-                        RB_ROTATE_RIGHT(head, gparent, tmp, field);     \
-                } else {                                                \
-                        tmp = RB_LEFT(gparent, field);                  \
-                        if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
-                                RB_COLOR(tmp, field) = RB_BLACK;        \
-                                RB_SET_BLACKRED(parent, gparent, field);\
-                                elm = gparent;                          \
-                                continue;                               \
-                        }                                               \
-                        if (RB_LEFT(parent, field) == elm) {            \
-                                RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                                tmp = parent;                           \
-                                parent = elm;                           \
-                                elm = tmp;                              \
-                        }                                               \
-                        RB_SET_BLACKRED(parent, gparent, field);        \
-                        RB_ROTATE_LEFT(head, gparent, tmp, field);      \
-                }                                                       \
-        }                                                               \
-        RB_COLOR(head->rbh_root, field) = RB_BLACK;                     \
-}                                                                       \
-                                                                        \
-attr void                                                               \
-name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
-{                                                                       \
-        struct type *tmp;                                               \
-        while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&     \
-            elm != RB_ROOT(head)) {                                     \
-                if (RB_LEFT(parent, field) == elm) {                    \
-                        tmp = RB_RIGHT(parent, field);                  \
-                        if (RB_COLOR(tmp, field) == RB_RED) {           \
-                                RB_SET_BLACKRED(tmp, parent, field);    \
-                                RB_ROTATE_LEFT(head, parent, tmp, field);\
-                                tmp = RB_RIGHT(parent, field);          \
-                        }                                               \
-                        if ((RB_LEFT(tmp, field) == NULL ||             \
-                            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-                            (RB_RIGHT(tmp, field) == NULL ||            \
-                            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-                                RB_COLOR(tmp, field) = RB_RED;          \
-                                elm = parent;                           \
-                                parent = RB_PARENT(elm, field);         \
-                        } else {                                        \
-                                if (RB_RIGHT(tmp, field) == NULL ||     \
-                                    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
-                                        struct type *oleft;             \
-                                        if ((oleft = RB_LEFT(tmp, field)) \
-                                            != NULL)                    \
-                                                RB_COLOR(oleft, field) = RB_BLACK;\
-                                        RB_COLOR(tmp, field) = RB_RED;  \
-                                        RB_ROTATE_RIGHT(head, tmp, oleft, field);\
-                                        tmp = RB_RIGHT(parent, field);  \
-                                }                                       \
-                                RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-                                RB_COLOR(parent, field) = RB_BLACK;     \
-                                if (RB_RIGHT(tmp, field))               \
-                                        RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
-                                RB_ROTATE_LEFT(head, parent, tmp, field);\
-                                elm = RB_ROOT(head);                    \
-                                break;                                  \
-                        }                                               \
-                } else {                                                \
-                        tmp = RB_LEFT(parent, field);                   \
-                        if (RB_COLOR(tmp, field) == RB_RED) {           \
-                                RB_SET_BLACKRED(tmp, parent, field);    \
-                                RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                                tmp = RB_LEFT(parent, field);           \
-                        }                                               \
-                        if ((RB_LEFT(tmp, field) == NULL ||             \
-                            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-                            (RB_RIGHT(tmp, field) == NULL ||            \
-                            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-                                RB_COLOR(tmp, field) = RB_RED;          \
-                                elm = parent;                           \
-                                parent = RB_PARENT(elm, field);         \
-                        } else {                                        \
-                                if (RB_LEFT(tmp, field) == NULL ||      \
-                                    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
-                                        struct type *oright;            \
-                                        if ((oright = RB_RIGHT(tmp, field)) \
-                                            != NULL)                    \
-                                                RB_COLOR(oright, field) = RB_BLACK;\
-                                        RB_COLOR(tmp, field) = RB_RED;  \
-                                        RB_ROTATE_LEFT(head, tmp, oright, field);\
-                                        tmp = RB_LEFT(parent, field);   \
-                                }                                       \
-                                RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-                                RB_COLOR(parent, field) = RB_BLACK;     \
-                                if (RB_LEFT(tmp, field))                \
-                                        RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
-                                RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                                elm = RB_ROOT(head);                    \
-                                break;                                  \
-                        }                                               \
-                }                                                       \
-        }                                                               \
-        if (elm)                                                        \
-                RB_COLOR(elm, field) = RB_BLACK;                        \
-}                                                                       \
-                                                                        \
-attr struct type *                                                      \
-name##_RB_REMOVE(struct name *head, struct type *elm)                   \
-{                                                                       \
-        struct type *child, *parent, *old = elm;                        \
-        int color;                                                      \
-        if (RB_LEFT(elm, field) == NULL)                                \
-                child = RB_RIGHT(elm, field);                           \
-        else if (RB_RIGHT(elm, field) == NULL)                          \
-                child = RB_LEFT(elm, field);                            \
-        else {                                                          \
-                struct type *left;                                      \
-                elm = RB_RIGHT(elm, field);                             \
-                while ((left = RB_LEFT(elm, field)) != NULL)            \
-                        elm = left;                                     \
-                child = RB_RIGHT(elm, field);                           \
-                parent = RB_PARENT(elm, field);                         \
-                color = RB_COLOR(elm, field);                           \
-                if (child)                                              \
-                        RB_PARENT(child, field) = parent;               \
-                if (parent) {                                           \
-                        if (RB_LEFT(parent, field) == elm)              \
-                                RB_LEFT(parent, field) = child;         \
-                        else                                            \
-                                RB_RIGHT(parent, field) = child;        \
-                        RB_AUGMENT(parent);                             \
-                } else                                                  \
-                        RB_ROOT(head) = child;                          \
-                if (RB_PARENT(elm, field) == old)                       \
-                        parent = elm;                                   \
-                (elm)->field = (old)->field;                            \
-                if (RB_PARENT(old, field)) {                            \
-                        if (RB_LEFT(RB_PARENT(old, field), field) == old)\
-                                RB_LEFT(RB_PARENT(old, field), field) = elm;\
-                        else                                            \
-                                RB_RIGHT(RB_PARENT(old, field), field) = elm;\
-                        RB_AUGMENT(RB_PARENT(old, field));              \
-                } else                                                  \
-                        RB_ROOT(head) = elm;                            \
-                RB_PARENT(RB_LEFT(old, field), field) = elm;            \
-                if (RB_RIGHT(old, field))                               \
-                        RB_PARENT(RB_RIGHT(old, field), field) = elm;   \
-                if (parent) {                                           \
-                        left = parent;                                  \
-                        do {                                            \
-                                RB_AUGMENT(left);                       \
-                        } while ((left = RB_PARENT(left, field)) != NULL); \
-                }                                                       \
-                goto color;                                             \
-        }                                                               \
-        parent = RB_PARENT(elm, field);                                 \
-        color = RB_COLOR(elm, field);                                   \
-        if (child)                                                      \
-                RB_PARENT(child, field) = parent;                       \
-        if (parent) {                                                   \
-                if (RB_LEFT(parent, field) == elm)                      \
-                        RB_LEFT(parent, field) = child;                 \
-                else                                                    \
-                        RB_RIGHT(parent, field) = child;                \
-                RB_AUGMENT(parent);                                     \
-        } else                                                          \
-                RB_ROOT(head) = child;                                  \
-color:                                                                  \
-        if (color == RB_BLACK)                                          \
-                name##_RB_REMOVE_COLOR(head, parent, child);            \
-        return (old);                                                   \
-}                                                                       \
-                                                                        \
-/* Inserts a node into the RB tree */                                   \
-attr struct type *                                                      \
-name##_RB_INSERT(struct name *head, struct type *elm)                   \
-{                                                                       \
-        struct type *tmp;                                               \
-        struct type *parent = NULL;                                     \
-        int comp = 0;                                                   \
-        tmp = RB_ROOT(head);                                            \
-        while (tmp) {                                                   \
-                parent = tmp;                                           \
-                comp = (cmp)(elm, parent);                              \
-                if (comp < 0)                                           \
-                        tmp = RB_LEFT(tmp, field);                      \
-                else if (comp > 0)                                      \
-                        tmp = RB_RIGHT(tmp, field);                     \
-                else                                                    \
-                        return (tmp);                                   \
-        }                                                               \
-        RB_SET(elm, parent, field);                                     \
-        if (parent != NULL) {                                           \
-                if (comp < 0)                                           \
-                        RB_LEFT(parent, field) = elm;                   \
-                else                                                    \
-                        RB_RIGHT(parent, field) = elm;                  \
-                RB_AUGMENT(parent);                                     \
-        } else                                                          \
-                RB_ROOT(head) = elm;                                    \
-        name##_RB_INSERT_COLOR(head, elm);                              \
-        return (NULL);                                                  \
-}                                                                       \
-                                                                        \
-/* Finds the node with the same key as elm */                           \
-attr struct type *                                                      \
-name##_RB_FIND(struct name *head, struct type *elm)                     \
-{                                                                       \
-        struct type *tmp = RB_ROOT(head);                               \
-        int comp;                                                       \
-        while (tmp) {                                                   \
-                comp = cmp(elm, tmp);                                   \
-                if (comp < 0)                                           \
-                        tmp = RB_LEFT(tmp, field);                      \
-                else if (comp > 0)                                      \
-                        tmp = RB_RIGHT(tmp, field);                     \
-                else                                                    \
-                        return (tmp);                                   \
-        }                                                               \
-        return (NULL);                                                  \
-}                                                                       \
-                                                                        \
-/* Finds the first node greater than or equal to the search key */      \
-attr struct type *                                                      \
-name##_RB_NFIND(struct name *head, struct type *elm)                    \
-{                                                                       \
-        struct type *tmp = RB_ROOT(head);                               \
-        struct type *res = NULL;                                        \
-        int comp;                                                       \
-        while (tmp) {                                                   \
-                comp = cmp(elm, tmp);                                   \
-                if (comp < 0) {                                         \
-                        res = tmp;                                      \
-                        tmp = RB_LEFT(tmp, field);                      \
-                }                                                       \
-                else if (comp > 0)                                      \
-                        tmp = RB_RIGHT(tmp, field);                     \
-                else                                                    \
-                        return (tmp);                                   \
-        }                                                               \
-        return (res);                                                   \
-}                                                                       \
-                                                                        \
-/* ARGSUSED */                                                          \
-attr struct type *                                                      \
-name##_RB_NEXT(struct type *elm)                                        \
-{                                                                       \
-        if (RB_RIGHT(elm, field)) {                                     \
-                elm = RB_RIGHT(elm, field);                             \
-                while (RB_LEFT(elm, field))                             \
-                        elm = RB_LEFT(elm, field);                      \
-        } else {                                                        \
-                if (RB_PARENT(elm, field) &&                            \
-                    (elm == RB_LEFT(RB_PARENT(elm, field), field)))     \
-                        elm = RB_PARENT(elm, field);                    \
-                else {                                                  \
-                        while (RB_PARENT(elm, field) &&                 \
-                            (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
-                                elm = RB_PARENT(elm, field);            \
-                        elm = RB_PARENT(elm, field);                    \
-                }                                                       \
-        }                                                               \
-        return (elm);                                                   \
-}                                                                       \
-                                                                        \
-/* ARGSUSED */                                                          \
-attr struct type *                                                      \
-name##_RB_PREV(struct type *elm)                                        \
-{                                                                       \
-        if (RB_LEFT(elm, field)) {                                      \
-                elm = RB_LEFT(elm, field);                              \
-                while (RB_RIGHT(elm, field))                            \
-                        elm = RB_RIGHT(elm, field);                     \
-        } else {                                                        \
-                if (RB_PARENT(elm, field) &&                            \
-                    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))    \
-                        elm = RB_PARENT(elm, field);                    \
-                else {                                                  \
-                        while (RB_PARENT(elm, field) &&                 \
-                            (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
-                                elm = RB_PARENT(elm, field);            \
-                        elm = RB_PARENT(elm, field);                    \
-                }                                                       \
-        }                                                               \
-        return (elm);                                                   \
-}                                                                       \
-                                                                        \
-attr struct type *                                                      \
-name##_RB_MINMAX(struct name *head, int val)                            \
-{                                                                       \
-        struct type *tmp = RB_ROOT(head);                               \
-        struct type *parent = NULL;                                     \
-        while (tmp) {                                                   \
-                parent = tmp;                                           \
-                if (val < 0)                                            \
-                        tmp = RB_LEFT(tmp, field);                      \
-                else                                                    \
-                        tmp = RB_RIGHT(tmp, field);                     \
-        }                                                               \
-        return (parent);                                                \
-}
-
-#define RB_NEGINF       -1
-#define RB_INF  1
-
-#define RB_INSERT(name, x, y)   name##_RB_INSERT(x, y)
-#define RB_REMOVE(name, x, y)   name##_RB_REMOVE(x, y)
-#define RB_FIND(name, x, y)     name##_RB_FIND(x, y)
-#define RB_NFIND(name, x, y)    name##_RB_NFIND(x, y)
-#define RB_NEXT(name, x, y)     name##_RB_NEXT(y)
-#define RB_PREV(name, x, y)     name##_RB_PREV(y)
-#define RB_MIN(name, x)         name##_RB_MINMAX(x, RB_NEGINF)
-#define RB_MAX(name, x)         name##_RB_MINMAX(x, RB_INF)
-
-#define RB_FOREACH(x, name, head)                                       \
-        for ((x) = RB_MIN(name, head);                                  \
-             (x) != NULL;                                               \
-             (x) = name##_RB_NEXT(x))
-
-#define RB_FOREACH_FROM(x, name, y)                                     \
-        for ((x) = (y);                                                 \
-            ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
-             (x) = (y))
-
-#define RB_FOREACH_SAFE(x, name, head, y)                               \
-        for ((x) = RB_MIN(name, head);                                  \
-            ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
-             (x) = (y))
-
-#define RB_FOREACH_REVERSE(x, name, head)                               \
-        for ((x) = RB_MAX(name, head);                                  \
-             (x) != NULL;                                               \
-             (x) = name##_RB_PREV(x))
-
-#define RB_FOREACH_REVERSE_FROM(x, name, y)                             \
-        for ((x) = (y);                                                 \
-            ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
-             (x) = (y))
-
-#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                       \
-        for ((x) = RB_MAX(name, head);                                  \
-            ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
-             (x) = (y))
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/lss/LICENSE b/app/src/main/cpp/shadowhook/third_party/lss/LICENSE
deleted file mode 100644
index 58ab3fb..0000000
--- a/app/src/main/cpp/shadowhook/third_party/lss/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) 2005-2011, Google Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-   Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-   Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-   Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h b/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h
deleted file mode 100644
index d3791cd..0000000
--- a/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h
+++ /dev/null
@@ -1,4867 +0,0 @@
-/* Copyright (c) 2005-2011, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ---
- * Author: Markus Gutschke
- */
-
-/* This file includes Linux-specific support functions common to the
- * coredumper and the thread lister; primarily, this is a collection
- * of direct system calls, and a couple of symbols missing from
- * standard header files.
- * There are a few options that the including file can set to control
- * the behavior of this file:
- *
- * SYS_CPLUSPLUS:
- *   The entire header file will normally be wrapped in 'extern "C" { }",
- *   making it suitable for compilation as both C and C++ source. If you
- *   do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit
- *   the wrapping. N.B. doing so will suppress inclusion of all prerequisite
- *   system header files, too. It is the caller's responsibility to provide
- *   the necessary definitions.
- *
- * SYS_ERRNO:
- *   All system calls will update "errno" unless overriden by setting the
- *   SYS_ERRNO macro prior to including this file. SYS_ERRNO should be
- *   an l-value.
- *
- * SYS_INLINE:
- *   New symbols will be defined "static inline", unless overridden by
- *   the SYS_INLINE macro.
- *
- * SYS_LINUX_SYSCALL_SUPPORT_H
- *   This macro is used to avoid multiple inclusions of this header file.
- *   If you need to include this file more than once, make sure to
- *   unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion.
- *
- * SYS_PREFIX:
- *   New system calls will have a prefix of "sys_" unless overridden by
- *   the SYS_PREFIX macro. Valid values for this macro are [0..9] which
- *   results in prefixes "sys[0..9]_". It is also possible to set this
- *   macro to -1, which avoids all prefixes.
- *
- * SYS_SYSCALL_ENTRYPOINT:
- *   Some applications (such as sandboxes that filter system calls), need
- *   to be able to run custom-code each time a system call is made. If this
- *   macro is defined, it expands to the name of a "common" symbol. If
- *   this symbol is assigned a non-NULL pointer value, it is used as the
- *   address of the system call entrypoint.
- *   A pointer to this symbol can be obtained by calling
- *   get_syscall_entrypoint()
- *
- * This file defines a few internal symbols that all start with "LSS_".
- * Do not access these symbols from outside this file. They are not part
- * of the supported API.
- */
-#ifndef SYS_LINUX_SYSCALL_SUPPORT_H
-#define SYS_LINUX_SYSCALL_SUPPORT_H
-
-/* We currently only support x86-32, x86-64, ARM, MIPS, PPC, s390 and s390x
- * on Linux.
- * Porting to other related platforms should not be difficult.
- */
-#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) ||   \
-     defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__) || \
-     defined(__aarch64__) || defined(__s390__) || defined(__e2k__))  \
-  && (defined(__linux) || defined(__ANDROID__))
-
-#ifndef SYS_CPLUSPLUS
-#ifdef __cplusplus
-/* Some system header files in older versions of gcc neglect to properly
- * handle being included from C++. As it appears to be harmless to have
- * multiple nested 'extern "C"' blocks, just add another one here.
- */
-extern "C" {
-#endif
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-#include <linux/unistd.h>
-#include <endian.h>
-
-#ifdef __mips__
-/* Include definitions of the ABI currently in use.                          */
-#ifdef __ANDROID__
-/* Android doesn't have sgidefs.h, but does have asm/sgidefs.h,
- * which has the definitions we need.
- */
-#include <asm/sgidefs.h>
-#else
-#include <sgidefs.h>
-#endif
-#endif
-#endif
-
-/* Some libcs, for example Android NDK and musl, #define these
- * macros as aliases to their non-64 counterparts. To avoid naming
- * conflict, remove them.
- *
- * These are restored by the corresponding #pragma pop_macro near
- * the end of this file.
- */
-#pragma push_macro("stat64")
-#pragma push_macro("fstat64")
-#pragma push_macro("lstat64")
-#pragma push_macro("pread64")
-#pragma push_macro("pwrite64")
-#pragma push_macro("getdents64")
-#undef stat64
-#undef fstat64
-#undef lstat64
-#undef pread64
-#undef pwrite64
-#undef getdents64
-
-#if defined(__ANDROID__) && defined(__x86_64__)
-// A number of x86_64 syscalls are blocked by seccomp on recent Android;
-// undefine them so that modern alternatives will be used instead where
-// possible.
-// The alternative syscalls have been sanity checked against linux-3.4+;
-// older versions might not work.
-# undef __NR_getdents
-# undef __NR_dup2
-# undef __NR_fork
-# undef __NR_getpgrp
-# undef __NR_open
-# undef __NR_poll
-# undef __NR_readlink
-# undef __NR_stat
-# undef __NR_unlink
-# undef __NR_pipe
-#endif
-
-#if defined(__ANDROID__)
-// waitpid is blocked by seccomp on all architectures on recent Android.
-# undef __NR_waitpid
-#endif
-
-/* As glibc often provides subtly incompatible data structures (and implicit
- * wrapper functions that convert them), we provide our own kernel data
- * structures for use by the system calls.
- * These structures have been developed by using Linux 2.6.23 headers for
- * reference. Note though, we do not care about exact API compatibility
- * with the kernel, and in fact the kernel often does not have a single
- * API that works across architectures. Instead, we try to mimic the glibc
- * API where reasonable, and only guarantee ABI compatibility with the
- * kernel headers.
- * Most notably, here are a few changes that were made to the structures
- * defined by kernel headers:
- *
- * - we only define structures, but not symbolic names for kernel data
- *   types. For the latter, we directly use the native C datatype
- *   (i.e. "unsigned" instead of "mode_t").
- * - in a few cases, it is possible to define identical structures for
- *   both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by
- *   standardizing on the 64bit version of the data types. In particular,
- *   this means that we use "unsigned" where the 32bit headers say
- *   "unsigned long".
- * - overall, we try to minimize the number of cases where we need to
- *   conditionally define different structures.
- * - the "struct kernel_sigaction" class of structures have been
- *   modified to more closely mimic glibc's API by introducing an
- *   anonymous union for the function pointer.
- * - a small number of field names had to have an underscore appended to
- *   them, because glibc defines a global macro by the same name.
- */
-
-/* include/linux/dirent.h                                                    */
-struct kernel_dirent64 {
-  unsigned long long d_ino;
-  long long          d_off;
-  unsigned short     d_reclen;
-  unsigned char      d_type;
-  char               d_name[256];
-};
-
-/* include/linux/dirent.h                                                    */
-#if !defined(__NR_getdents)
-// when getdents is not available, getdents64 is used for both.
-#define kernel_dirent kernel_dirent64
-#else
-struct kernel_dirent {
-  long               d_ino;
-  long               d_off;
-  unsigned short     d_reclen;
-  char               d_name[256];
-};
-#endif
-
-/* include/linux/uio.h                                                       */
-struct kernel_iovec {
-  void               *iov_base;
-  unsigned long      iov_len;
-};
-
-/* include/linux/socket.h                                                    */
-struct kernel_msghdr {
-  void               *msg_name;
-  int                msg_namelen;
-  struct kernel_iovec*msg_iov;
-  unsigned long      msg_iovlen;
-  void               *msg_control;
-  unsigned long      msg_controllen;
-  unsigned           msg_flags;
-};
-
-/* include/asm-generic/poll.h                                                */
-struct kernel_pollfd {
-  int                fd;
-  short              events;
-  short              revents;
-};
-
-/* include/linux/resource.h                                                  */
-struct kernel_rlimit {
-  unsigned long      rlim_cur;
-  unsigned long      rlim_max;
-};
-
-/* include/linux/time.h                                                      */
-struct kernel_timespec {
-  long               tv_sec;
-  long               tv_nsec;
-};
-
-/* include/linux/time.h                                                      */
-struct kernel_timeval {
-  long               tv_sec;
-  long               tv_usec;
-};
-
-/* include/linux/resource.h                                                  */
-struct kernel_rusage {
-  struct kernel_timeval ru_utime;
-  struct kernel_timeval ru_stime;
-  long               ru_maxrss;
-  long               ru_ixrss;
-  long               ru_idrss;
-  long               ru_isrss;
-  long               ru_minflt;
-  long               ru_majflt;
-  long               ru_nswap;
-  long               ru_inblock;
-  long               ru_oublock;
-  long               ru_msgsnd;
-  long               ru_msgrcv;
-  long               ru_nsignals;
-  long               ru_nvcsw;
-  long               ru_nivcsw;
-};
-
-#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \
-  || defined(__PPC__) || (defined(__s390__) && !defined(__s390x__)) \
-  || defined(__e2k__)
-
-/* include/asm-{arm,i386,mips,ppc}/signal.h                                  */
-struct kernel_old_sigaction {
-  union {
-    void             (*sa_handler_)(int);
-    void             (*sa_sigaction_)(int, siginfo_t *, void *);
-  };
-  unsigned long      sa_mask;
-  unsigned long      sa_flags;
-  void               (*sa_restorer)(void);
-} __attribute__((packed,aligned(4)));
-#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
-  #define kernel_old_sigaction kernel_sigaction
-#elif defined(__aarch64__)
-  // No kernel_old_sigaction defined for arm64.
-#endif
-
-/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the
- * exactly match the size of the signal set, even though the API was
- * intended to be extensible. We define our own KERNEL_NSIG to deal with
- * this.
- * Please note that glibc provides signals [1.._NSIG-1], whereas the
- * kernel (and this header) provides the range [1..KERNEL_NSIG]. The
- * actual number of signals is obviously the same, but the constants
- * differ by one.
- */
-#ifdef __mips__
-#define KERNEL_NSIG 128
-#else
-#define KERNEL_NSIG  64
-#endif
-
-/* include/asm-{arm,aarch64,i386,mips,x86_64}/signal.h                       */
-struct kernel_sigset_t {
-  unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/
-                    (8*sizeof(unsigned long))];
-};
-
-/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h                           */
-struct kernel_sigaction {
-#ifdef __mips__
-  unsigned long      sa_flags;
-  union {
-    void             (*sa_handler_)(int);
-    void             (*sa_sigaction_)(int, siginfo_t *, void *);
-  };
-  struct kernel_sigset_t sa_mask;
-#else
-  union {
-    void             (*sa_handler_)(int);
-    void             (*sa_sigaction_)(int, siginfo_t *, void *);
-  };
-  unsigned long      sa_flags;
-  void               (*sa_restorer)(void);
-  struct kernel_sigset_t sa_mask;
-#endif
-};
-
-/* include/linux/socket.h                                                    */
-struct kernel_sockaddr {
-  unsigned short     sa_family;
-  char               sa_data[14];
-};
-
-/* include/asm-{arm,aarch64,i386,mips,ppc,s390}/stat.h                       */
-#ifdef __mips__
-#if _MIPS_SIM == _MIPS_SIM_ABI64
-struct kernel_stat {
-#else
-struct kernel_stat64 {
-#endif
-  unsigned           st_dev;
-  unsigned           __pad0[3];
-  unsigned long long st_ino;
-  unsigned           st_mode;
-  unsigned           st_nlink;
-  unsigned           st_uid;
-  unsigned           st_gid;
-  unsigned           st_rdev;
-  unsigned           __pad1[3];
-  long long          st_size;
-  unsigned           st_atime_;
-  unsigned           st_atime_nsec_;
-  unsigned           st_mtime_;
-  unsigned           st_mtime_nsec_;
-  unsigned           st_ctime_;
-  unsigned           st_ctime_nsec_;
-  unsigned           st_blksize;
-  unsigned           __pad2;
-  unsigned long long st_blocks;
-};
-#elif defined __PPC__
-struct kernel_stat64 {
-  unsigned long long st_dev;
-  unsigned long long st_ino;
-  unsigned           st_mode;
-  unsigned           st_nlink;
-  unsigned           st_uid;
-  unsigned           st_gid;
-  unsigned long long st_rdev;
-  unsigned short int __pad2;
-  long long          st_size;
-  long               st_blksize;
-  long long          st_blocks;
-  long               st_atime_;
-  unsigned long      st_atime_nsec_;
-  long               st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  long               st_ctime_;
-  unsigned long      st_ctime_nsec_;
-  unsigned long      __unused4;
-  unsigned long      __unused5;
-};
-#elif defined(__e2k__)
-struct kernel_stat64 {
-  unsigned long long st_dev;
-  unsigned long long st_ino;
-  unsigned int       st_mode;
-  unsigned int       st_nlink;
-  unsigned int       st_uid;
-  unsigned int       st_gid;
-  unsigned long long st_rdev;
-  long long          st_size;
-  int                st_blksize;
-  int                __pad2;
-  unsigned long long st_blocks;
-  int                st_atime_;
-  unsigned int       st_atime_nsec_;
-  int                st_mtime_;
-  unsigned int       st_mtime_nsec_;
-  int                st_ctime_;
-  unsigned int       st_ctime_nsec_;
-  unsigned int       __unused4;
-  unsigned int       __unused5;
-};
-#else
-struct kernel_stat64 {
-  unsigned long long st_dev;
-  unsigned char      __pad0[4];
-  unsigned           __st_ino;
-  unsigned           st_mode;
-  unsigned           st_nlink;
-  unsigned           st_uid;
-  unsigned           st_gid;
-  unsigned long long st_rdev;
-  unsigned char      __pad3[4];
-  long long          st_size;
-  unsigned           st_blksize;
-  unsigned long long st_blocks;
-  unsigned           st_atime_;
-  unsigned           st_atime_nsec_;
-  unsigned           st_mtime_;
-  unsigned           st_mtime_nsec_;
-  unsigned           st_ctime_;
-  unsigned           st_ctime_nsec_;
-  unsigned long long st_ino;
-};
-#endif
-
-/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/stat.h                */
-#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
-struct kernel_stat {
-  /* The kernel headers suggest that st_dev and st_rdev should be 32bit
-   * quantities encoding 12bit major and 20bit minor numbers in an interleaved
-   * format. In reality, we do not see useful data in the top bits. So,
-   * we'll leave the padding in here, until we find a better solution.
-   */
-  unsigned short     st_dev;
-  short              pad1;
-  unsigned           st_ino;
-  unsigned short     st_mode;
-  unsigned short     st_nlink;
-  unsigned short     st_uid;
-  unsigned short     st_gid;
-  unsigned short     st_rdev;
-  short              pad2;
-  unsigned           st_size;
-  unsigned           st_blksize;
-  unsigned           st_blocks;
-  unsigned           st_atime_;
-  unsigned           st_atime_nsec_;
-  unsigned           st_mtime_;
-  unsigned           st_mtime_nsec_;
-  unsigned           st_ctime_;
-  unsigned           st_ctime_nsec_;
-  unsigned           __unused4;
-  unsigned           __unused5;
-};
-#elif defined(__x86_64__)
-struct kernel_stat {
-  uint64_t           st_dev;
-  uint64_t           st_ino;
-  uint64_t           st_nlink;
-  unsigned           st_mode;
-  unsigned           st_uid;
-  unsigned           st_gid;
-  unsigned           __pad0;
-  uint64_t           st_rdev;
-  int64_t            st_size;
-  int64_t            st_blksize;
-  int64_t            st_blocks;
-  uint64_t           st_atime_;
-  uint64_t           st_atime_nsec_;
-  uint64_t           st_mtime_;
-  uint64_t           st_mtime_nsec_;
-  uint64_t           st_ctime_;
-  uint64_t           st_ctime_nsec_;
-  int64_t            __unused4[3];
-};
-#elif defined(__PPC__)
-struct kernel_stat {
-  unsigned           st_dev;
-  unsigned long      st_ino;      // ino_t
-  unsigned long      st_mode;     // mode_t
-  unsigned short     st_nlink;    // nlink_t
-  unsigned           st_uid;      // uid_t
-  unsigned           st_gid;      // gid_t
-  unsigned           st_rdev;
-  long               st_size;     // off_t
-  unsigned long      st_blksize;
-  unsigned long      st_blocks;
-  unsigned long      st_atime_;
-  unsigned long      st_atime_nsec_;
-  unsigned long      st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  unsigned long      st_ctime_;
-  unsigned long      st_ctime_nsec_;
-  unsigned long      __unused4;
-  unsigned long      __unused5;
-};
-#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
-struct kernel_stat {
-  unsigned           st_dev;
-  int                st_pad1[3];
-  unsigned           st_ino;
-  unsigned           st_mode;
-  unsigned           st_nlink;
-  unsigned           st_uid;
-  unsigned           st_gid;
-  unsigned           st_rdev;
-  int                st_pad2[2];
-  long               st_size;
-  int                st_pad3;
-  long               st_atime_;
-  long               st_atime_nsec_;
-  long               st_mtime_;
-  long               st_mtime_nsec_;
-  long               st_ctime_;
-  long               st_ctime_nsec_;
-  int                st_blksize;
-  int                st_blocks;
-  int                st_pad4[14];
-};
-#elif defined(__aarch64__)
-struct kernel_stat {
-  unsigned long      st_dev;
-  unsigned long      st_ino;
-  unsigned int       st_mode;
-  unsigned int       st_nlink;
-  unsigned int       st_uid;
-  unsigned int       st_gid;
-  unsigned long      st_rdev;
-  unsigned long      __pad1;
-  long               st_size;
-  int                st_blksize;
-  int                __pad2;
-  long               st_blocks;
-  long               st_atime_;
-  unsigned long      st_atime_nsec_;
-  long               st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  long               st_ctime_;
-  unsigned long      st_ctime_nsec_;
-  unsigned int       __unused4;
-  unsigned int       __unused5;
-};
-#elif defined(__s390x__)
-struct kernel_stat {
-  unsigned long      st_dev;
-  unsigned long      st_ino;
-  unsigned long      st_nlink;
-  unsigned int       st_mode;
-  unsigned int       st_uid;
-  unsigned int       st_gid;
-  unsigned int       __pad1;
-  unsigned long      st_rdev;
-  unsigned long      st_size;
-  unsigned long      st_atime_;
-  unsigned long      st_atime_nsec_;
-  unsigned long      st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  unsigned long      st_ctime_;
-  unsigned long      st_ctime_nsec_;
-  unsigned long      st_blksize;
-  long               st_blocks;
-  unsigned long      __unused[3];
-};
-#elif defined(__s390__)
-struct kernel_stat {
-  unsigned short     st_dev;
-  unsigned short     __pad1;
-  unsigned long      st_ino;
-  unsigned short     st_mode;
-  unsigned short     st_nlink;
-  unsigned short     st_uid;
-  unsigned short     st_gid;
-  unsigned short     st_rdev;
-  unsigned short     __pad2;
-  unsigned long      st_size;
-  unsigned long      st_blksize;
-  unsigned long      st_blocks;
-  unsigned long      st_atime_;
-  unsigned long      st_atime_nsec_;
-  unsigned long      st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  unsigned long      st_ctime_;
-  unsigned long      st_ctime_nsec_;
-  unsigned long      __unused4;
-  unsigned long      __unused5;
-};
-#elif defined(__e2k__)
-struct kernel_stat {
-  unsigned long      st_dev;
-  unsigned long      st_ino;
-  unsigned int       st_mode;
-  unsigned long      st_nlink;
-  unsigned int       st_uid;
-  unsigned int       st_gid;
-  unsigned long      st_rdev;
-  unsigned long      st_size;
-  unsigned long      st_blksize;
-  unsigned long      st_blocks;
-  unsigned long      st_atime_;
-  unsigned long      st_atime_nsec_;
-  unsigned long      st_mtime_;
-  unsigned long      st_mtime_nsec_;
-  unsigned long      st_ctime_;
-  unsigned long      st_ctime_nsec_;
-};
-#endif
-
-/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/statfs.h              */
-#ifdef __mips__
-#if _MIPS_SIM != _MIPS_SIM_ABI64
-struct kernel_statfs64 {
-  unsigned long      f_type;
-  unsigned long      f_bsize;
-  unsigned long      f_frsize;
-  unsigned long      __pad;
-  unsigned long long f_blocks;
-  unsigned long long f_bfree;
-  unsigned long long f_files;
-  unsigned long long f_ffree;
-  unsigned long long f_bavail;
-  struct { int val[2]; } f_fsid;
-  unsigned long      f_namelen;
-  unsigned long      f_spare[6];
-};
-#endif
-#elif defined(__s390__)
-/* See also arch/s390/include/asm/compat.h                                   */
-struct kernel_statfs64 {
-  unsigned int       f_type;
-  unsigned int       f_bsize;
-  unsigned long long f_blocks;
-  unsigned long long f_bfree;
-  unsigned long long f_bavail;
-  unsigned long long f_files;
-  unsigned long long f_ffree;
-  struct { int val[2]; } f_fsid;
-  unsigned int       f_namelen;
-  unsigned int       f_frsize;
-  unsigned int       f_flags;
-  unsigned int       f_spare[4];
-};
-#elif !defined(__x86_64__)
-struct kernel_statfs64 {
-  unsigned long      f_type;
-  unsigned long      f_bsize;
-  unsigned long long f_blocks;
-  unsigned long long f_bfree;
-  unsigned long long f_bavail;
-  unsigned long long f_files;
-  unsigned long long f_ffree;
-  struct { int val[2]; } f_fsid;
-  unsigned long      f_namelen;
-  unsigned long      f_frsize;
-  unsigned long      f_spare[5];
-};
-#endif
-
-/* include/asm-{arm,i386,mips,x86_64,ppc,generic,s390}/statfs.h              */
-#ifdef __mips__
-struct kernel_statfs {
-  long               f_type;
-  long               f_bsize;
-  long               f_frsize;
-  long               f_blocks;
-  long               f_bfree;
-  long               f_files;
-  long               f_ffree;
-  long               f_bavail;
-  struct { int val[2]; } f_fsid;
-  long               f_namelen;
-  long               f_spare[6];
-};
-#elif defined(__x86_64__)
-struct kernel_statfs {
-  /* x86_64 actually defines all these fields as signed, whereas all other  */
-  /* platforms define them as unsigned. Leaving them at unsigned should not */
-  /* cause any problems. Make sure these are 64-bit even on x32.            */
-  uint64_t           f_type;
-  uint64_t           f_bsize;
-  uint64_t           f_blocks;
-  uint64_t           f_bfree;
-  uint64_t           f_bavail;
-  uint64_t           f_files;
-  uint64_t           f_ffree;
-  struct { int val[2]; } f_fsid;
-  uint64_t           f_namelen;
-  uint64_t           f_frsize;
-  uint64_t           f_spare[5];
-};
-#elif defined(__s390__)
-struct kernel_statfs {
-  unsigned int       f_type;
-  unsigned int       f_bsize;
-  unsigned long      f_blocks;
-  unsigned long      f_bfree;
-  unsigned long      f_bavail;
-  unsigned long      f_files;
-  unsigned long      f_ffree;
-  struct { int val[2]; } f_fsid;
-  unsigned int       f_namelen;
-  unsigned int       f_frsize;
-  unsigned int       f_flags;
-  unsigned int       f_spare[4];
-};
-#else
-struct kernel_statfs {
-  unsigned long      f_type;
-  unsigned long      f_bsize;
-  unsigned long      f_blocks;
-  unsigned long      f_bfree;
-  unsigned long      f_bavail;
-  unsigned long      f_files;
-  unsigned long      f_ffree;
-  struct { int val[2]; } f_fsid;
-  unsigned long      f_namelen;
-  unsigned long      f_frsize;
-  unsigned long      f_spare[5];
-};
-#endif
-
-
-/* Definitions missing from the standard header files                        */
-#ifndef O_DIRECTORY
-#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || defined(__aarch64__)
-#define O_DIRECTORY             0040000
-#else
-#define O_DIRECTORY             0200000
-#endif
-#endif
-#ifndef NT_PRXFPREG
-#define NT_PRXFPREG             0x46e62b7f
-#endif
-#ifndef PTRACE_GETFPXREGS
-#define PTRACE_GETFPXREGS       ((enum __ptrace_request)18)
-#endif
-#ifndef PR_GET_DUMPABLE
-#define PR_GET_DUMPABLE         3
-#endif
-#ifndef PR_SET_DUMPABLE
-#define PR_SET_DUMPABLE         4
-#endif
-#ifndef PR_GET_SECCOMP
-#define PR_GET_SECCOMP          21
-#endif
-#ifndef PR_SET_SECCOMP
-#define PR_SET_SECCOMP          22
-#endif
-#ifndef AT_FDCWD
-#define AT_FDCWD                (-100)
-#endif
-#ifndef AT_SYMLINK_NOFOLLOW
-#define AT_SYMLINK_NOFOLLOW     0x100
-#endif
-#ifndef AT_REMOVEDIR
-#define AT_REMOVEDIR            0x200
-#endif
-#ifndef MREMAP_FIXED
-#define MREMAP_FIXED            2
-#endif
-#ifndef SA_RESTORER
-#define SA_RESTORER             0x04000000
-#endif
-#ifndef CPUCLOCK_PROF
-#define CPUCLOCK_PROF           0
-#endif
-#ifndef CPUCLOCK_VIRT
-#define CPUCLOCK_VIRT           1
-#endif
-#ifndef CPUCLOCK_SCHED
-#define CPUCLOCK_SCHED          2
-#endif
-#ifndef CPUCLOCK_PERTHREAD_MASK
-#define CPUCLOCK_PERTHREAD_MASK 4
-#endif
-#ifndef MAKE_PROCESS_CPUCLOCK
-#define MAKE_PROCESS_CPUCLOCK(pid, clock)                                     \
-        ((int)(~(unsigned)(pid) << 3) | (int)(clock))
-#endif
-#ifndef MAKE_THREAD_CPUCLOCK
-#define MAKE_THREAD_CPUCLOCK(tid, clock)                                      \
-        ((int)(~(unsigned)(tid) << 3) |                                       \
-         (int)((clock) | CPUCLOCK_PERTHREAD_MASK))
-#endif
-
-#ifndef FUTEX_WAIT
-#define FUTEX_WAIT                0
-#endif
-#ifndef FUTEX_WAKE
-#define FUTEX_WAKE                1
-#endif
-#ifndef FUTEX_FD
-#define FUTEX_FD                  2
-#endif
-#ifndef FUTEX_REQUEUE
-#define FUTEX_REQUEUE             3
-#endif
-#ifndef FUTEX_CMP_REQUEUE
-#define FUTEX_CMP_REQUEUE         4
-#endif
-#ifndef FUTEX_WAKE_OP
-#define FUTEX_WAKE_OP             5
-#endif
-#ifndef FUTEX_LOCK_PI
-#define FUTEX_LOCK_PI             6
-#endif
-#ifndef FUTEX_UNLOCK_PI
-#define FUTEX_UNLOCK_PI           7
-#endif
-#ifndef FUTEX_TRYLOCK_PI
-#define FUTEX_TRYLOCK_PI          8
-#endif
-#ifndef FUTEX_PRIVATE_FLAG
-#define FUTEX_PRIVATE_FLAG        128
-#endif
-#ifndef FUTEX_CMD_MASK
-#define FUTEX_CMD_MASK            ~FUTEX_PRIVATE_FLAG
-#endif
-#ifndef FUTEX_WAIT_PRIVATE
-#define FUTEX_WAIT_PRIVATE        (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_WAKE_PRIVATE
-#define FUTEX_WAKE_PRIVATE        (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_REQUEUE_PRIVATE
-#define FUTEX_REQUEUE_PRIVATE     (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_CMP_REQUEUE_PRIVATE
-#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_WAKE_OP_PRIVATE
-#define FUTEX_WAKE_OP_PRIVATE     (FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_LOCK_PI_PRIVATE
-#define FUTEX_LOCK_PI_PRIVATE     (FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_UNLOCK_PI_PRIVATE
-#define FUTEX_UNLOCK_PI_PRIVATE   (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG)
-#endif
-#ifndef FUTEX_TRYLOCK_PI_PRIVATE
-#define FUTEX_TRYLOCK_PI_PRIVATE  (FUTEX_TRYLOCK_PI | FUTEX_PRIVATE_FLAG)
-#endif
-
-
-#if defined(__x86_64__)
-#ifndef ARCH_SET_GS
-#define ARCH_SET_GS             0x1001
-#endif
-#ifndef ARCH_GET_GS
-#define ARCH_GET_GS             0x1004
-#endif
-#endif
-
-#if defined(__i386__)
-#ifndef __NR_quotactl
-#define __NR_quotactl           131
-#endif
-#ifndef __NR_setresuid
-#define __NR_setresuid          164
-#define __NR_getresuid          165
-#define __NR_setresgid          170
-#define __NR_getresgid          171
-#endif
-#ifndef __NR_rt_sigaction
-#define __NR_rt_sigreturn       173
-#define __NR_rt_sigaction       174
-#define __NR_rt_sigprocmask     175
-#define __NR_rt_sigpending      176
-#define __NR_rt_sigsuspend      179
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64            180
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           181
-#endif
-#ifndef __NR_ugetrlimit
-#define __NR_ugetrlimit         191
-#endif
-#ifndef __NR_stat64
-#define __NR_stat64             195
-#endif
-#ifndef __NR_fstat64
-#define __NR_fstat64            197
-#endif
-#ifndef __NR_setresuid32
-#define __NR_setresuid32        208
-#define __NR_getresuid32        209
-#define __NR_setresgid32        210
-#define __NR_getresgid32        211
-#endif
-#ifndef __NR_setfsuid32
-#define __NR_setfsuid32         215
-#define __NR_setfsgid32         216
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         220
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             224
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          225
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           226
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          227
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           229
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          230
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          232
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         233
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              238
-#endif
-#ifndef __NR_futex
-#define __NR_futex              240
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  241
-#define __NR_sched_getaffinity  242
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    258
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      265
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       266
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           268
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          269
-#endif
-#ifndef __NR_fadvise64_64
-#define __NR_fadvise64_64       272
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         289
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         290
-#endif
-#ifndef __NR_openat
-#define __NR_openat             295
-#endif
-#ifndef __NR_fstatat64
-#define __NR_fstatat64          300
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           301
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         317
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             318
-#endif
-#ifndef __NR_fallocate
-#define __NR_fallocate          324
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          355
-#endif
-/* End of i386 definitions                                                   */
-#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
-#ifndef __NR_setresuid
-#define __NR_setresuid          (__NR_SYSCALL_BASE + 164)
-#define __NR_getresuid          (__NR_SYSCALL_BASE + 165)
-#define __NR_setresgid          (__NR_SYSCALL_BASE + 170)
-#define __NR_getresgid          (__NR_SYSCALL_BASE + 171)
-#endif
-#ifndef __NR_rt_sigaction
-#define __NR_rt_sigreturn       (__NR_SYSCALL_BASE + 173)
-#define __NR_rt_sigaction       (__NR_SYSCALL_BASE + 174)
-#define __NR_rt_sigprocmask     (__NR_SYSCALL_BASE + 175)
-#define __NR_rt_sigpending      (__NR_SYSCALL_BASE + 176)
-#define __NR_rt_sigsuspend      (__NR_SYSCALL_BASE + 179)
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64            (__NR_SYSCALL_BASE + 180)
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           (__NR_SYSCALL_BASE + 181)
-#endif
-#ifndef __NR_ugetrlimit
-#define __NR_ugetrlimit         (__NR_SYSCALL_BASE + 191)
-#endif
-#ifndef __NR_stat64
-#define __NR_stat64             (__NR_SYSCALL_BASE + 195)
-#endif
-#ifndef __NR_fstat64
-#define __NR_fstat64            (__NR_SYSCALL_BASE + 197)
-#endif
-#ifndef __NR_setresuid32
-#define __NR_setresuid32        (__NR_SYSCALL_BASE + 208)
-#define __NR_getresuid32        (__NR_SYSCALL_BASE + 209)
-#define __NR_setresgid32        (__NR_SYSCALL_BASE + 210)
-#define __NR_getresgid32        (__NR_SYSCALL_BASE + 211)
-#endif
-#ifndef __NR_setfsuid32
-#define __NR_setfsuid32         (__NR_SYSCALL_BASE + 215)
-#define __NR_setfsgid32         (__NR_SYSCALL_BASE + 216)
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         (__NR_SYSCALL_BASE + 217)
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             (__NR_SYSCALL_BASE + 224)
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          (__NR_SYSCALL_BASE + 225)
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           (__NR_SYSCALL_BASE + 226)
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          (__NR_SYSCALL_BASE + 227)
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           (__NR_SYSCALL_BASE + 229)
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          (__NR_SYSCALL_BASE + 230)
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          (__NR_SYSCALL_BASE + 232)
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         (__NR_SYSCALL_BASE + 233)
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              (__NR_SYSCALL_BASE + 238)
-#endif
-#ifndef __NR_futex
-#define __NR_futex              (__NR_SYSCALL_BASE + 240)
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  (__NR_SYSCALL_BASE + 241)
-#define __NR_sched_getaffinity  (__NR_SYSCALL_BASE + 242)
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    (__NR_SYSCALL_BASE + 256)
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      (__NR_SYSCALL_BASE + 263)
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       (__NR_SYSCALL_BASE + 264)
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           (__NR_SYSCALL_BASE + 266)
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          (__NR_SYSCALL_BASE + 267)
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         (__NR_SYSCALL_BASE + 314)
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         (__NR_SYSCALL_BASE + 315)
-#endif
-#ifndef __NR_fstatat64
-#define __NR_fstatat64          (__NR_SYSCALL_BASE + 327)
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         (__NR_SYSCALL_BASE + 344)
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             (__NR_SYSCALL_BASE + 345)
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          (__NR_SYSCALL_BASE + 384)
-#endif
-/* End of ARM 3/EABI definitions                                             */
-#elif defined(__aarch64__)
-#ifndef __NR_setxattr
-#define __NR_setxattr             5
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr            6
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr             8
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr            9
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr           11
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr          12
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set          30
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get          31
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat            35
-#endif
-#ifndef __NR_fallocate
-#define __NR_fallocate           47
-#endif
-#ifndef __NR_openat
-#define __NR_openat              56
-#endif
-#ifndef __NR_quotactl
-#define __NR_quotactl            60
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64          61
-#endif
-#ifndef __NR_getdents
-// when getdents is not available, getdents64 is used for both.
-#define __NR_getdents            __NR_getdents64
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64             67
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64            68
-#endif
-#ifndef __NR_ppoll
-#define __NR_ppoll               73
-#endif
-#ifndef __NR_readlinkat
-#define __NR_readlinkat          78
-#endif
-#ifndef __NR_newfstatat
-#define __NR_newfstatat          79
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address     96
-#endif
-#ifndef __NR_futex
-#define __NR_futex               98
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      113
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       114
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  122
-#define __NR_sched_getaffinity  123
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              130
-#endif
-#ifndef __NR_setresuid
-#define __NR_setresuid          147
-#define __NR_getresuid          148
-#define __NR_setresgid          149
-#define __NR_getresgid          150
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             178
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          213
-#endif
-#ifndef __NR_fadvise64
-#define __NR_fadvise64          223
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         239
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          278
-#endif
-/* End of aarch64 definitions                                                */
-#elif defined(__x86_64__)
-#ifndef __NR_pread64
-#define __NR_pread64             17
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64            18
-#endif
-#ifndef __NR_setresuid
-#define __NR_setresuid          117
-#define __NR_getresuid          118
-#define __NR_setresgid          119
-#define __NR_getresgid          120
-#endif
-#ifndef __NR_quotactl
-#define __NR_quotactl           179
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             186
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          187
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           188
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          189
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           191
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          192
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          194
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         195
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              200
-#endif
-#ifndef __NR_futex
-#define __NR_futex              202
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  203
-#define __NR_sched_getaffinity  204
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         217
-#endif
-#ifndef __NR_getdents
-// when getdents is not available, getdents64 is used for both.
-#define __NR_getdents           __NR_getdents64
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    218
-#endif
-#ifndef __NR_fadvise64
-#define __NR_fadvise64          221
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      228
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       229
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         251
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         252
-#endif
-#ifndef __NR_openat
-#define __NR_openat             257
-#endif
-#ifndef __NR_newfstatat
-#define __NR_newfstatat         262
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           263
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         279
-#endif
-#ifndef __NR_fallocate
-#define __NR_fallocate          285
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          318
-#endif
-/* End of x86-64 definitions                                                 */
-#elif defined(__mips__)
-#if _MIPS_SIM == _MIPS_SIM_ABI32
-#ifndef __NR_setresuid
-#define __NR_setresuid          (__NR_Linux + 185)
-#define __NR_getresuid          (__NR_Linux + 186)
-#define __NR_setresgid          (__NR_Linux + 190)
-#define __NR_getresgid          (__NR_Linux + 191)
-#endif
-#ifndef __NR_rt_sigaction
-#define __NR_rt_sigreturn       (__NR_Linux + 193)
-#define __NR_rt_sigaction       (__NR_Linux + 194)
-#define __NR_rt_sigprocmask     (__NR_Linux + 195)
-#define __NR_rt_sigpending      (__NR_Linux + 196)
-#define __NR_rt_sigsuspend      (__NR_Linux + 199)
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64            (__NR_Linux + 200)
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           (__NR_Linux + 201)
-#endif
-#ifndef __NR_stat64
-#define __NR_stat64             (__NR_Linux + 213)
-#endif
-#ifndef __NR_fstat64
-#define __NR_fstat64            (__NR_Linux + 215)
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         (__NR_Linux + 219)
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             (__NR_Linux + 222)
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          (__NR_Linux + 223)
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           (__NR_Linux + 224)
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          (__NR_Linux + 225)
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           (__NR_Linux + 227)
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          (__NR_Linux + 228)
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          (__NR_Linux + 230)
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         (__NR_Linux + 231)
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              (__NR_Linux + 236)
-#endif
-#ifndef __NR_futex
-#define __NR_futex              (__NR_Linux + 238)
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  (__NR_Linux + 239)
-#define __NR_sched_getaffinity  (__NR_Linux + 240)
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    (__NR_Linux + 252)
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           (__NR_Linux + 255)
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          (__NR_Linux + 256)
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      (__NR_Linux + 263)
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       (__NR_Linux + 264)
-#endif
-#ifndef __NR_openat
-#define __NR_openat             (__NR_Linux + 288)
-#endif
-#ifndef __NR_fstatat
-#define __NR_fstatat            (__NR_Linux + 293)
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           (__NR_Linux + 294)
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         (__NR_Linux + 308)
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             (__NR_Linux + 312)
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         (__NR_Linux + 314)
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         (__NR_Linux + 315)
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          (__NR_Linux + 353)
-#endif
-/* End of MIPS (old 32bit API) definitions */
-#elif  _MIPS_SIM == _MIPS_SIM_ABI64
-#ifndef __NR_pread64
-#define __NR_pread64            (__NR_Linux +  16)
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           (__NR_Linux +  17)
-#endif
-#ifndef __NR_setresuid
-#define __NR_setresuid          (__NR_Linux + 115)
-#define __NR_getresuid          (__NR_Linux + 116)
-#define __NR_setresgid          (__NR_Linux + 117)
-#define __NR_getresgid          (__NR_Linux + 118)
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             (__NR_Linux + 178)
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          (__NR_Linux + 179)
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           (__NR_Linux + 180)
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          (__NR_Linux + 181)
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           (__NR_Linux + 183)
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          (__NR_Linux + 184)
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          (__NR_Linux + 186)
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         (__NR_Linux + 187)
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              (__NR_Linux + 192)
-#endif
-#ifndef __NR_futex
-#define __NR_futex              (__NR_Linux + 194)
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  (__NR_Linux + 195)
-#define __NR_sched_getaffinity  (__NR_Linux + 196)
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    (__NR_Linux + 212)
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      (__NR_Linux + 222)
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       (__NR_Linux + 223)
-#endif
-#ifndef __NR_openat
-#define __NR_openat             (__NR_Linux + 247)
-#endif
-#ifndef __NR_fstatat
-#define __NR_fstatat            (__NR_Linux + 252)
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           (__NR_Linux + 253)
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         (__NR_Linux + 267)
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             (__NR_Linux + 271)
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         (__NR_Linux + 273)
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         (__NR_Linux + 274)
-#endif
-#ifndef __NR_getrandom
-#define __NR_getrandom          (__NR_Linux + 313)
-#endif
-/* End of MIPS (64bit API) definitions */
-#else
-#ifndef __NR_setresuid
-#define __NR_setresuid          (__NR_Linux + 115)
-#define __NR_getresuid          (__NR_Linux + 116)
-#define __NR_setresgid          (__NR_Linux + 117)
-#define __NR_getresgid          (__NR_Linux + 118)
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             (__NR_Linux + 178)
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          (__NR_Linux + 179)
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           (__NR_Linux + 180)
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          (__NR_Linux + 181)
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           (__NR_Linux + 183)
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          (__NR_Linux + 184)
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          (__NR_Linux + 186)
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         (__NR_Linux + 187)
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              (__NR_Linux + 192)
-#endif
-#ifndef __NR_futex
-#define __NR_futex              (__NR_Linux + 194)
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  (__NR_Linux + 195)
-#define __NR_sched_getaffinity  (__NR_Linux + 196)
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    (__NR_Linux + 213)
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           (__NR_Linux + 217)
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          (__NR_Linux + 218)
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      (__NR_Linux + 226)
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       (__NR_Linux + 227)
-#endif
-#ifndef __NR_openat
-#define __NR_openat             (__NR_Linux + 251)
-#endif
-#ifndef __NR_fstatat
-#define __NR_fstatat            (__NR_Linux + 256)
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           (__NR_Linux + 257)
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         (__NR_Linux + 271)
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             (__NR_Linux + 275)
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         (__NR_Linux + 277)
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         (__NR_Linux + 278)
-#endif
-/* End of MIPS (new 32bit API) definitions                                   */
-#endif
-/* End of MIPS definitions                                                   */
-#elif defined(__PPC__)
-#ifndef __NR_setfsuid
-#define __NR_setfsuid           138
-#define __NR_setfsgid           139
-#endif
-#ifndef __NR_setresuid
-#define __NR_setresuid          164
-#define __NR_getresuid          165
-#define __NR_setresgid          169
-#define __NR_getresgid          170
-#endif
-#ifndef __NR_rt_sigaction
-#define __NR_rt_sigreturn       172
-#define __NR_rt_sigaction       173
-#define __NR_rt_sigprocmask     174
-#define __NR_rt_sigpending      175
-#define __NR_rt_sigsuspend      178
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64            179
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           180
-#endif
-#ifndef __NR_ugetrlimit
-#define __NR_ugetrlimit         190
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          191
-#endif
-#ifndef __NR_stat64
-#define __NR_stat64             195
-#endif
-#ifndef __NR_fstat64
-#define __NR_fstat64            197
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         202
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             207
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              208
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           209
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          210
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           212
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          213
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          215
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         216
-#endif
-#ifndef __NR_futex
-#define __NR_futex              221
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  222
-#define __NR_sched_getaffinity  223
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    232
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      246
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       247
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           252
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          253
-#endif
-#ifndef __NR_fadvise64_64
-#define __NR_fadvise64_64       254
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         273
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         274
-#endif
-#ifndef __NR_openat
-#define __NR_openat             286
-#endif
-#ifndef __NR_fstatat64
-#define __NR_fstatat64          291
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           292
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         301
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             302
-#endif
-/* End of powerpc defininitions                                              */
-#elif defined(__s390__)
-#ifndef __NR_quotactl
-#define __NR_quotactl           131
-#endif
-#ifndef __NR_rt_sigreturn
-#define __NR_rt_sigreturn       173
-#endif
-#ifndef __NR_rt_sigaction
-#define __NR_rt_sigaction       174
-#endif
-#ifndef __NR_rt_sigprocmask
-#define __NR_rt_sigprocmask     175
-#endif
-#ifndef __NR_rt_sigpending
-#define __NR_rt_sigpending      176
-#endif
-#ifndef __NR_rt_sigsuspend
-#define __NR_rt_sigsuspend      179
-#endif
-#ifndef __NR_pread64
-#define __NR_pread64            180
-#endif
-#ifndef __NR_pwrite64
-#define __NR_pwrite64           181
-#endif
-#ifndef __NR_getdents64
-#define __NR_getdents64         220
-#endif
-#ifndef __NR_readahead
-#define __NR_readahead          222
-#endif
-#ifndef __NR_setxattr
-#define __NR_setxattr           224
-#endif
-#ifndef __NR_lsetxattr
-#define __NR_lsetxattr          225
-#endif
-#ifndef __NR_getxattr
-#define __NR_getxattr           227
-#endif
-#ifndef __NR_lgetxattr
-#define __NR_lgetxattr          228
-#endif
-#ifndef __NR_listxattr
-#define __NR_listxattr          230
-#endif
-#ifndef __NR_llistxattr
-#define __NR_llistxattr         231
-#endif
-#ifndef __NR_gettid
-#define __NR_gettid             236
-#endif
-#ifndef __NR_tkill
-#define __NR_tkill              237
-#endif
-#ifndef __NR_futex
-#define __NR_futex              238
-#endif
-#ifndef __NR_sched_setaffinity
-#define __NR_sched_setaffinity  239
-#endif
-#ifndef __NR_sched_getaffinity
-#define __NR_sched_getaffinity  240
-#endif
-#ifndef __NR_set_tid_address
-#define __NR_set_tid_address    252
-#endif
-#ifndef __NR_clock_gettime
-#define __NR_clock_gettime      260
-#endif
-#ifndef __NR_clock_getres
-#define __NR_clock_getres       261
-#endif
-#ifndef __NR_statfs64
-#define __NR_statfs64           265
-#endif
-#ifndef __NR_fstatfs64
-#define __NR_fstatfs64          266
-#endif
-#ifndef __NR_ioprio_set
-#define __NR_ioprio_set         282
-#endif
-#ifndef __NR_ioprio_get
-#define __NR_ioprio_get         283
-#endif
-#ifndef __NR_openat
-#define __NR_openat             288
-#endif
-#ifndef __NR_unlinkat
-#define __NR_unlinkat           294
-#endif
-#ifndef __NR_move_pages
-#define __NR_move_pages         310
-#endif
-#ifndef __NR_getcpu
-#define __NR_getcpu             311
-#endif
-#ifndef __NR_fallocate
-#define __NR_fallocate          314
-#endif
-/* Some syscalls are named/numbered differently between s390 and s390x. */
-#ifdef __s390x__
-# ifndef __NR_getrlimit
-# define __NR_getrlimit          191
-# endif
-# ifndef __NR_setresuid
-# define __NR_setresuid          208
-# endif
-# ifndef __NR_getresuid
-# define __NR_getresuid          209
-# endif
-# ifndef __NR_setresgid
-# define __NR_setresgid          210
-# endif
-# ifndef __NR_getresgid
-# define __NR_getresgid          211
-# endif
-# ifndef __NR_setfsuid
-# define __NR_setfsuid           215
-# endif
-# ifndef __NR_setfsgid
-# define __NR_setfsgid           216
-# endif
-# ifndef __NR_fadvise64
-# define __NR_fadvise64          253
-# endif
-# ifndef __NR_newfstatat
-# define __NR_newfstatat         293
-# endif
-#else /* __s390x__ */
-# ifndef __NR_getrlimit
-# define __NR_getrlimit          76
-# endif
-# ifndef __NR_setfsuid
-# define __NR_setfsuid           138
-# endif
-# ifndef __NR_setfsgid
-# define __NR_setfsgid           139
-# endif
-# ifndef __NR_setresuid
-# define __NR_setresuid          164
-# endif
-# ifndef __NR_getresuid
-# define __NR_getresuid          165
-# endif
-# ifndef __NR_setresgid
-# define __NR_setresgid          170
-# endif
-# ifndef __NR_getresgid
-# define __NR_getresgid          171
-# endif
-# ifndef __NR_ugetrlimit
-# define __NR_ugetrlimit         191
-# endif
-# ifndef __NR_mmap2
-# define __NR_mmap2              192
-# endif
-# ifndef __NR_setresuid32
-# define __NR_setresuid32        208
-# endif
-# ifndef __NR_getresuid32
-# define __NR_getresuid32        209
-# endif
-# ifndef __NR_setresgid32
-# define __NR_setresgid32        210
-# endif
-# ifndef __NR_getresgid32
-# define __NR_getresgid32        211
-# endif
-# ifndef __NR_setfsuid32
-# define __NR_setfsuid32         215
-# endif
-# ifndef __NR_setfsgid32
-# define __NR_setfsgid32         216
-# endif
-# ifndef __NR_fadvise64_64
-# define __NR_fadvise64_64       264
-# endif
-# ifndef __NR_fstatat64
-# define __NR_fstatat64          293
-# endif
-#endif /* __s390__ */
-/* End of s390/s390x definitions                                             */
-#endif
-
-
-/* After forking, we must make sure to only call system calls.               */
-#if defined(__BOUNDED_POINTERS__)
-  #error "Need to port invocations of syscalls for bounded ptrs"
-#else
-  /* The core dumper and the thread lister get executed after threads
-   * have been suspended. As a consequence, we cannot call any functions
-   * that acquire locks. Unfortunately, libc wraps most system calls
-   * (e.g. in order to implement pthread_atfork, and to make calls
-   * cancellable), which means we cannot call these functions. Instead,
-   * we have to call syscall() directly.
-   */
-  #undef LSS_ERRNO
-  #ifdef SYS_ERRNO
-    /* Allow the including file to override the location of errno. This can
-     * be useful when using clone() with the CLONE_VM option.
-     */
-    #define LSS_ERRNO SYS_ERRNO
-  #else
-    #define LSS_ERRNO errno
-  #endif
-
-  #undef LSS_INLINE
-  #ifdef SYS_INLINE
-    #define LSS_INLINE SYS_INLINE
-  #else
-    #define LSS_INLINE static inline
-  #endif
-
-  /* Allow the including file to override the prefix used for all new
-   * system calls. By default, it will be set to "sys_".
-   */
-  #undef LSS_NAME
-  #ifndef SYS_PREFIX
-    #define LSS_NAME(name) sys_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX < 0
-    #define LSS_NAME(name) name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 0
-    #define LSS_NAME(name) sys0_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 1
-    #define LSS_NAME(name) sys1_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 2
-    #define LSS_NAME(name) sys2_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 3
-    #define LSS_NAME(name) sys3_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 4
-    #define LSS_NAME(name) sys4_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 5
-    #define LSS_NAME(name) sys5_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 6
-    #define LSS_NAME(name) sys6_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 7
-    #define LSS_NAME(name) sys7_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 8
-    #define LSS_NAME(name) sys8_##name
-  #elif defined(SYS_PREFIX) && SYS_PREFIX == 9
-    #define LSS_NAME(name) sys9_##name
-  #endif
-
-  #undef  LSS_RETURN
-  #if defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \
-       || defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__) \
-       || defined(__e2k__)
-  /* Failing system calls return a negative result in the range of
-   * -1..-4095. These are "errno" values with the sign inverted.
-   */
-  #define LSS_RETURN(type, res)                                               \
-    do {                                                                      \
-      if ((unsigned long)(res) >= (unsigned long)(-4095)) {                   \
-        LSS_ERRNO = -(res);                                                   \
-        res = -1;                                                             \
-      }                                                                       \
-      return (type) (res);                                                    \
-    } while (0)
-  #elif defined(__mips__)
-  /* On MIPS, failing system calls return -1, and set errno in a
-   * separate CPU register.
-   */
-  #define LSS_RETURN(type, res, err)                                          \
-    do {                                                                      \
-      if (err) {                                                              \
-        unsigned long __errnovalue = (res);                                   \
-        LSS_ERRNO = __errnovalue;                                             \
-        res = -1;                                                             \
-      }                                                                       \
-      return (type) (res);                                                    \
-    } while (0)
-  #elif defined(__PPC__)
-  /* On PPC, failing system calls return -1, and set errno in a
-   * separate CPU register. See linux/unistd.h.
-   */
-  #define LSS_RETURN(type, res, err)                                          \
-   do {                                                                       \
-     if (err & 0x10000000 ) {                                                 \
-       LSS_ERRNO = (res);                                                     \
-       res = -1;                                                              \
-     }                                                                        \
-     return (type) (res);                                                     \
-   } while (0)
-  #endif
-  #if defined(__i386__)
-    /* In PIC mode (e.g. when building shared libraries), gcc for i386
-     * reserves ebx. Unfortunately, most distribution ship with implementations
-     * of _syscallX() which clobber ebx.
-     * Also, most definitions of _syscallX() neglect to mark "memory" as being
-     * clobbered. This causes problems with compilers, that do a better job
-     * at optimizing across __asm__ calls.
-     * So, we just have to redefine all of the _syscallX() macros.
-     */
-    #undef LSS_ENTRYPOINT
-    #ifdef SYS_SYSCALL_ENTRYPOINT
-    static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) {
-      void (**entrypoint)(void);
-      asm volatile(".bss\n"
-                   ".align 8\n"
-                   ".globl " SYS_SYSCALL_ENTRYPOINT "\n"
-                   ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n"
-                   ".previous\n"
-                   /* This logically does 'lea "SYS_SYSCALL_ENTRYPOINT", %0' */
-                   "call 0f\n"
-                 "0:pop  %0\n"
-                   "add  $_GLOBAL_OFFSET_TABLE_+[.-0b], %0\n"
-                   "mov  " SYS_SYSCALL_ENTRYPOINT "@GOT(%0), %0\n"
-                   : "=r"(entrypoint));
-      return entrypoint;
-    }
-
-    #define LSS_ENTRYPOINT ".bss\n"                                           \
-                           ".align 8\n"                                       \
-                           ".globl " SYS_SYSCALL_ENTRYPOINT "\n"              \
-                           ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n"         \
-                           ".previous\n"                                      \
-                           /* Check the SYS_SYSCALL_ENTRYPOINT vector      */ \
-                           "push %%eax\n"                                     \
-                           "call 10000f\n"                                    \
-                     "10000:pop  %%eax\n"                                     \
-                           "add  $_GLOBAL_OFFSET_TABLE_+[.-10000b], %%eax\n"  \
-                           "mov  " SYS_SYSCALL_ENTRYPOINT                     \
-                                 "@GOT(%%eax), %%eax\n"                       \
-                           "mov  0(%%eax), %%eax\n"                           \
-                           "test %%eax, %%eax\n"                              \
-                           "jz   10002f\n"                                    \
-                           "push %%eax\n"                                     \
-                           "call 10001f\n"                                    \
-                     "10001:pop  %%eax\n"                                     \
-                           "add  $(10003f-10001b), %%eax\n"                   \
-                           "xchg 4(%%esp), %%eax\n"                           \
-                           "ret\n"                                            \
-                     "10002:pop  %%eax\n"                                     \
-                           "int $0x80\n"                                      \
-                     "10003:\n"
-    #else
-    #define LSS_ENTRYPOINT "int $0x80\n"
-    #endif
-    #undef  LSS_BODY
-    #define LSS_BODY(type,args...)                                            \
-      long __res;                                                             \
-      __asm__ __volatile__("push %%ebx\n"                                     \
-                           "movl %2,%%ebx\n"                                  \
-                           LSS_ENTRYPOINT                                     \
-                           "pop %%ebx"                                        \
-                           args                                               \
-                           : "memory");                                       \
-      LSS_RETURN(type,__res)
-    #undef  _syscall0
-    #define _syscall0(type,name)                                              \
-      type LSS_NAME(name)(void) {                                             \
-        long __res;                                                           \
-        __asm__ volatile(LSS_ENTRYPOINT                                       \
-                         : "=a" (__res)                                       \
-                         : "0" (__NR_##name)                                  \
-                         : "memory");                                         \
-        LSS_RETURN(type,__res);                                               \
-      }
-    #undef  _syscall1
-    #define _syscall1(type,name,type1,arg1)                                   \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        LSS_BODY(type,                                                        \
-             : "=a" (__res)                                                   \
-             : "0" (__NR_##name), "ri" ((long)(arg1)));                       \
-      }
-    #undef  _syscall2
-    #define _syscall2(type,name,type1,arg1,type2,arg2)                        \
-      type LSS_NAME(name)(type1 arg1,type2 arg2) {                            \
-        LSS_BODY(type,                                                        \
-             : "=a" (__res)                                                   \
-             : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2)));    \
-      }
-    #undef  _syscall3
-    #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)             \
-      type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) {                 \
-        LSS_BODY(type,                                                        \
-             : "=a" (__res)                                                   \
-             : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)),    \
-               "d" ((long)(arg3)));                                           \
-      }
-    #undef  _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_BODY(type,                                                        \
-             : "=a" (__res)                                                   \
-             : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)),    \
-               "d" ((long)(arg3)),"S" ((long)(arg4)));                        \
-      }
-    #undef  _syscall5
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        long __res;                                                           \
-        __asm__ __volatile__("push %%ebx\n"                                   \
-                             "movl %2,%%ebx\n"                                \
-                             "movl %1,%%eax\n"                                \
-                             LSS_ENTRYPOINT                                   \
-                             "pop  %%ebx"                                     \
-                             : "=a" (__res)                                   \
-                             : "i" (__NR_##name), "ri" ((long)(arg1)),        \
-                               "c" ((long)(arg2)), "d" ((long)(arg3)),        \
-                               "S" ((long)(arg4)), "D" ((long)(arg5))         \
-                             : "memory");                                     \
-        LSS_RETURN(type,__res);                                               \
-      }
-    #undef  _syscall6
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        long __res;                                                           \
-        struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 };   \
-        __asm__ __volatile__("push %%ebp\n"                                   \
-                             "push %%ebx\n"                                   \
-                             "movl 4(%2),%%ebp\n"                             \
-                             "movl 0(%2), %%ebx\n"                            \
-                             "movl %1,%%eax\n"                                \
-                             LSS_ENTRYPOINT                                   \
-                             "pop  %%ebx\n"                                   \
-                             "pop  %%ebp"                                     \
-                             : "=a" (__res)                                   \
-                             : "i" (__NR_##name),  "0" ((long)(&__s)),        \
-                               "c" ((long)(arg2)), "d" ((long)(arg3)),        \
-                               "S" ((long)(arg4)), "D" ((long)(arg5))         \
-                             : "memory");                                     \
-        LSS_RETURN(type,__res);                                               \
-      }
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long __res;
-      __asm__ __volatile__(/* if (fn == NULL)
-                            *   return -EINVAL;
-                            */
-                           "movl   %3,%%ecx\n"
-                           "jecxz  1f\n"
-
-                           /* if (child_stack == NULL)
-                            *   return -EINVAL;
-                            */
-                           "movl   %4,%%ecx\n"
-                           "jecxz  1f\n"
-
-                           /* Set up alignment of the child stack:
-                            * child_stack = (child_stack & ~0xF) - 20;
-                            */
-                           "andl   $-16,%%ecx\n"
-                           "subl   $20,%%ecx\n"
-
-                           /* Push "arg" and "fn" onto the stack that will be
-                            * used by the child.
-                            */
-                           "movl   %6,%%eax\n"
-                           "movl   %%eax,4(%%ecx)\n"
-                           "movl   %3,%%eax\n"
-                           "movl   %%eax,(%%ecx)\n"
-
-                           /* %eax = syscall(%eax = __NR_clone,
-                            *                %ebx = flags,
-                            *                %ecx = child_stack,
-                            *                %edx = parent_tidptr,
-                            *                %esi = newtls,
-                            *                %edi = child_tidptr)
-                            * Also, make sure that %ebx gets preserved as it is
-                            * used in PIC mode.
-                            */
-                           "movl   %8,%%esi\n"
-                           "movl   %7,%%edx\n"
-                           "movl   %5,%%eax\n"
-                           "movl   %9,%%edi\n"
-                           "pushl  %%ebx\n"
-                           "movl   %%eax,%%ebx\n"
-                           "movl   %2,%%eax\n"
-                           LSS_ENTRYPOINT
-
-                           /* In the parent: restore %ebx
-                            * In the child:  move "fn" into %ebx
-                            */
-                           "popl   %%ebx\n"
-
-                           /* if (%eax != 0)
-                            *   return %eax;
-                            */
-                           "test   %%eax,%%eax\n"
-                           "jnz    1f\n"
-
-                           /* In the child, now. Terminate frame pointer chain.
-                            */
-                           "movl   $0,%%ebp\n"
-
-                           /* Call "fn". "arg" is already on the stack.
-                            */
-                           "call   *%%ebx\n"
-
-                           /* Call _exit(%ebx). Unfortunately older versions
-                            * of gcc restrict the number of arguments that can
-                            * be passed to asm(). So, we need to hard-code the
-                            * system call number.
-                            */
-                           "movl   %%eax,%%ebx\n"
-                           "movl   $1,%%eax\n"
-                           LSS_ENTRYPOINT
-
-                           /* Return to parent.
-                            */
-                         "1:\n"
-                           : "=a" (__res)
-                           : "0"(-EINVAL), "i"(__NR_clone),
-                             "m"(fn), "m"(child_stack), "m"(flags), "m"(arg),
-                             "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr)
-                           : "memory", "ecx", "edx", "esi", "edi");
-      LSS_RETURN(int, __res);
-    }
-
-    LSS_INLINE _syscall1(int, set_thread_area, void *, u)
-    LSS_INLINE _syscall1(int, get_thread_area, void *, u)
-
-    LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) {
-      /* On i386, the kernel does not know how to return from a signal
-       * handler. Instead, it relies on user space to provide a
-       * restorer function that calls the {rt_,}sigreturn() system call.
-       * Unfortunately, we cannot just reference the glibc version of this
-       * function, as glibc goes out of its way to make it inaccessible.
-       */
-      void (*res)(void);
-      __asm__ __volatile__("call   2f\n"
-                         "0:.align 16\n"
-                         "1:movl   %1,%%eax\n"
-                           LSS_ENTRYPOINT
-                         "2:popl   %0\n"
-                           "addl   $(1b-0b),%0\n"
-                           : "=a" (res)
-                           : "i"  (__NR_rt_sigreturn));
-      return res;
-    }
-    LSS_INLINE void (*LSS_NAME(restore)(void))(void) {
-      /* On i386, the kernel does not know how to return from a signal
-       * handler. Instead, it relies on user space to provide a
-       * restorer function that calls the {rt_,}sigreturn() system call.
-       * Unfortunately, we cannot just reference the glibc version of this
-       * function, as glibc goes out of its way to make it inaccessible.
-       */
-      void (*res)(void);
-      __asm__ __volatile__("call   2f\n"
-                         "0:.align 16\n"
-                         "1:pop    %%eax\n"
-                           "movl   %1,%%eax\n"
-                           LSS_ENTRYPOINT
-                         "2:popl   %0\n"
-                           "addl   $(1b-0b),%0\n"
-                           : "=a" (res)
-                           : "i"  (__NR_sigreturn));
-      return res;
-    }
-  #elif defined(__x86_64__)
-    /* There are no known problems with any of the _syscallX() macros
-     * currently shipping for x86_64, but we still need to be able to define
-     * our own version so that we can override the location of the errno
-     * location (e.g. when using the clone() system call with the CLONE_VM
-     * option).
-     */
-    #undef LSS_ENTRYPOINT
-    #ifdef SYS_SYSCALL_ENTRYPOINT
-    static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) {
-      void (**entrypoint)(void);
-      asm volatile(".bss\n"
-                   ".align 8\n"
-                   ".globl " SYS_SYSCALL_ENTRYPOINT "\n"
-                   ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n"
-                   ".previous\n"
-                   "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %0\n"
-                   : "=r"(entrypoint));
-      return entrypoint;
-    }
-
-    #define LSS_ENTRYPOINT                                                    \
-              ".bss\n"                                                        \
-              ".align 8\n"                                                    \
-              ".globl " SYS_SYSCALL_ENTRYPOINT "\n"                           \
-              ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n"                      \
-              ".previous\n"                                                   \
-              "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %%rcx\n"       \
-              "mov  0(%%rcx), %%rcx\n"                                        \
-              "test %%rcx, %%rcx\n"                                           \
-              "jz   10001f\n"                                                 \
-              "call *%%rcx\n"                                                 \
-              "jmp  10002f\n"                                                 \
-        "10001:syscall\n"                                                     \
-        "10002:\n"
-
-    #else
-    #define LSS_ENTRYPOINT "syscall\n"
-    #endif
-
-    /* The x32 ABI has 32 bit longs, but the syscall interface is 64 bit.
-     * We need to explicitly cast to an unsigned 64 bit type to avoid implicit
-     * sign extension.  We can't cast pointers directly because those are
-     * 32 bits, and gcc will dump ugly warnings about casting from a pointer
-     * to an integer of a different size.
-     */
-    #undef  LSS_SYSCALL_ARG
-    #define LSS_SYSCALL_ARG(a) ((uint64_t)(uintptr_t)(a))
-    #undef  _LSS_RETURN
-    #define _LSS_RETURN(type, res, cast)                                      \
-      do {                                                                    \
-        if ((uint64_t)(res) >= (uint64_t)(-4095)) {                           \
-          LSS_ERRNO = -(res);                                                 \
-          res = -1;                                                           \
-        }                                                                     \
-        return (type)(cast)(res);                                             \
-      } while (0)
-    #undef  LSS_RETURN
-    #define LSS_RETURN(type, res) _LSS_RETURN(type, res, uintptr_t)
-
-    #undef  _LSS_BODY
-    #define _LSS_BODY(nr, type, name, cast, ...)                              \
-          long long __res;                                                    \
-          __asm__ __volatile__(LSS_BODY_ASM##nr LSS_ENTRYPOINT                \
-            : "=a" (__res)                                                    \
-            : "0" (__NR_##name) LSS_BODY_ARG##nr(__VA_ARGS__)                 \
-            : LSS_BODY_CLOBBER##nr "r11", "rcx", "memory");                   \
-          _LSS_RETURN(type, __res, cast)
-    #undef  LSS_BODY
-    #define LSS_BODY(nr, type, name, args...) \
-      _LSS_BODY(nr, type, name, uintptr_t, ## args)
-
-    #undef  LSS_BODY_ASM0
-    #undef  LSS_BODY_ASM1
-    #undef  LSS_BODY_ASM2
-    #undef  LSS_BODY_ASM3
-    #undef  LSS_BODY_ASM4
-    #undef  LSS_BODY_ASM5
-    #undef  LSS_BODY_ASM6
-    #define LSS_BODY_ASM0
-    #define LSS_BODY_ASM1 LSS_BODY_ASM0
-    #define LSS_BODY_ASM2 LSS_BODY_ASM1
-    #define LSS_BODY_ASM3 LSS_BODY_ASM2
-    #define LSS_BODY_ASM4 LSS_BODY_ASM3 "movq %5,%%r10;"
-    #define LSS_BODY_ASM5 LSS_BODY_ASM4 "movq %6,%%r8;"
-    #define LSS_BODY_ASM6 LSS_BODY_ASM5 "movq %7,%%r9;"
-
-    #undef  LSS_BODY_CLOBBER0
-    #undef  LSS_BODY_CLOBBER1
-    #undef  LSS_BODY_CLOBBER2
-    #undef  LSS_BODY_CLOBBER3
-    #undef  LSS_BODY_CLOBBER4
-    #undef  LSS_BODY_CLOBBER5
-    #undef  LSS_BODY_CLOBBER6
-    #define LSS_BODY_CLOBBER0
-    #define LSS_BODY_CLOBBER1 LSS_BODY_CLOBBER0
-    #define LSS_BODY_CLOBBER2 LSS_BODY_CLOBBER1
-    #define LSS_BODY_CLOBBER3 LSS_BODY_CLOBBER2
-    #define LSS_BODY_CLOBBER4 LSS_BODY_CLOBBER3 "r10",
-    #define LSS_BODY_CLOBBER5 LSS_BODY_CLOBBER4 "r8",
-    #define LSS_BODY_CLOBBER6 LSS_BODY_CLOBBER5 "r9",
-
-    #undef  LSS_BODY_ARG0
-    #undef  LSS_BODY_ARG1
-    #undef  LSS_BODY_ARG2
-    #undef  LSS_BODY_ARG3
-    #undef  LSS_BODY_ARG4
-    #undef  LSS_BODY_ARG5
-    #undef  LSS_BODY_ARG6
-    #define LSS_BODY_ARG0()
-    #define LSS_BODY_ARG1(arg1) \
-      LSS_BODY_ARG0(), "D" (arg1)
-    #define LSS_BODY_ARG2(arg1, arg2) \
-      LSS_BODY_ARG1(arg1), "S" (arg2)
-    #define LSS_BODY_ARG3(arg1, arg2, arg3) \
-      LSS_BODY_ARG2(arg1, arg2), "d" (arg3)
-    #define LSS_BODY_ARG4(arg1, arg2, arg3, arg4) \
-      LSS_BODY_ARG3(arg1, arg2, arg3), "r" (arg4)
-    #define LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5) \
-      LSS_BODY_ARG4(arg1, arg2, arg3, arg4), "r" (arg5)
-    #define LSS_BODY_ARG6(arg1, arg2, arg3, arg4, arg5, arg6) \
-      LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5), "r" (arg6)
-
-    #undef _syscall0
-    #define _syscall0(type,name)                                              \
-      type LSS_NAME(name)(void) {                                             \
-        LSS_BODY(0, type, name);                                              \
-      }
-    #undef _syscall1
-    #define _syscall1(type,name,type1,arg1)                                   \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        LSS_BODY(1, type, name, LSS_SYSCALL_ARG(arg1));                       \
-      }
-    #undef _syscall2
-    #define _syscall2(type,name,type1,arg1,type2,arg2)                        \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) {                           \
-        LSS_BODY(2, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2));\
-      }
-    #undef _syscall3
-    #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {               \
-        LSS_BODY(3, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \
-                                LSS_SYSCALL_ARG(arg3));                       \
-      }
-    #undef _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_BODY(4, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \
-                                LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4));\
-      }
-    #undef _syscall5
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_BODY(5, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \
-                                LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \
-                                LSS_SYSCALL_ARG(arg5));                       \
-      }
-    #undef _syscall6
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        LSS_BODY(6, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \
-                                LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \
-                                LSS_SYSCALL_ARG(arg5), LSS_SYSCALL_ARG(arg6));\
-      }
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long long __res;
-      {
-        __asm__ __volatile__(/* if (fn == NULL)
-                              *   return -EINVAL;
-                              */
-                             "testq  %4,%4\n"
-                             "jz     1f\n"
-
-                             /* if (child_stack == NULL)
-                              *   return -EINVAL;
-                              */
-                             "testq  %5,%5\n"
-                             "jz     1f\n"
-
-                             /* childstack -= 2*sizeof(void *);
-                              */
-                             "subq   $16,%5\n"
-
-                             /* Push "arg" and "fn" onto the stack that will be
-                              * used by the child.
-                              */
-                             "movq   %7,8(%5)\n"
-                             "movq   %4,0(%5)\n"
-
-                             /* %rax = syscall(%rax = __NR_clone,
-                              *                %rdi = flags,
-                              *                %rsi = child_stack,
-                              *                %rdx = parent_tidptr,
-                              *                %r8  = new_tls,
-                              *                %r10 = child_tidptr)
-                              */
-                             "movq   %2,%%rax\n"
-                             "movq   %9,%%r8\n"
-                             "movq   %10,%%r10\n"
-                             LSS_ENTRYPOINT
-
-                             /* if (%rax != 0)
-                              *   return;
-                              */
-                             "testq  %%rax,%%rax\n"
-                             "jnz    1f\n"
-
-                             /* In the child. Terminate frame pointer chain.
-                              */
-                             "xorq   %%rbp,%%rbp\n"
-
-                             /* Call "fn(arg)".
-                              */
-                             "popq   %%rax\n"
-                             "popq   %%rdi\n"
-                             "call   *%%rax\n"
-
-                             /* Call _exit(%ebx).
-                              */
-                             "movq   %%rax,%%rdi\n"
-                             "movq   %3,%%rax\n"
-                             LSS_ENTRYPOINT
-
-                             /* Return to parent.
-                              */
-                           "1:\n"
-                             : "=a" (__res)
-                             : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit),
-                               "r"(LSS_SYSCALL_ARG(fn)),
-                               "S"(LSS_SYSCALL_ARG(child_stack)),
-                               "D"(LSS_SYSCALL_ARG(flags)),
-                               "r"(LSS_SYSCALL_ARG(arg)),
-                               "d"(LSS_SYSCALL_ARG(parent_tidptr)),
-                               "r"(LSS_SYSCALL_ARG(newtls)),
-                               "r"(LSS_SYSCALL_ARG(child_tidptr))
-                             : "memory", "r8", "r10", "r11", "rcx");
-      }
-      LSS_RETURN(int, __res);
-    }
-    LSS_INLINE _syscall2(int, arch_prctl, int, c, void *, a)
-
-    LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) {
-      /* On x86-64, the kernel does not know how to return from
-       * a signal handler. Instead, it relies on user space to provide a
-       * restorer function that calls the rt_sigreturn() system call.
-       * Unfortunately, we cannot just reference the glibc version of this
-       * function, as glibc goes out of its way to make it inaccessible.
-       */
-      long long res;
-      __asm__ __volatile__("jmp    2f\n"
-                           ".align 16\n"
-                         "1:movq   %1,%%rax\n"
-                           LSS_ENTRYPOINT
-                         "2:leaq   1b(%%rip),%0\n"
-                           : "=r" (res)
-                           : "i"  (__NR_rt_sigreturn));
-      return (void (*)(void))(uintptr_t)res;
-    }
-  #elif defined(__ARM_ARCH_3__)
-    /* Most definitions of _syscallX() neglect to mark "memory" as being
-     * clobbered. This causes problems with compilers, that do a better job
-     * at optimizing across __asm__ calls.
-     * So, we just have to redefine all of the _syscallX() macros.
-     */
-    #undef LSS_REG
-    #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
-    #undef  LSS_BODY
-    #define LSS_BODY(type,name,args...)                                       \
-          register long __res_r0 __asm__("r0");                               \
-          long __res;                                                         \
-          __asm__ __volatile__ (__syscall(name)                               \
-                                : "=r"(__res_r0) : args : "lr", "memory");    \
-          __res = __res_r0;                                                   \
-          LSS_RETURN(type, __res)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-      type LSS_NAME(name)(void) {                                             \
-        LSS_BODY(type, name);                                                 \
-      }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0));                    \
-      }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1));                           \
-      }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {               \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2));                \
-      }
-    #undef _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4);                                                     \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3));     \
-      }
-    #undef _syscall5
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4));                                      \
-      }
-    #undef _syscall6
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4), "r"(__r5));                           \
-      }
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long __res;
-      {
-        register int   __flags __asm__("r0") = flags;
-        register void *__stack __asm__("r1") = child_stack;
-        register void *__ptid  __asm__("r2") = parent_tidptr;
-        register void *__tls   __asm__("r3") = newtls;
-        register int  *__ctid  __asm__("r4") = child_tidptr;
-        __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL)
-                              *   return -EINVAL;
-                              */
-                             "cmp   %2,#0\n"
-                             "cmpne %3,#0\n"
-                             "moveq %0,%1\n"
-                             "beq   1f\n"
-
-                             /* Push "arg" and "fn" onto the stack that will be
-                              * used by the child.
-                              */
-                             "str   %5,[%3,#-4]!\n"
-                             "str   %2,[%3,#-4]!\n"
-
-                             /* %r0 = syscall(%r0 = flags,
-                              *               %r1 = child_stack,
-                              *               %r2 = parent_tidptr,
-                              *               %r3 = newtls,
-                              *               %r4 = child_tidptr)
-                              */
-                             __syscall(clone)"\n"
-
-                             /* if (%r0 != 0)
-                              *   return %r0;
-                              */
-                             "movs  %0,r0\n"
-                             "bne   1f\n"
-
-                             /* In the child, now. Call "fn(arg)".
-                              */
-                             "ldr   r0,[sp, #4]\n"
-                             "mov   lr,pc\n"
-                             "ldr   pc,[sp]\n"
-
-                             /* Call _exit(%r0).
-                              */
-                             __syscall(exit)"\n"
-                           "1:\n"
-                             : "=r" (__res)
-                             : "i"(-EINVAL),
-                               "r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
-                               "r"(__ptid), "r"(__tls), "r"(__ctid)
-                             : "cc", "lr", "memory");
-      }
-      LSS_RETURN(int, __res);
-    }
-  #elif defined(__ARM_EABI__)
-    /* Most definitions of _syscallX() neglect to mark "memory" as being
-     * clobbered. This causes problems with compilers, that do a better job
-     * at optimizing across __asm__ calls.
-     * So, we just have to redefine all fo the _syscallX() macros.
-     */
-    #undef LSS_REG
-    #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
-    #undef  LSS_BODY
-    #define LSS_BODY(type,name,args...)                                       \
-          register long __res_r0 __asm__("r0");                               \
-          long __res;                                                         \
-          __asm__ __volatile__ ("push {r7}\n"                                 \
-                                "mov r7, %1\n"                                \
-                                "swi 0x0\n"                                   \
-                                "pop {r7}\n"                                  \
-                                : "=r"(__res_r0)                              \
-                                : "i"(__NR_##name) , ## args                  \
-                                : "lr", "memory");                            \
-          __res = __res_r0;                                                   \
-          LSS_RETURN(type, __res)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-      type LSS_NAME(name)(void) {                                             \
-        LSS_BODY(type, name);                                                 \
-      }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0));                    \
-      }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1));                           \
-      }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {               \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2));                \
-      }
-    #undef _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4);                                                     \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3));     \
-      }
-    #undef _syscall5
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4));                                      \
-      }
-    #undef _syscall6
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4), "r"(__r5));                           \
-      }
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long __res;
-      if (fn == NULL || child_stack == NULL) {
-        __res = -EINVAL;
-        LSS_RETURN(int, __res);
-      }
-
-      /* Push "arg" and "fn" onto the stack that will be
-       * used by the child.
-       */
-      {
-        uintptr_t* cstack = (uintptr_t*)child_stack - 2;
-        cstack[0] = (uintptr_t)fn;
-        cstack[1] = (uintptr_t)arg;
-        child_stack = cstack;
-      }
-      {
-        register int   __flags __asm__("r0") = flags;
-        register void *__stack __asm__("r1") = child_stack;
-        register void *__ptid  __asm__("r2") = parent_tidptr;
-        register void *__tls   __asm__("r3") = newtls;
-        register int  *__ctid  __asm__("r4") = child_tidptr;
-        __asm__ __volatile__(
-#ifdef __thumb2__
-            "push {r7}\n"
-#endif
-            /* %r0 = syscall(%r0 = flags,
-             *               %r1 = child_stack,
-             *               %r2 = parent_tidptr,
-             *               %r3 = newtls,
-             *               %r4 = child_tidptr)
-             */
-            "mov r7, %6\n"
-            "swi 0x0\n"
-
-            /* if (%r0 != 0)
-             *   return %r0;
-             */
-            "cmp   r0, #0\n"
-            "bne   1f\n"
-
-            /* In the child, now. Call "fn(arg)".
-             */
-            "ldr   r0,[sp, #4]\n"
-
-            "ldr   lr,[sp]\n"
-            "blx   lr\n"
-
-            /* Call _exit(%r0).
-             */
-            "mov r7, %7\n"
-            "swi 0x0\n"
-            /* Unreachable */
-            "bkpt #0\n"
-         "1:\n"
-#ifdef __thumb2__
-            "pop {r7}\n"
-#endif
-            "movs  %0,r0\n"
-            : "=r"(__res)
-            : "r"(__stack), "r"(__flags), "r"(__ptid), "r"(__tls), "r"(__ctid),
-              "i"(__NR_clone), "i"(__NR_exit)
-            : "cc", "lr", "memory"
-#ifndef __thumb2__
-            , "r7"
-#endif
-            );
-      }
-      LSS_RETURN(int, __res);
-    }
-  #elif defined(__aarch64__)
-    /* Most definitions of _syscallX() neglect to mark "memory" as being
-     * clobbered. This causes problems with compilers, that do a better job
-     * at optimizing across __asm__ calls.
-     * So, we just have to redefine all of the _syscallX() macros.
-     */
-    #undef LSS_REG
-    #define LSS_REG(r,a) register int64_t __r##r __asm__("x"#r) = (int64_t)a
-    #undef  LSS_BODY
-    #define LSS_BODY(type,name,args...)                                       \
-          register int64_t __res_x0 __asm__("x0");                            \
-          int64_t __res;                                                      \
-          __asm__ __volatile__ ("mov x8, %1\n"                                \
-                                "svc 0x0\n"                                   \
-                                : "=r"(__res_x0)                              \
-                                : "i"(__NR_##name) , ## args                  \
-                                : "x8", "memory");                            \
-          __res = __res_x0;                                                   \
-          LSS_RETURN(type, __res)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-      type LSS_NAME(name)(void) {                                             \
-        LSS_BODY(type, name);                                                 \
-      }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0));                    \
-      }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1));                           \
-      }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {               \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2));                \
-      }
-    #undef _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4);                                                     \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3));     \
-      }
-    #undef _syscall5
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5);                                   \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4));                                      \
-      }
-    #undef _syscall6
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3);                 \
-        LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6);                 \
-        LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),      \
-                             "r"(__r4), "r"(__r5));                           \
-      }
-
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      int64_t __res;
-      {
-        register uint64_t __flags __asm__("x0") = flags;
-        register void *__stack __asm__("x1") = child_stack;
-        register void *__ptid  __asm__("x2") = parent_tidptr;
-        register void *__tls   __asm__("x3") = newtls;
-        register int  *__ctid  __asm__("x4") = child_tidptr;
-        __asm__ __volatile__(/* Push "arg" and "fn" onto the stack that will be
-                              * used by the child.
-                              */
-                             "stp     %1, %4, [%2, #-16]!\n"
-
-                             /* %x0 = syscall(%x0 = flags,
-                              *               %x1 = child_stack,
-                              *               %x2 = parent_tidptr,
-                              *               %x3 = newtls,
-                              *               %x4 = child_tidptr)
-                              */
-                             "mov     x8, %8\n"
-                             "svc     0x0\n"
-
-                             /* if (%r0 != 0)
-                              *   return %r0;
-                              */
-                             "mov     %0, x0\n"
-                             "cbnz    x0, 1f\n"
-
-                             /* In the child, now. Call "fn(arg)".
-                              */
-                             "ldp     x1, x0, [sp], #16\n"
-                             "blr     x1\n"
-
-                             /* Call _exit(%r0).
-                              */
-                             "mov     x8, %9\n"
-                             "svc     0x0\n"
-                           "1:\n"
-                             : "=r" (__res)
-                             : "r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
-                               "r"(__ptid), "r"(__tls), "r"(__ctid),
-                               "i"(__NR_clone), "i"(__NR_exit)
-                             : "cc", "x8", "memory");
-      }
-      LSS_RETURN(int, __res);
-    }
-  #elif defined(__mips__)
-    #undef LSS_REG
-    #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) =       \
-                                 (unsigned long)(a)
-    #undef  LSS_BODY
-    #undef LSS_SYSCALL_CLOBBERS
-    #if _MIPS_SIM == _MIPS_SIM_ABI32
-    #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10",               \
-                                 "$11", "$12", "$13", "$14", "$15",           \
-                                 "$24", "$25", "hi", "lo", "memory"
-    #else
-    #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12",             \
-                                 "$13", "$14", "$15", "$24", "$25",           \
-                                 "hi", "lo", "memory"
-    #endif
-    #define LSS_BODY(type,name,r7,...)                                        \
-          register unsigned long __v0 __asm__("$2") = __NR_##name;            \
-          __asm__ __volatile__ ("syscall\n"                                   \
-                                : "=r"(__v0), r7 (__r7)                       \
-                                : "0"(__v0), ##__VA_ARGS__                    \
-                                : LSS_SYSCALL_CLOBBERS);                      \
-          LSS_RETURN(type, __v0, __r7)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-      type LSS_NAME(name)(void) {                                             \
-        register unsigned long __r7 __asm__("$7");                            \
-        LSS_BODY(type, name, "=r");                                           \
-      }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-      type LSS_NAME(name)(type1 arg1) {                                       \
-        register unsigned long __r7 __asm__("$7");                            \
-        LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4));              \
-      }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) {                           \
-        register unsigned long __r7 __asm__("$7");                            \
-        LSS_REG(4, arg1); LSS_REG(5, arg2);                                   \
-        LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5));                     \
-      }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {               \
-        register unsigned long __r7 __asm__("$7");                            \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6));          \
-      }
-    #undef _syscall4
-    #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {   \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_REG(7, arg4);                                                     \
-        LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6));          \
-      }
-    #undef _syscall5
-    #if _MIPS_SIM == _MIPS_SIM_ABI32
-    /* The old 32bit MIPS system call API passes the fifth and sixth argument
-     * on the stack, whereas the new APIs use registers "r8" and "r9".
-     */
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_REG(7, arg4);                                                     \
-        register unsigned long __v0 __asm__("$2") = __NR_##name;              \
-        __asm__ __volatile__ (".set noreorder\n"                              \
-                              "subu  $29, 32\n"                               \
-                              "sw    %5, 16($29)\n"                           \
-                              "syscall\n"                                     \
-                              "addiu $29, 32\n"                               \
-                              ".set reorder\n"                                \
-                              : "+r"(__v0), "+r" (__r7)                       \
-                              : "r"(__r4), "r"(__r5),                         \
-                                "r"(__r6), "r" ((unsigned long)arg5)          \
-                              : "$8", "$9", "$10", "$11", "$12",              \
-                                "$13", "$14", "$15", "$24", "$25",            \
-                                "memory");                                    \
-        LSS_RETURN(type, __v0, __r7);                                         \
-      }
-    #else
-    #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5)                                             \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5) {                                       \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_REG(7, arg4); LSS_REG(8, arg5);                                   \
-        LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6),           \
-                 "r"(__r8));                                                  \
-      }
-    #endif
-    #undef _syscall6
-    #if _MIPS_SIM == _MIPS_SIM_ABI32
-    /* The old 32bit MIPS system call API passes the fifth and sixth argument
-     * on the stack, whereas the new APIs use registers "r8" and "r9".
-     */
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5, type6 arg6) {                           \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_REG(7, arg4);                                                     \
-        register unsigned long __v0 __asm__("$2") = __NR_##name;              \
-        __asm__ __volatile__ (".set noreorder\n"                              \
-                              "subu  $29, 32\n"                               \
-                              "sw    %5, 16($29)\n"                           \
-                              "sw    %6, 20($29)\n"                           \
-                              "syscall\n"                                     \
-                              "addiu $29, 32\n"                               \
-                              ".set reorder\n"                                \
-                              : "+r"(__v0), "+r" (__r7)                       \
-                              : "r"(__r4), "r"(__r5),                         \
-                                "r"(__r6), "r" ((unsigned long)arg5),         \
-                                "r" ((unsigned long)arg6)                     \
-                              : "$8", "$9", "$10", "$11", "$12",              \
-                                "$13", "$14", "$15", "$24", "$25",            \
-                                "memory");                                    \
-        LSS_RETURN(type, __v0, __r7);                                         \
-      }
-    #else
-    #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,  \
-                      type5,arg5,type6,arg6)                                  \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,     \
-                          type5 arg5,type6 arg6) {                            \
-        LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3);                 \
-        LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6);                 \
-        LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6),           \
-                 "r"(__r8), "r"(__r9));                                       \
-      }
-    #endif
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      register unsigned long __v0 __asm__("$2") = -EINVAL;
-      register unsigned long __r7 __asm__("$7") = (unsigned long)newtls;
-      {
-        register int   __flags __asm__("$4") = flags;
-        register void *__stack __asm__("$5") = child_stack;
-        register void *__ptid  __asm__("$6") = parent_tidptr;
-        register int  *__ctid  __asm__("$8") = child_tidptr;
-        __asm__ __volatile__(
-          #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
-                             "subu  $29,24\n"
-          #elif _MIPS_SIM == _MIPS_SIM_NABI32
-                             "sub   $29,16\n"
-          #else
-                             "dsubu $29,16\n"
-          #endif
-
-                             /* if (fn == NULL || child_stack == NULL)
-                              *   return -EINVAL;
-                              */
-                             "beqz  %4,1f\n"
-                             "beqz  %5,1f\n"
-
-                             /* Push "arg" and "fn" onto the stack that will be
-                              * used by the child.
-                              */
-          #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
-                             "subu  %5,32\n"
-                             "sw    %4,0(%5)\n"
-                             "sw    %7,4(%5)\n"
-          #elif _MIPS_SIM == _MIPS_SIM_NABI32
-                             "sub   %5,32\n"
-                             "sw    %4,0(%5)\n"
-                             "sw    %7,8(%5)\n"
-          #else
-                             "dsubu %5,32\n"
-                             "sd    %4,0(%5)\n"
-                             "sd    %7,8(%5)\n"
-          #endif
-
-                             /* $7 = syscall($4 = flags,
-                              *              $5 = child_stack,
-                              *              $6 = parent_tidptr,
-                              *              $7 = newtls,
-                              *              $8 = child_tidptr)
-                              */
-                             "li    $2,%2\n"
-                             "syscall\n"
-
-                             /* if ($7 != 0)
-                              *   return $2;
-                              */
-                             "bnez  $7,1f\n"
-                             "bnez  $2,1f\n"
-
-                             /* In the child, now. Call "fn(arg)".
-                              */
-          #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
-                            "lw    $25,0($29)\n"
-                            "lw    $4,4($29)\n"
-          #elif _MIPS_SIM == _MIPS_SIM_NABI32
-                            "lw    $25,0($29)\n"
-                            "lw    $4,8($29)\n"
-          #else
-                            "ld    $25,0($29)\n"
-                            "ld    $4,8($29)\n"
-          #endif
-                            "jalr  $25\n"
-
-                             /* Call _exit($2)
-                              */
-                            "move  $4,$2\n"
-                            "li    $2,%3\n"
-                            "syscall\n"
-
-                           "1:\n"
-          #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
-                             "addu  $29, 24\n"
-          #elif _MIPS_SIM == _MIPS_SIM_NABI32
-                             "add   $29, 16\n"
-          #else
-                             "daddu $29,16\n"
-          #endif
-                             : "+r" (__v0), "+r" (__r7)
-                             : "i"(__NR_clone), "i"(__NR_exit), "r"(fn),
-                               "r"(__stack), "r"(__flags), "r"(arg),
-                               "r"(__ptid), "r"(__ctid)
-                             : "$9", "$10", "$11", "$12", "$13", "$14", "$15",
-                               "$24", "$25", "memory");
-      }
-      LSS_RETURN(int, __v0, __r7);
-    }
-  #elif defined (__PPC__)
-    #undef  LSS_LOADARGS_0
-    #define LSS_LOADARGS_0(name, dummy...)                                    \
-        __sc_0 = __NR_##name
-    #undef  LSS_LOADARGS_1
-    #define LSS_LOADARGS_1(name, arg1)                                        \
-            LSS_LOADARGS_0(name);                                             \
-            __sc_3 = (unsigned long) (arg1)
-    #undef  LSS_LOADARGS_2
-    #define LSS_LOADARGS_2(name, arg1, arg2)                                  \
-            LSS_LOADARGS_1(name, arg1);                                       \
-            __sc_4 = (unsigned long) (arg2)
-    #undef  LSS_LOADARGS_3
-    #define LSS_LOADARGS_3(name, arg1, arg2, arg3)                            \
-            LSS_LOADARGS_2(name, arg1, arg2);                                 \
-            __sc_5 = (unsigned long) (arg3)
-    #undef  LSS_LOADARGS_4
-    #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4)                      \
-            LSS_LOADARGS_3(name, arg1, arg2, arg3);                           \
-            __sc_6 = (unsigned long) (arg4)
-    #undef  LSS_LOADARGS_5
-    #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5)                \
-            LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4);                     \
-            __sc_7 = (unsigned long) (arg5)
-    #undef  LSS_LOADARGS_6
-    #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6)          \
-            LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5);               \
-            __sc_8 = (unsigned long) (arg6)
-    #undef  LSS_ASMINPUT_0
-    #define LSS_ASMINPUT_0 "0" (__sc_0)
-    #undef  LSS_ASMINPUT_1
-    #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3)
-    #undef  LSS_ASMINPUT_2
-    #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4)
-    #undef  LSS_ASMINPUT_3
-    #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5)
-    #undef  LSS_ASMINPUT_4
-    #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6)
-    #undef  LSS_ASMINPUT_5
-    #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7)
-    #undef  LSS_ASMINPUT_6
-    #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8)
-    #undef  LSS_BODY
-    #define LSS_BODY(nr, type, name, args...)                                 \
-        long __sc_ret, __sc_err;                                              \
-        {                                                                     \
-                        register unsigned long __sc_0 __asm__ ("r0");         \
-                        register unsigned long __sc_3 __asm__ ("r3");         \
-                        register unsigned long __sc_4 __asm__ ("r4");         \
-                        register unsigned long __sc_5 __asm__ ("r5");         \
-                        register unsigned long __sc_6 __asm__ ("r6");         \
-                        register unsigned long __sc_7 __asm__ ("r7");         \
-                        register unsigned long __sc_8 __asm__ ("r8");         \
-                                                                              \
-            LSS_LOADARGS_##nr(name, args);                                    \
-            __asm__ __volatile__                                              \
-                ("sc\n\t"                                                     \
-                 "mfcr %0"                                                    \
-                 : "=&r" (__sc_0),                                            \
-                   "=&r" (__sc_3), "=&r" (__sc_4),                            \
-                   "=&r" (__sc_5), "=&r" (__sc_6),                            \
-                   "=&r" (__sc_7), "=&r" (__sc_8)                             \
-                 : LSS_ASMINPUT_##nr                                          \
-                 : "cr0", "ctr", "memory",                                    \
-                   "r9", "r10", "r11", "r12");                                \
-            __sc_ret = __sc_3;                                                \
-            __sc_err = __sc_0;                                                \
-        }                                                                     \
-        LSS_RETURN(type, __sc_ret, __sc_err)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-       type LSS_NAME(name)(void) {                                            \
-          LSS_BODY(0, type, name);                                            \
-       }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-       type LSS_NAME(name)(type1 arg1) {                                      \
-          LSS_BODY(1, type, name, arg1);                                      \
-       }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-       type LSS_NAME(name)(type1 arg1, type2 arg2) {                          \
-          LSS_BODY(2, type, name, arg1, arg2);                                \
-       }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {              \
-          LSS_BODY(3, type, name, arg1, arg2, arg3);                          \
-       }
-    #undef _syscall4
-    #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4)                                \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {  \
-          LSS_BODY(4, type, name, arg1, arg2, arg3, arg4);                    \
-       }
-    #undef _syscall5
-    #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4, type5, arg5)                   \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,    \
-                                               type5 arg5) {                  \
-          LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5);              \
-       }
-    #undef _syscall6
-    #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4, type5, arg5, type6, arg6)      \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4,    \
-                                               type5 arg5, type6 arg6) {      \
-          LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6);        \
-       }
-    /* clone function adapted from glibc 2.3.6 clone.S                       */
-    /* TODO(csilvers): consider wrapping some args up in a struct, like we
-     * do for i386's _syscall6, so we can compile successfully on gcc 2.95
-     */
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long __ret, __err;
-      {
-        register int (*__fn)(void *)    __asm__ ("r8")  = fn;
-        register void *__cstack                 __asm__ ("r4")  = child_stack;
-        register int __flags                    __asm__ ("r3")  = flags;
-        register void * __arg                   __asm__ ("r9")  = arg;
-        register int * __ptidptr                __asm__ ("r5")  = parent_tidptr;
-        register void * __newtls                __asm__ ("r6")  = newtls;
-        register int * __ctidptr                __asm__ ("r7")  = child_tidptr;
-        __asm__ __volatile__(
-            /* check for fn == NULL
-             * and child_stack == NULL
-             */
-            "cmpwi cr0, %6, 0\n\t"
-            "cmpwi cr1, %7, 0\n\t"
-            "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t"
-            "beq- cr0, 1f\n\t"
-
-            /* set up stack frame for child                                  */
-            "clrrwi %7, %7, 4\n\t"
-            "li 0, 0\n\t"
-            "stwu 0, -16(%7)\n\t"
-
-            /* fn, arg, child_stack are saved across the syscall: r28-30     */
-            "mr 28, %6\n\t"
-            "mr 29, %7\n\t"
-            "mr 27, %9\n\t"
-
-            /* syscall                                                       */
-            "li 0, %4\n\t"
-            /* flags already in r3
-             * child_stack already in r4
-             * ptidptr already in r5
-             * newtls already in r6
-             * ctidptr already in r7
-             */
-            "sc\n\t"
-
-            /* Test if syscall was successful                                */
-            "cmpwi cr1, 3, 0\n\t"
-            "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
-            "bne- cr1, 1f\n\t"
-
-            /* Do the function call                                          */
-            "mtctr 28\n\t"
-            "mr 3, 27\n\t"
-            "bctrl\n\t"
-
-            /* Call _exit(r3)                                                */
-            "li 0, %5\n\t"
-            "sc\n\t"
-
-            /* Return to parent                                              */
-            "1:\n"
-            "mfcr %1\n\t"
-            "mr %0, 3\n\t"
-              : "=r" (__ret), "=r" (__err)
-              : "0" (-1), "1" (EINVAL),
-                "i" (__NR_clone), "i" (__NR_exit),
-                "r" (__fn), "r" (__cstack), "r" (__flags),
-                "r" (__arg), "r" (__ptidptr), "r" (__newtls),
-                "r" (__ctidptr)
-              : "cr0", "cr1", "memory", "ctr",
-                "r0", "r29", "r27", "r28");
-      }
-      LSS_RETURN(int, __ret, __err);
-    }
-  #elif defined(__s390__)
-    #undef  LSS_REG
-    #define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a
-    #undef  LSS_BODY
-    #define LSS_BODY(type, name, args...)                                     \
-        register unsigned long __nr __asm__("r1")                             \
-            = (unsigned long)(__NR_##name);                                   \
-        register long __res_r2 __asm__("r2");                                 \
-        long __res;                                                           \
-        __asm__ __volatile__                                                  \
-            ("svc 0\n\t"                                                      \
-             : "=d"(__res_r2)                                                 \
-             : "d"(__nr), ## args                                             \
-             : "memory");                                                     \
-        __res = __res_r2;                                                     \
-        LSS_RETURN(type, __res)
-    #undef _syscall0
-    #define _syscall0(type, name)                                             \
-       type LSS_NAME(name)(void) {                                            \
-          LSS_BODY(type, name);                                               \
-       }
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1)                                \
-       type LSS_NAME(name)(type1 arg1) {                                      \
-          LSS_REG(2, arg1);                                                   \
-          LSS_BODY(type, name, "0"(__r2));                                    \
-       }
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2)                   \
-       type LSS_NAME(name)(type1 arg1, type2 arg2) {                          \
-          LSS_REG(2, arg1); LSS_REG(3, arg2);                                 \
-          LSS_BODY(type, name, "0"(__r2), "d"(__r3));                         \
-       }
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)      \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) {              \
-          LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3);               \
-          LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4));              \
-       }
-    #undef _syscall4
-    #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4)                                \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3,                \
-                           type4 arg4) {                                      \
-          LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3);               \
-          LSS_REG(5, arg4);                                                   \
-          LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4),               \
-                               "d"(__r5));                                    \
-       }
-    #undef _syscall5
-    #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4, type5, arg5)                   \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3,                \
-                           type4 arg4, type5 arg5) {                          \
-          LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3);               \
-          LSS_REG(5, arg4); LSS_REG(6, arg5);                                 \
-          LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4),               \
-                               "d"(__r5), "d"(__r6));                         \
-       }
-    #undef _syscall6
-    #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3,      \
-                                  type4, arg4, type5, arg5, type6, arg6)      \
-       type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3,                \
-                           type4 arg4, type5 arg5, type6 arg6) {              \
-          LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3);               \
-          LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6);               \
-          LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4),               \
-                               "d"(__r5), "d"(__r6), "d"(__r7));              \
-       }
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      long __ret;
-      {
-        register int  (*__fn)(void *)    __asm__ ("r1")  = fn;
-        register void  *__cstack         __asm__ ("r2")  = child_stack;
-        register int    __flags          __asm__ ("r3")  = flags;
-        register void  *__arg            __asm__ ("r0")  = arg;
-        register int   *__ptidptr        __asm__ ("r4")  = parent_tidptr;
-        register void  *__newtls         __asm__ ("r6")  = newtls;
-        register int   *__ctidptr        __asm__ ("r5")  = child_tidptr;
-        __asm__ __volatile__ (
-    #ifndef __s390x__
-                                  /* arg already in r0 */
-          "ltr %4, %4\n\t"        /* check fn, which is already in r1 */
-          "jz 1f\n\t"             /* NULL function pointer, return -EINVAL */
-          "ltr %5, %5\n\t"        /* check child_stack, which is already in r2 */
-          "jz 1f\n\t"             /* NULL stack pointer, return -EINVAL */
-                                  /* flags already in r3 */
-                                  /* parent_tidptr already in r4 */
-                                  /* child_tidptr already in r5 */
-                                  /* newtls already in r6 */
-          "svc %2\n\t"            /* invoke clone syscall */
-          "ltr %0,%%r2\n\t"       /* load return code into __ret and test */
-          "jnz 1f\n\t"            /* return to parent if non-zero */
-                                  /* start child thread */
-          "lr %%r2, %7\n\t"       /* set first parameter to void *arg */
-          "ahi %%r15, -96\n\t"    /* make room on the stack for the save area */
-          "xc 0(4,%%r15), 0(%%r15)\n\t"
-          "basr %%r14, %4\n\t"    /* jump to fn */
-          "svc %3\n"              /* invoke exit syscall */
-          "1:\n"
-    #else
-                                  /* arg already in r0 */
-          "ltgr %4, %4\n\t"       /* check fn, which is already in r1 */
-          "jz 1f\n\t"             /* NULL function pointer, return -EINVAL */
-          "ltgr %5, %5\n\t"       /* check child_stack, which is already in r2 */
-          "jz 1f\n\t"             /* NULL stack pointer, return -EINVAL */
-                                  /* flags already in r3 */
-                                  /* parent_tidptr already in r4 */
-                                  /* child_tidptr already in r5 */
-                                  /* newtls already in r6 */
-          "svc %2\n\t"            /* invoke clone syscall */
-          "ltgr %0, %%r2\n\t"     /* load return code into __ret and test */
-          "jnz 1f\n\t"            /* return to parent if non-zero */
-                                  /* start child thread */
-          "lgr %%r2, %7\n\t"      /* set first parameter to void *arg */
-          "aghi %%r15, -160\n\t"  /* make room on the stack for the save area */
-          "xc 0(8,%%r15), 0(%%r15)\n\t"
-          "basr %%r14, %4\n\t"    /* jump to fn */
-          "svc %3\n"              /* invoke exit syscall */
-          "1:\n"
-    #endif
-          : "=r" (__ret)
-          : "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit),
-            "d" (__fn), "d" (__cstack), "d" (__flags), "d" (__arg),
-            "d" (__ptidptr), "d" (__newtls), "d" (__ctidptr)
-          : "cc", "r14", "memory"
-        );
-      }
-      LSS_RETURN(int, __ret);
-    }
-  #elif defined(__e2k__)
-
-    #undef _LSS_BODY
-    #define _LSS_BODY(nr, type, name, ...)                                    \
-      register unsigned long long __res;                                      \
-      __asm__ __volatile__                                                    \
-      (                                                                       \
-       "{\n\t"                                                                \
-       "  sdisp %%ctpr1, 0x3\n\t"                                             \
-       "  addd, s 0x0, %[sys_num], %%b[0]\n\t"                                \
-       LSS_BODY_ASM##nr                                                       \
-       "}\n\t"                                                                \
-       "{\n\t"                                                                \
-       "  call %%ctpr1, wbs = %#\n\t"                                         \
-       "}\n\t"                                                                \
-       "{\n\t"                                                                \
-       "  addd, s 0x0, %%b[0], %[res]\n\t"                                    \
-       "}\n\t"                                                                \
-       : [res] "=r" (__res)                                                   \
-       :                                                                      \
-       LSS_BODY_ARG##nr(__VA_ARGS__)                                          \
-       [sys_num] "ri" (__NR_##name)                                           \
-       : "ctpr1", "ctpr2", "ctpr3",                                           \
-       "b[0]", "b[1]", "b[2]", "b[3]",                                        \
-       "b[4]", "b[5]", "b[6]", "b[7]"                                         \
-       );                                                                     \
-       LSS_RETURN(type, __res);
-
-    #undef LSS_BODY
-    #define LSS_BODY(nr, type, name, args...) \
-      _LSS_BODY(nr, type, name, ## args)
-
-    #undef LSS_BODY_ASM0
-    #undef LSS_BODY_ASM1
-    #undef LSS_BODY_ASM2
-    #undef LSS_BODY_ASM3
-    #undef LSS_BODY_ASM4
-    #undef LSS_BODY_ASM5
-    #undef LSS_BODY_ASM6
-
-    #define LSS_BODY_ASM0
-    #define LSS_BODY_ASM1 LSS_BODY_ASM0 \
-      "  addd, s 0x0, %[arg1], %%b[1]\n\t"
-    #define LSS_BODY_ASM2 LSS_BODY_ASM1 \
-      "  addd, s 0x0, %[arg2], %%b[2]\n\t"
-    #define LSS_BODY_ASM3 LSS_BODY_ASM2 \
-      "  addd, s 0x0, %[arg3], %%b[3]\n\t"
-    #define LSS_BODY_ASM4 LSS_BODY_ASM3 \
-      "  addd, s 0x0, %[arg4], %%b[4]\n\t"
-    #define LSS_BODY_ASM5 LSS_BODY_ASM4 \
-      "  addd, s 0x0, %[arg5], %%b[5]\n\t"
-    #define LSS_BODY_ASM6 LSS_BODY_ASM5 \
-      "}\n\t" \
-      "{\n\t" \
-      "  addd, s 0x0, %[arg6], %%b[6]\n\t"
-
-    #undef LSS_SYSCALL_ARG
-    #define LSS_SYSCALL_ARG(a) ((unsigned long long)(uintptr_t)(a))
-
-    #undef LSS_BODY_ARG0
-    #undef LSS_BODY_ARG1
-    #undef LSS_BODY_ARG2
-    #undef LSS_BODY_ARG3
-    #undef LSS_BODY_ARG4
-    #undef LSS_BODY_ARG5
-    #undef LSS_BODY_ARG6
-
-    #define LSS_BODY_ARG0()
-    #define LSS_BODY_ARG1(_arg1) \
-      [arg1] "ri" LSS_SYSCALL_ARG(_arg1),
-    #define LSS_BODY_ARG2(_arg1, _arg2) \
-      LSS_BODY_ARG1(_arg1) \
-      [arg2] "ri" LSS_SYSCALL_ARG(_arg2),
-    #define LSS_BODY_ARG3(_arg1, _arg2, _arg3) \
-      LSS_BODY_ARG2(_arg1, _arg2) \
-      [arg3] "ri" LSS_SYSCALL_ARG(_arg3),
-    #define LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \
-      LSS_BODY_ARG3(_arg1, _arg2, _arg3) \
-      [arg4] "ri" LSS_SYSCALL_ARG(_arg4),
-    #define LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \
-      LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \
-      [arg5] "ri" LSS_SYSCALL_ARG(_arg5),
-    #define LSS_BODY_ARG6(_arg1, _arg2, _arg3, _arg4, _arg5, _arg6) \
-      LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \
-      [arg6] "ri" LSS_SYSCALL_ARG(_arg6),
-
-    #undef _syscall0
-    #define _syscall0(type, name) \
-      type LSS_NAME(name)(void) { \
-        LSS_BODY(0, type, name);     \
-      }
-
-    #undef _syscall1
-    #define _syscall1(type, name, type1, arg1) \
-      type LSS_NAME(name)(type1 arg1) { \
-         LSS_BODY(1, type, name, arg1) \
-      }
-
-    #undef _syscall2
-    #define _syscall2(type, name, type1, arg1, type2, arg2) \
-      type LSS_NAME(name)(type1 arg1, type2 arg2) { \
-          LSS_BODY(2, type, name, arg1, arg2) \
-      }
-
-    #undef _syscall3
-    #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
-          LSS_BODY(3, type, name, arg1, arg2, arg3) \
-      }
-
-    #undef _syscall4
-    #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \
-                      type4, arg4) \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
-          LSS_BODY(4, type, name, arg1, arg2, arg3, arg4) \
-      }
-
-    #undef _syscall5
-    #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \
-                      type4, arg4, type5, arg5) \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
-                          type5 arg5) { \
-          LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5) \
-      }
-
-    #undef _syscall6
-    #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \
-                      type4, arg4, type5, arg5, type6, arg6) \
-      type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
-                          type5 arg5, type6 arg6) { \
-          LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6) \
-      }
-
-    LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
-                                   int flags, void *arg, int *parent_tidptr,
-                                   void *newtls, int *child_tidptr) {
-      unsigned long long __res;
-
-      __asm__ __volatile__ (
-                            "{\n\t"
-                            "  addd,s 0x0, %[nr_clone], %%b[0]\n\t"
-                            "  addd,s 0x0, %[flags], %%db[1]\n\t"
-                            "  addd,s 0x0, %[child_stack], %%db[2]\n\t"
-                            "  addd,s 0x0, %[parent_tidptr], %%db[3]\n\t"
-                            "  addd,s 0x0, %[child_tidptr], %%db[4]\n\t"
-                            "  addd,s 0x0, %[newtls], %%db[5]\n\t"
-                            "}\n\t"
-                            /* if (fn == NULL)
-                             *   return -EINVAL;
-                             */
-
-                            "{\n\t"
-                            "  disp %%ctpr1, .L1\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  cmpesb,s 0x0, %[fn], %%pred0\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  ct %%ctpr1 ? %%pred0\n\t"
-                            "}\n\t"
-
-                            /* if (child_stack == NULL)
-                             *   return -EINVAL;
-                             */
-                            "{\n\t"
-                            "  cmpesb,s 0x0, %%db[2], %%pred0\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  ct %%ctpr1 ? %%pred0\n\t"
-                            "}\n\t"
-
-                            /* b[0] = syscall(%b[0] = __NR_clone,
-                             *                %db[1] = flags,
-                             *                %db[2] = child_stack,
-                             *                %db[3] = parent_tidptr,
-                             *                %db[4] = child_tidptr,
-                             *                %db[5] = newtls)
-                             */
-                            "{\n\t"
-                            "  sdisp %%ctpr1, 0x3\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  call %%ctpr1, wbs = %#\n\t"
-                            "}\n\t"
-
-                            /* if (%[b0] != 0)
-                             *   return %b[0];
-                             */
-                            "{\n\t"
-                            "  disp %%ctpr1, .L2\n\t"
-                            "  cmpesb,s 0x0, %%b[0], %%pred0\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  ct %%ctpr1 ? ~%%pred0\n\t"
-                            "}\n\t"
-                            /* In the child, now. Call "fn(arg)".
-                             */
-
-                            "{\n\t"
-                            "  movtd,s %[fn], %%ctpr1\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  addd,s 0x0, %[arg], %%db[0]\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  call %%ctpr1, wbs = %#\n\t"
-                            "}\n\t"
-                            /* Call _exit(%b[0]).
-                             */
-
-                            "{\n\t"
-                            "  sdisp %%ctpr1, 0x3\n\t"
-                            "  addd,s 0x0, %%b[0], %%b[1]\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  addd,s 0x0, %[nr_exit], %%b[0]\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  call %%ctpr1, wbs = %#\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  disp %%ctpr1, .L2\n\t"
-                            "  adds,s 0x0, 0x0, %%b[0]\n\t"
-                            "}\n\t"
-                            "{\n\t"
-                            "  ct %%ctpr1\n\t"
-                            "}\n\t"
-                            ".L1:\n\t"
-                            "{\n\t"
-                            "  addd,s 0x0, %[einval], %%b[0]\n\t"
-                            "}\n\t"
-                            ".L2:\n\t"
-                            "{\n\t"
-                            "  addd,s 0x0, %%b[0], %[res]\n\t"
-                            "}\n\t"
-                            : [res] "=r" LSS_SYSCALL_ARG(__res)
-                            : [nr_clone] "ri" LSS_SYSCALL_ARG(__NR_clone)
-                              [arg] "ri" LSS_SYSCALL_ARG(arg)
-                              [nr_exit] "ri" LSS_SYSCALL_ARG(__NR_exit)
-                              [flags] "ri" LSS_SYSCALL_ARG(flags)
-                              [child_stack] "ri" LSS_SYSCALL_ARG(child_stack)
-                              [parent_tidptr] "ri"
-                              LSS_SYSCALL_ARG(parent_tidptr)
-                              [newtls] "ri" LSS_SYSCALL_ARG(newtls)
-                              [child_tidptr] "ri"
-                              LSS_SYSCALL_ARG(child_tidptr)
-                              [fn] "ri" LSS_SYSCALL_ARG(fn)
-                              [einval] "ri" LSS_SYSCALL_ARG(-EINVAL)
-                            : "ctpr1", "b[0]", "b[1]", "b[2]", "b[3]",
-                              "b[4]", "b[5]", "pred0");
-      LSS_RETURN(int, __res);
-    }
-
-  #endif
-  #define __NR__exit   __NR_exit
-  #define __NR__gettid __NR_gettid
-  #define __NR__mremap __NR_mremap
-  LSS_INLINE _syscall1(void *,  brk,             void *,      e)
-  LSS_INLINE _syscall1(int,     chdir,           const char *,p)
-  LSS_INLINE _syscall1(int,     close,           int,         f)
-  LSS_INLINE _syscall2(int,     clock_getres,    int,         c,
-                       struct kernel_timespec*, t)
-  LSS_INLINE _syscall2(int,     clock_gettime,   int,         c,
-                       struct kernel_timespec*, t)
-  LSS_INLINE _syscall1(int,     dup,             int,         f)
-  #if defined(__NR_dup2)
-    // dup2 is polyfilled below when not available.
-    LSS_INLINE _syscall2(int,     dup2,            int,         s,
-                         int,            d)
-  #endif
-  #if defined(__NR_dup3)
-    LSS_INLINE _syscall3(int, dup3,  int, s, int, d, int, f)
-  #endif
-  LSS_INLINE _syscall3(int,     execve,          const char*, f,
-                       const char*const*,a,const char*const*, e)
-  LSS_INLINE _syscall1(int,     _exit,           int,         e)
-  LSS_INLINE _syscall1(int,     exit_group,      int,         e)
-  LSS_INLINE _syscall3(int,     fcntl,           int,         f,
-                       int,            c, long,   a)
-  #if defined(__NR_fork)
-    // fork is polyfilled below when not available.
-    LSS_INLINE _syscall0(pid_t,   fork)
-  #endif
-  LSS_INLINE _syscall2(int,     fstat,           int,         f,
-                      struct kernel_stat*,   b)
-  LSS_INLINE _syscall2(int,     fstatfs,         int,         f,
-                      struct kernel_statfs*, b)
-  #if defined(__x86_64__)
-    /* Need to make sure off_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE int LSS_NAME(ftruncate)(int f, off_t l) {
-      LSS_BODY(2, int, ftruncate, LSS_SYSCALL_ARG(f), (uint64_t)(l));
-    }
-  #else
-    LSS_INLINE _syscall2(int, ftruncate,           int,         f,
-                         off_t,          l)
-  #endif
-  LSS_INLINE _syscall6(int,     futex,          int*,        u,
-                       int,     o,              int,         v,
-                       struct kernel_timespec*, t,
-                       int*,    u2,             int,         v2)
-  LSS_INLINE _syscall3(int,     getdents,        int,         f,
-                       struct kernel_dirent*, d, int,    c)
-  LSS_INLINE _syscall3(int,     getdents64,      int,         f,
-                      struct kernel_dirent64*, d, int,    c)
-  LSS_INLINE _syscall0(gid_t,   getegid)
-  LSS_INLINE _syscall0(uid_t,   geteuid)
-  #if defined(__NR_getpgrp)
-    LSS_INLINE _syscall0(pid_t,   getpgrp)
-  #endif
-  LSS_INLINE _syscall0(pid_t,   getpid)
-  LSS_INLINE _syscall0(pid_t,   getppid)
-  LSS_INLINE _syscall2(int,     getpriority,     int,         a,
-                       int,            b)
-  LSS_INLINE _syscall3(int,     getresgid,       gid_t *,     r,
-                       gid_t *,         e,       gid_t *,     s)
-  LSS_INLINE _syscall3(int,     getresuid,       uid_t *,     r,
-                       uid_t *,         e,       uid_t *,     s)
-#if !defined(__ARM_EABI__)
-  LSS_INLINE _syscall2(int,     getrlimit,       int,         r,
-                      struct kernel_rlimit*, l)
-#endif
-  LSS_INLINE _syscall1(pid_t,   getsid,          pid_t,       p)
-  LSS_INLINE _syscall0(pid_t,   _gettid)
-  LSS_INLINE _syscall2(pid_t,   gettimeofday,    struct kernel_timeval*, t,
-                       void*, tz)
-  LSS_INLINE _syscall5(int,     setxattr,        const char *,p,
-                       const char *,   n,        const void *,v,
-                       size_t,         s,        int,         f)
-  LSS_INLINE _syscall5(int,     lsetxattr,       const char *,p,
-                       const char *,   n,        const void *,v,
-                       size_t,         s,        int,         f)
-  LSS_INLINE _syscall4(ssize_t, getxattr,        const char *,p,
-                       const char *,   n,        void *,      v, size_t, s)
-  LSS_INLINE _syscall4(ssize_t, lgetxattr,       const char *,p,
-                       const char *,   n,        void *,      v, size_t, s)
-  LSS_INLINE _syscall3(ssize_t, listxattr,       const char *,p,
-                       char *,   l,              size_t,      s)
-  LSS_INLINE _syscall3(ssize_t, llistxattr,      const char *,p,
-                       char *,   l,              size_t,      s)
-  LSS_INLINE _syscall3(int,     ioctl,           int,         d,
-                       int,     r,               void *,      a)
-  LSS_INLINE _syscall2(int,     ioprio_get,      int,         which,
-                       int,     who)
-  LSS_INLINE _syscall3(int,     ioprio_set,      int,         which,
-                       int,     who,             int,         ioprio)
-  LSS_INLINE _syscall2(int,     kill,            pid_t,       p,
-                       int,            s)
-  #if defined(__x86_64__)
-    /* Need to make sure off_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE off_t LSS_NAME(lseek)(int f, off_t o, int w) {
-      _LSS_BODY(3, off_t, lseek, off_t, LSS_SYSCALL_ARG(f), (uint64_t)(o),
-                                        LSS_SYSCALL_ARG(w));
-    }
-  #else
-    LSS_INLINE _syscall3(off_t,   lseek,           int,         f,
-                         off_t,          o, int,    w)
-  #endif
-  LSS_INLINE _syscall2(int,     munmap,          void*,       s,
-                       size_t,         l)
-  LSS_INLINE _syscall6(long,    move_pages,      pid_t,       p,
-                       unsigned long,  n, void **,g, int *,   d,
-                       int *,          s, int,    f)
-  LSS_INLINE _syscall3(int,     mprotect,        const void *,a,
-                       size_t,         l,        int,         p)
-  LSS_INLINE _syscall5(void*,   _mremap,         void*,       o,
-                       size_t,         os,       size_t,      ns,
-                       unsigned long,  f, void *, a)
-  #if defined(__NR_open)
-    // open is polyfilled below when not available.
-    LSS_INLINE _syscall3(int,     open,            const char*, p,
-                         int,            f, int,    m)
-  #endif
-  #if defined(__NR_poll)
-    // poll is polyfilled below when not available.
-    LSS_INLINE _syscall3(int,     poll,           struct kernel_pollfd*, u,
-                         unsigned int,   n, int,    t)
-  #endif
-  #if defined(__NR_ppoll)
-    LSS_INLINE _syscall5(int, ppoll, struct kernel_pollfd *, u,
-                         unsigned int, n, const struct kernel_timespec *, t,
-                         const struct kernel_sigset_t *, sigmask, size_t, s)
-  #endif
-  LSS_INLINE _syscall5(int,     prctl,           int,         option,
-                       unsigned long,  arg2,
-                       unsigned long,  arg3,
-                       unsigned long,  arg4,
-                       unsigned long,  arg5)
-  LSS_INLINE _syscall4(long,    ptrace,          int,         r,
-                       pid_t,          p, void *, a, void *, d)
-  #if defined(__NR_quotactl)
-    // Defined on x86_64 / i386 only
-    LSS_INLINE _syscall4(int,  quotactl,  int,  cmd,  const char *, special,
-                         int, id, caddr_t, addr)
-  #endif
-  LSS_INLINE _syscall3(ssize_t, read,            int,         f,
-                       void *,         b, size_t, c)
-  #if defined(__NR_readlink)
-    // readlink is polyfilled below when not available.
-    LSS_INLINE _syscall3(int,     readlink,        const char*, p,
-                         char*,          b, size_t, s)
-  #endif
-  #if defined(__NR_readlinkat)
-    LSS_INLINE _syscall4(int, readlinkat, int, d, const char *, p, char *, b,
-                         size_t, s)
-  #endif
-  LSS_INLINE _syscall4(int,     rt_sigaction,    int,         s,
-                       const struct kernel_sigaction*, a,
-                       struct kernel_sigaction*, o, size_t,   c)
-  LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s,
-                       size_t,         c)
-  LSS_INLINE _syscall4(int, rt_sigprocmask,      int,         h,
-                       const struct kernel_sigset_t*,  s,
-                       struct kernel_sigset_t*,        o, size_t, c)
-  LSS_INLINE _syscall2(int, rt_sigsuspend,
-                       const struct kernel_sigset_t*, s,  size_t, c)
-  LSS_INLINE _syscall4(int, rt_sigtimedwait, const struct kernel_sigset_t*, s,
-                       siginfo_t*, i, const struct timespec*, t, size_t, c)
-  LSS_INLINE _syscall3(int,     sched_getaffinity,pid_t,      p,
-                       unsigned int,   l, unsigned long *, m)
-  LSS_INLINE _syscall3(int,     sched_setaffinity,pid_t,      p,
-                       unsigned int,   l, unsigned long *, m)
-  LSS_INLINE _syscall0(int,     sched_yield)
-  LSS_INLINE _syscall1(long,    set_tid_address, int *,       t)
-  LSS_INLINE _syscall1(int,     setfsgid,        gid_t,       g)
-  LSS_INLINE _syscall1(int,     setfsuid,        uid_t,       u)
-  LSS_INLINE _syscall1(int,     setuid,          uid_t,       u)
-  LSS_INLINE _syscall1(int,     setgid,          gid_t,       g)
-  LSS_INLINE _syscall2(int,     setpgid,         pid_t,       p,
-                       pid_t,          g)
-  LSS_INLINE _syscall3(int,     setpriority,     int,         a,
-                       int,            b, int,    p)
-  LSS_INLINE _syscall3(int,     setresgid,       gid_t,       r,
-                       gid_t,          e, gid_t,  s)
-  LSS_INLINE _syscall3(int,     setresuid,       uid_t,       r,
-                       uid_t,          e, uid_t,  s)
-  LSS_INLINE _syscall2(int,     setrlimit,       int,         r,
-                       const struct kernel_rlimit*, l)
-  LSS_INLINE _syscall0(pid_t,    setsid)
-  LSS_INLINE _syscall2(int,     sigaltstack,     const stack_t*, s,
-                       const stack_t*, o)
-  #if defined(__NR_sigreturn)
-    LSS_INLINE _syscall1(int,     sigreturn,       unsigned long, u)
-  #endif
-  #if defined(__NR_stat)
-    // stat and lstat are polyfilled below when not available.
-    LSS_INLINE _syscall2(int,     stat,            const char*, f,
-                        struct kernel_stat*,   b)
-  #endif
-  #if defined(__NR_lstat)
-    LSS_INLINE _syscall2(int,     lstat,           const char*, f,
-                         struct kernel_stat*,   b)
-  #endif
-  LSS_INLINE _syscall2(int,     statfs,          const char*, f,
-                      struct kernel_statfs*, b)
-  LSS_INLINE _syscall3(int,     tgkill,          pid_t,       p,
-                       pid_t,          t, int,            s)
-  LSS_INLINE _syscall2(int,     tkill,           pid_t,       p,
-                       int,            s)
-  #if defined(__NR_unlink)
-    // unlink is polyfilled below when not available.
-    LSS_INLINE _syscall1(int,     unlink,           const char*, f)
-  #endif
-  LSS_INLINE _syscall3(ssize_t, write,            int,        f,
-                       const void *,   b, size_t, c)
-  LSS_INLINE _syscall3(ssize_t, writev,           int,        f,
-                       const struct kernel_iovec*, v, size_t, c)
-  #if defined(__NR_getcpu)
-    LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu,
-                         unsigned *, node, void *, unused)
-  #endif
-  #if defined(__x86_64__) || defined(__e2k__) ||                              \
-     (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
-    LSS_INLINE _syscall3(int, recvmsg,            int,   s,
-                        struct kernel_msghdr*,     m, int, f)
-    LSS_INLINE _syscall3(int, sendmsg,            int,   s,
-                         const struct kernel_msghdr*, m, int, f)
-    LSS_INLINE _syscall6(int, sendto,             int,   s,
-                         const void*,             m, size_t, l,
-                         int,                     f,
-                         const struct kernel_sockaddr*, a, int, t)
-    LSS_INLINE _syscall2(int, shutdown,           int,   s,
-                         int,                     h)
-    LSS_INLINE _syscall3(int, socket,             int,   d,
-                         int,                     t, int,       p)
-    LSS_INLINE _syscall4(int, socketpair,         int,   d,
-                         int,                     t, int,       p, int*, s)
-  #endif
-  #if defined(__NR_fadvise64)
-    #if defined(__x86_64__)
-    /* Need to make sure loff_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, loff_t len,
-                                       int advice) {
-      LSS_BODY(4, int, fadvise64, LSS_SYSCALL_ARG(fd), (uint64_t)(offset),
-                                  (uint64_t)(len), LSS_SYSCALL_ARG(advice));
-    }
-    #else
-    LSS_INLINE _syscall4(int, fadvise64,
-                         int, fd, loff_t, offset, loff_t, len, int, advice)
-    #endif
-  #elif defined(__i386__)
-    #define __NR__fadvise64_64 __NR_fadvise64_64
-    LSS_INLINE _syscall6(int, _fadvise64_64, int, fd,
-                         unsigned, offset_lo, unsigned, offset_hi,
-                         unsigned, len_lo, unsigned, len_hi,
-                         int, advice)
-
-    LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset,
-                                       loff_t len, int advice) {
-      return LSS_NAME(_fadvise64_64)(fd,
-                                     (unsigned)offset, (unsigned)(offset >>32),
-                                     (unsigned)len, (unsigned)(len >> 32),
-                                     advice);
-    }
-
-  #elif defined(__s390__) && !defined(__s390x__)
-    #define __NR__fadvise64_64 __NR_fadvise64_64
-    struct kernel_fadvise64_64_args {
-      int fd;
-      long long offset;
-      long long len;
-      int advice;
-    };
-
-    LSS_INLINE _syscall1(int, _fadvise64_64,
-                         struct kernel_fadvise64_64_args *args)
-
-    LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset,
-                                       loff_t len, int advice) {
-      struct kernel_fadvise64_64_args args = { fd, offset, len, advice };
-      return LSS_NAME(_fadvise64_64)(&args);
-    }
-  #endif
-  #if defined(__NR_fallocate)
-    #if defined(__x86_64__)
-    /* Need to make sure loff_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE int LSS_NAME(fallocate)(int f, int mode, loff_t offset,
-                                       loff_t len) {
-      LSS_BODY(4, int, fallocate, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(mode),
-                                  (uint64_t)(offset), (uint64_t)(len));
-    }
-    #elif (defined(__i386__) || (defined(__s390__) && !defined(__s390x__)) \
-           || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) \
-           || (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) \
-           || defined(__PPC__))
-    #define __NR__fallocate __NR_fallocate
-    LSS_INLINE _syscall6(int, _fallocate, int, fd,
-                         int, mode,
-                         unsigned, offset_lo, unsigned, offset_hi,
-                         unsigned, len_lo, unsigned, len_hi)
-
-    LSS_INLINE int LSS_NAME(fallocate)(int fd, int mode,
-                                       loff_t offset, loff_t len) {
-      union { loff_t off; unsigned w[2]; } o = { offset }, l = { len };
-      return LSS_NAME(_fallocate)(fd, mode, o.w[0], o.w[1], l.w[0], l.w[1]);
-    }
-    #else
-    LSS_INLINE _syscall4(int, fallocate,
-                         int, f, int, mode, loff_t, offset, loff_t, len)
-    #endif
-  #endif
-  #if defined(__NR_getrandom)
-    LSS_INLINE _syscall3(ssize_t, getrandom, void*, buffer, size_t, length,
-                         unsigned int, flags)
-  #endif
-  #if defined(__NR_newfstatat)
-    LSS_INLINE _syscall4(int, newfstatat,         int,   d,
-                         const char *,            p,
-                         struct kernel_stat*,     b, int, f)
-  #endif
-  #if defined(__x86_64__) || defined(__s390x__)
-    LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid,
-                                         gid_t *egid,
-                                         gid_t *sgid) {
-      return LSS_NAME(getresgid)(rgid, egid, sgid);
-    }
-
-    LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid,
-                                         uid_t *euid,
-                                         uid_t *suid) {
-      return LSS_NAME(getresuid)(ruid, euid, suid);
-    }
-
-    LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) {
-      return LSS_NAME(setfsgid)(gid);
-    }
-
-    LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) {
-      return LSS_NAME(setfsuid)(uid);
-    }
-
-    LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) {
-      return LSS_NAME(setresgid)(rgid, egid, sgid);
-    }
-
-    LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) {
-      return LSS_NAME(setresuid)(ruid, euid, suid);
-    }
-
-    LSS_INLINE int LSS_NAME(sigaction)(int signum,
-                                       const struct kernel_sigaction *act,
-                                       struct kernel_sigaction *oldact) {
-      #if defined(__x86_64__)
-      /* On x86_64, the kernel requires us to always set our own
-       * SA_RESTORER in order to be able to return from a signal handler.
-       * This function must have a "magic" signature that the "gdb"
-       * (and maybe the kernel?) can recognize.
-       */
-      if (act != NULL && !(act->sa_flags & SA_RESTORER)) {
-        struct kernel_sigaction a = *act;
-        a.sa_flags   |= SA_RESTORER;
-        a.sa_restorer = LSS_NAME(restore_rt)();
-        return LSS_NAME(rt_sigaction)(signum, &a, oldact,
-                                      (KERNEL_NSIG+7)/8);
-      } else
-      #endif
-        return LSS_NAME(rt_sigaction)(signum, act, oldact,
-                                      (KERNEL_NSIG+7)/8);
-    }
-
-    LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) {
-      return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8);
-    }
-
-    LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) {
-      return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8);
-    }
-  #endif
-  #if defined(__NR_rt_sigprocmask)
-    LSS_INLINE int LSS_NAME(sigprocmask)(int how,
-                                         const struct kernel_sigset_t *set,
-                                         struct kernel_sigset_t *oldset) {
-      return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8);
-    }
-  #endif
-  #if defined(__NR_rt_sigtimedwait)
-    LSS_INLINE int LSS_NAME(sigtimedwait)(const struct kernel_sigset_t *set,
-                                          siginfo_t *info,
-                                          const struct timespec *timeout) {
-      return LSS_NAME(rt_sigtimedwait)(set, info, timeout, (KERNEL_NSIG+7)/8);
-    }
-  #endif
-  #if defined(__NR_wait4)
-    LSS_INLINE _syscall4(pid_t, wait4,            pid_t, p,
-                         int*,                    s, int,       o,
-                        struct kernel_rusage*,     r)
-  #endif
-  #if defined(__NR_openat)
-    LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m)
-  #endif
-  #if defined(__NR_unlinkat)
-    LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f)
-  #endif
-  #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
-      (defined(__s390__) && !defined(__s390x__))
-    #define __NR__getresgid32 __NR_getresgid32
-    #define __NR__getresuid32 __NR_getresuid32
-    #define __NR__setfsgid32  __NR_setfsgid32
-    #define __NR__setfsuid32  __NR_setfsuid32
-    #define __NR__setresgid32 __NR_setresgid32
-    #define __NR__setresuid32 __NR_setresuid32
-#if defined(__ARM_EABI__)
-    LSS_INLINE _syscall2(int,   ugetrlimit,        int,          r,
-                        struct kernel_rlimit*, l)
-#endif
-    LSS_INLINE _syscall3(int,     _getresgid32,    gid_t *,      r,
-                         gid_t *,            e,    gid_t *,      s)
-    LSS_INLINE _syscall3(int,     _getresuid32,    uid_t *,      r,
-                         uid_t *,            e,    uid_t *,      s)
-    LSS_INLINE _syscall1(int,     _setfsgid32,     gid_t,        f)
-    LSS_INLINE _syscall1(int,     _setfsuid32,     uid_t,        f)
-    LSS_INLINE _syscall3(int,     _setresgid32,    gid_t,        r,
-                         gid_t,              e,    gid_t,        s)
-    LSS_INLINE _syscall3(int,     _setresuid32,    uid_t,        r,
-                         uid_t,              e,    uid_t,        s)
-
-    LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid,
-                                         gid_t *egid,
-                                         gid_t *sgid) {
-      int rc;
-      if ((rc = LSS_NAME(_getresgid32)(rgid, egid, sgid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((rgid == NULL) || (egid == NULL) || (sgid == NULL)) {
-          return EFAULT;
-        }
-        // Clear the high bits first, since getresgid only sets 16 bits
-        *rgid = *egid = *sgid = 0;
-        rc = LSS_NAME(getresgid)(rgid, egid, sgid);
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid,
-                                         uid_t *euid,
-                                         uid_t *suid) {
-      int rc;
-      if ((rc = LSS_NAME(_getresuid32)(ruid, euid, suid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((ruid == NULL) || (euid == NULL) || (suid == NULL)) {
-          return EFAULT;
-        }
-        // Clear the high bits first, since getresuid only sets 16 bits
-        *ruid = *euid = *suid = 0;
-        rc = LSS_NAME(getresuid)(ruid, euid, suid);
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) {
-      int rc;
-      if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((unsigned int)gid & ~0xFFFFu) {
-          rc = EINVAL;
-        } else {
-          rc = LSS_NAME(setfsgid)(gid);
-        }
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) {
-      int rc;
-      if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((unsigned int)uid & ~0xFFFFu) {
-          rc = EINVAL;
-        } else {
-          rc = LSS_NAME(setfsuid)(uid);
-        }
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) {
-      int rc;
-      if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((unsigned int)rgid & ~0xFFFFu ||
-            (unsigned int)egid & ~0xFFFFu ||
-            (unsigned int)sgid & ~0xFFFFu) {
-          rc = EINVAL;
-        } else {
-          rc = LSS_NAME(setresgid)(rgid, egid, sgid);
-        }
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) {
-      int rc;
-      if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 &&
-          LSS_ERRNO == ENOSYS) {
-        if ((unsigned int)ruid & ~0xFFFFu ||
-            (unsigned int)euid & ~0xFFFFu ||
-            (unsigned int)suid & ~0xFFFFu) {
-          rc = EINVAL;
-        } else {
-          rc = LSS_NAME(setresuid)(ruid, euid, suid);
-        }
-      }
-      return rc;
-    }
-  #endif
-  LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) {
-    memset(&set->sig, 0, sizeof(set->sig));
-    return 0;
-  }
-
-  LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) {
-    memset(&set->sig, -1, sizeof(set->sig));
-    return 0;
-  }
-
-  LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set,
-                                     int signum) {
-    if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
-      LSS_ERRNO = EINVAL;
-      return -1;
-    } else {
-      set->sig[(signum - 1)/(8*sizeof(set->sig[0]))]
-          |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0])));
-      return 0;
-    }
-  }
-
-  LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set,
-                                        int signum) {
-    if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
-      LSS_ERRNO = EINVAL;
-      return -1;
-    } else {
-      set->sig[(signum - 1)/(8*sizeof(set->sig[0]))]
-          &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0]))));
-      return 0;
-    }
-  }
-
-  LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set,
-                                          int signum) {
-    if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
-      LSS_ERRNO = EINVAL;
-      return -1;
-    } else {
-      return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] &
-                (1UL << ((signum - 1) % (8*sizeof(set->sig[0])))));
-    }
-  }
-  #if defined(__i386__) ||                                                    \
-      defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) ||                     \
-     (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) ||                   \
-      defined(__PPC__) ||                                                     \
-     (defined(__s390__) && !defined(__s390x__)) || defined(__e2k__)
-    #define __NR__sigaction   __NR_sigaction
-    #define __NR__sigpending  __NR_sigpending
-    #define __NR__sigsuspend  __NR_sigsuspend
-    #define __NR__socketcall  __NR_socketcall
-    LSS_INLINE _syscall2(int, fstat64,             int, f,
-                         struct kernel_stat64 *, b)
-    LSS_INLINE _syscall5(int, _llseek,     uint, fd,
-                         unsigned long, hi, unsigned long, lo,
-                         loff_t *, res, uint, wh)
-#if defined(__s390__) && !defined(__s390x__)
-    /* On s390, mmap2() arguments are passed in memory. */
-    LSS_INLINE void* LSS_NAME(_mmap2)(void *s, size_t l, int p, int f, int d,
-                                      off_t o) {
-      unsigned long buf[6] = { (unsigned long) s, (unsigned long) l,
-                               (unsigned long) p, (unsigned long) f,
-                               (unsigned long) d, (unsigned long) o };
-      LSS_REG(2, buf);
-      LSS_BODY(void*, mmap2, "0"(__r2));
-    }
-#else
-    #define __NR__mmap2 __NR_mmap2
-    LSS_INLINE _syscall6(void*, _mmap2,            void*, s,
-                         size_t,                   l, int,               p,
-                         int,                      f, int,               d,
-                         off_t,                    o)
-#endif
-    LSS_INLINE _syscall3(int,   _sigaction,        int,   s,
-                         const struct kernel_old_sigaction*,  a,
-                         struct kernel_old_sigaction*,        o)
-    LSS_INLINE _syscall1(int,   _sigpending, unsigned long*, s)
-    #ifdef __PPC__
-    LSS_INLINE _syscall1(int, _sigsuspend,         unsigned long, s)
-    #else
-    LSS_INLINE _syscall3(int, _sigsuspend,         const void*, a,
-                         int,                      b,
-                         unsigned long,            s)
-    #endif
-    LSS_INLINE _syscall2(int, stat64,              const char *, p,
-                         struct kernel_stat64 *, b)
-
-    LSS_INLINE int LSS_NAME(sigaction)(int signum,
-                                       const struct kernel_sigaction *act,
-                                       struct kernel_sigaction *oldact) {
-      int old_errno = LSS_ERRNO;
-      int rc;
-      struct kernel_sigaction a;
-      if (act != NULL) {
-        a             = *act;
-        #ifdef __i386__
-        /* On i386, the kernel requires us to always set our own
-         * SA_RESTORER when using realtime signals. Otherwise, it does not
-         * know how to return from a signal handler. This function must have
-         * a "magic" signature that the "gdb" (and maybe the kernel?) can
-         * recognize.
-         * Apparently, a SA_RESTORER is implicitly set by the kernel, when
-         * using non-realtime signals.
-         *
-         * TODO: Test whether ARM needs a restorer
-         */
-        if (!(a.sa_flags & SA_RESTORER)) {
-          a.sa_flags   |= SA_RESTORER;
-          a.sa_restorer = (a.sa_flags & SA_SIGINFO)
-                          ? LSS_NAME(restore_rt)() : LSS_NAME(restore)();
-        }
-        #endif
-      }
-      rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact,
-                                  (KERNEL_NSIG+7)/8);
-      if (rc < 0 && LSS_ERRNO == ENOSYS) {
-        struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa;
-        if (!act) {
-          ptr_a            = NULL;
-        } else {
-          oa.sa_handler_   = act->sa_handler_;
-          memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask));
-          #ifndef __mips__
-          oa.sa_restorer   = act->sa_restorer;
-          #endif
-          oa.sa_flags      = act->sa_flags;
-        }
-        if (!oldact) {
-          ptr_oa           = NULL;
-        }
-        LSS_ERRNO = old_errno;
-        rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa);
-        if (rc == 0 && oldact) {
-          if (act) {
-            memcpy(oldact, act, sizeof(*act));
-          } else {
-            memset(oldact, 0, sizeof(*oldact));
-          }
-          oldact->sa_handler_    = ptr_oa->sa_handler_;
-          oldact->sa_flags       = ptr_oa->sa_flags;
-          memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask));
-          #ifndef __mips__
-          oldact->sa_restorer    = ptr_oa->sa_restorer;
-          #endif
-        }
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) {
-      int old_errno = LSS_ERRNO;
-      int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8);
-      if (rc < 0 && LSS_ERRNO == ENOSYS) {
-        LSS_ERRNO = old_errno;
-        LSS_NAME(sigemptyset)(set);
-        rc = LSS_NAME(_sigpending)(&set->sig[0]);
-      }
-      return rc;
-    }
-
-    LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) {
-      int olderrno = LSS_ERRNO;
-      int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8);
-      if (rc < 0 && LSS_ERRNO == ENOSYS) {
-        LSS_ERRNO = olderrno;
-        rc = LSS_NAME(_sigsuspend)(
-        #ifndef __PPC__
-                                   set, 0,
-        #endif
-                                   set->sig[0]);
-      }
-      return rc;
-    }
-  #endif
-  #if defined(__i386__) ||                                                    \
-      defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) ||                     \
-     (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) ||                   \
-      defined(__PPC__) ||                                                     \
-     (defined(__s390__) && !defined(__s390x__))
-    /* On these architectures, implement mmap() with mmap2(). */
-    LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
-                                    int64_t o) {
-      if (o % 4096) {
-        LSS_ERRNO = EINVAL;
-        return (void *) -1;
-      }
-      return LSS_NAME(_mmap2)(s, l, p, f, d, (o / 4096));
-    }
-  #elif defined(__s390x__)
-    /* On s390x, mmap() arguments are passed in memory. */
-    LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
-                                    int64_t o) {
-      unsigned long buf[6] = { (unsigned long) s, (unsigned long) l,
-                               (unsigned long) p, (unsigned long) f,
-                               (unsigned long) d, (unsigned long) o };
-      LSS_REG(2, buf);
-      LSS_BODY(void*, mmap, "0"(__r2));
-    }
-  #elif defined(__x86_64__)
-    /* Need to make sure __off64_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
-                                    int64_t o) {
-      LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l),
-                               LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f),
-                               LSS_SYSCALL_ARG(d), (uint64_t)(o));
-    }
-  #else
-    /* Remaining 64-bit architectures. */
-    LSS_INLINE _syscall6(void*, mmap, void*, addr, size_t, length, int, prot,
-                         int, flags, int, fd, int64_t, offset)
-  #endif
-  #if defined(__PPC__)
-    #undef LSS_SC_LOADARGS_0
-    #define LSS_SC_LOADARGS_0(dummy...)
-    #undef LSS_SC_LOADARGS_1
-    #define LSS_SC_LOADARGS_1(arg1)                                           \
-        __sc_4  = (unsigned long) (arg1)
-    #undef LSS_SC_LOADARGS_2
-    #define LSS_SC_LOADARGS_2(arg1, arg2)                                     \
-        LSS_SC_LOADARGS_1(arg1);                                              \
-        __sc_5  = (unsigned long) (arg2)
-    #undef LSS_SC_LOADARGS_3
-    #define LSS_SC_LOADARGS_3(arg1, arg2, arg3)                               \
-        LSS_SC_LOADARGS_2(arg1, arg2);                                        \
-        __sc_6  = (unsigned long) (arg3)
-    #undef LSS_SC_LOADARGS_4
-    #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4)                         \
-        LSS_SC_LOADARGS_3(arg1, arg2, arg3);                                  \
-        __sc_7  = (unsigned long) (arg4)
-    #undef LSS_SC_LOADARGS_5
-    #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5)                   \
-        LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4);                            \
-        __sc_8  = (unsigned long) (arg5)
-    #undef LSS_SC_BODY
-    #define LSS_SC_BODY(nr, type, opt, args...)                               \
-        long __sc_ret, __sc_err;                                              \
-        {                                                                     \
-          register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall;     \
-          register unsigned long __sc_3 __asm__ ("r3") = opt;                 \
-          register unsigned long __sc_4 __asm__ ("r4");                       \
-          register unsigned long __sc_5 __asm__ ("r5");                       \
-          register unsigned long __sc_6 __asm__ ("r6");                       \
-          register unsigned long __sc_7 __asm__ ("r7");                       \
-          register unsigned long __sc_8 __asm__ ("r8");                       \
-          LSS_SC_LOADARGS_##nr(args);                                         \
-          __asm__ __volatile__                                                \
-              ("stwu 1, -48(1)\n\t"                                           \
-               "stw 4, 20(1)\n\t"                                             \
-               "stw 5, 24(1)\n\t"                                             \
-               "stw 6, 28(1)\n\t"                                             \
-               "stw 7, 32(1)\n\t"                                             \
-               "stw 8, 36(1)\n\t"                                             \
-               "addi 4, 1, 20\n\t"                                            \
-               "sc\n\t"                                                       \
-               "mfcr %0"                                                      \
-                 : "=&r" (__sc_0),                                            \
-                   "=&r" (__sc_3), "=&r" (__sc_4),                            \
-                   "=&r" (__sc_5), "=&r" (__sc_6),                            \
-                   "=&r" (__sc_7), "=&r" (__sc_8)                             \
-                 : LSS_ASMINPUT_##nr                                          \
-                 : "cr0", "ctr", "memory");                                   \
-          __sc_ret = __sc_3;                                                  \
-          __sc_err = __sc_0;                                                  \
-        }                                                                     \
-        LSS_RETURN(type, __sc_ret, __sc_err)
-
-    LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
-                                         int flags){
-      LSS_SC_BODY(3, ssize_t, 17, s, msg, flags);
-    }
-
-    LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
-                                         const struct kernel_msghdr *msg,
-                                         int flags) {
-      LSS_SC_BODY(3, ssize_t, 16, s, msg, flags);
-    }
-
-    // TODO(csilvers): why is this ifdef'ed out?
-#if 0
-    LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
-                                        int flags,
-                                        const struct kernel_sockaddr *to,
-                                        unsigned int tolen) {
-      LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen);
-    }
-#endif
-
-    LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
-      LSS_SC_BODY(2, int, 13, s, how);
-    }
-
-    LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
-      LSS_SC_BODY(3, int, 1, domain, type, protocol);
-    }
-
-    LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol,
-                                        int sv[2]) {
-      LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
-    }
-  #endif
-  #if defined(__ARM_EABI__) || defined (__aarch64__)
-    LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg,
-                         int, flags)
-    LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*,
-                         msg, int, flags)
-    LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t,len,
-                         int, flags, const struct kernel_sockaddr*, to,
-                         unsigned int, tolen)
-    LSS_INLINE _syscall2(int, shutdown, int, s, int, how)
-    LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol)
-    LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol,
-                         int*, sv)
-  #endif
-  #if defined(__i386__) || defined(__ARM_ARCH_3__) ||                         \
-      (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) ||                  \
-      defined(__s390__)
-    #define __NR__socketcall  __NR_socketcall
-    LSS_INLINE _syscall2(int,      _socketcall,    int,   c,
-                         va_list,                  a)
-    LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
-      int rc;
-      va_list ap;
-      va_start(ap, op);
-      rc = LSS_NAME(_socketcall)(op, ap);
-      va_end(ap);
-      return rc;
-    }
-
-    LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
-                                         int flags){
-      return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
-    }
-
-    LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
-                                         const struct kernel_msghdr *msg,
-                                         int flags) {
-      return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
-    }
-
-    LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
-                                        int flags,
-                                        const struct kernel_sockaddr *to,
-                                        unsigned int tolen) {
-      return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
-    }
-
-    LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
-      return LSS_NAME(socketcall)(13, s, how);
-    }
-
-    LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
-      return LSS_NAME(socketcall)(1, domain, type, protocol);
-    }
-
-    LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol,
-                                        int sv[2]) {
-      return LSS_NAME(socketcall)(8, d, type, protocol, sv);
-    }
-  #endif
-  #if defined(__NR_fstatat64)
-    LSS_INLINE _syscall4(int,   fstatat64,        int,   d,
-                         const char *,      p,
-                         struct kernel_stat64 *,   b,    int,   f)
-  #endif
-  #if defined(__NR_waitpid)
-    // waitpid is polyfilled below when not available.
-    LSS_INLINE _syscall3(pid_t, waitpid,          pid_t, p,
-                         int*,              s,    int,   o)
-  #endif
-  #if defined(__mips__)
-    /* sys_pipe() on MIPS has non-standard calling conventions, as it returns
-     * both file handles through CPU registers.
-     */
-    LSS_INLINE int LSS_NAME(pipe)(int *p) {
-      register unsigned long __v0 __asm__("$2") = __NR_pipe;
-      register unsigned long __v1 __asm__("$3");
-      register unsigned long __r7 __asm__("$7");
-      __asm__ __volatile__ ("syscall\n"
-                            : "=r"(__v0), "=r"(__v1), "=r" (__r7)
-                            : "0"(__v0)
-                            : "$8", "$9", "$10", "$11", "$12",
-                              "$13", "$14", "$15", "$24", "$25", "memory");
-      if (__r7) {
-        unsigned long __errnovalue = __v0;
-        LSS_ERRNO = __errnovalue;
-        return -1;
-      } else {
-        p[0] = __v0;
-        p[1] = __v1;
-        return 0;
-      }
-    }
-  #elif defined(__NR_pipe)
-    // pipe is polyfilled below when not available.
-    LSS_INLINE _syscall1(int,     pipe,           int *, p)
-  #endif
-  #if defined(__NR_pipe2)
-    LSS_INLINE _syscall2(int, pipe2, int *, pipefd, int, flags)
-  #endif
-  /* TODO(csilvers): see if ppc can/should support this as well              */
-  #if defined(__i386__) || defined(__ARM_ARCH_3__) ||                         \
-      defined(__ARM_EABI__) ||                                                \
-     (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) ||                   \
-     (defined(__s390__) && !defined(__s390x__))
-    #define __NR__statfs64  __NR_statfs64
-    #define __NR__fstatfs64 __NR_fstatfs64
-    LSS_INLINE _syscall3(int, _statfs64,     const char*, p,
-                         size_t, s,struct kernel_statfs64*, b)
-    LSS_INLINE _syscall3(int, _fstatfs64,          int,   f,
-                         size_t, s,struct kernel_statfs64*, b)
-    LSS_INLINE int LSS_NAME(statfs64)(const char *p,
-                                     struct kernel_statfs64 *b) {
-      return LSS_NAME(_statfs64)(p, sizeof(*b), b);
-    }
-    LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) {
-      return LSS_NAME(_fstatfs64)(f, sizeof(*b), b);
-    }
-  #endif
-
-  LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) {
-    extern char **environ;
-    return LSS_NAME(execve)(path, argv, (const char *const *)environ);
-  }
-
-  LSS_INLINE pid_t LSS_NAME(gettid)(void) {
-    pid_t tid = LSS_NAME(_gettid)();
-    if (tid != -1) {
-      return tid;
-    }
-    return LSS_NAME(getpid)();
-  }
-
-  LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size,
-                                    size_t new_size, int flags, ...) {
-    va_list ap;
-    void *new_address, *rc;
-    va_start(ap, flags);
-    new_address = va_arg(ap, void *);
-    rc = LSS_NAME(_mremap)(old_address, old_size, new_size,
-                           flags, new_address);
-    va_end(ap);
-    return rc;
-  }
-
-  LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) {
-    /* PTRACE_DETACH can sometimes forget to wake up the tracee and it
-     * then sends job control signals to the real parent, rather than to
-     * the tracer. We reduce the risk of this happening by starting a
-     * whole new time slice, and then quickly sending a SIGCONT signal
-     * right after detaching from the tracee.
-     *
-     * We use tkill to ensure that we only issue a wakeup for the thread being
-     * detached.  Large multi threaded apps can take a long time in the kernel
-     * processing SIGCONT.
-     */
-    int rc, err;
-    LSS_NAME(sched_yield)();
-    rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0);
-    err = LSS_ERRNO;
-    LSS_NAME(tkill)(pid, SIGCONT);
-    /* Old systems don't have tkill */
-    if (LSS_ERRNO == ENOSYS)
-      LSS_NAME(kill)(pid, SIGCONT);
-    LSS_ERRNO = err;
-    return rc;
-  }
-
-  LSS_INLINE int LSS_NAME(raise)(int sig) {
-    return LSS_NAME(kill)(LSS_NAME(getpid)(), sig);
-  }
-
-  LSS_INLINE int LSS_NAME(setpgrp)(void) {
-    return LSS_NAME(setpgid)(0, 0);
-  }
-
-  #if defined(__x86_64__)
-    /* Need to make sure loff_t isn't truncated to 32-bits under x32.  */
-    LSS_INLINE ssize_t LSS_NAME(pread64)(int f, void *b, size_t c, loff_t o) {
-      LSS_BODY(4, ssize_t, pread64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b),
-                                    LSS_SYSCALL_ARG(c), (uint64_t)(o));
-    }
-
-    LSS_INLINE ssize_t LSS_NAME(pwrite64)(int f, const void *b, size_t c,
-                                          loff_t o) {
-      LSS_BODY(4, ssize_t, pwrite64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b),
-                                     LSS_SYSCALL_ARG(c), (uint64_t)(o));
-    }
-
-    LSS_INLINE int LSS_NAME(readahead)(int f, loff_t o, unsigned c) {
-      LSS_BODY(3, int, readahead, LSS_SYSCALL_ARG(f), (uint64_t)(o),
-                                  LSS_SYSCALL_ARG(c));
-    }
-  #elif defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64
-    LSS_INLINE _syscall4(ssize_t, pread64,        int,         f,
-                         void *,         b, size_t,   c,
-                         loff_t,         o)
-    LSS_INLINE _syscall4(ssize_t, pwrite64,       int,         f,
-                         const void *,   b, size_t,   c,
-                         loff_t,         o)
-    LSS_INLINE _syscall3(int,     readahead,      int,         f,
-                         loff_t,         o, unsigned, c)
-  #else
-    #define __NR__pread64   __NR_pread64
-    #define __NR__pwrite64  __NR_pwrite64
-    #define __NR__readahead __NR_readahead
-    #if defined(__ARM_EABI__) || defined(__mips__)
-      /* On ARM and MIPS, a 64-bit parameter has to be in an even-odd register
-       * pair. Hence these calls ignore their fourth argument (r3) so that their
-       * fifth and sixth make such a pair (r4,r5).
-       */
-      #define LSS_LLARG_PAD 0,
-      LSS_INLINE _syscall6(ssize_t, _pread64,        int,         f,
-                           void *,         b, size_t, c,
-                           unsigned, skip, unsigned, o1, unsigned, o2)
-      LSS_INLINE _syscall6(ssize_t, _pwrite64,       int,         f,
-                           const void *,   b, size_t, c,
-                           unsigned, skip, unsigned, o1, unsigned, o2)
-      LSS_INLINE _syscall5(int, _readahead,          int,         f,
-                           unsigned,     skip,
-                           unsigned,       o1, unsigned, o2, size_t, c)
-    #else
-      #define LSS_LLARG_PAD
-      LSS_INLINE _syscall5(ssize_t, _pread64,        int,         f,
-                           void *,         b, size_t, c, unsigned, o1,
-                           unsigned, o2)
-      LSS_INLINE _syscall5(ssize_t, _pwrite64,       int,         f,
-                           const void *,   b, size_t, c, unsigned, o1,
-                           long, o2)
-      LSS_INLINE _syscall4(int, _readahead,          int,         f,
-                           unsigned,       o1, unsigned, o2, size_t, c)
-    #endif
-    /* We force 64bit-wide parameters onto the stack, then access each
-     * 32-bit component individually. This guarantees that we build the
-     * correct parameters independent of the native byte-order of the
-     * underlying architecture.
-     */
-    LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count,
-                                         loff_t off) {
-      union { loff_t off; unsigned arg[2]; } o = { off };
-      return LSS_NAME(_pread64)(fd, buf, count,
-                                LSS_LLARG_PAD o.arg[0], o.arg[1]);
-    }
-    LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf,
-                                          size_t count, loff_t off) {
-      union { loff_t off; unsigned arg[2]; } o = { off };
-      return LSS_NAME(_pwrite64)(fd, buf, count,
-                                 LSS_LLARG_PAD o.arg[0], o.arg[1]);
-    }
-    LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) {
-      union { loff_t off; unsigned arg[2]; } o = { off };
-      return LSS_NAME(_readahead)(fd, LSS_LLARG_PAD o.arg[0], o.arg[1], len);
-    }
-  #endif
-#endif
-
-/*
- * Polyfills for deprecated syscalls.
- */
-
-#if !defined(__NR_dup2)
-  LSS_INLINE int LSS_NAME(dup2)(int s, int d) {
-    return LSS_NAME(dup3)(s, d, 0);
-  }
-#endif
-
-#if !defined(__NR_open)
-  LSS_INLINE int LSS_NAME(open)(const char *pathname, int flags, int mode) {
-    return LSS_NAME(openat)(AT_FDCWD, pathname, flags, mode);
-  }
-#endif
-
-#if !defined(__NR_unlink)
-  LSS_INLINE int LSS_NAME(unlink)(const char *pathname) {
-    return LSS_NAME(unlinkat)(AT_FDCWD, pathname, 0);
-  }
-#endif
-
-#if !defined(__NR_readlink)
-  LSS_INLINE int LSS_NAME(readlink)(const char *pathname, char *buffer,
-                                    size_t size) {
-    return LSS_NAME(readlinkat)(AT_FDCWD, pathname, buffer, size);
-  }
-#endif
-
-#if !defined(__NR_pipe)
-  LSS_INLINE int LSS_NAME(pipe)(int *pipefd) {
-    return LSS_NAME(pipe2)(pipefd, 0);
-  }
-#endif
-
-#if !defined(__NR_poll)
-  LSS_INLINE int LSS_NAME(poll)(struct kernel_pollfd *fds, unsigned int nfds,
-                                int timeout) {
-   struct kernel_timespec timeout_ts;
-   struct kernel_timespec *timeout_ts_p = NULL;
-
-    if (timeout >= 0) {
-      timeout_ts.tv_sec = timeout / 1000;
-      timeout_ts.tv_nsec = (timeout % 1000) * 1000000;
-      timeout_ts_p = &timeout_ts;
-    }
-    return LSS_NAME(ppoll)(fds, nfds, timeout_ts_p, NULL, 0);
-  }
-#endif
-
-#if !defined(__NR_stat)
-  LSS_INLINE int LSS_NAME(stat)(const char *pathname,
-                                struct kernel_stat *buf) {
-    return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, 0);
-  }
-#endif
-#if !defined(__NR_lstat)
-  LSS_INLINE int LSS_NAME(lstat)(const char *pathname,
-                                 struct kernel_stat *buf) {
-    return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, AT_SYMLINK_NOFOLLOW);
-  }
-#endif
-
-#if !defined(__NR_waitpid)
-  LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options) {
-    return LSS_NAME(wait4)(pid, status, options, 0);
-  }
-#endif
-
-#if !defined(__NR_fork)
-// TODO: define this in an arch-independant way instead of inlining the clone
-//       syscall body.
-
-# if defined(__aarch64__)
-  LSS_INLINE pid_t LSS_NAME(fork)(void) {
-    // No fork syscall on aarch64 - implement by means of the clone syscall.
-    // Note that this does not reset glibc's cached view of the PID/TID, so
-    // some glibc interfaces might go wrong in the forked subprocess.
-    int flags = SIGCHLD;
-    void *child_stack = NULL;
-    void *parent_tidptr = NULL;
-    void *newtls = NULL;
-    void *child_tidptr = NULL;
-
-    LSS_REG(0, flags);
-    LSS_REG(1, child_stack);
-    LSS_REG(2, parent_tidptr);
-    LSS_REG(3, newtls);
-    LSS_REG(4, child_tidptr);
-    LSS_BODY(pid_t, clone, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3),
-             "r"(__r4));
-  }
-# elif defined(__x86_64__)
-  LSS_INLINE pid_t LSS_NAME(fork)(void) {
-    // Android disallows the fork syscall on x86_64 - implement by means of the
-    // clone syscall as above for aarch64.
-    int flags = SIGCHLD;
-    void *child_stack = NULL;
-    void *parent_tidptr = NULL;
-    void *newtls = NULL;
-    void *child_tidptr = NULL;
-
-    LSS_BODY(5, pid_t, clone, LSS_SYSCALL_ARG(flags),
-             LSS_SYSCALL_ARG(child_stack), LSS_SYSCALL_ARG(parent_tidptr),
-             LSS_SYSCALL_ARG(newtls), LSS_SYSCALL_ARG(child_tidptr));
-  }
-# else
-#  error missing fork polyfill for this architecture
-# endif
-#endif
-
-/* These restore the original values of these macros saved by the
- * corresponding #pragma push_macro near the top of this file. */
-#pragma pop_macro("stat64")
-#pragma pop_macro("fstat64")
-#pragma pop_macro("lstat64")
-#pragma pop_macro("pread64")
-#pragma pop_macro("pwrite64")
-#pragma pop_macro("getdents64")
-
-#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS)
-}
-#endif
-
-#endif
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE b/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE
deleted file mode 100644
index c20d2c8..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020-2023 HexHacking Team
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c
deleted file mode 100644
index a707961..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c
+++ /dev/null
@@ -1,910 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-#include "xdl.h"
-
-#include <android/api-level.h>
-#include <elf.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <link.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/auxv.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "xdl_iterate.h"
-#include "xdl_linker.h"
-#include "xdl_lzma.h"
-#include "xdl_util.h"
-
-#ifndef __LP64__
-#define XDL_LIB_PATH "/system/lib"
-#else
-#define XDL_LIB_PATH "/system/lib64"
-#endif
-
-#define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx))
-#define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \
-  (SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE))
-
-extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-
-typedef struct xdl {
-  char *pathname;
-  uintptr_t load_bias;
-  const ElfW(Phdr) *dlpi_phdr;
-  ElfW(Half) dlpi_phnum;
-
-  struct xdl *next;     // to next xdl obj for cache in xdl_addr()
-  void *linker_handle;  // hold handle returned by xdl_linker_force_dlopen()
-
-  //
-  // (1) for searching symbols from .dynsym
-  //
-
-  bool dynsym_try_load;
-  ElfW(Sym) *dynsym;   // .dynsym
-  const char *dynstr;  // .dynstr
-
-  // .hash (SYSV hash for .dynstr)
-  struct {
-    const uint32_t *buckets;
-    uint32_t buckets_cnt;
-    const uint32_t *chains;
-    uint32_t chains_cnt;
-  } sysv_hash;
-
-  // .gnu.hash (GNU hash for .dynstr)
-  struct {
-    const uint32_t *buckets;
-    uint32_t buckets_cnt;
-    const uint32_t *chains;
-    uint32_t symoffset;
-    const ElfW(Addr) *bloom;
-    uint32_t bloom_cnt;
-    uint32_t bloom_shift;
-  } gnu_hash;
-
-  //
-  // (2) for searching symbols from .symtab
-  //
-
-  bool symtab_try_load;
-  uintptr_t base;
-
-  ElfW(Sym) *symtab;  // .symtab
-  size_t symtab_cnt;
-  char *strtab;  // .strtab
-  size_t strtab_sz;
-} xdl_t;
-
-#pragma clang diagnostic pop
-
-// load from memory
-static int xdl_dynsym_load(xdl_t *self) {
-  // find the dynamic segment
-  ElfW(Dyn) *dynamic = NULL;
-  for (size_t i = 0; i < self->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
-    if (PT_DYNAMIC == phdr->p_type) {
-      dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr);
-      break;
-    }
-  }
-  if (NULL == dynamic) return -1;
-
-  // iterate the dynamic segment
-  for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) {
-    switch (entry->d_tag) {
-      case DT_SYMTAB:  //.dynsym
-        self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr);
-        break;
-      case DT_STRTAB:  //.dynstr
-        self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr);
-        break;
-      case DT_HASH:  //.hash
-        self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
-        self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
-        self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]);
-        self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]);
-        break;
-      case DT_GNU_HASH:  //.gnu.hash
-        self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
-        self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
-        self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2];
-        self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3];
-        self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16);
-        self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt]));
-        self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt]));
-        break;
-      default:
-        break;
-    }
-  }
-
-  if (NULL == self->dynsym || NULL == self->dynstr ||
-      (0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) {
-    self->dynsym = NULL;
-    self->dynstr = NULL;
-    self->sysv_hash.buckets_cnt = 0;
-    self->gnu_hash.buckets_cnt = 0;
-    return -1;
-  }
-
-  return 0;
-}
-
-static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) {
-  if (0 == data_len) return NULL;
-  if (data_offset >= file_sz) return NULL;
-  if (data_offset + data_len > file_sz) return NULL;
-
-  if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL;
-
-  void *data = malloc(data_len);
-  if (NULL == data) return NULL;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-  if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len)))
-#pragma clang diagnostic pop
-  {
-    free(data);
-    return NULL;
-  }
-
-  return data;
-}
-
-static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) {
-  return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size);
-}
-
-static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
-  if (0 == data_len) return NULL;
-  if (data_offset >= mem_sz) return NULL;
-  if (data_offset + data_len > mem_sz) return NULL;
-
-  void *data = malloc(data_len);
-  if (NULL == data) return NULL;
-
-  memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len);
-  return data;
-}
-
-static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
-  return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
-}
-
-static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
-  if (0 == data_len) return NULL;
-  if (data_offset >= mem_sz) return NULL;
-  if (data_offset + data_len > mem_sz) return NULL;
-
-  return (void *)((uintptr_t)mem + data_offset);
-}
-
-static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
-  return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
-}
-
-// load from disk and memory
-static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz,
-                                          ElfW(Shdr) *shdr_debugdata) {
-  void *debugdata = NULL;
-  ElfW(Shdr) *shdrs = NULL;
-  int r = -1;
-
-  // get zipped .gnu_debugdata
-  uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata);
-  if (NULL == debugdata_zip) return -1;
-
-  // get unzipped .gnu_debugdata
-  size_t debugdata_sz;
-  if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz))
-    goto end;
-
-  // get ELF header
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata;
-  if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
-
-  // get section headers
-  shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff,
-                                                ehdr->e_shentsize * ehdr->e_shnum);
-  if (NULL == shdrs) goto end;
-
-  // get .shstrtab
-  if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
-  char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx);
-  if (NULL == shstrtab) goto end;
-
-  // find .symtab & .strtab
-  for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
-    char *shdr_name = shstrtab + shdr->sh_name;
-
-    if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
-      // get & check associated .strtab section
-      if (shdr->sh_link >= ehdr->e_shnum) continue;
-      ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
-      if (SHT_STRTAB != shdr_strtab->sh_type) continue;
-
-      // get .symtab & .strtab
-      ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr);
-      if (NULL == symtab) continue;
-      char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab);
-      if (NULL == strtab) {
-        free(symtab);
-        continue;
-      }
-
-      // OK
-      self->symtab = symtab;
-      self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
-      self->strtab = strtab;
-      self->strtab_sz = shdr_strtab->sh_size;
-      r = 0;
-      break;
-    }
-  }
-
-end:
-  free(debugdata_zip);
-  if (NULL != debugdata) free(debugdata);
-  if (NULL != shdrs) free(shdrs);
-  return r;
-}
-
-// load from disk and memory
-static int xdl_symtab_load(xdl_t *self) {
-  if ('[' == self->pathname[0]) return -1;
-
-  int r = -1;
-  ElfW(Shdr) *shdrs = NULL;
-  char *shstrtab = NULL;
-
-  // get base address
-  uintptr_t vaddr_min = UINTPTR_MAX;
-  for (size_t i = 0; i < self->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type) {
-      if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr;
-    }
-  }
-  if (UINTPTR_MAX == vaddr_min) return -1;
-  self->base = self->load_bias + vaddr_min;
-
-  // open file
-  int flags = O_RDONLY | O_CLOEXEC;
-  int file_fd;
-  if ('/' == self->pathname[0]) {
-    file_fd = open(self->pathname, flags);
-  } else {
-    char full_pathname[1024];
-    // try the fast method
-    snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname);
-    file_fd = open(full_pathname, flags);
-    if (file_fd < 0) {
-      // try the slow method
-      if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1;
-      file_fd = open(full_pathname, flags);
-    }
-  }
-  if (file_fd < 0) return -1;
-  struct stat st;
-  if (0 != fstat(file_fd, &st)) goto end;
-  size_t file_sz = (size_t)st.st_size;
-
-  // get ELF header
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base;
-  if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
-
-  // get section headers
-  shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff,
-                                              ehdr->e_shentsize * ehdr->e_shnum);
-  if (NULL == shdrs) goto end;
-
-  // get .shstrtab
-  if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
-  shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx);
-  if (NULL == shstrtab) goto end;
-
-  // find .symtab & .strtab
-  for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
-    char *shdr_name = shstrtab + shdr->sh_name;
-
-    if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
-      // get & check associated .strtab section
-      if (shdr->sh_link >= ehdr->e_shnum) continue;
-      ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
-      if (SHT_STRTAB != shdr_strtab->sh_type) continue;
-
-      // get .symtab & .strtab
-      ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr);
-      if (NULL == symtab) continue;
-      char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab);
-      if (NULL == strtab) {
-        free(symtab);
-        continue;
-      }
-
-      // OK
-      self->symtab = symtab;
-      self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
-      self->strtab = strtab;
-      self->strtab_sz = shdr_strtab->sh_size;
-      r = 0;
-      break;
-    } else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) {
-      if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) {
-        // OK
-        r = 0;
-        break;
-      }
-    }
-  }
-
-end:
-  close(file_fd);
-  if (NULL != shdrs) free(shdrs);
-  if (NULL != shstrtab) free(shstrtab);
-  return r;
-}
-
-static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) {
-  if (NULL == getauxval) return NULL;  // API level < 18
-
-  uintptr_t val = (uintptr_t)getauxval(type);
-  if (0 == val) return NULL;
-
-  // get base
-  uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val);
-  if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL;
-
-  // ELF info
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
-  const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
-  ElfW(Half) dlpi_phnum = ehdr->e_phnum;
-
-  // get bias
-  uintptr_t min_vaddr = UINTPTR_MAX;
-  for (size_t i = 0; i < dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type) {
-      if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
-    }
-  }
-  if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL;
-  uintptr_t load_bias = base - min_vaddr;
-
-  // create xDL object
-  xdl_t *self;
-  if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL;
-  if (NULL == (self->pathname = strdup(pathname))) {
-    free(self);
-    return NULL;
-  }
-  self->load_bias = load_bias;
-  self->dlpi_phdr = dlpi_phdr;
-  self->dlpi_phnum = dlpi_phnum;
-  self->dynsym_try_load = false;
-  self->symtab_try_load = false;
-  return self;
-}
-
-static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
-  (void)size;
-
-  uintptr_t *pkg = (uintptr_t *)arg;
-  xdl_t **self = (xdl_t **)*pkg++;
-  const char *filename = (const char *)*pkg;
-
-  // check load_bias
-  if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0;
-
-  // check pathname
-  if ('[' == filename[0]) {
-    if (0 != strcmp(info->dlpi_name, filename)) return 0;
-  } else if ('/' == filename[0]) {
-    if ('/' == info->dlpi_name[0]) {
-      if (0 != strcmp(info->dlpi_name, filename)) return 0;
-    } else {
-      if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0;
-    }
-  } else {
-    if ('/' == info->dlpi_name[0]) {
-      if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0;
-    } else {
-      if (0 != strcmp(info->dlpi_name, filename)) return 0;
-    }
-  }
-
-  // found the target ELF
-  if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1;  // return failed
-  if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) {
-    free(*self);
-    *self = NULL;
-    return 1;  // return failed
-  }
-  (*self)->load_bias = info->dlpi_addr;
-  (*self)->dlpi_phdr = info->dlpi_phdr;
-  (*self)->dlpi_phnum = info->dlpi_phnum;
-  (*self)->dynsym_try_load = false;
-  (*self)->symtab_try_load = false;
-  return 1;  // return OK
-}
-
-static xdl_t *xdl_find(const char *filename) {
-  // from auxv (linker, vDSO)
-  xdl_t *self = NULL;
-  if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME))
-    self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME);
-  else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME))
-    self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME);
-
-  // from auxv (app_process)
-  const char *basename, *pathname;
-#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
-  if (xdl_util_get_api_level() < __ANDROID_API_L__) {
-    basename = XDL_UTIL_APP_PROCESS_BASENAME_K;
-    pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K;
-  } else
-#endif
-  {
-    basename = XDL_UTIL_APP_PROCESS_BASENAME;
-    pathname = XDL_UTIL_APP_PROCESS_PATHNAME;
-  }
-  if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname);
-
-  if (NULL != self) return self;
-
-  // from dl_iterate_phdr
-  uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename};
-  xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT);
-  return self;
-}
-
-static void *xdl_open_always_force(const char *filename) {
-  // always force dlopen()
-  void *linker_handle = xdl_linker_force_dlopen(filename);
-  if (NULL == linker_handle) return NULL;
-
-  // find
-  xdl_t *self = xdl_find(filename);
-  if (NULL == self)
-    dlclose(linker_handle);
-  else
-    self->linker_handle = linker_handle;
-
-  return (void *)self;
-}
-
-static void *xdl_open_try_force(const char *filename) {
-  // find
-  xdl_t *self = xdl_find(filename);
-  if (NULL != self) return (void *)self;
-
-  // try force dlopen()
-  void *linker_handle = xdl_linker_force_dlopen(filename);
-  if (NULL == linker_handle) return NULL;
-
-  // find again
-  self = xdl_find(filename);
-  if (NULL == self)
-    dlclose(linker_handle);
-  else
-    self->linker_handle = linker_handle;
-
-  return (void *)self;
-}
-
-void *xdl_open(const char *filename, int flags) {
-  if (NULL == filename) return NULL;
-
-  if (flags & XDL_ALWAYS_FORCE_LOAD)
-    return xdl_open_always_force(filename);
-  else if (flags & XDL_TRY_FORCE_LOAD)
-    return xdl_open_try_force(filename);
-  else
-    return xdl_find(filename);
-}
-
-void *xdl_close(void *handle) {
-  if (NULL == handle) return NULL;
-
-  xdl_t *self = (xdl_t *)handle;
-  if (NULL != self->pathname) free(self->pathname);
-  if (NULL != self->symtab) free(self->symtab);
-  if (NULL != self->strtab) free(self->strtab);
-
-  void *linker_handle = self->linker_handle;
-  free(self);
-  return linker_handle;
-}
-
-static uint32_t xdl_sysv_hash(const uint8_t *name) {
-  uint32_t h = 0, g;
-
-  while (*name) {
-    h = (h << 4) + *name++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
-  }
-  return h;
-}
-
-static uint32_t xdl_gnu_hash(const uint8_t *name) {
-  uint32_t h = 5381;
-
-  while (*name) {
-    h += (h << 5) + *name++;
-  }
-  return h;
-}
-
-static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) {
-  uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name);
-
-  for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i;
-       i = self->sysv_hash.chains[i]) {
-    ElfW(Sym) *sym = self->dynsym + i;
-    if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue;
-    return sym;
-  }
-
-  return NULL;
-}
-
-static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) {
-  uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name);
-
-  static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;
-  size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt];
-  size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) |
-                (size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits);
-
-  // if at least one bit is not set, this symbol is surely missing
-  if ((word & mask) != mask) return NULL;
-
-  // ignore STN_UNDEF
-  uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt];
-  if (i < self->gnu_hash.symoffset) return NULL;
-
-  // loop through the chain
-  while (1) {
-    ElfW(Sym) *sym = self->dynsym + i;
-    uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset];
-
-    if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) {
-      if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) {
-        return sym;
-      }
-    }
-
-    // chain ends with an element with the lowest bit set to 1
-    if (sym_hash & (uint32_t)1) break;
-
-    i++;
-  }
-
-  return NULL;
-}
-
-void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) {
-  if (NULL == handle || NULL == symbol) return NULL;
-  if (NULL != symbol_size) *symbol_size = 0;
-
-  xdl_t *self = (xdl_t *)handle;
-
-  // load .dynsym only once
-  if (!self->dynsym_try_load) {
-    self->dynsym_try_load = true;
-    if (0 != xdl_dynsym_load(self)) return NULL;
-  }
-
-  // find symbol
-  if (NULL == self->dynsym) return NULL;
-  ElfW(Sym) *sym = NULL;
-  if (self->gnu_hash.buckets_cnt > 0) {
-    // use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
-    sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol);
-  }
-  if (NULL == sym && self->sysv_hash.buckets_cnt > 0) {
-    // use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
-    sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol);
-  }
-  if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL;
-
-  if (NULL != symbol_size) *symbol_size = sym->st_size;
-  return (void *)(self->load_bias + sym->st_value);
-}
-
-// clang-format off
-/*
- * For internal symbols in .symtab, LLVM may add some suffixes (for example for thinLTO).
- * The format of the suffix is: ".xxxx.[hash]". LLVM may add multiple suffixes at once.
- * The symbol name after removing these all suffixes is called canonical name.
- *
- * Because the hash part in the suffix may change when recompiled, so here we only match
- * the canonical name.
- *
- * IN ADDITION: According to C/C++ syntax, it is illegal for a function name to contain
- * dot character('.'), either in the middle or at the end.
- *
- * samples:
- *
- * symbol name in .symtab          lookup                       is match
- * ----------------------          ----------------             --------
- * abcd                            abc                          N
- * abcd                            abcd                         Y
- * abcd.llvm.10190306339727611508  abc                          N
- * abcd.llvm.10190306339727611508  abcd                         Y
- * abcd.llvm.10190306339727611508  abcd.                        N
- * abcd.llvm.10190306339727611508  abcd.llvm                    Y
- * abcd.llvm.10190306339727611508  abcd.llvm.                   N
- * abcd.__uniq.513291356003753     abcd.__uniq.51329            N
- * abcd.__uniq.513291356003753     abcd.__uniq.513291356003753  Y
- */
-// clang-format on
-static inline bool xdl_dsym_is_match(const char *str, const char *sym, size_t str_len) {
-  if (__predict_false(0 == str_len)) return false;
-
-  do {
-    if (*str != *sym) return __predict_false('.' == *str && '\0' == *sym);
-    str++;
-    sym++;
-    if ('\0' == *str) break;
-  } while (0 != --str_len);
-
-  return true;
-}
-
-void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) {
-  if (NULL == handle || NULL == symbol) return NULL;
-  if (NULL != symbol_size) *symbol_size = 0;
-
-  xdl_t *self = (xdl_t *)handle;
-
-  // load .symtab only once
-  if (!self->symtab_try_load) {
-    self->symtab_try_load = true;
-    if (0 != xdl_symtab_load(self)) return NULL;
-  }
-
-  // find symbol
-  if (NULL == self->symtab) return NULL;
-  for (size_t i = 0; i < self->symtab_cnt; i++) {
-    ElfW(Sym) *sym = self->symtab + i;
-
-    if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue;
-    // if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue;
-    if (!xdl_dsym_is_match(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue;
-
-    if (NULL != symbol_size) *symbol_size = sym->st_size;
-    return (void *)(self->load_bias + sym->st_value);
-  }
-
-  return NULL;
-}
-
-static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum,
-                             uintptr_t addr) {
-  if (addr < load_bias) return false;
-
-  uintptr_t vaddr = addr - load_bias;
-  for (size_t i = 0; i < dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
-    if (PT_LOAD != phdr->p_type) continue;
-
-    if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true;
-  }
-
-  return false;
-}
-
-static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
-  (void)size;
-
-  uintptr_t *pkg = (uintptr_t *)arg;
-  xdl_t **self = (xdl_t **)*pkg++;
-  uintptr_t addr = *pkg;
-
-  if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) {
-    // found the target ELF
-    if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1;  // failed
-    if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) {
-      free(*self);
-      *self = NULL;
-      return 1;  // failed
-    }
-    (*self)->load_bias = info->dlpi_addr;
-    (*self)->dlpi_phdr = info->dlpi_phdr;
-    (*self)->dlpi_phnum = info->dlpi_phnum;
-    (*self)->dynsym_try_load = false;
-    (*self)->symtab_try_load = false;
-    return 1;  // OK
-  }
-
-  return 0;  // mismatch
-}
-
-static void *xdl_open_by_addr(void *addr) {
-  if (NULL == addr) return NULL;
-
-  xdl_t *self = NULL;
-  uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr};
-  xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT);
-
-  return (void *)self;
-}
-
-static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) {
-  if (is_symtab) {
-    if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) false;
-  } else {
-    if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) false;
-  }
-
-  return ELF_ST_TYPE(sym->st_info) != STT_TLS && offset >= sym->st_value &&
-         offset < sym->st_value + sym->st_size;
-}
-
-static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) {
-  xdl_t *self = (xdl_t *)handle;
-
-  // load .dynsym only once
-  if (!self->dynsym_try_load) {
-    self->dynsym_try_load = true;
-    if (0 != xdl_dynsym_load(self)) return NULL;
-  }
-
-  // find symbol
-  if (NULL == self->dynsym) return NULL;
-  uintptr_t offset = (uintptr_t)addr - self->load_bias;
-  if (self->gnu_hash.buckets_cnt > 0) {
-    const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset;
-    for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) {
-      uint32_t n = self->gnu_hash.buckets[i];
-      if (n < self->gnu_hash.symoffset) continue;
-      do {
-        ElfW(Sym) *sym = self->dynsym + n;
-        if (xdl_sym_is_match(sym, offset, false)) return sym;
-      } while ((chains_all[n++] & 1) == 0);
-    }
-  } else if (self->sysv_hash.chains_cnt > 0) {
-    for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) {
-      ElfW(Sym) *sym = self->dynsym + i;
-      if (xdl_sym_is_match(sym, offset, false)) return sym;
-    }
-  }
-
-  return NULL;
-}
-
-static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) {
-  xdl_t *self = (xdl_t *)handle;
-
-  // load .symtab only once
-  if (!self->symtab_try_load) {
-    self->symtab_try_load = true;
-    if (0 != xdl_symtab_load(self)) return NULL;
-  }
-
-  // find symbol
-  if (NULL == self->symtab) return NULL;
-  uintptr_t offset = (uintptr_t)addr - self->load_bias;
-  for (size_t i = 0; i < self->symtab_cnt; i++) {
-    ElfW(Sym) *sym = self->symtab + i;
-    if (xdl_sym_is_match(sym, offset, true)) return sym;
-  }
-
-  return NULL;
-}
-
-int xdl_addr(void *addr, xdl_info_t *info, void **cache) {
-  if (NULL == addr || NULL == info || NULL == cache) return 0;
-
-  memset(info, 0, sizeof(Dl_info));
-
-  // find handle from cache
-  xdl_t *handle = NULL;
-  for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next)
-    if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break;
-
-  // create new handle, save handle to cache
-  if (NULL == handle) {
-    handle = (xdl_t *)xdl_open_by_addr(addr);
-    if (NULL == handle) return 0;
-    handle->next = *(xdl_t **)cache;
-    *(xdl_t **)cache = handle;
-  }
-
-  // we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum
-  info->dli_fbase = (void *)handle->load_bias;
-  info->dli_fname = handle->pathname;
-  info->dli_sname = NULL;
-  info->dli_saddr = 0;
-  info->dli_ssize = 0;
-  info->dlpi_phdr = handle->dlpi_phdr;
-  info->dlpi_phnum = (size_t)handle->dlpi_phnum;
-
-  // keep looking for: symbol name, symbol offset, symbol size
-  ElfW(Sym) *sym;
-  if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) {
-    info->dli_sname = handle->dynstr + sym->st_name;
-    info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
-    info->dli_ssize = sym->st_size;
-  } else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) {
-    info->dli_sname = handle->strtab + sym->st_name;
-    info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
-    info->dli_ssize = sym->st_size;
-  }
-
-  return 1;
-}
-
-void xdl_addr_clean(void **cache) {
-  if (NULL == cache) return;
-
-  xdl_t *handle = *((xdl_t **)cache);
-  while (NULL != handle) {
-    xdl_t *tmp = handle;
-    handle = handle->next;
-    xdl_close(tmp);
-  }
-  *cache = NULL;
-}
-
-int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) {
-  if (NULL == callback) return 0;
-
-  return xdl_iterate_phdr_impl(callback, data, flags);
-}
-
-int xdl_info(void *handle, int request, void *info) {
-  if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1;
-
-  xdl_t *self = (xdl_t *)handle;
-  xdl_info_t *dlinfo = (xdl_info_t *)info;
-
-  dlinfo->dli_fbase = (void *)self->load_bias;
-  dlinfo->dli_fname = self->pathname;
-  dlinfo->dli_sname = NULL;
-  dlinfo->dli_saddr = 0;
-  dlinfo->dli_ssize = 0;
-  dlinfo->dlpi_phdr = self->dlpi_phdr;
-  dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum;
-  return 0;
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h
deleted file mode 100644
index d927c43..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-//
-// xDL version: 2.1.0
-//
-// xDL is an enhanced implementation of the Android DL series functions.
-// For more information, documentation, and the latest version please check:
-// https://github.com/hexhacking/xDL
-//
-
-#ifndef IO_GITHUB_HEXHACKING_XDL
-#define IO_GITHUB_HEXHACKING_XDL
-
-#include <dlfcn.h>
-#include <link.h>
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
-  // same as Dl_info:
-  const char *dli_fname;  // Pathname of shared object that contains address.
-  void *dli_fbase;        // Address at which shared object is loaded.
-  const char *dli_sname;  // Name of nearest symbol with address lower than addr.
-  void *dli_saddr;        // Exact address of symbol named in dli_sname.
-  // added by xDL:
-  size_t dli_ssize;             // Symbol size of nearest symbol with address lower than addr.
-  const ElfW(Phdr) *dlpi_phdr;  // Pointer to array of ELF program headers for this object.
-  size_t dlpi_phnum;            // Number of items in dlpi_phdr.
-} xdl_info_t;
-
-//
-// Default value for flags in both xdl_open() and xdl_iterate_phdr().
-//
-#define XDL_DEFAULT 0x00
-
-//
-// Enhanced dlopen() / dlclose() / dlsym().
-//
-#define XDL_TRY_FORCE_LOAD    0x01
-#define XDL_ALWAYS_FORCE_LOAD 0x02
-void *xdl_open(const char *filename, int flags);
-void *xdl_close(void *handle);
-void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size);
-void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size);
-
-//
-// Enhanced dladdr().
-//
-int xdl_addr(void *addr, xdl_info_t *info, void **cache);
-void xdl_addr_clean(void **cache);
-
-//
-// Enhanced dl_iterate_phdr().
-//
-#define XDL_FULL_PATHNAME 0x01
-int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags);
-
-//
-// Custom dlinfo().
-//
-#define XDL_DI_DLINFO 1  // type of info: xdl_info_t
-int xdl_info(void *handle, int request, void *info);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c
deleted file mode 100644
index b89b642..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-#include "xdl_iterate.h"
-
-#include <android/api-level.h>
-#include <ctype.h>
-#include <dlfcn.h>
-#include <elf.h>
-#include <inttypes.h>
-#include <link.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/auxv.h>
-
-#include "xdl.h"
-#include "xdl_linker.h"
-#include "xdl_util.h"
-
-/*
- * =========================================================================================================
- * API-LEVEL  ANDROID-VERSION  SOLUTION
- * =========================================================================================================
- * 16         4.1              /proc/self/maps
- * 17         4.2              /proc/self/maps
- * 18         4.3              /proc/self/maps
- * 19         4.4              /proc/self/maps
- * 20         4.4W             /proc/self/maps
- * ---------------------------------------------------------------------------------------------------------
- * 21         5.0              dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
- * 22         5.1              dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
- * ---------------------------------------------------------------------------------------------------------
- * 23         >= 6.0           dl_iterate_phdr() + linker/linker64 from getauxval(3)
- * =========================================================================================================
- */
-
-extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *);
-extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
-
-static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) {
-  uintptr_t min_vaddr = UINTPTR_MAX;
-  for (size_t i = 0; i < info->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type) {
-      if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
-    }
-  }
-  return min_vaddr;
-}
-
-static int xdl_iterate_open_or_rewind_maps(FILE **maps) {
-  if (NULL == *maps) {
-    *maps = fopen("/proc/self/maps", "r");
-    if (NULL == *maps) return -1;
-  } else
-    rewind(*maps);
-
-  return 0;
-}
-
-static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) {
-  // open or rewind maps-file
-  if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1;  // failed
-
-  char line[1024];
-  while (fgets(line, sizeof(line), *maps)) {
-    // check base address
-    uintptr_t start, end;
-    if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue;
-    if (base < start) break;  // failed
-    if (base >= end) continue;
-
-    // get pathname
-    char *pathname = strchr(line, '/');
-    if (NULL == pathname) break;  // failed
-    xdl_util_trim_ending(pathname);
-
-    // found it
-    strlcpy(buf, pathname, buf_len);
-    return 0;  // OK
-  }
-
-  return -1;  // failed
-}
-
-static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) {
-  uintptr_t *pkg = (uintptr_t *)arg;
-  xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++;
-  void *cb_arg = (void *)*pkg++;
-  FILE **maps = (FILE **)*pkg++;
-  uintptr_t linker_load_bias = *pkg++;
-  int flags = (int)*pkg;
-
-  // ignore invalid ELF
-  if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0;
-
-  // ignore linker if we have returned it already
-  if (linker_load_bias == info->dlpi_addr) return 0;
-
-  struct dl_phdr_info info_fixed;
-  info_fixed.dlpi_addr = info->dlpi_addr;
-  info_fixed.dlpi_name = info->dlpi_name;
-  info_fixed.dlpi_phdr = info->dlpi_phdr;
-  info_fixed.dlpi_phnum = info->dlpi_phnum;
-  info = &info_fixed;
-
-  // fix dlpi_phdr & dlpi_phnum (from memory)
-  if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) {
-    ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr;
-    info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff);
-    info->dlpi_phnum = ehdr->e_phnum;
-  }
-
-  // fix dlpi_name (from /proc/self/maps)
-  if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) {
-    // get base address
-    uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info);
-    if (UINTPTR_MAX == min_vaddr) return 0;  // ignore this ELF
-    uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr);
-
-    char buf[1024];
-    if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0;  // ignore this ELF
-
-    info->dlpi_name = (const char *)buf;
-  }
-
-  // callback
-  return cb(info, size, cb_arg);
-}
-
-static uintptr_t xdl_iterate_get_linker_base(void) {
-  if (NULL == getauxval) return 0;
-
-  uintptr_t base = (uintptr_t)getauxval(AT_BASE);
-  if (0 == base) return 0;
-  if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0;
-
-  return base;
-}
-
-static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base,
-                                   const char *pathname, uintptr_t *load_bias) {
-  ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
-
-  struct dl_phdr_info info;
-  info.dlpi_name = pathname;
-  info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
-  info.dlpi_phnum = ehdr->e_phnum;
-
-  // get load bias
-  uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info);
-  if (UINTPTR_MAX == min_vaddr) return 0;  // ignore invalid ELF
-  info.dlpi_addr = (ElfW(Addr))(base - min_vaddr);
-  if (NULL != load_bias) *load_bias = info.dlpi_addr;
-
-  return cb(&info, sizeof(struct dl_phdr_info), cb_arg);
-}
-
-static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
-  if (NULL == dl_iterate_phdr) return 0;
-
-  int api_level = xdl_util_get_api_level();
-  FILE *maps = NULL;
-  int r;
-
-  // dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27).
-  // Here we always try to get linker base address from auxv.
-  uintptr_t linker_load_bias = 0;
-  uintptr_t linker_base = xdl_iterate_get_linker_base();
-  if (0 != linker_base) {
-    if (0 !=
-        (r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias)))
-      return r;
-  }
-
-  // for other ELF
-  uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags};
-  if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock();
-  r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg);
-  if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock();
-
-  if (NULL != maps) fclose(maps);
-  return r;
-}
-
-#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
-static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) {
-  FILE *maps = fopen("/proc/self/maps", "r");
-  if (NULL == maps) return 0;
-
-  int r = 0;
-  char buf1[1024], buf2[1024];
-  char *line = buf1;
-  uintptr_t prev_base = 0;
-  bool try_next_line = false;
-
-  while (fgets(line, sizeof(buf1), maps)) {
-    // Try to find an ELF which loaded by linker.
-    uintptr_t base, offset;
-    char exec;
-    if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset))
-      goto clean;
-
-    if ('-' == exec && 0 == offset) {
-      // r--p
-      prev_base = base;
-      line = (line == buf1 ? buf2 : buf1);
-      try_next_line = true;
-      continue;
-    } else if (exec == 'x') {
-      // r-xp
-      char *pathname = NULL;
-      if (try_next_line && 0 != offset) {
-        char *prev = (line == buf1 ? buf2 : buf1);
-        char *prev_pathname = strchr(prev, '/');
-        if (NULL == prev_pathname) goto clean;
-
-        pathname = strchr(line, '/');
-        if (NULL == pathname) goto clean;
-
-        xdl_util_trim_ending(prev_pathname);
-        xdl_util_trim_ending(pathname);
-        if (0 != strcmp(prev_pathname, pathname)) goto clean;
-
-        // we found the line with r-xp in the next line
-        base = prev_base;
-        offset = 0;
-      }
-
-      if (0 != offset) goto clean;
-
-      // get pathname
-      if (NULL == pathname) {
-        pathname = strchr(line, '/');
-        if (NULL == pathname) goto clean;
-        xdl_util_trim_ending(pathname);
-      }
-
-      if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean;
-      ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
-      struct dl_phdr_info info;
-      info.dlpi_name = pathname;
-      info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
-      info.dlpi_phnum = ehdr->e_phnum;
-
-      // callback
-      if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break;
-    }
-
-  clean:
-    try_next_line = false;
-  }
-
-  fclose(maps);
-  return r;
-}
-#endif
-
-int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
-  // iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86)
-#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
-  if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg);
-#endif
-
-  // iterate by dl_iterate_phdr()
-  return xdl_iterate_by_linker(cb, cb_arg, flags);
-}
-
-int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) {
-  FILE *maps = NULL;
-  int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps);
-  if (NULL != maps) fclose(maps);
-  return r;
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h
deleted file mode 100644
index ab22866..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-#ifndef IO_GITHUB_HEXHACKING_XDL_ITERATE
-#define IO_GITHUB_HEXHACKING_XDL_ITERATE
-
-#include <link.h>
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg);
-int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags);
-
-int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c
deleted file mode 100644
index 9021867..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2021-02-21.
-
-#include "xdl_linker.h"
-
-#include <dlfcn.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "xdl.h"
-#include "xdl_iterate.h"
-#include "xdl_util.h"
-
-#define XDL_LINKER_SYM_MUTEX           "__dl__ZL10g_dl_mutex"
-#define XDL_LINKER_SYM_DLOPEN_EXT_N    "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv"
-#define XDL_LINKER_SYM_DO_DLOPEN_N     "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv"
-#define XDL_LINKER_SYM_DLOPEN_O        "__dl__Z8__dlopenPKciPKv"
-#define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen"
-
-#ifndef __LP64__
-#define LIB "lib"
-#else
-#define LIB "lib64"
-#endif
-
-typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *);
-typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *);
-
-static pthread_mutex_t *xdl_linker_mutex = NULL;
-static void *xdl_linker_dlopen = NULL;
-
-typedef enum { MATCH_PREFIX, MATCH_SUFFIX } xdl_linker_match_type_t;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-typedef struct {
-  xdl_linker_match_type_t type;
-  const char *value;
-} xdl_linker_match_t;
-#pragma clang diagnostic pop
-
-typedef struct {
-  void *addr;
-  xdl_linker_match_t *matches;
-  size_t matches_cursor;
-} xdl_linker_caller_t;
-
-// https://source.android.com/docs/core/architecture/vndk/linker-namespace
-// The following rules are loose and incomplete, you can add more according to your needs.
-static xdl_linker_match_t xdl_linker_match_default[] = {{MATCH_SUFFIX, "/libc.so"}};
-static xdl_linker_match_t xdl_linker_match_art[] = {{MATCH_SUFFIX, "/libart.so"}};
-static xdl_linker_match_t xdl_linker_match_sphal[] = {{MATCH_PREFIX, "/vendor/" LIB "/egl/"},
-                                                      {MATCH_PREFIX, "/vendor/" LIB "/hw/"},
-                                                      {MATCH_PREFIX, "/vendor/" LIB "/"},
-                                                      {MATCH_PREFIX, "/odm/" LIB "/"}};
-static xdl_linker_match_t xdl_linker_match_vndk[] = {{MATCH_PREFIX, "/apex/com.android.vndk.v"},
-                                                     {MATCH_PREFIX, "/vendor/" LIB "/vndk-sp/"},
-                                                     {MATCH_PREFIX, "/odm/" LIB "/vndk-sp/"}};
-static xdl_linker_caller_t xdl_linker_callers[] = {
-    {NULL, xdl_linker_match_default, sizeof(xdl_linker_match_default) / sizeof(xdl_linker_match_t)},
-    {NULL, xdl_linker_match_art, sizeof(xdl_linker_match_art) / sizeof(xdl_linker_match_t)},
-    {NULL, xdl_linker_match_sphal, sizeof(xdl_linker_match_sphal) / sizeof(xdl_linker_match_t)},
-    {NULL, xdl_linker_match_vndk, sizeof(xdl_linker_match_vndk) / sizeof(xdl_linker_match_t)}};
-
-static void xdl_linker_init_symbols_impl(void) {
-  // find linker from: /proc/self/maps (API level < 18) or getauxval (API level >= 18)
-  void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT);
-  if (NULL == handle) return;
-
-  int api_level = xdl_util_get_api_level();
-  if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) {
-    // == Android 5.x
-    xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
-  } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
-    // == Android 7.x
-    xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL);
-    if (NULL == xdl_linker_dlopen) {
-      xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL);
-      xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
-    }
-  } else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) {
-    // == Android 8.x
-    xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL);
-  } else if (api_level >= __ANDROID_API_P__) {
-    // >= Android 9.0
-    xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL);
-  }
-
-  xdl_close(handle);
-}
-
-static void xdl_linker_init_symbols(void) {
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  static bool inited = false;
-  if (!inited) {
-    pthread_mutex_lock(&lock);
-    if (!inited) {
-      xdl_linker_init_symbols_impl();
-      inited = true;
-    }
-    pthread_mutex_unlock(&lock);
-  }
-}
-
-void xdl_linker_lock(void) {
-  xdl_linker_init_symbols();
-
-  if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex);
-}
-
-void xdl_linker_unlock(void) {
-  if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex);
-}
-
-static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) {
-  for (size_t i = 0; i < info->dlpi_phnum; i++) {
-    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
-    if (PT_LOAD == phdr->p_type) {
-      return (void *)(info->dlpi_addr + phdr->p_vaddr);
-    }
-  }
-  return NULL;
-}
-
-static void xdl_linker_save_caller_addr(struct dl_phdr_info *info, xdl_linker_caller_t *caller,
-                                        size_t cursor) {
-  void *addr = xdl_linker_get_caller_addr(info);
-  if (NULL != addr) {
-    caller->addr = addr;
-    caller->matches_cursor = cursor;
-  }
-}
-
-static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) {
-  (void)size, (void)arg;
-  if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0;  // continue
-
-  int ret = 1;  // OK
-  for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
-    xdl_linker_caller_t *caller = &xdl_linker_callers[i];
-    for (size_t j = 0; j < caller->matches_cursor; j++) {
-      xdl_linker_match_t *match = &caller->matches[j];
-      switch (match->type) {
-        case MATCH_PREFIX:
-          if (xdl_util_starts_with(info->dlpi_name, match->value)) {
-            xdl_linker_save_caller_addr(info, caller, j);
-          }
-          break;
-        case MATCH_SUFFIX:
-          if (xdl_util_ends_with(info->dlpi_name, match->value)) {
-            xdl_linker_save_caller_addr(info, caller, j);
-          }
-          break;
-      }
-    }
-    if (NULL == caller->addr || 0 != caller->matches_cursor) ret = 0;  // continue
-  }
-  return ret;
-}
-
-static void xdl_linker_init_caller_addr_impl(void) {
-  xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, NULL, XDL_DEFAULT);
-}
-
-static void xdl_linker_init_caller_addr(void) {
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  static bool inited = false;
-  if (!inited) {
-    pthread_mutex_lock(&lock);
-    if (!inited) {
-      xdl_linker_init_caller_addr_impl();
-      inited = true;
-    }
-    pthread_mutex_unlock(&lock);
-  }
-}
-
-void *xdl_linker_force_dlopen(const char *filename) {
-  int api_level = xdl_util_get_api_level();
-
-  if (api_level <= __ANDROID_API_M__) {
-    // <= Android 6.0
-    return dlopen(filename, RTLD_NOW);
-  } else {
-    xdl_linker_init_symbols();
-    if (NULL == xdl_linker_dlopen) return NULL;
-    xdl_linker_init_caller_addr();
-
-    void *handle = NULL;
-    if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
-      // == Android 7.x
-      xdl_linker_lock();
-      for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
-        xdl_linker_caller_t *caller = &xdl_linker_callers[i];
-        if (NULL != caller->addr) {
-          handle = ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, caller->addr);
-          if (NULL != handle) break;
-        }
-      }
-      xdl_linker_unlock();
-    } else {
-      // >= Android 8.0
-      for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
-        xdl_linker_caller_t *caller = &xdl_linker_callers[i];
-        if (NULL != caller->addr) {
-          handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, caller->addr);
-          if (NULL != handle) break;
-        }
-      }
-    }
-    return handle;
-  }
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h
deleted file mode 100644
index 00cbf44..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2021-02-21.
-
-#ifndef IO_GITHUB_HEXHACKING_XDL_LINKER
-#define IO_GITHUB_HEXHACKING_XDL_LINKER
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void xdl_linker_lock(void);
-void xdl_linker_unlock(void);
-
-void *xdl_linker_force_dlopen(const char *filename);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c
deleted file mode 100644
index 541300d..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-11-08.
-
-#include "xdl_lzma.h"
-
-#include <ctype.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "xdl.h"
-#include "xdl_util.h"
-
-// LZMA library pathname & symbol names
-#ifndef __LP64__
-#define XDL_LZMA_PATHNAME "/system/lib/liblzma.so"
-#else
-#define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so"
-#endif
-#define XDL_LZMA_SYM_CRCGEN     "CrcGenerateTable"
-#define XDL_LZMA_SYM_CRC64GEN   "Crc64GenerateTable"
-#define XDL_LZMA_SYM_CONSTRUCT  "XzUnpacker_Construct"
-#define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished"
-#define XDL_LZMA_SYM_FREE       "XzUnpacker_Free"
-#define XDL_LZMA_SYM_CODE       "XzUnpacker_Code"
-
-// LZMA data type definition
-#define SZ_OK 0
-typedef struct ISzAlloc ISzAlloc;
-typedef const ISzAlloc *ISzAllocPtr;
-struct ISzAlloc {
-  void *(*Alloc)(ISzAllocPtr p, size_t size);
-  void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
-};
-typedef enum {
-  CODER_STATUS_NOT_SPECIFIED,      /* use main error code instead */
-  CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
-  CODER_STATUS_NOT_FINISHED,       /* stream was not finished */
-  CODER_STATUS_NEEDS_MORE_INPUT    /* you must provide more input bytes */
-} ECoderStatus;
-typedef enum {
-  CODER_FINISH_ANY, /* finish at any point */
-  CODER_FINISH_END  /* block must be finished at the end */
-} ECoderFinishMode;
-
-// LZMA function type definition
-typedef void (*xdl_lzma_crcgen_t)(void);
-typedef void (*xdl_lzma_crc64gen_t)(void);
-typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr);
-typedef int (*xdl_lzma_isfinished_t)(const void *);
-typedef void (*xdl_lzma_free_t)(void *);
-typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode,
-                               ECoderStatus *);
-typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int,
-                                 ECoderFinishMode, ECoderStatus *);
-
-// LZMA function pointor
-static xdl_lzma_construct_t xdl_lzma_construct = NULL;
-static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL;
-static xdl_lzma_free_t xdl_lzma_free = NULL;
-static void *xdl_lzma_code = NULL;
-
-// LZMA init
-static void xdl_lzma_init() {
-  void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD);
-  if (NULL == lzma) return;
-
-  xdl_lzma_crcgen_t crcgen = NULL;
-  xdl_lzma_crc64gen_t crc64gen = NULL;
-  if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end;
-  if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end;
-  if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL)))
-    goto end;
-  if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL)))
-    goto end;
-  if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end;
-  if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end;
-  crcgen();
-  crc64gen();
-
-end:
-  xdl_close(lzma);
-}
-
-// LZMA internal alloc / free
-static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) {
-  (void)p;
-  return malloc(size);
-}
-static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) {
-  (void)p;
-  free(address);
-}
-
-int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) {
-  size_t src_offset = 0;
-  size_t dst_offset = 0;
-  size_t src_remaining;
-  size_t dst_remaining;
-  ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free};
-  long long state[4096 / sizeof(long long)];  // must be enough, 8-bit aligned
-  ECoderStatus status;
-  int api_level = xdl_util_get_api_level();
-
-  // init and check
-  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-  static bool inited = false;
-  if (!inited) {
-    pthread_mutex_lock(&lock);
-    if (!inited) {
-      xdl_lzma_init();
-      inited = true;
-    }
-    pthread_mutex_unlock(&lock);
-  }
-  if (NULL == xdl_lzma_code) return -1;
-
-  xdl_lzma_construct(&state, &alloc);
-
-  *dst_size = 2 * src_size;
-  *dst = NULL;
-  do {
-    *dst_size *= 2;
-    if (NULL == (*dst = realloc(*dst, *dst_size))) {
-      xdl_lzma_free(&state);
-      return -1;
-    }
-
-    src_remaining = src_size - src_offset;
-    dst_remaining = *dst_size - dst_offset;
-
-    int result;
-    if (api_level >= __ANDROID_API_Q__) {
-      xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code;
-      result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1,
-                           CODER_FINISH_ANY, &status);
-    } else {
-      xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code;
-      result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining,
-                         CODER_FINISH_ANY, &status);
-    }
-    if (SZ_OK != result) {
-      free(*dst);
-      xdl_lzma_free(&state);
-      return -1;
-    }
-
-    src_offset += src_remaining;
-    dst_offset += dst_remaining;
-  } while (status == CODER_STATUS_NOT_FINISHED);
-
-  xdl_lzma_free(&state);
-
-  if (!xdl_lzma_isfinished(&state)) {
-    free(*dst);
-    return -1;
-  }
-
-  *dst_size = dst_offset;
-  *dst = realloc(*dst, *dst_size);
-  return 0;
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h
deleted file mode 100644
index 63c848a..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-11-08.
-
-#ifndef IO_GITHUB_HEXHACKING_XDL_LZMA
-#define IO_GITHUB_HEXHACKING_XDL_LZMA
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c
deleted file mode 100644
index 7d9e640..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-#include "xdl_util.h"
-
-#include <android/api-level.h>
-#include <ctype.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-bool xdl_util_starts_with(const char *str, const char *start) {
-  while (*str && *str == *start) {
-    str++;
-    start++;
-  }
-
-  return '\0' == *start;
-}
-
-bool xdl_util_ends_with(const char *str, const char *ending) {
-  size_t str_len = strlen(str);
-  size_t ending_len = strlen(ending);
-
-  if (ending_len > str_len) return false;
-
-  return 0 == strcmp(str + (str_len - ending_len), ending);
-}
-
-size_t xdl_util_trim_ending(char *start) {
-  char *end = start + strlen(start);
-  while (start < end && isspace((int)(*(end - 1)))) {
-    end--;
-    *end = '\0';
-  }
-  return (size_t)(end - start);
-}
-
-static int xdl_util_get_api_level_from_build_prop(void) {
-  char buf[128];
-  int api_level = -1;
-
-  FILE *fp = fopen("/system/build.prop", "r");
-  if (NULL == fp) goto end;
-
-  while (fgets(buf, sizeof(buf), fp)) {
-    if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) {
-      api_level = atoi(buf + 21);
-      break;
-    }
-  }
-  fclose(fp);
-
-end:
-  return (api_level > 0) ? api_level : -1;
-}
-
-int xdl_util_get_api_level(void) {
-  static int xdl_util_api_level = -1;
-
-  if (xdl_util_api_level < 0) {
-    int api_level = android_get_device_api_level();
-    if (api_level < 0)
-      api_level = xdl_util_get_api_level_from_build_prop();  // compatible with unusual models
-    if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__;
-
-    __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST);
-  }
-
-  return xdl_util_api_level;
-}
diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h
deleted file mode 100644
index 582e99f..0000000
--- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2020-2023 HexHacking Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Created by caikelun on 2020-10-04.
-
-#ifndef IO_GITHUB_HEXHACKING_XDL_UTIL
-#define IO_GITHUB_HEXHACKING_XDL_UTIL
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stddef.h>
-
-#ifndef __LP64__
-#define XDL_UTIL_LINKER_BASENAME        "linker"
-#define XDL_UTIL_LINKER_PATHNAME        "/system/bin/linker"
-#define XDL_UTIL_APP_PROCESS_BASENAME   "app_process32"
-#define XDL_UTIL_APP_PROCESS_PATHNAME   "/system/bin/app_process32"
-#define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process"
-#define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process"
-#else
-#define XDL_UTIL_LINKER_BASENAME      "linker64"
-#define XDL_UTIL_LINKER_PATHNAME      "/system/bin/linker64"
-#define XDL_UTIL_APP_PROCESS_BASENAME "app_process64"
-#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64"
-#endif
-#define XDL_UTIL_VDSO_BASENAME "[vdso]"
-
-#define XDL_UTIL_TEMP_FAILURE_RETRY(exp)   \
-  ({                                       \
-    __typeof__(exp) _rc;                   \
-    do {                                   \
-      errno = 0;                           \
-      _rc = (exp);                         \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc;                                   \
-  })
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-bool xdl_util_starts_with(const char *str, const char *start);
-bool xdl_util_ends_with(const char *str, const char *ending);
-
-size_t xdl_util_trim_ending(char *start);
-
-int xdl_util_get_api_level(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java
index b7c364a..5fbe3b0 100644
--- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java
+++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java
@@ -1,22 +1,54 @@
 package es.chiteroman.playintegrityfix;
 
 import android.os.Build;
+import android.util.JsonReader;
 import android.util.Log;
 
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
 import java.security.Provider;
 import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
 
 public final class EntryPoint {
+    private static final Map<String, String> map = new HashMap<>();
+
+    private static void defaultProps() {
+        map.putIfAbsent("PRODUCT", "bullhead");
+        map.putIfAbsent("DEVICE", "bullhead");
+        map.putIfAbsent("MANUFACTURER", "LGE");
+        map.putIfAbsent("BRAND", "google");
+        map.putIfAbsent("MODEL", "Nexus 5X");
+        map.putIfAbsent("FINGERPRINT", "google/bullhead/bullhead:8.0.0/OPR6.170623.013/4283548:user/release-keys");
+        map.putIfAbsent("SECURITY_PATCH", "2017-08-05");
+    }
 
     public static void init() {
         spoofProvider();
         spoofDevice();
     }
 
+    public static void readProps(String path) {
+        LOG("Read from Zygisk lib path: " + path);
+        File file = new File(path);
+        try (JsonReader reader = new JsonReader(new FileReader(file))) {
+            reader.beginObject();
+            while (reader.hasNext()) {
+                map.put(reader.nextName(), reader.nextString());
+            }
+            reader.endObject();
+        } catch (IOException e) {
+            LOG("Couldn't read pif.prop file: " + e);
+            defaultProps();
+        }
+    }
+
     private static void spoofProvider() {
         final String KEYSTORE = "AndroidKeyStore";
 
@@ -45,13 +77,13 @@ public final class EntryPoint {
     }
 
     public static void spoofDevice() {
-        setProp("PRODUCT", "bullhead");
-        setProp("DEVICE", "bullhead");
-        setProp("MANUFACTURER", "LGE");
-        setProp("BRAND", "google");
-        setProp("MODEL", "Nexus 5X");
-        setProp("FINGERPRINT", "google/bullhead/bullhead:8.0.0/OPR6.170623.013/4283548:user/release-keys");
-        setVersionProp("SECURITY_PATCH", "2017-08-05");
+        setProp("PRODUCT", map.get("PRODUCT"));
+        setProp("DEVICE", map.get("DEVICE"));
+        setProp("MANUFACTURER", map.get("MANUFACTURER"));
+        setProp("BRAND", map.get("BRAND"));
+        setProp("MODEL", map.get("MODEL"));
+        setProp("FINGERPRINT", map.get("FINGERPRINT"));
+        setVersionProp("SECURITY_PATCH", map.get("SECURITY_PATCH"));
     }
 
     private static void setProp(String name, String value) {