smats  0.0.1
Satisfability Modulo Arithmetic Theories Symbols
Loading...
Searching...
No Matches
hash.hpp
1
52#pragma once
53
54#include <cmath>
55#include <cstddef>
56#include <cstdint>
57#include <functional>
58#include <iostream>
59#include <map>
60#include <optional>
61#include <set>
62#include <string>
63#include <type_traits>
64#include <utility>
65#include <vector>
66
67#include "smats/util/concepts.h"
68
69namespace smats {
70
78template <InvocableHashAlgorithm HashAlgorithm, Hashable<HashAlgorithm> T>
79void hash_append(HashAlgorithm& hasher, const T& hashable) noexcept {
80 hashable.hash(hasher);
81}
82
90void hash_append(InvocableHashAlgorithm auto& hasher, const std::integral auto& item) noexcept {
91 hasher(std::addressof(item), sizeof(item));
92}
93
101template <class T>
102void hash_append(InvocableHashAlgorithm auto& hasher, const T* item) noexcept {
103 using smats::hash_append;
104 hash_append(hasher, reinterpret_cast<std::uintptr_t>(item));
105};
106
114void hash_append(InvocableHashAlgorithm auto& hasher, const IsEnum auto& item) noexcept {
115 using smats::hash_append;
116 hasher(std::addressof(item), sizeof(item));
117}
118
126template <std::floating_point T>
127void hash_append(InvocableHashAlgorithm auto& hasher, const T& item) noexcept {
128// Hashing a NaN makes no sense, since they cannot compare as equal.
129#ifndef NDEBUG
130 if (std::isnan(item)) {
131 std::cerr << "Hashing a NaN makes no sense, since they cannot compare as equal." << std::endl;
132 std::terminate();
133 }
134#endif
135 // +0.0 and -0.0 are equal, so must hash identically.
136 if (item == 0.0) {
137 const T zero{0.0};
138 hasher(std::addressof(zero), sizeof(zero));
139 } else {
140 hasher(std::addressof(item), sizeof(item));
141 }
142}
143
152template <class Traits, class Allocator>
153void hash_append(InvocableHashAlgorithm auto& hasher, const std::basic_string<char, Traits, Allocator>& item) noexcept {
154 hasher(item.data(), item.size());
155 // All collection types must send their size, after their contents.
156 // See the #hash_append_vector anchor in N3980.
157 hash_append(hasher, item.size());
158}
159
160// Prior to this point, we've defined hashers for primitive types and similar kinds of "singular" types,
161// where the template arguments form a bounded set.
162//
163// Following this point, we'll define hashers for types where the template argument can be anything (e.g., collections).
164// That means for proper namespace lookups we need to declare them all first, and then define them all second.
165
174template <class T1, class T2>
175void hash_append(InvocableHashAlgorithm auto& hasher, const std::pair<T1, T2>& item) noexcept;
176
187template <class T>
188void hash_append(InvocableHashAlgorithm auto& hasher, const std::optional<T>& item) noexcept;
189
202template <class T1, class T2, class Compare, class Allocator>
203void hash_append(InvocableHashAlgorithm auto& hasher, const std::map<T1, T2, Compare, Allocator>& item) noexcept;
204
216template <class Key, class Compare, class Allocator>
217void hash_append(InvocableHashAlgorithm auto& hasher, const std::set<Key, Compare, Allocator>& item) noexcept;
218
219// Now the templates can be defined
220
221template <class T1, class T2>
222void hash_append(InvocableHashAlgorithm auto& hasher, const std::pair<T1, T2>& item) noexcept {
223 using smats::hash_append;
224 hash_append(hasher, item.first);
225 hash_append(hasher, item.second);
226}
227template <class T>
228void hash_append(InvocableHashAlgorithm auto& hasher, const std::optional<T>& item) noexcept {
229 if (item) {
230 hash_append(hasher, *item);
231 }
232 hash_append(hasher, item.has_value());
233};
234
235// NOLINTNEXTLINE(runtime/references) Per hash_append convention.
236// Provides @ref hash_append for a range, as given by two iterators.
237template <class Iter>
238void hash_append_range(InvocableHashAlgorithm auto& hasher, Iter begin, Iter end) noexcept {
239 using smats::hash_append;
240 size_t count{0};
241 for (Iter iter = begin; iter != end; ++iter, ++count) {
242 hash_append(hasher, *iter);
243 }
244 // All collection types must send their size, after their contents.
245 // See the #hash_append_vector anchor in N3980.
246 hash_append(hasher, count);
247}
248template <class T1, class T2, class Compare, class Allocator>
249void hash_append(InvocableHashAlgorithm auto& hasher, const std::map<T1, T2, Compare, Allocator>& item) noexcept {
250 return hash_append_range(hasher, item.begin(), item.end());
251};
252template <class Key, class Compare, class Allocator>
253void hash_append(InvocableHashAlgorithm auto& hasher, const std::set<Key, Compare, Allocator>& item) noexcept {
254 return hash_append_range(hasher, item.begin(), item.end());
255};
256
264template <class HashAlgorithm>
265struct uhash {
266 using result_type = typename HashAlgorithm::result_type;
267
268 template <class T>
269 result_type operator()(const T& item) const noexcept {
270 HashAlgorithm hasher;
271 using smats::hash_append;
272 hash_append(hasher, item);
273 return static_cast<result_type>(hasher);
274 }
275};
276
277namespace hash {
284class FNV1a {
285 public:
286 using result_type = size_t;
287
293 void operator()(const void* data, const size_t length) noexcept {
294 const auto* const begin = static_cast<const uint8_t*>(data);
295 const uint8_t* const end = begin + length;
296 for (const uint8_t* iter = begin; iter < end; ++iter) {
297 hash_ = (hash_ ^ *iter) * k_fnv_prime;
298 }
299 }
300
305 constexpr void add_byte(const uint8_t byte) noexcept { hash_ = (hash_ ^ byte) * k_fnv_prime; }
306
308 explicit constexpr operator size_t() const noexcept { return hash_; }
309
310 private:
311 static_assert(sizeof(result_type) == (64 / 8), "We require a 64-bit size_t");
312 result_type hash_{0xcbf29ce484222325u};
313 static constexpr size_t k_fnv_prime = 1099511628211u;
314};
315} // namespace hash
316
319
326 using Func = std::function<void(const void*, size_t)>;
327 using result_type = typename DefaultHashAlgorithm::result_type;
328
334 explicit DelegatingHasher(Func func) : func_(std::move(func)) {
335 if (!static_cast<bool>(func_)) throw std::runtime_error("The function must be non-empty");
336 }
342 void operator()(const void* data, size_t length) noexcept { func_(data, length); }
349 operator result_type() noexcept { std::terminate(); }
350
351 private:
352 const Func func_;
353};
354
355} // namespace smats
Definition hash.hpp:284
static constexpr size_t k_fnv_prime
FNV-1a prime.
Definition hash.hpp:313
constexpr void add_byte(const uint8_t byte) noexcept
Definition hash.hpp:305
result_type hash_
FNV-1a offset basis.
Definition hash.hpp:312
void operator()(const void *data, const size_t length) noexcept
Definition hash.hpp:293
Definition concepts.h:88
Definition concepts.h:25
Definition hash.hpp:277
ExpressionKind enum.
void hash_append(HashAlgorithm &hasher, const T &hashable) noexcept
Definition hash.hpp:79
Definition gmp.cpp:14
Definition hash.hpp:325
typename DefaultHashAlgorithm::result_type result_type
Necessary to be accepted by hash_append.
Definition hash.hpp:327
const Func func_
Concrete hash algorithm implementation.
Definition hash.hpp:352
DelegatingHasher(Func func)
Definition hash.hpp:334
void operator()(const void *data, size_t length) noexcept
Definition hash.hpp:342
Definition hash.hpp:265