1 /** FNV(Fowler-Noll-Vo) hash implementation.
2  *
3  * This module conforms to the APIs defined in std.digest.
4  */
5 module nxt.digest.fnv;
6 
7 public import std.digest;
8 
9 /**
10  * Template API FNV-1(a) hash implementation.
11  */
12 struct FNV(ulong bitLength, bool fnv1a = false) {
13 	static if (bitLength == 32) {
14 		alias Element = uint;
15 	}
16 	else static if (bitLength == 64) {
17 		alias Element = ulong;
18 	}
19 	else
20 	{
21 		static assert(0, "Unsupported hash length " ~ bitLength.stringof);
22 	}
23 
24 	pragma(inline, true):
25 
26 	/// Initializes the digest calculation.
27 	void start() pure nothrow @safe @nogc
28 	{
29 		_hash = fnvOffsetBasis;
30 	}
31 
32 	/// Feeds the digest with data.
33 	void put(scope const(ubyte)[] data...) pure nothrow @nogc
34 	{
35 		foreach (immutable ubyte e; data) {
36 			static if (fnv1a) {
37 				_hash ^= e;
38 				_hash *= fnvPrime;
39 			}
40 			else
41 			{
42 				_hash *= fnvPrime;
43 				_hash ^= e;
44 			}
45 		}
46 	}
47 
48 	/// Feeds the digest with `data` being a static array.
49 	void putStaticArray(size_t n)(scope auto ref const(ubyte)[n] data)
50 		pure nothrow @nogc
51 	{
52 		static foreach (i; 0 .. n) // unroll
53 		{
54 			static if (fnv1a) {
55 				_hash ^= data[i];
56 				_hash *= fnvPrime;
57 			}
58 			else
59 			{
60 				_hash *= fnvPrime;
61 				_hash ^= data[i];
62 			}
63 		}
64 	}
65 
66 	/// Returns the finished FNV digest. This also calls start to reset the internal state.
67 	ubyte[bitLength / 8] finish() @trusted pure nothrow @nogc
68 	{
69 		import std.bitmanip : nativeToBigEndian;
70 		_result = _hash;
71 		start();
72 		return nativeToBigEndian(_result);
73 	}
74 
75 	Element get() const
76 	{
77 		return _result;
78 	}
79 
80 private:
81 
82 	// FNV-1 hash parameters
83 	static if (bitLength == 32) {
84 		enum Element fnvPrime = 0x1000193U;
85 		enum Element fnvOffsetBasis = 0x811C9DC5U;
86 	}
87 	else static if (bitLength == 64) {
88 		enum Element fnvPrime = 0x100000001B3UL;
89 		enum Element fnvOffsetBasis = 0xCBF29CE484222325UL;
90 	}
91 	else
92 	{
93 		static assert(0, "Unsupported hash length " ~ bitLength.stringof);
94 	}
95 
96 	Element _hash;
97 	Element _result;
98 }
99 
100 alias FNV32 = FNV!32; /// 32bit FNV-1, hash size is ubyte[4]
101 alias FNV64 = FNV!64; /// 64bit FNV-1, hash size is ubyte[8]
102 alias FNV32A = FNV!(32, true); /// 32bit FNV-1a, hash size is ubyte[4]
103 alias FNV64A = FNV!(64, true); /// 64bit FNV-1a, hash size is ubyte[8]
104 
105 ///
106 unittest {
107 	import std.conv : hexString;
108 	// alias FNV32Digest = WrapperDigest!FNV32; /// OOP API for 32bit FNV-1
109 	// alias FNV64Digest = WrapperDigest!FNV64; /// OOP API for 64bit FNV-1
110 	alias FNV32ADigest = WrapperDigest!FNV32A; /// OOP API for 32bit FNV-1a
111 	// alias FNV64ADigest = WrapperDigest!FNV64A; /// OOP API for 64bit FNV-1a
112 
113 	const immutable(char)[5] hello = "hello";
114 
115 	FNV64 fnv64;
116 	fnv64.start();
117 	fnv64.put(cast(ubyte[])hello[]);
118 	assert(toHexString(fnv64.finish()) == "7B495389BDBDD4C7");
119 
120 	fnv64.putStaticArray(cast(ubyte[5])hello);
121 	assert(toHexString(fnv64.finish()) == "7B495389BDBDD4C7");
122 
123 	// Template API
124 	assert(digest!FNV32("abc") == hexString!"439C2F4B");
125 	assert(digest!FNV64("abc") == hexString!"D8DCCA186BAFADCB");
126 	assert(digest!FNV32A("abc") == hexString!"1A47E90B");
127 	assert(digest!FNV64A("abc") == hexString!"E71FA2190541574B");
128 
129 	// OOP API
130 	Digest fnv = new FNV32ADigest;
131 	ubyte[] d = fnv.digest("1234");
132 	assert(d == hexString!"FDC422FD");
133 }
134 
135 /// Convenience aliases for std.digest.digest using the FNV implementation.
136 auto fnv32Of(T...)(in T data) {
137 	return digest!(FNV32, T)(data);
138 }
139 /// ditto
140 auto fnv64Of(T...)(in T data) {
141 	return digest!(FNV64, T)(data);
142 }
143 /// ditto
144 auto fnv32aOf(T...)(in T data) {
145 	return digest!(FNV32A, T)(data);
146 }
147 /// ditto
148 auto fnv64aOf(T...)(in T data) {
149 	return digest!(FNV64A, T)(data);
150 }
151 
152 ///
153 pure nothrow @safe @nogc unittest {
154 	import std.conv : hexString;
155 	assert(fnv32Of("") == hexString!"811C9DC5");
156 	assert(fnv64Of("") == hexString!"CBF29CE484222325");
157 	assert(fnv32aOf("") == hexString!"811C9DC5");
158 	assert(fnv64aOf("") == hexString!"CBF29CE484222325");
159 }