summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2018-03-19 16:43:57 +0100
committerEugen Wissner <belka@caraus.de>2018-03-27 05:09:09 +0200
commit0d6d8f6a9111ea54b8c148135f69a060ddcb534d (patch)
tree42fd945ea9464550bad64c113b9dd5f84d86de90 /source
parentcefc4e24b5c7d0c05617cd13a51202e07ae06f9f (diff)
downloadtanya-0d6d8f6a9111ea54b8c148135f69a060ddcb534d.tar.gz
Add hash combining for ranges
Diffstat (limited to 'source')
-rw-r--r--source/tanya/hash/lookup.d150
1 files changed, 122 insertions, 28 deletions
diff --git a/source/tanya/hash/lookup.d b/source/tanya/hash/lookup.d
index 82c686a..643601e 100644
--- a/source/tanya/hash/lookup.d
+++ b/source/tanya/hash/lookup.d
@@ -15,59 +15,83 @@
module tanya.hash.lookup;
import tanya.meta.trait;
+import tanya.range.primitive;
-/**
- * FNV-1a (Fowler-Noll-Vo) hash function.
- *
- * See_Also: $(LINK http://www.isthe.com/chongo/tech/comp/fnv/).
- */
-size_t fnv(const ubyte[] key) @nogc nothrow pure @safe
+private struct FNV
{
static if (size_t.sizeof == 4)
{
enum uint offsetBasis = 2166136261;
- enum uint fnvPrime = 16777619;
+ enum uint prime = 16777619;
}
else static if (size_t.sizeof == 8)
{
enum ulong offsetBasis = 14695981039346656037UL;
- enum ulong fnvPrime = 1099511628211UL;
+ enum ulong prime = 1099511628211UL;
}
else
{
static assert(false, "FNV requires at least 32-bit hash length");
}
- size_t h = offsetBasis;
+ size_t hash = offsetBasis;
- foreach (c; key)
+ void opCall(T)(auto ref T key)
{
- h = (h ^ c) * fnvPrime;
+ static if (isScalarType!T)
+ {
+ (() @trusted => add(cast(const ubyte[]) (&key)[0 .. T.sizeof]))();
+ }
+ else static if (isArray!T && isScalarType!(ElementType!T))
+ {
+ add(cast(const ubyte[]) key);
+ }
+ else static if (is(T == typeof(null)))
+ {
+ add(key);
+ }
+ else static if (is(typeof(key.toHash()) == size_t))
+ {
+ opCall(key.toHash()); // Combine user-defined hashes
+ }
+ else static if (isInputRange!T && !isInfinite!T)
+ {
+ foreach (e; key)
+ {
+ opCall(e);
+ }
+ }
+ else
+ {
+ static assert(false, "Hash function is not available");
+ }
}
- return h;
-}
-
-static if (size_t.sizeof == 4) @nogc nothrow pure @safe unittest
-{
- assert(fnv(null) == 2166136261U);
-}
-
-static if (size_t.sizeof == 8) @nogc nothrow pure @safe unittest
-{
- assert(fnv(null) == 14695981039346656037UL);
+ void add(const ubyte[] key) @nogc nothrow pure @safe
+ {
+ foreach (c; key)
+ {
+ this.hash = (this.hash ^ c) * prime;
+ }
+ }
}
-size_t hash(T)(auto ref const T key)
+/**
+ * FNV-1a (Fowler-Noll-Vo) hash function.
+ *
+ * See_Also: $(LINK http://www.isthe.com/chongo/tech/comp/fnv/).
+ */
+size_t hash(T)(auto ref T key)
{
- static if (isScalarType!T)
+ static if (is(typeof(key.toHash()) == size_t))
{
- return (() @trusted => fnv(cast(const ubyte[]) (&key)[0 .. T.sizeof]))();
+ return key.toHash();
}
- else static if (isSomeString!T
- || (isArray && isScalarType!(ElementType!T)))
+ else
{
- return fnv(cast(const ubyte[]) key);
+ FNV fnv;
+ fnv(key);
+ return fnv.hash;
}
}
@@ -77,8 +101,78 @@ version (unittest)
enum string r100(string x) = r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x
~ r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x;
enum string r500(string x) = r100!x ~ r100!x ~ r100!x ~ r100!x ~ r100!x;
+
+ private struct ToHash
+ {
+ size_t toHash() const @nogc nothrow pure @safe
+ {
+ return 0;
+ }
+ }
+
+ private struct HashRange
+ {
+ string fo = "fo";
+
+ @property ubyte front() const @nogc nothrow pure @safe
+ {
+ return this.fo[0];
+ }
+
+ void popFront() @nogc nothrow pure @safe
+ {
+ this.fo = this.fo[1 .. $];
+ }
+
+ @property bool empty() const @nogc nothrow pure @safe
+ {
+ return this.fo.length == 0;
+ }
+ }
+
+ private struct ToHashRange
+ {
+ bool empty_;
+
+ @property ToHash front() const @nogc nothrow pure @safe
+ {
+ return ToHash();
+ }
+
+ void popFront() @nogc nothrow pure @safe
+ {
+ this.empty_ = true;
+ }
+
+ @property bool empty() const @nogc nothrow pure @safe
+ {
+ return this.empty_;
+ }
+ }
}
+// Tests that work for any hash size
+@nogc nothrow pure @safe unittest
+{
+ assert(hash(null) == FNV.offsetBasis);
+ assert(hash(ToHash()) == 0U);
+}
+
+static if (size_t.sizeof == 4) @nogc nothrow pure @safe unittest
+{
+ assert(hash('a') == 0xe40c292cU);
+ assert(hash(HashRange()) == 0x6222e842U);
+}
+
+static if (size_t.sizeof == 8) @nogc nothrow pure @safe unittest
+{
+ assert(hash('a') == 0xaf63dc4c8601ec8cUL);
+ assert(hash(HashRange()) == 0x08985907b541d342UL);
+}
+
+/*
+ * These are official FNV-1a test vectors and they are in the public domain.
+ */
// FNV-1a 32 bit test vectors
static if (size_t.sizeof == 4) @nogc nothrow pure @safe unittest
{