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 (¤t->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) {