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