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 // 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 +std::size_t hash(const BasicJsonType& j) +{ + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::discarded: + return combine(static_cast(j.type()), 0); + + case BasicJsonType::value_t::object: + { + auto seed = combine(static_cast(j.type()), j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(static_cast(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 {}(j.template get_ref()); + return combine(static_cast(j.type()), h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(static_cast(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 {}(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 #include #include +#include #include #include #include @@ -8698,9 +8699,7 @@ struct hash */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash(); - 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 +// #include + + +#include // 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 +std::size_t hash(const BasicJsonType& j) +{ + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::discarded: + return combine(static_cast(j.type()), 0); + + case BasicJsonType::value_t::object: + { + auto seed = combine(static_cast(j.type()), j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(static_cast(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 {}(j.template get_ref()); + return combine(static_cast(j.type()), h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(static_cast(j.type()), h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(static_cast(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 {}(byte)); + } + return seed; + } + } + + return 0; +} + +} // namespace detail +} // namespace nlohmann + // #include @@ -24686,9 +24781,7 @@ struct hash */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash(); - return h(j.dump()); + return nlohmann::detail::hash(j); } };