Skip to content

Commit 325aec9

Browse files
authored
Merge pull request #41 from iris-cpp/hash-range
Add `hash_value` and `hash_all`
2 parents fc42765 + 5184d68 commit 325aec9

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

include/iris/hash.hpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#ifndef IRIS_HASH_HPP
1+
#ifndef IRIS_HASH_HPP
22
#define IRIS_HASH_HPP
33

44
// SPDX-License-Identifier: MIT
@@ -7,7 +7,7 @@
77
#include <iris/type_traits.hpp>
88

99
#include <functional>
10-
#include <bit>
10+
#include <ranges>
1111

1212
#include <cstddef>
1313

@@ -111,18 +111,50 @@ template<>
111111

112112
} // detail
113113

114+
template<class T>
115+
[[nodiscard]] constexpr std::size_t hash_combine(std::size_t const seed, T const& v) noexcept;
116+
117+
template<class T>
118+
[[nodiscard]] constexpr std::size_t hash_value(T const& var) noexcept
119+
{
120+
static_assert(is_hash_enabled_v<T>);
121+
return std::hash<T>{}(var);
122+
}
123+
124+
template<std::ranges::input_range R>
125+
[[nodiscard]] constexpr std::size_t hash_value(R const& r)
126+
noexcept(
127+
noexcept(++std::ranges::begin(r)) &&
128+
noexcept(std::ranges::end(r)) &&
129+
std::is_nothrow_copy_assignable_v<std::ranges::iterator_t<R>>
130+
)
131+
{
132+
std::size_t seed = 0;
133+
for (auto it = std::ranges::begin(r), se = std::ranges::end(r); it != se; ++it) {
134+
seed = iris::hash_combine(seed, iris::hash_value(*it));
135+
}
136+
return seed;
137+
}
138+
114139
// https://github.com/boostorg/container_hash/blob/5d8b8ac2b9d9d7cb3818f88fd7e6372e5f072ff5/include/boost/container_hash/hash.hpp#L472C53-L472C63
115140
// https://softwareengineering.stackexchange.com/questions/402542/where-do-magic-hashing-constants-like-0x9e3779b9-and-0x9e3779b1-come-from
116141

117142
template<class T>
118143
[[nodiscard]] constexpr std::size_t hash_combine(std::size_t const seed, T const& v) noexcept
119144
{
120-
static_assert(is_hash_enabled_v<T>);
121145
return detail::hash_mix<sizeof(std::size_t)>(
122-
seed + 0x9e3779b97f4a7c55uz + std::hash<T>{}(v)
146+
seed + 0x9e3779b97f4a7c55uz + iris::hash_value(v)
123147
);
124148
}
125149

150+
template<class T, class... Ts>
151+
[[nodiscard]] constexpr std::size_t hash_all(T const& first, Ts const&... rest) noexcept
152+
{
153+
std::size_t seed = iris::hash_value(first);
154+
((seed = iris::hash_combine(seed, rest)), ...);
155+
return seed;
156+
}
157+
126158
} // iris
127159

128160
#endif

test/core.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
#include <iris/compare.hpp>
88
#include <iris/fixed_string.hpp>
99
#include <iris/exception.hpp>
10+
#include <iris/hash.hpp>
1011

1112
#include <concepts>
1213
#include <utility>
1314
#include <type_traits>
1415
#include <ranges>
16+
#include <vector>
1517

1618
namespace unit_test {
1719

@@ -522,4 +524,24 @@ TEST_CASE("throwf")
522524
);
523525
}
524526

527+
TEST_CASE("hash")
528+
{
529+
static_assert(iris::hash_all(std::vector<int>{}) == 0);
530+
{
531+
int const value = 0;
532+
CHECK(iris::hash_all(value) == iris::hash_value(value));
533+
CHECK(iris::hash_all(value) == std::hash<int>{}(value));
534+
}
535+
{
536+
std::vector<int> const vec{1};
537+
CHECK(iris::hash_all(vec) == iris::hash_combine(0, iris::hash_value(1)));
538+
CHECK(iris::hash_all(vec) == iris::hash_combine(0, std::hash<int>{}(1)));
539+
}
540+
{
541+
std::vector<int> const vec{1, 2};
542+
CHECK(iris::hash_all(vec) == iris::hash_combine(iris::hash_combine(0, iris::hash_value(1)), iris::hash_value(2)));
543+
CHECK(iris::hash_all(vec) == iris::hash_combine(iris::hash_combine(0, std::hash<int>{}(1)), std::hash<int>{}(2)));
544+
}
545+
}
546+
525547
} // unit_test

0 commit comments

Comments
 (0)