summaryrefslogtreecommitdiff
path: root/asl/hashing/hash.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'asl/hashing/hash.hpp')
-rw-r--r--asl/hashing/hash.hpp138
1 files changed, 138 insertions, 0 deletions
diff --git a/asl/hashing/hash.hpp b/asl/hashing/hash.hpp
new file mode 100644
index 0000000..60550d0
--- /dev/null
+++ b/asl/hashing/hash.hpp
@@ -0,0 +1,138 @@
+#pragma once
+
+#include "asl/base/integers.hpp"
+#include "asl/base/meta.hpp"
+#include "asl/types/span.hpp"
+#include "asl/base/utility.hpp"
+
+namespace asl::city_hash
+{
+
+// Hash function for a byte array.
+uint64_t CityHash64(const char *s, size_t len);
+
+// Hash function for a byte array. For convenience, a 64-bit seed is also
+// hashed into the result.
+uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed);
+
+// Hash function for a byte array. For convenience, two seeds are also
+// hashed into the result.
+uint64_t CityHash64WithSeeds(const char *s, size_t len,
+ uint64_t seed0, uint64_t seed1);
+
+// Hash function for a byte array.
+uint128_t CityHash128(const char *s, size_t len);
+
+// Hash function for a byte array. For convenience, a 128-bit seed is also
+// hashed into the result.
+uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed);
+
+// Hash function for a byte array. Most useful in 32-bit binaries.
+uint32_t CityHash32(const char *s, size_t len);
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+constexpr uint64_t Hash128to64(uint64_t high, uint64_t low)
+{
+ // Murmur-inspired hashing.
+ const uint64_t kMul = 0x9ddfea08eb382d69ULL;
+ uint64_t a = (low ^ high) * kMul;
+ a ^= (a >> 47);
+ uint64_t b = (high ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+constexpr uint64_t Hash128to64(const uint128_t& x)
+{
+ return Hash128to64(x.high, x.low);
+}
+
+} // namespace asl::city_hash
+
+namespace asl
+{
+
+template<typename T, typename H>
+concept hashable_generic = requires(const T& value, H h)
+{
+ { AslHashValue(h, value) } -> same_as<H>;
+};
+
+struct HashState
+{
+ uint128_t state{};
+
+ constexpr HashState() = default;
+ explicit constexpr HashState(uint128_t s) : state{s} {}
+
+ template<typename T>
+ static HashState combine_contiguous(HashState h, span<const T> s)
+ {
+ if constexpr (uniquely_represented<T>)
+ {
+ auto bytes = as_bytes(s);
+ auto hashed = city_hash::CityHash128WithSeed(
+ reinterpret_cast<const char*>(bytes.data()),
+ static_cast<size_t>(bytes.size()),
+ h.state);
+ return HashState{hashed};
+ }
+ else
+ {
+ for (const auto& value: s)
+ {
+ h = AslHashValue(ASL_MOVE(h), value);
+ }
+ return h;
+ }
+ }
+
+ static constexpr HashState combine(HashState h)
+ {
+ return h;
+ }
+
+ template<hashable_generic<HashState> Arg, hashable_generic<HashState>... Remaining>
+ static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining)
+ {
+ return combine(AslHashValue(ASL_MOVE(h), arg), remaining...);
+ }
+};
+
+template<typename T>
+concept hashable = hashable_generic<T, HashState>;
+
+template<typename H, uniquely_represented T>
+constexpr H AslHashValue(H h, const T& value)
+{
+ return H::combine_contiguous(ASL_MOVE(h), span<const T>{&value, 1});
+}
+
+template<typename H>
+constexpr H AslHashValue(H h, bool value)
+{
+ return AslHashValue(ASL_MOVE(h), value ? 1 : 0);
+}
+
+template<typename H, typename T>
+constexpr void AslHashValue(H h, T*); // Don't hash pointers
+
+template<typename H, hashable T>
+constexpr H AslHashValue(H h, const span<T>& s)
+{
+ return H::combine_contiguous(ASL_MOVE(h), span<const T>{s.data(), s.size()});
+}
+
+template<hashable T>
+constexpr uint64_t hash_value(const T& value)
+{
+ auto result = AslHashValue(HashState{}, value).state;
+ return city_hash::Hash128to64(result);
+}
+
+} // namespace asl
+