diff --git a/include/nlohmann/detail/hash.hpp b/include/nlohmann/detail/hash.hpp new file mode 100644 index 00000000..b0be7fb2 --- /dev/null +++ b/include/nlohmann/detail/hash.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include <functional> // hash + +namespace nlohmann +{ +namespace detail +{ + +std::size_t combine(std::size_t seed, std::size_t h) +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +template<typename BasicJsonType> +std::size_t hash(const BasicJsonType& j) +{ + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::discarded: + return combine(static_cast<std::size_t>(j.type()), 0); + + case BasicJsonType::value_t::object: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash<typename BasicJsonType::string_t> {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash<typename BasicJsonType::string_t> {}(j.template get_ref<const typename BasicJsonType::string_t&>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash<bool> {}(j.template get<bool>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash<typename BasicJsonType::number_integer_t> {}(j.template get<typename BasicJsonType::number_integer_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash<typename BasicJsonType::number_unsigned_t> {}(j.template get<typename BasicJsonType::number_unsigned_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash<typename BasicJsonType::number_float_t> {}(j.template get<typename BasicJsonType::number_float_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.get_binary().size()); + seed = combine(seed, j.get_binary().subtype()); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash<std::uint8_t> {}(byte)); + } + return seed; + } + } + + return 0; +} + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 7c2cf2b1..1a45b776 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -51,6 +51,7 @@ SOFTWARE. #include <nlohmann/detail/conversions/from_json.hpp> #include <nlohmann/detail/conversions/to_json.hpp> #include <nlohmann/detail/exceptions.hpp> +#include <nlohmann/detail/hash.hpp> #include <nlohmann/detail/input/binary_reader.hpp> #include <nlohmann/detail/input/input_adapters.hpp> #include <nlohmann/detail/input/lexer.hpp> @@ -8698,9 +8699,7 @@ struct hash<nlohmann::json> */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash<nlohmann::json::string_t>(); - return h(j.dump()); + return nlohmann::detail::hash(j); } }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 82c0d484..9a9b3a2f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4442,6 +4442,101 @@ class byte_container_with_subtype : public BinaryType // #include <nlohmann/detail/exceptions.hpp> +// #include <nlohmann/detail/hash.hpp> + + +#include <functional> // hash + +namespace nlohmann +{ +namespace detail +{ + +std::size_t combine(std::size_t seed, std::size_t h) +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +template<typename BasicJsonType> +std::size_t hash(const BasicJsonType& j) +{ + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::discarded: + return combine(static_cast<std::size_t>(j.type()), 0); + + case BasicJsonType::value_t::object: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash<typename BasicJsonType::string_t> {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash<typename BasicJsonType::string_t> {}(j.template get_ref<const typename BasicJsonType::string_t&>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash<bool> {}(j.template get<bool>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash<typename BasicJsonType::number_integer_t> {}(j.template get<typename BasicJsonType::number_integer_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash<typename BasicJsonType::number_unsigned_t> {}(j.template get<typename BasicJsonType::number_unsigned_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash<typename BasicJsonType::number_float_t> {}(j.template get<typename BasicJsonType::number_float_t>()); + return combine(static_cast<std::size_t>(j.type()), h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(static_cast<std::size_t>(j.type()), j.get_binary().size()); + seed = combine(seed, j.get_binary().subtype()); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash<std::uint8_t> {}(byte)); + } + return seed; + } + } + + return 0; +} + +} // namespace detail +} // namespace nlohmann + // #include <nlohmann/detail/input/binary_reader.hpp> @@ -24686,9 +24781,7 @@ struct hash<nlohmann::json> */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash<nlohmann::json::string_t>(); - return h(j.dump()); + return nlohmann::detail::hash(j); } };