1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package org.waarp.compress.zstdsafe;
35
36 import static java.lang.Long.*;
37 import static org.waarp.compress.zstdsafe.UnsafeUtil.getLong;
38 import static org.waarp.compress.zstdsafe.UnsafeUtil.*;
39
40
41 final class XxHash64 {
42 private static final long PRIME64_1 = 0x9E3779B185EBCA87L;
43 private static final long PRIME64_2 = 0xC2B2AE3D27D4EB4FL;
44 private static final long PRIME64_3 = 0x165667B19E3779F9L;
45 private static final long PRIME64_4 = 0x85EBCA77C2b2AE63L;
46 private static final long PRIME64_5 = 0x27D4EB2F165667C5L;
47
48 private XxHash64() {
49 }
50
51 public static long hash(final long seed, final byte[] base, final int address,
52 final int length) {
53 long hash;
54 if (length >= 32) {
55 hash = updateBody(seed, base, address, length);
56 } else {
57 hash = seed + PRIME64_5;
58 }
59
60 hash += length;
61
62
63
64 final int index = length & 0xFFFFFFE0;
65
66 return updateTail(hash, base, address, index, length);
67 }
68
69 private static long updateTail(long hash, final byte[] base,
70 final int address, int index,
71 final int length) {
72 while (index <= length - 8) {
73 hash = updateTail(hash, getLong(base, address + index));
74 index += 8;
75 }
76
77 if (index <= length - 4) {
78 hash = updateTail(hash, getInt(base, address + index));
79 index += 4;
80 }
81
82 while (index < length) {
83 hash = updateTail(hash, base[address + index]);
84 index++;
85 }
86
87 hash = finalShuffle(hash);
88
89 return hash;
90 }
91
92 private static long updateBody(final long seed, final byte[] base,
93 int address, final int length) {
94 long v1 = seed + PRIME64_1 + PRIME64_2;
95 long v2 = seed + PRIME64_2;
96 long v3 = seed;
97 long v4 = seed - PRIME64_1;
98
99 int remaining = length;
100 while (remaining >= 32) {
101 v1 = mix(v1, getLong(base, address));
102 v2 = mix(v2, getLong(base, address + 8));
103 v3 = mix(v3, getLong(base, address + 16));
104 v4 = mix(v4, getLong(base, address + 24));
105
106 address += 32;
107 remaining -= 32;
108 }
109
110 long hash = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) +
111 rotateLeft(v4, 18);
112
113 hash = update(hash, v1);
114 hash = update(hash, v2);
115 hash = update(hash, v3);
116 hash = update(hash, v4);
117
118 return hash;
119 }
120
121 private static long mix(final long current, final long value) {
122 return rotateLeft(current + value * PRIME64_2, 31) * PRIME64_1;
123 }
124
125 private static long update(final long hash, final long value) {
126 final long temp = hash ^ mix(0, value);
127 return temp * PRIME64_1 + PRIME64_4;
128 }
129
130 private static long updateTail(final long hash, final long value) {
131 final long temp = hash ^ mix(0, value);
132 return rotateLeft(temp, 27) * PRIME64_1 + PRIME64_4;
133 }
134
135 private static long updateTail(final long hash, final int value) {
136 final long unsigned = value & 0xFFFFFFFFL;
137 final long temp = hash ^ (unsigned * PRIME64_1);
138 return rotateLeft(temp, 23) * PRIME64_2 + PRIME64_3;
139 }
140
141 private static long updateTail(final long hash, final byte value) {
142 final int unsigned = value & 0xFF;
143 final long temp = hash ^ (unsigned * PRIME64_5);
144 return rotateLeft(temp, 11) * PRIME64_1;
145 }
146
147 private static long finalShuffle(long hash) {
148 hash ^= hash >>> 33;
149 hash *= PRIME64_2;
150 hash ^= hash >>> 29;
151 hash *= PRIME64_3;
152 hash ^= hash >>> 32;
153 return hash;
154 }
155 }