diff --git a/include/iris/hash.hpp b/include/iris/hash.hpp index b8de15b..b3f9240 100644 --- a/include/iris/hash.hpp +++ b/include/iris/hash.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_HASH_HPP +#ifndef IRIS_HASH_HPP #define IRIS_HASH_HPP // SPDX-License-Identifier: MIT @@ -7,7 +7,7 @@ #include #include -#include +#include #include @@ -111,18 +111,50 @@ template<> } // detail +template +[[nodiscard]] constexpr std::size_t hash_combine(std::size_t const seed, T const& v) noexcept; + +template +[[nodiscard]] constexpr std::size_t hash_value(T const& var) noexcept +{ + static_assert(is_hash_enabled_v); + return std::hash{}(var); +} + +template +[[nodiscard]] constexpr std::size_t hash_value(R const& r) + noexcept( + noexcept(++std::ranges::begin(r)) && + noexcept(std::ranges::end(r)) && + std::is_nothrow_copy_assignable_v> + ) +{ + std::size_t seed = 0; + for (auto it = std::ranges::begin(r), se = std::ranges::end(r); it != se; ++it) { + seed = iris::hash_combine(seed, iris::hash_value(*it)); + } + return seed; +} + // https://github.com/boostorg/container_hash/blob/5d8b8ac2b9d9d7cb3818f88fd7e6372e5f072ff5/include/boost/container_hash/hash.hpp#L472C53-L472C63 // https://softwareengineering.stackexchange.com/questions/402542/where-do-magic-hashing-constants-like-0x9e3779b9-and-0x9e3779b1-come-from template [[nodiscard]] constexpr std::size_t hash_combine(std::size_t const seed, T const& v) noexcept { - static_assert(is_hash_enabled_v); return detail::hash_mix( - seed + 0x9e3779b97f4a7c55uz + std::hash{}(v) + seed + 0x9e3779b97f4a7c55uz + iris::hash_value(v) ); } +template +[[nodiscard]] constexpr std::size_t hash_all(T const& first, Ts const&... rest) noexcept +{ + std::size_t seed = iris::hash_value(first); + ((seed = iris::hash_combine(seed, rest)), ...); + return seed; +} + } // iris #endif diff --git a/test/core.cpp b/test/core.cpp index a60b550..a9bb376 100644 --- a/test/core.cpp +++ b/test/core.cpp @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include +#include namespace unit_test { @@ -522,4 +524,24 @@ TEST_CASE("throwf") ); } +TEST_CASE("hash") +{ + static_assert(iris::hash_all(std::vector{}) == 0); + { + int const value = 0; + CHECK(iris::hash_all(value) == iris::hash_value(value)); + CHECK(iris::hash_all(value) == std::hash{}(value)); + } + { + std::vector const vec{1}; + CHECK(iris::hash_all(vec) == iris::hash_combine(0, iris::hash_value(1))); + CHECK(iris::hash_all(vec) == iris::hash_combine(0, std::hash{}(1))); + } + { + std::vector const vec{1, 2}; + CHECK(iris::hash_all(vec) == iris::hash_combine(iris::hash_combine(0, iris::hash_value(1)), iris::hash_value(2))); + CHECK(iris::hash_all(vec) == iris::hash_combine(iris::hash_combine(0, std::hash{}(1)), std::hash{}(2))); + } +} + } // unit_test