This is page 4 of 17. Use http://codebase.md/wasdubya/x64dbgmcp?page={x} to view the full context. # Directory Structure ``` ├── build │ ├── MCPx64dbg.dp32 │ └── MCPx64dbg.dp64 ├── CMakeLists.txt ├── deps │ ├── pluginsdk │ │ ├── _dbgfunctions.h │ │ ├── _plugin_types.h │ │ ├── _plugins.h │ │ ├── _scriptapi_argument.h │ │ ├── _scriptapi_assembler.h │ │ ├── _scriptapi_bookmark.h │ │ ├── _scriptapi_comment.h │ │ ├── _scriptapi_debug.h │ │ ├── _scriptapi_flag.h │ │ ├── _scriptapi_function.h │ │ ├── _scriptapi_gui.h │ │ ├── _scriptapi_label.h │ │ ├── _scriptapi_memory.h │ │ ├── _scriptapi_misc.h │ │ ├── _scriptapi_module.h │ │ ├── _scriptapi_pattern.h │ │ ├── _scriptapi_register.h │ │ ├── _scriptapi_stack.h │ │ ├── _scriptapi_symbol.h │ │ ├── _scriptapi.h │ │ ├── bridgegraph.h │ │ ├── bridgelist.h │ │ ├── bridgemain.h │ │ ├── dbghelp │ │ │ ├── dbghelp_x64.a │ │ │ ├── dbghelp_x64.lib │ │ │ ├── dbghelp_x86.a │ │ │ ├── dbghelp_x86.lib │ │ │ └── dbghelp.h │ │ ├── DeviceNameResolver │ │ │ ├── DeviceNameResolver_x64.a │ │ │ ├── DeviceNameResolver_x64.lib │ │ │ ├── DeviceNameResolver_x86.a │ │ │ ├── DeviceNameResolver_x86.lib │ │ │ └── DeviceNameResolver.h │ │ ├── jansson │ │ │ ├── jansson_config.h │ │ │ ├── jansson_x64.a │ │ │ ├── jansson_x64.lib │ │ │ ├── jansson_x64dbg.h │ │ │ ├── jansson_x86.a │ │ │ ├── jansson_x86.lib │ │ │ └── jansson.h │ │ ├── lz4 │ │ │ ├── lz4_x64.a │ │ │ ├── lz4_x64.lib │ │ │ ├── lz4_x86.a │ │ │ ├── lz4_x86.lib │ │ │ ├── lz4.h │ │ │ ├── lz4file.h │ │ │ └── lz4hc.h │ │ ├── nlohmann │ │ │ ├── adl_serializer.hpp │ │ │ ├── byte_container_with_subtype.hpp │ │ │ ├── detail │ │ │ │ ├── abi_macros.hpp │ │ │ │ ├── conversions │ │ │ │ │ ├── from_json.hpp │ │ │ │ │ ├── to_chars.hpp │ │ │ │ │ └── to_json.hpp │ │ │ │ ├── exceptions.hpp │ │ │ │ ├── hash.hpp │ │ │ │ ├── input │ │ │ │ │ ├── binary_reader.hpp │ │ │ │ │ ├── input_adapters.hpp │ │ │ │ │ ├── json_sax.hpp │ │ │ │ │ ├── lexer.hpp │ │ │ │ │ ├── parser.hpp │ │ │ │ │ └── position_t.hpp │ │ │ │ ├── iterators │ │ │ │ │ ├── internal_iterator.hpp │ │ │ │ │ ├── iter_impl.hpp │ │ │ │ │ ├── iteration_proxy.hpp │ │ │ │ │ ├── iterator_traits.hpp │ │ │ │ │ ├── json_reverse_iterator.hpp │ │ │ │ │ └── primitive_iterator.hpp │ │ │ │ ├── json_custom_base_class.hpp │ │ │ │ ├── json_pointer.hpp │ │ │ │ ├── json_ref.hpp │ │ │ │ ├── macro_scope.hpp │ │ │ │ ├── macro_unscope.hpp │ │ │ │ ├── meta │ │ │ │ │ ├── call_std │ │ │ │ │ │ ├── begin.hpp │ │ │ │ │ │ └── end.hpp │ │ │ │ │ ├── cpp_future.hpp │ │ │ │ │ ├── detected.hpp │ │ │ │ │ ├── identity_tag.hpp │ │ │ │ │ ├── is_sax.hpp │ │ │ │ │ ├── std_fs.hpp │ │ │ │ │ ├── type_traits.hpp │ │ │ │ │ └── void_t.hpp │ │ │ │ ├── output │ │ │ │ │ ├── binary_writer.hpp │ │ │ │ │ ├── output_adapters.hpp │ │ │ │ │ └── serializer.hpp │ │ │ │ ├── string_concat.hpp │ │ │ │ ├── string_escape.hpp │ │ │ │ └── value_t.hpp │ │ │ ├── json_fwd.hpp │ │ │ ├── json.hpp │ │ │ ├── ordered_map.hpp │ │ │ └── thirdparty │ │ │ └── hedley │ │ │ ├── hedley_undef.hpp │ │ │ └── hedley.hpp │ │ ├── TitanEngine │ │ │ ├── TitanEngine_x64.a │ │ │ ├── TitanEngine_x64.lib │ │ │ ├── TitanEngine_x86.a │ │ │ ├── TitanEngine_x86.lib │ │ │ └── TitanEngine.h │ │ ├── x32bridge.lib │ │ ├── x32dbg.lib │ │ ├── x64bridge.lib │ │ ├── x64dbg.lib │ │ └── XEDParse │ │ ├── XEDParse_x64.a │ │ ├── XEDParse_x64.lib │ │ ├── XEDParse_x86.a │ │ ├── XEDParse_x86.lib │ │ └── XEDParse.h │ └── x64dbg_sdk │ └── pluginsdk │ ├── _dbgfunctions.h │ ├── _plugin_types.h │ ├── _plugins.h │ ├── _scriptapi_argument.h │ ├── _scriptapi_assembler.h │ ├── _scriptapi_bookmark.h │ ├── _scriptapi_comment.h │ ├── _scriptapi_debug.h │ ├── _scriptapi_flag.h │ ├── _scriptapi_function.h │ ├── _scriptapi_gui.h │ ├── _scriptapi_label.h │ ├── _scriptapi_memory.h │ ├── _scriptapi_misc.h │ ├── _scriptapi_module.h │ ├── _scriptapi_pattern.h │ ├── _scriptapi_register.h │ ├── _scriptapi_stack.h │ ├── _scriptapi_symbol.h │ ├── _scriptapi.h │ ├── bridgegraph.h │ ├── bridgelist.h │ ├── bridgemain.h │ ├── dbghelp │ │ ├── dbghelp_x64.a │ │ ├── dbghelp_x64.lib │ │ ├── dbghelp_x86.a │ │ ├── dbghelp_x86.lib │ │ └── dbghelp.h │ ├── DeviceNameResolver │ │ ├── DeviceNameResolver_x64.a │ │ ├── DeviceNameResolver_x64.lib │ │ ├── DeviceNameResolver_x86.a │ │ ├── DeviceNameResolver_x86.lib │ │ └── DeviceNameResolver.h │ ├── jansson │ │ ├── jansson_config.h │ │ ├── jansson_x64.a │ │ ├── jansson_x64.lib │ │ ├── jansson_x64dbg.h │ │ ├── jansson_x86.a │ │ ├── jansson_x86.lib │ │ └── jansson.h │ ├── lz4 │ │ ├── lz4_x64.a │ │ ├── lz4_x64.lib │ │ ├── lz4_x86.a │ │ ├── lz4_x86.lib │ │ ├── lz4.h │ │ ├── lz4file.h │ │ └── lz4hc.h │ ├── TitanEngine │ │ ├── TitanEngine_x64.a │ │ ├── TitanEngine_x64.lib │ │ ├── TitanEngine_x86.a │ │ ├── TitanEngine_x86.lib │ │ └── TitanEngine.h │ ├── TitanEngine_x64.a │ ├── TitanEngine_x64.lib │ ├── TitanEngine_x86.a │ ├── TitanEngine_x86.lib │ ├── TitanEngine.h │ ├── x32bridge.lib │ ├── x32dbg.lib │ ├── x64bridge.lib │ ├── x64dbg.lib │ └── XEDParse │ ├── XEDParse_x64.a │ ├── XEDParse_x64.lib │ ├── XEDParse_x86.a │ ├── XEDParse_x86.lib │ └── XEDParse.h ├── include │ └── nlohmann │ ├── adl_serializer.hpp │ ├── byte_container_with_subtype.hpp │ ├── detail │ │ ├── abi_macros.hpp │ │ ├── conversions │ │ │ ├── from_json.hpp │ │ │ ├── to_chars.hpp │ │ │ └── to_json.hpp │ │ ├── exceptions.hpp │ │ ├── hash.hpp │ │ ├── input │ │ │ ├── binary_reader.hpp │ │ │ ├── input_adapters.hpp │ │ │ ├── json_sax.hpp │ │ │ ├── lexer.hpp │ │ │ ├── parser.hpp │ │ │ └── position_t.hpp │ │ ├── iterators │ │ │ ├── internal_iterator.hpp │ │ │ ├── iter_impl.hpp │ │ │ ├── iteration_proxy.hpp │ │ │ ├── iterator_traits.hpp │ │ │ ├── json_reverse_iterator.hpp │ │ │ └── primitive_iterator.hpp │ │ ├── json_custom_base_class.hpp │ │ ├── json_pointer.hpp │ │ ├── json_ref.hpp │ │ ├── macro_scope.hpp │ │ ├── macro_unscope.hpp │ │ ├── meta │ │ │ ├── call_std │ │ │ │ ├── begin.hpp │ │ │ │ └── end.hpp │ │ │ ├── cpp_future.hpp │ │ │ ├── detected.hpp │ │ │ ├── identity_tag.hpp │ │ │ ├── is_sax.hpp │ │ │ ├── std_fs.hpp │ │ │ ├── type_traits.hpp │ │ │ └── void_t.hpp │ │ ├── output │ │ │ ├── binary_writer.hpp │ │ │ ├── output_adapters.hpp │ │ │ └── serializer.hpp │ │ ├── string_concat.hpp │ │ ├── string_escape.hpp │ │ └── value_t.hpp │ ├── json_fwd.hpp │ ├── json.hpp │ ├── ordered_map.hpp │ └── thirdparty │ └── hedley │ ├── hedley_undef.hpp │ └── hedley.hpp ├── README.md ├── Showcase.gif ├── side profile of a voxel spider walking.jpg └── src ├── MCPx64dbg.cpp └── x64dbg.py ``` # Files -------------------------------------------------------------------------------- /deps/pluginsdk/nlohmann/detail/input/json_sax.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #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_data.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_data.m_value.array->emplace_back(std::forward<Value>(v)); return &(ref_stack.back()->m_data.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_data.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_data.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_data.m_value.array->emplace_back(std::move(value)); return {true, & (ref_stack.back()->m_data.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/json_sax.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #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_data.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_data.m_value.array->emplace_back(std::forward<Value>(v)); return &(ref_stack.back()->m_data.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_data.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_data.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_data.m_value.array->emplace_back(std::move(value)); return {true, & (ref_stack.back()->m_data.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 ``` -------------------------------------------------------------------------------- /deps/pluginsdk/nlohmann/detail/iterators/iter_impl.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #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_data.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_data.m_type) { case value_t::object: { m_it.object_iterator = m_object->m_data.m_value.object->begin(); break; } case value_t::array: { m_it.array_iterator = m_object->m_data.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_data.m_type) { case value_t::object: { m_it.object_iterator = m_object->m_data.m_value.object->end(); break; } case value_t::array: { m_it.array_iterator = m_object->m_data.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_data.m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_data.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_data.m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_data.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_data.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_data.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_data.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_data.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_data.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_data.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_data.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/iter_impl.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #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_data.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_data.m_type) { case value_t::object: { m_it.object_iterator = m_object->m_data.m_value.object->begin(); break; } case value_t::array: { m_it.array_iterator = m_object->m_data.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_data.m_type) { case value_t::object: { m_it.object_iterator = m_object->m_data.m_value.object->end(); break; } case value_t::array: { m_it.array_iterator = m_object->m_data.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_data.m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_data.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_data.m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_data.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_data.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_data.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_data.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_data.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_data.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_data.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_data.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 ``` -------------------------------------------------------------------------------- /deps/pluginsdk/nlohmann/detail/meta/type_traits.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #include <limits> // numeric_limits #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type #include <utility> // declval #include <tuple> // tuple #include <string> // char_traits #include <nlohmann/detail/iterators/iterator_traits.hpp> #include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/meta/call_std/begin.hpp> #include <nlohmann/detail/meta/call_std/end.hpp> #include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/detected.hpp> #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; ///////////////// // char_traits // ///////////////// // Primary template of char_traits calls std char_traits template<typename T> struct char_traits : std::char_traits<T> {}; // Explicitly define char traits for unsigned char since it is not standard template<> struct char_traits<unsigned char> : std::char_traits<char> { using char_type = unsigned char; using int_type = uint64_t; // Redefine to_int_type function static int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); } static char_type to_char_type(int_type i) noexcept { return static_cast<char_type>(i); } static constexpr int_type eof() noexcept { return static_cast<int_type>(EOF); } }; // Explicitly define char traits for signed char since it is not standard template<> struct char_traits<signed char> : std::char_traits<char> { using char_type = signed char; using int_type = uint64_t; // Redefine to_int_type function static int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); } static char_type to_char_type(int_type i) noexcept { return static_cast<char_type>(i); } static constexpr int_type eof() noexcept { return static_cast<int_type>(EOF); } }; /////////////////// // 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/meta/type_traits.hpp: -------------------------------------------------------------------------------- ``` // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> // SPDX-License-Identifier: MIT #pragma once #include <limits> // numeric_limits #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type #include <utility> // declval #include <tuple> // tuple #include <string> // char_traits #include <nlohmann/detail/iterators/iterator_traits.hpp> #include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/meta/call_std/begin.hpp> #include <nlohmann/detail/meta/call_std/end.hpp> #include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/detected.hpp> #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; ///////////////// // char_traits // ///////////////// // Primary template of char_traits calls std char_traits template<typename T> struct char_traits : std::char_traits<T> {}; // Explicitly define char traits for unsigned char since it is not standard template<> struct char_traits<unsigned char> : std::char_traits<char> { using char_type = unsigned char; using int_type = uint64_t; // Redefine to_int_type function static int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); } static char_type to_char_type(int_type i) noexcept { return static_cast<char_type>(i); } static constexpr int_type eof() noexcept { return static_cast<int_type>(EOF); } }; // Explicitly define char traits for signed char since it is not standard template<> struct char_traits<signed char> : std::char_traits<char> { using char_type = signed char; using int_type = uint64_t; // Redefine to_int_type function static int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); } static char_type to_char_type(int_type i) noexcept { return static_cast<char_type>(i); } static constexpr int_type eof() noexcept { return static_cast<int_type>(EOF); } }; /////////////////// // 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 ``` -------------------------------------------------------------------------------- /src/x64dbg.py: -------------------------------------------------------------------------------- ```python import sys import os import inspect import json from typing import Any, Dict, List, Callable import requests from mcp.server.fastmcp import FastMCP DEFAULT_X64DBG_SERVER = "http://127.0.0.1:8888/" def _resolve_server_url_from_args_env() -> str: env_url = os.getenv("X64DBG_URL") if env_url and env_url.startswith("http"): return env_url if len(sys.argv) > 1 and isinstance(sys.argv[1], str) and sys.argv[1].startswith("http"): return sys.argv[1] return DEFAULT_X64DBG_SERVER x64dbg_server_url = _resolve_server_url_from_args_env() def set_x64dbg_server_url(url: str) -> None: global x64dbg_server_url if url and url.startswith("http"): x64dbg_server_url = url mcp = FastMCP("x64dbg-mcp") def safe_get(endpoint: str, params: dict = None): """ Perform a GET request with optional query parameters. Returns parsed JSON if possible, otherwise text content """ if params is None: params = {} url = f"{x64dbg_server_url}{endpoint}" try: response = requests.get(url, params=params, timeout=15) response.encoding = 'utf-8' if response.ok: # Try to parse as JSON first try: return response.json() except ValueError: return response.text.strip() else: return f"Error {response.status_code}: {response.text.strip()}" except Exception as e: return f"Request failed: {str(e)}" def safe_post(endpoint: str, data: dict | str): """ Perform a POST request with data. Returns parsed JSON if possible, otherwise text content """ try: url = f"{x64dbg_server_url}{endpoint}" if isinstance(data, dict): response = requests.post(url, data=data, timeout=5) else: response = requests.post(url, data=data.encode("utf-8"), timeout=5) response.encoding = 'utf-8' if response.ok: # Try to parse as JSON first try: return response.json() except ValueError: return response.text.strip() else: return f"Error {response.status_code}: {response.text.strip()}" except Exception as e: return f"Request failed: {str(e)}" # ============================================================================= # TOOL REGISTRY INTROSPECTION (for CLI/Claude tool-use) # ============================================================================= def _get_mcp_tools_registry() -> Dict[str, Callable[..., Any]]: """ Build a registry of available MCP-exposed tool callables in this module. Heuristic: exported callables starting with an uppercase letter. """ registry: Dict[str, Callable[..., Any]] = {} for name, obj in globals().items(): if not name or not name[0].isupper(): continue if callable(obj): try: # Validate signature to ensure it's a plain function inspect.signature(obj) registry[name] = obj except (TypeError, ValueError): pass return registry def _describe_tool(name: str, func: Callable[..., Any]) -> Dict[str, Any]: sig = inspect.signature(func) params = [] for p in sig.parameters.values(): if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD): # Skip non-JSON friendly params in schema continue params.append({ "name": p.name, "required": p.default is inspect._empty, "type": "string" if p.annotation in (str, inspect._empty) else ("boolean" if p.annotation is bool else ("integer" if p.annotation is int else "string")) }) return { "name": name, "description": (func.__doc__ or "").strip(), "params": params } def _list_tools_description() -> List[Dict[str, Any]]: reg = _get_mcp_tools_registry() return [_describe_tool(n, f) for n, f in sorted(reg.items(), key=lambda x: x[0].lower())] def _invoke_tool_by_name(name: str, args: Dict[str, Any]) -> Any: reg = _get_mcp_tools_registry() if name not in reg: return {"error": f"Unknown tool: {name}"} func = reg[name] try: # Prefer keyword invocation; convert all values to strings unless bool/int expected sig = inspect.signature(func) bound_kwargs: Dict[str, Any] = {} for p in sig.parameters.values(): if p.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD, inspect.Parameter.POSITIONAL_ONLY): continue if p.name in args: value = args[p.name] # Simple coercions for common types if p.annotation is bool and isinstance(value, str): value = value.lower() in ("1", "true", "yes", "on") elif p.annotation is int and isinstance(value, str): try: value = int(value, 0) except Exception: try: value = int(value) except Exception: pass bound_kwargs[p.name] = value return func(**bound_kwargs) except Exception as e: return {"error": str(e)} # ============================================================================= # Claude block normalization helpers # ============================================================================= def _block_to_dict(block: Any) -> Dict[str, Any]: try: # Newer anthropic SDK objects are Pydantic models if hasattr(block, "model_dump") and callable(getattr(block, "model_dump")): return block.model_dump() except Exception: pass if isinstance(block, dict): return block btype = getattr(block, "type", None) if btype == "text": return {"type": "text", "text": getattr(block, "text", "")} if btype == "tool_use": return { "type": "tool_use", "id": getattr(block, "id", None), "name": getattr(block, "name", None), "input": getattr(block, "input", {}) or {}, } # Fallback generic representation return {"type": str(btype or "unknown"), "raw": str(block)} # ============================================================================= # UNIFIED COMMAND EXECUTION # ============================================================================= @mcp.tool() def ExecCommand(cmd: str) -> str: """ Execute a command in x64dbg and return its output Parameters: cmd: Command to execute Returns: Command execution status and output """ return safe_get("ExecCommand", {"cmd": cmd}) # ============================================================================= # DEBUGGING STATUS # ============================================================================= @mcp.tool() def IsDebugActive() -> bool: """ Check if debugger is active (running) Returns: True if running, False otherwise """ result = safe_get("IsDebugActive") if isinstance(result, dict) and "isRunning" in result: return result["isRunning"] is True if isinstance(result, str): try: import json parsed = json.loads(result) return parsed.get("isRunning", False) is True except Exception: return False return False @mcp.tool() def IsDebugging() -> bool: """ Check if x64dbg is debugging a process Returns: True if debugging, False otherwise """ result = safe_get("Is_Debugging") if isinstance(result, dict) and "isDebugging" in result: return result["isDebugging"] is True if isinstance(result, str): try: import json parsed = json.loads(result) return parsed.get("isDebugging", False) is True except Exception: return False return False # ============================================================================= # REGISTER API # ============================================================================= @mcp.tool() def RegisterGet(register: str) -> str: """ Get register value using Script API Parameters: register: Register name (e.g. "eax", "rax", "rip") Returns: Register value in hex format """ return safe_get("Register/Get", {"register": register}) @mcp.tool() def RegisterSet(register: str, value: str) -> str: """ Set register value using Script API Parameters: register: Register name (e.g. "eax", "rax", "rip") value: Value to set (in hex format, e.g. "0x1000") Returns: Status message """ return safe_get("Register/Set", {"register": register, "value": value}) # ============================================================================= # MEMORY API (Enhanced) # ============================================================================= @mcp.tool() def MemoryRead(addr: str, size: str) -> str: """ Read memory using enhanced Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") size: Number of bytes to read Returns: Hexadecimal string representing the memory contents """ return safe_get("Memory/Read", {"addr": addr, "size": size}) @mcp.tool() def MemoryWrite(addr: str, data: str) -> str: """ Write memory using enhanced Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") data: Hexadecimal string representing the data to write Returns: Status message """ return safe_get("Memory/Write", {"addr": addr, "data": data}) @mcp.tool() def MemoryIsValidPtr(addr: str) -> bool: """ Check if memory address is valid Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: True if valid, False otherwise """ result = safe_get("Memory/IsValidPtr", {"addr": addr}) if isinstance(result, str): return result.lower() == "true" return False @mcp.tool() def MemoryGetProtect(addr: str) -> str: """ Get memory protection flags Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Protection flags in hex format """ return safe_get("Memory/GetProtect", {"addr": addr}) # ============================================================================= # DEBUG API # ============================================================================= @mcp.tool() def DebugRun() -> str: """ Resume execution of the debugged process using Script API Returns: Status message """ return safe_get("Debug/Run") @mcp.tool() def DebugPause() -> str: """ Pause execution of the debugged process using Script API Returns: Status message """ return safe_get("Debug/Pause") @mcp.tool() def DebugStop() -> str: """ Stop debugging using Script API Returns: Status message """ return safe_get("Debug/Stop") @mcp.tool() def DebugStepIn() -> str: """ Step into the next instruction using Script API Returns: Status message """ return safe_get("Debug/StepIn") @mcp.tool() def DebugStepOver() -> str: """ Step over the next instruction using Script API Returns: Status message """ return safe_get("Debug/StepOver") @mcp.tool() def DebugStepOut() -> str: """ Step out of the current function using Script API Returns: Status message """ return safe_get("Debug/StepOut") @mcp.tool() def DebugSetBreakpoint(addr: str) -> str: """ Set breakpoint at address using Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Status message """ return safe_get("Debug/SetBreakpoint", {"addr": addr}) @mcp.tool() def DebugDeleteBreakpoint(addr: str) -> str: """ Delete breakpoint at address using Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Status message """ return safe_get("Debug/DeleteBreakpoint", {"addr": addr}) # ============================================================================= # ASSEMBLER API # ============================================================================= @mcp.tool() def AssemblerAssemble(addr: str, instruction: str) -> dict: """ Assemble instruction at address using Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") instruction: Assembly instruction (e.g. "mov eax, 1") Returns: Dictionary with assembly result """ result = safe_get("Assembler/Assemble", {"addr": addr, "instruction": instruction}) if isinstance(result, dict): return result elif isinstance(result, str): try: return json.loads(result) except: return {"error": "Failed to parse assembly result", "raw": result} return {"error": "Unexpected response format"} @mcp.tool() def AssemblerAssembleMem(addr: str, instruction: str) -> str: """ Assemble instruction directly into memory using Script API Parameters: addr: Memory address (in hex format, e.g. "0x1000") instruction: Assembly instruction (e.g. "mov eax, 1") Returns: Status message """ return safe_get("Assembler/AssembleMem", {"addr": addr, "instruction": instruction}) # ============================================================================= # STACK API # ============================================================================= @mcp.tool() def StackPop() -> str: """ Pop value from stack using Script API Returns: Popped value in hex format """ return safe_get("Stack/Pop") @mcp.tool() def StackPush(value: str) -> str: """ Push value to stack using Script API Parameters: value: Value to push (in hex format, e.g. "0x1000") Returns: Previous top value in hex format """ return safe_get("Stack/Push", {"value": value}) @mcp.tool() def StackPeek(offset: str = "0") -> str: """ Peek at stack value using Script API Parameters: offset: Stack offset (default: "0") Returns: Stack value in hex format """ return safe_get("Stack/Peek", {"offset": offset}) # ============================================================================= # FLAG API # ============================================================================= @mcp.tool() def FlagGet(flag: str) -> bool: """ Get CPU flag value using Script API Parameters: flag: Flag name (ZF, OF, CF, PF, SF, TF, AF, DF, IF) Returns: Flag value (True/False) """ result = safe_get("Flag/Get", {"flag": flag}) if isinstance(result, str): return result.lower() == "true" return False @mcp.tool() def FlagSet(flag: str, value: bool) -> str: """ Set CPU flag value using Script API Parameters: flag: Flag name (ZF, OF, CF, PF, SF, TF, AF, DF, IF) value: Flag value (True/False) Returns: Status message """ return safe_get("Flag/Set", {"flag": flag, "value": "true" if value else "false"}) # ============================================================================= # PATTERN API # ============================================================================= @mcp.tool() def PatternFindMem(start: str, size: str, pattern: str) -> str: """ Find pattern in memory using Script API Parameters: start: Start address (in hex format, e.g. "0x1000") size: Size to search pattern: Pattern to find (e.g. "48 8B 05 ? ? ? ?") Returns: Found address in hex format or error message """ return safe_get("Pattern/FindMem", {"start": start, "size": size, "pattern": pattern}) # ============================================================================= # MISC API # ============================================================================= @mcp.tool() def MiscParseExpression(expression: str) -> str: """ Parse expression using Script API Parameters: expression: Expression to parse (e.g. "[esp+8]", "kernel32.GetProcAddress") Returns: Parsed value in hex format """ return safe_get("Misc/ParseExpression", {"expression": expression}) @mcp.tool() def MiscRemoteGetProcAddress(module: str, api: str) -> str: """ Get remote procedure address using Script API Parameters: module: Module name (e.g. "kernel32.dll") api: API name (e.g. "GetProcAddress") Returns: Function address in hex format """ return safe_get("Misc/RemoteGetProcAddress", {"module": module, "api": api}) # ============================================================================= # LEGACY COMPATIBILITY FUNCTIONS # ============================================================================= @mcp.tool() def SetRegister(name: str, value: str) -> str: """ Set register value using command (legacy compatibility) Parameters: name: Register name (e.g. "eax", "rip") value: Value to set (in hex format, e.g. "0x1000") Returns: Status message """ # Construct command to set register cmd = f"r {name}={value}" return ExecCommand(cmd) @mcp.tool() def MemRead(addr: str, size: str) -> str: """ Read memory at address (legacy compatibility) Parameters: addr: Memory address (in hex format, e.g. "0x1000") size: Number of bytes to read Returns: Hexadecimal string representing the memory contents """ return safe_get("MemRead", {"addr": addr, "size": size}) @mcp.tool() def MemWrite(addr: str, data: str) -> str: """ Write memory at address (legacy compatibility) Parameters: addr: Memory address (in hex format, e.g. "0x1000") data: Hexadecimal string representing the data to write Returns: Status message """ return safe_get("MemWrite", {"addr": addr, "data": data}) @mcp.tool() def SetBreakpoint(addr: str) -> str: """ Set breakpoint at address (legacy compatibility) Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Status message """ return ExecCommand(f"bp {addr}") @mcp.tool() def DeleteBreakpoint(addr: str) -> str: """ Delete breakpoint at address (legacy compatibility) Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Status message """ return ExecCommand(f"bpc {addr}") @mcp.tool() def Run() -> str: """ Resume execution of the debugged process (legacy compatibility) Returns: Status message """ return ExecCommand("run") @mcp.tool() def Pause() -> str: """ Pause execution of the debugged process (legacy compatibility) Returns: Status message """ return ExecCommand("pause") @mcp.tool() def StepIn() -> str: """ Step into the next instruction (legacy compatibility) Returns: Status message """ return ExecCommand("sti") @mcp.tool() def StepOver() -> str: """ Step over the next instruction (legacy compatibility) Returns: Status message """ return ExecCommand("sto") @mcp.tool() def StepOut() -> str: """ Step out of the current function (legacy compatibility) Returns: Status message """ return ExecCommand("rtr") @mcp.tool() def GetCallStack() -> list: """ Get call stack of the current thread (legacy compatibility) Returns: Command result information """ result = ExecCommand("k") return [{"info": "Call stack information requested via command", "result": result}] @mcp.tool() def Disassemble(addr: str) -> dict: """ Disassemble at address (legacy compatibility) Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Dictionary containing disassembly information """ return {"addr": addr, "command_result": ExecCommand(f"dis {addr}")} @mcp.tool() def DisasmGetInstruction(addr: str) -> dict: """ Get disassembly of a single instruction at the specified address Parameters: addr: Memory address (in hex format, e.g. "0x1000") Returns: Dictionary containing instruction details """ result = safe_get("Disasm/GetInstruction", {"addr": addr}) if isinstance(result, dict): return result elif isinstance(result, str): try: return json.loads(result) except: return {"error": "Failed to parse disassembly result", "raw": result} return {"error": "Unexpected response format"} @mcp.tool() def DisasmGetInstructionRange(addr: str, count: int = 1) -> list: """ Get disassembly of multiple instructions starting at the specified address Parameters: addr: Memory address (in hex format, e.g. "0x1000") count: Number of instructions to disassemble (default: 1, max: 100) Returns: List of dictionaries containing instruction details """ result = safe_get("Disasm/GetInstructionRange", {"addr": addr, "count": str(count)}) if isinstance(result, list): return result elif isinstance(result, str): try: return json.loads(result) except: return [{"error": "Failed to parse disassembly result", "raw": result}] return [{"error": "Unexpected response format"}] @mcp.tool() def DisasmGetInstructionAtRIP() -> dict: """ Get disassembly of the instruction at the current RIP Returns: Dictionary containing current instruction details """ result = safe_get("Disasm/GetInstructionAtRIP") if isinstance(result, dict): return result elif isinstance(result, str): try: return json.loads(result) except: return {"error": "Failed to parse disassembly result", "raw": result} return {"error": "Unexpected response format"} @mcp.tool() def StepInWithDisasm() -> dict: """ Step into the next instruction and return both step result and current instruction disassembly Returns: Dictionary containing step result and current instruction info """ result = safe_get("Disasm/StepInWithDisasm") if isinstance(result, dict): return result elif isinstance(result, str): try: return json.loads(result) except: return {"error": "Failed to parse step result", "raw": result} return {"error": "Unexpected response format"} @mcp.tool() def GetModuleList() -> list: """ Get list of loaded modules Returns: List of module information (name, base address, size, etc.) """ result = safe_get("GetModuleList") if isinstance(result, list): return result elif isinstance(result, str): try: return json.loads(result) except: return [{"error": "Failed to parse module list", "raw": result}] return [{"error": "Unexpected response format"}] @mcp.tool() def MemoryBase(addr: str) -> dict: """ Find the base address and size of a module containing the given address Parameters: addr: Memory address (in hex format, e.g. "0x7FF12345") Returns: Dictionary containing base_address and size of the module """ try: # Make the request to the endpoint result = safe_get("MemoryBase", {"addr": addr}) # Handle different response types if isinstance(result, dict): return result elif isinstance(result, str): try: # Try to parse the string as JSON return json.loads(result) except: # Fall back to string parsing if needed if "," in result: parts = result.split(",") return { "base_address": parts[0], "size": parts[1] } return {"raw_response": result} return {"error": "Unexpected response format"} except Exception as e: return {"error": str(e)} import argparse def main_cli(): parser = argparse.ArgumentParser(description="x64dbg MCP CLI wrapper") parser.add_argument("tool", help="Tool/function name (e.g. ExecCommand, RegisterGet, MemoryRead)") parser.add_argument("args", nargs="*", help="Arguments for the tool") parser.add_argument("--x64dbg-url", dest="x64dbg_url", default=os.getenv("X64DBG_URL"), help="x64dbg HTTP server URL") opts = parser.parse_args() if opts.x64dbg_url: set_x64dbg_server_url(opts.x64dbg_url) # Map CLI call → actual MCP tool function if opts.tool in globals(): func = globals()[opts.tool] if callable(func): try: # Try to unpack args dynamically result = func(*opts.args) print(json.dumps(result, indent=2)) except TypeError as e: print(f"Error calling {opts.tool}: {e}") else: print(f"{opts.tool} is not callable") else: print(f"Unknown tool: {opts.tool}") def claude_cli(): parser = argparse.ArgumentParser(description="Chat with Claude using x64dbg MCP tools") parser.add_argument("prompt", nargs=argparse.REMAINDER, help="Initial user prompt. If empty, read from stdin") parser.add_argument("--model", dest="model", default=os.getenv("ANTHROPIC_MODEL", "claude-3-7-sonnet-2025-06-20"), help="Claude model") parser.add_argument("--api-key", dest="api_key", default=os.getenv("ANTHROPIC_API_KEY"), help="Anthropic API key") parser.add_argument("--system", dest="system", default="You can control x64dbg via MCP tools.", help="System prompt") parser.add_argument("--max-steps", dest="max_steps", type=int, default=100, help="Max tool-use iterations") parser.add_argument("--x64dbg-url", dest="x64dbg_url", default=os.getenv("X64DBG_URL"), help="x64dbg HTTP server URL") parser.add_argument("--no-tools", dest="no_tools", action="store_true", help="Disable tool-use (text-only)") opts = parser.parse_args() if opts.x64dbg_url: set_x64dbg_server_url(opts.x64dbg_url) # Resolve prompt user_prompt = " ".join(opts.prompt).strip() if not user_prompt: user_prompt = sys.stdin.read().strip() if not user_prompt: print("No prompt provided.") return try: import anthropic except Exception as e: print("Anthropic SDK not installed. Run: pip install anthropic") print(str(e)) return if not opts.api_key: print("Missing Anthropic API key. Set ANTHROPIC_API_KEY or pass --api-key.") return client = anthropic.Anthropic(api_key=opts.api_key) tools_spec: List[Dict[str, Any]] = [] if not opts.no_tools: tools_spec = [ { "name": "mcp_list_tools", "description": "List available MCP tool functions and their parameters.", "input_schema": {"type": "object", "properties": {}}, }, { "name": "mcp_call_tool", "description": "Invoke an MCP tool by name with arguments.", "input_schema": { "type": "object", "properties": { "tool": {"type": "string"}, "args": {"type": "object"} }, "required": ["tool"], }, }, ] messages: List[Dict[str, Any]] = [ {"role": "user", "content": user_prompt} ] step = 0 while True: step += 1 response = client.messages.create( model=opts.model, system=opts.system, messages=messages, tools=tools_spec if not opts.no_tools else None, max_tokens=1024, ) # Print any assistant text assistant_text_chunks: List[str] = [] tool_uses: List[Dict[str, Any]] = [] for block in response.content: b = _block_to_dict(block) if b.get("type") == "text": assistant_text_chunks.append(b.get("text", "")) elif b.get("type") == "tool_use": tool_uses.append(b) if assistant_text_chunks: print("\n".join(assistant_text_chunks)) if not tool_uses or opts.no_tools: break # Prepare tool results as a new user message tool_result_blocks: List[Dict[str, Any]] = [] for tu in tool_uses: name = tu.get("name") tu_id = tu.get("id") input_obj = tu.get("input", {}) or {} result: Any if name == "mcp_list_tools": result = {"tools": _list_tools_description()} elif name == "mcp_call_tool": tool_name = input_obj.get("tool") args = input_obj.get("args", {}) or {} result = _invoke_tool_by_name(tool_name, args) else: result = {"error": f"Unknown tool: {name}"} # Ensure serializable content (string) try: result_text = json.dumps(result) except Exception: result_text = str(result) tool_result_blocks.append({ "type": "tool_result", "tool_use_id": tu_id, "content": result_text, }) # Normalize assistant content to plain dicts assistant_blocks = [_block_to_dict(b) for b in response.content] messages.append({"role": "assistant", "content": assistant_blocks}) messages.append({"role": "user", "content": tool_result_blocks}) if step >= opts.max_steps: break if __name__ == "__main__": # Support multiple modes: # - "serve" or "--serve": run MCP server # - "claude" subcommand: run Claude Messages chat loop # - default: tool invocation CLI if len(sys.argv) > 1: if sys.argv[1] in ("--serve", "serve"): mcp.run() elif sys.argv[1] == "claude": # Shift off the subcommand and re-dispatch sys.argv.pop(1) claude_cli() else: main_cli() else: mcp.run() ```