diff --git a/include/nlohmann/detail/hash.hpp b/include/nlohmann/detail/hash.hpp index a4aaae4a3..d3313e968 100644 --- a/include/nlohmann/detail/hash.hpp +++ b/include/nlohmann/detail/hash.hpp @@ -1,6 +1,6 @@ #pragma once -#include // size_t +#include // size_t, uint8_t #include // hash namespace nlohmann @@ -8,12 +8,24 @@ namespace nlohmann namespace detail { +// boost::hash_combine std::size_t combine(std::size_t seed, std::size_t h) noexcept { seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); return seed; } +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ template std::size_t hash(const BasicJsonType& j) { @@ -22,15 +34,18 @@ std::size_t hash(const BasicJsonType& j) using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + const auto type = static_cast(j.type()); switch (j.type()) { case BasicJsonType::value_t::null: case BasicJsonType::value_t::discarded: - return combine(static_cast(j.type()), 0); + { + return combine(type, 0); + } case BasicJsonType::value_t::object: { - auto seed = combine(static_cast(j.type()), j.size()); + auto seed = combine(type, j.size()); for (const auto& element : j.items()) { const auto h = std::hash {}(element.key()); @@ -42,7 +57,7 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::array: { - auto seed = combine(static_cast(j.type()), j.size()); + auto seed = combine(type, j.size()); for (const auto& element : j) { seed = combine(seed, hash(element)); @@ -53,36 +68,36 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::string: { const auto h = std::hash {}(j.template get_ref()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case BasicJsonType::value_t::boolean: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case BasicJsonType::value_t::number_integer: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::number_unsigned: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::number_float: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::binary: { - auto seed = combine(static_cast(j.type()), j.get_binary().size()); + auto seed = combine(type, j.get_binary().size()); const auto h = std::hash {}(j.get_binary().has_subtype()); seed = combine(seed, h); seed = combine(seed, j.get_binary().subtype()); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f5a6d8fea..5e32f0b6b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4445,7 +4445,7 @@ class byte_container_with_subtype : public BinaryType // #include -#include // size_t +#include // size_t, uint8_t #include // hash namespace nlohmann @@ -4453,12 +4453,24 @@ namespace nlohmann namespace detail { +// boost::hash_combine std::size_t combine(std::size_t seed, std::size_t h) noexcept { seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); return seed; } +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ template std::size_t hash(const BasicJsonType& j) { @@ -4467,15 +4479,18 @@ std::size_t hash(const BasicJsonType& j) using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + const auto type = static_cast(j.type()); switch (j.type()) { case BasicJsonType::value_t::null: case BasicJsonType::value_t::discarded: - return combine(static_cast(j.type()), 0); + { + return combine(type, 0); + } case BasicJsonType::value_t::object: { - auto seed = combine(static_cast(j.type()), j.size()); + auto seed = combine(type, j.size()); for (const auto& element : j.items()) { const auto h = std::hash {}(element.key()); @@ -4487,7 +4502,7 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::array: { - auto seed = combine(static_cast(j.type()), j.size()); + auto seed = combine(type, j.size()); for (const auto& element : j) { seed = combine(seed, hash(element)); @@ -4498,36 +4513,36 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::string: { const auto h = std::hash {}(j.template get_ref()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case BasicJsonType::value_t::boolean: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case BasicJsonType::value_t::number_integer: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::number_unsigned: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::number_float: { const auto h = std::hash {}(j.template get()); - return combine(static_cast(j.type()), h); + return combine(type, h); } case nlohmann::detail::value_t::binary: { - auto seed = combine(static_cast(j.type()), j.get_binary().size()); + auto seed = combine(type, j.get_binary().size()); const auto h = std::hash {}(j.get_binary().has_subtype()); seed = combine(seed, h); seed = combine(seed, j.get_binary().subtype()); diff --git a/test/src/unit-hash.cpp b/test/src/unit-hash.cpp index fddaceb98..186cad884 100644 --- a/test/src/unit-hash.cpp +++ b/test/src/unit-hash.cpp @@ -54,7 +54,7 @@ TEST_CASE("hash") hashes.insert(std::hash {}(json("foo"))); // number - hashes.insert(std::hash {}(json(int(0)))); + hashes.insert(std::hash {}(json(0))); hashes.insert(std::hash {}(json(unsigned(0)))); hashes.insert(std::hash {}(json(-1))); @@ -81,4 +81,4 @@ TEST_CASE("hash") hashes.insert(std::hash {}(json(json::value_t::discarded))); CHECK(hashes.size() == 21); -} \ No newline at end of file +}