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