• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Higher Frequency Trading http://www.higherfrequencytrading.com
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  * Forked from zero-allocation-hashing-0.14 (https://github.com/OpenHFT/Zero-Allocation-Hashing).
18  * Modified by the gRPC Authors
19  */
20 
21 package io.grpc.xds;
22 
23 import static com.google.common.base.Preconditions.checkArgument;
24 import static com.google.common.base.Preconditions.checkNotNull;
25 
26 import java.nio.ByteOrder;
27 
28 /**
29  * The XxHash is a fast, non-cryptographic, 64-bit hash function that has excellent avalanche
30  * and 2-way bit independence properties.
31  *
32  * <p>This implementation is a simplified version adapted from
33  * <a href="https://github.com/OpenHFT/Zero-Allocation-Hashing/blob/master/src/main/java/net/openhft/hashing/XxHash.java">
34  * OpenHFT/Zero-Allocation-Hashing</a>.
35  */
36 final class XxHash64 {
37   static final XxHash64 INSTANCE = new XxHash64(0);
38 
39   // Primes if treated as unsigned
40   private static final long P1 = -7046029288634856825L;
41   private static final long P2 = -4417276706812531889L;
42   private static final long P3 = 1609587929392839161L;
43   private static final long P4 = -8796714831421723037L;
44   private static final long P5 = 2870177450012600261L;
45 
46   private static final ByteOrder byteOrder = ByteOrder.nativeOrder();
47   private final long seed;
48   private final long voidHash;
49 
XxHash64(long seed)50   XxHash64(long seed) {
51     this.seed = seed;
52     this.voidHash = finalize(seed + P5);
53   }
54 
hashLong(long input)55   long hashLong(long input) {
56     input = byteOrder == ByteOrder.LITTLE_ENDIAN ? input : Long.reverseBytes(input);
57     long hash = seed + P5 + 8;
58     input *= P2;
59     input = Long.rotateLeft(input, 31);
60     input *= P1;
61     hash ^= input;
62     hash = Long.rotateLeft(hash, 27) * P1 + P4;
63     return finalize(hash);
64   }
65 
hashInt(int input)66   long hashInt(int input) {
67     input = byteOrder == ByteOrder.LITTLE_ENDIAN ? input : Integer.reverseBytes(input);
68     long hash = seed + P5 + 4;
69     hash ^= (input & 0xFFFFFFFFL) * P1;
70     hash = Long.rotateLeft(hash, 23) * P2 + P3;
71     return finalize(hash);
72   }
73 
hashShort(short input)74   long hashShort(short input) {
75     input = byteOrder == ByteOrder.LITTLE_ENDIAN ? input : Short.reverseBytes(input);
76     long hash = seed + P5 + 2;
77     hash ^= (input & 0xFFL) * P5;
78     hash = Long.rotateLeft(hash, 11) * P1;
79     hash ^= ((input >> 8) & 0xFFL) * P5;
80     hash = Long.rotateLeft(hash, 11) * P1;
81     return finalize(hash);
82   }
83 
hashChar(char input)84   long hashChar(char input) {
85     return hashShort((short) input);
86   }
87 
hashByte(byte input)88   long hashByte(byte input) {
89     long hash = seed + P5 + 1;
90     hash ^= (input & 0xFF) * P5;
91     hash = Long.rotateLeft(hash, 11) * P1;
92     return finalize(hash);
93   }
94 
hashVoid()95   long hashVoid() {
96     return voidHash;
97   }
98 
hashAsciiString(String input)99   long hashAsciiString(String input) {
100     ByteSupplier supplier = new AsciiStringByteSupplier(input);
101     return hashBytes(supplier);
102   }
103 
hashBytes(byte[] bytes)104   long hashBytes(byte[] bytes) {
105     ByteSupplier supplier = new PlainByteSupplier(bytes);
106     return hashBytes(supplier);
107   }
108 
hashBytes(byte[] bytes, int offset, int len)109   long hashBytes(byte[] bytes, int offset, int len) {
110     ByteSupplier supplier = new PlainByteSupplier(bytes, offset, len);
111     return hashBytes(supplier);
112   }
113 
hashBytes(ByteSupplier supplier)114   private long hashBytes(ByteSupplier supplier) {
115     long hash;
116     if (supplier.remaining() >= 32) {
117       long v1 = seed + P1 + P2;
118       long v2 = seed + P2;
119       long v3 = seed;
120       long v4 = seed - P1;
121 
122       do {
123         v1 += supplier.next64() * P2;
124         v1 = Long.rotateLeft(v1, 31);
125         v1 *= P1;
126 
127         v2 += supplier.next64() * P2;
128         v2 = Long.rotateLeft(v2, 31);
129         v2 *= P1;
130 
131         v3 += supplier.next64() * P2;
132         v3 = Long.rotateLeft(v3, 31);
133         v3 *= P1;
134 
135         v4 += supplier.next64() * P2;
136         v4 = Long.rotateLeft(v4, 31);
137         v4 *= P1;
138       } while (supplier.remaining() >= 32);
139 
140       hash = Long.rotateLeft(v1, 1)
141           + Long.rotateLeft(v2, 7)
142           + Long.rotateLeft(v3, 12)
143           + Long.rotateLeft(v4, 18);
144 
145       v1 *= P2;
146       v1 = Long.rotateLeft(v1, 31);
147       v1 *= P1;
148       hash ^= v1;
149       hash = (hash * P1) + P4;
150 
151       v2 *= P2;
152       v2 = Long.rotateLeft(v2, 31);
153       v2 *= P1;
154       hash ^= v2;
155       hash = (hash * P1) + P4;
156 
157       v3 *= P2;
158       v3 = Long.rotateLeft(v3, 31);
159       v3 *= P1;
160       hash ^= v3;
161       hash = (hash * P1) + P4;
162 
163       v4 *= P2;
164       v4 = Long.rotateLeft(v4, 31);
165       v4 *= P1;
166       hash ^= v4;
167       hash = (hash * P1) + P4;
168     } else {
169       hash = seed + P5;
170     }
171 
172     hash += supplier.bytes();
173 
174     while (supplier.remaining() >= 8) {
175       long k1 = supplier.next64();
176       k1 *= P2;
177       k1 = Long.rotateLeft(k1, 31);
178       k1 *= P1;
179       hash ^= k1;
180       hash = (Long.rotateLeft(hash, 27) * P1) + P4;
181     }
182 
183     if (supplier.remaining() >= 4) { //treat as unsigned ints
184       hash ^= (supplier.next32() & 0xFFFFFFFFL) * P1;
185       hash = (Long.rotateLeft(hash, 23) * P2) + P3;
186     }
187 
188     while (supplier.remaining() != 0) { //treat as unsigned bytes
189       hash ^= (supplier.next8() & 0xFF) * P5;
190       hash = Long.rotateLeft(hash, 11) * P1;
191     }
192 
193     return finalize(hash);
194   }
195 
finalize(long hash)196   private static long finalize(long hash) {
197     hash ^= hash >>> 33;
198     hash *= P2;
199     hash ^= hash >>> 29;
200     hash *= P3;
201     hash ^= hash >>> 32;
202     return hash;
203   }
204 
205   private static class PlainByteSupplier extends ByteSupplier {
206     private final byte[] src;
207     private final int len;
208     private int pos;
209     private int remain;
210 
PlainByteSupplier(byte[] src)211     private PlainByteSupplier(byte[] src) {
212       this(src, 0, src.length);
213     }
214 
PlainByteSupplier(byte[] src, int offset, int len)215     private PlainByteSupplier(byte[] src, int offset, int len) {
216       this.src = checkNotNull(src, "src");
217       checkArgument(offset <= src.length, "offset > src length");
218       checkArgument(offset + len <= src.length, "offset + len > src length");
219       this.len = len;
220       pos = offset;
221       remain = len;
222     }
223 
224     @Override
bytes()225     public long bytes() {
226       return len;
227     }
228 
229     @Override
remaining()230     public long remaining() {
231       return remain;
232     }
233 
234     @Override
next8()235     public byte next8() {
236       remain--;
237       return src[pos++];
238     }
239   }
240 
241   private static class AsciiStringByteSupplier extends ByteSupplier {
242     private final String str;
243     private final int bytes;
244     private int pos;
245 
AsciiStringByteSupplier(String str)246     private AsciiStringByteSupplier(String str) {
247       this.str = checkNotNull(str, "str");
248       this.bytes = str.length();
249     }
250 
251     @Override
bytes()252     public long bytes() {
253       return bytes;
254     }
255 
256     @Override
remaining()257     public long remaining() {
258       return (long) bytes - pos;
259     }
260 
261     @Override
next8()262     public byte next8() {
263       return (byte) str.charAt(pos++);
264     }
265   }
266 
267   private abstract static class ByteSupplier {
268 
next64()269     public long next64() {
270       return ((next32() & 0xFFFFFFFFL) | ((next32() & 0xFFFFFFFFL) << 32));
271     }
272 
next32()273     public int next32() {
274       return ((next16() & 0xFFFF) | ((next16() & 0xFFFF) << 16));
275     }
276 
next16()277     char next16() {
278       return (char) ((next8() & 0xFF) | ((next8() & 0xFF) << 8));
279     }
280 
next8()281     abstract byte next8();
282 
bytes()283     abstract long bytes();
284 
remaining()285     abstract long remaining();
286   }
287 }
288