From 9449dfcc6a48493877c81e06ae6116ac05f76385 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 15 Jul 2020 09:27:01 +0200 Subject: [PATCH] :green_heart: add test cases for hash --- include/nlohmann/detail/hash.hpp | 24 ++++++--- single_include/nlohmann/json.hpp | 24 ++++++--- test/CMakeLists.txt | 1 + test/Makefile | 1 + test/src/unit-hash.cpp | 90 ++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 test/src/unit-hash.cpp diff --git a/include/nlohmann/detail/hash.hpp b/include/nlohmann/detail/hash.hpp index 404a2bd3c..a4aaae4a3 100644 --- a/include/nlohmann/detail/hash.hpp +++ b/include/nlohmann/detail/hash.hpp @@ -8,7 +8,7 @@ namespace nlohmann namespace detail { -std::size_t combine(std::size_t seed, std::size_t h) +std::size_t combine(std::size_t seed, std::size_t h) noexcept { seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); return seed; @@ -17,6 +17,11 @@ std::size_t combine(std::size_t seed, std::size_t h) template std::size_t hash(const BasicJsonType& j) { + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + switch (j.type()) { case BasicJsonType::value_t::null: @@ -28,7 +33,7 @@ std::size_t hash(const BasicJsonType& j) auto seed = combine(static_cast(j.type()), j.size()); for (const auto& element : j.items()) { - const auto h = std::hash {}(element.key()); + const auto h = std::hash {}(element.key()); seed = combine(seed, h); seed = combine(seed, hash(element.value())); } @@ -47,7 +52,7 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::string: { - const auto h = std::hash {}(j.template get_ref()); + const auto h = std::hash {}(j.template get_ref()); return combine(static_cast(j.type()), h); } @@ -59,25 +64,27 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::number_integer: { - const auto h = std::hash {}(j.template get()); + 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()); + 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()); + 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()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); seed = combine(seed, j.get_binary().subtype()); for (const auto byte : j.get_binary()) { @@ -85,9 +92,10 @@ std::size_t hash(const BasicJsonType& j) } return seed; } - } - return 0; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } } } // namespace detail diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6a7a9ff41..f5a6d8fea 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4453,7 +4453,7 @@ namespace nlohmann namespace detail { -std::size_t combine(std::size_t seed, std::size_t h) +std::size_t combine(std::size_t seed, std::size_t h) noexcept { seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); return seed; @@ -4462,6 +4462,11 @@ std::size_t combine(std::size_t seed, std::size_t h) template std::size_t hash(const BasicJsonType& j) { + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + switch (j.type()) { case BasicJsonType::value_t::null: @@ -4473,7 +4478,7 @@ std::size_t hash(const BasicJsonType& j) auto seed = combine(static_cast(j.type()), j.size()); for (const auto& element : j.items()) { - const auto h = std::hash {}(element.key()); + const auto h = std::hash {}(element.key()); seed = combine(seed, h); seed = combine(seed, hash(element.value())); } @@ -4492,7 +4497,7 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::string: { - const auto h = std::hash {}(j.template get_ref()); + const auto h = std::hash {}(j.template get_ref()); return combine(static_cast(j.type()), h); } @@ -4504,25 +4509,27 @@ std::size_t hash(const BasicJsonType& j) case BasicJsonType::value_t::number_integer: { - const auto h = std::hash {}(j.template get()); + 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()); + 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()); + 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()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); seed = combine(seed, j.get_binary().subtype()); for (const auto byte : j.get_binary()) { @@ -4530,9 +4537,10 @@ std::size_t hash(const BasicJsonType& j) } return seed; } - } - return 0; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } } } // namespace detail diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9146d43b7..91ff5a21c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -112,6 +112,7 @@ set(files src/unit-deserialization.cpp src/unit-element_access1.cpp src/unit-element_access2.cpp + src/unit-hash.cpp src/unit-inspection.cpp src/unit-items.cpp src/unit-iterators1.cpp diff --git a/test/Makefile b/test/Makefile index bcf8de7de..b01fa8720 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,6 +10,7 @@ SOURCES = src/unit.cpp \ src/unit-algorithms.cpp \ src/unit-allocator.cpp \ src/unit-alt-string.cpp \ + src/unit-assert_macro.cpp \ src/unit-bson.cpp \ src/unit-capacity.cpp \ src/unit-cbor.cpp \ diff --git a/test/src/unit-hash.cpp b/test/src/unit-hash.cpp new file mode 100644 index 000000000..f80a65966 --- /dev/null +++ b/test/src/unit-hash.cpp @@ -0,0 +1,90 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.8.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#include +using json = nlohmann::json; + +TEST_CASE("hash") +{ + SECTION("null") + { + CHECK(std::hash {}(json(nullptr)) == 2654435769U); + } + + SECTION("boolean") + { + CHECK(std::hash {}(json(true)) == 2654436031U); + CHECK(std::hash {}(json(false)) == 2654436030U); + } + + SECTION("string") + { + CHECK(std::hash {}(json("")) == 11160318156688833227U); + CHECK(std::hash {}(json("foo")) == 910203211069189493U); + } + + SECTION("number") + { + CHECK(std::hash {}(json(int(0))) == 2654436095U); + CHECK(std::hash {}(json(unsigned(0))) == 2654436156U); + + CHECK(std::hash {}(json(-1)) == 2654436092U); + CHECK(std::hash {}(json(0.0)) == 2654436221U); + CHECK(std::hash {}(json(42.23)) == 4631140164097181104U); + } + + SECTION("array") + { + CHECK(std::hash {}(json::array()) == 2654435899U); + CHECK(std::hash {}(json::array({1, 2, 3})) == 717272658337467U); + } + + SECTION("object") + { + CHECK(std::hash {}(json::object()) == 2654435832U); + CHECK(std::hash {}(json::object({{"foo", "bar"}})) == 4042265434648078139U); + } + + SECTION("binary") + { + CHECK(std::hash {}(json::binary({})) == 11093832941624U); + CHECK(std::hash {}(json::binary({}, 0)) == 11093832941691U); + CHECK(std::hash {}(json::binary({}, 42)) == 11093832941581U); + CHECK(std::hash {}(json::binary({1, 2, 3})) == 3005324138949694928U); + CHECK(std::hash {}(json::binary({1, 2, 3}, 0)) == 3005324138988516582U); + CHECK(std::hash {}(json::binary({1, 2, 3}, 42)) == 3005324138986241627U); + } + + SECTION("discarded") + { + CHECK(std::hash {}(json(json::value_t::discarded)) == 2654436338U); + } +} \ No newline at end of file