|
@@ -8,8 +8,10 @@
|
|
|
|
|
|
package org.elasticsearch.common.hash;
|
|
|
|
|
|
+import org.elasticsearch.common.Numbers;
|
|
|
import org.elasticsearch.common.util.ByteUtils;
|
|
|
|
|
|
+import java.math.BigInteger;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
@@ -45,6 +47,27 @@ public enum MurmurHash3 {
|
|
|
public int hashCode() {
|
|
|
return Objects.hash(h1, h2);
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ byte[] longBytes = new byte[17];
|
|
|
+ System.arraycopy(Numbers.longToBytes(h1), 0, longBytes, 1, 8);
|
|
|
+ System.arraycopy(Numbers.longToBytes(h2), 0, longBytes, 9, 8);
|
|
|
+ BigInteger bi = new BigInteger(longBytes);
|
|
|
+ return "0x" + bi.toString(16);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static class IntermediateResult {
|
|
|
+ int offset;
|
|
|
+ long h1;
|
|
|
+ long h2;
|
|
|
+
|
|
|
+ IntermediateResult(int offset, long h1, long h2) {
|
|
|
+ this.offset = offset;
|
|
|
+ this.h1 = h1;
|
|
|
+ this.h2 = h2;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static long C1 = 0x87c37b91114253d5L;
|
|
@@ -77,75 +100,88 @@ public enum MurmurHash3 {
|
|
|
long h2 = seed;
|
|
|
|
|
|
if (length >= 16) {
|
|
|
+ IntermediateResult result = intermediateHash(key, offset, length, h1, h2);
|
|
|
+ h1 = result.h1;
|
|
|
+ h2 = result.h2;
|
|
|
+ offset = result.offset;
|
|
|
+ }
|
|
|
|
|
|
- final int len16 = length & 0xFFFFFFF0; // higher multiple of 16 that is lower than or equal to length
|
|
|
- final int end = offset + len16;
|
|
|
- for (int i = offset; i < end; i += 16) {
|
|
|
- long k1 = ByteUtils.readLongLE(key, i);
|
|
|
- long k2 = ByteUtils.readLongLE(key, i + 8);
|
|
|
-
|
|
|
- k1 *= C1;
|
|
|
- k1 = Long.rotateLeft(k1, 31);
|
|
|
- k1 *= C2;
|
|
|
- h1 ^= k1;
|
|
|
-
|
|
|
- h1 = Long.rotateLeft(h1, 27);
|
|
|
- h1 += h2;
|
|
|
- h1 = h1 * 5 + 0x52dce729;
|
|
|
+ return finalizeHash(hash, key, offset, length, h1, h2);
|
|
|
+ }
|
|
|
|
|
|
- k2 *= C2;
|
|
|
- k2 = Long.rotateLeft(k2, 33);
|
|
|
- k2 *= C1;
|
|
|
- h2 ^= k2;
|
|
|
+ static IntermediateResult intermediateHash(byte[] key, int offset, int length, long h1, long h2) {
|
|
|
+ final int len16 = length & 0xFFFFFFF0; // higher multiple of 16 that is lower than or equal to length
|
|
|
+ final int end = offset + len16;
|
|
|
+ for (int i = offset; i < end; i += 16) {
|
|
|
+ long k1 = ByteUtils.readLongLE(key, i);
|
|
|
+ long k2 = ByteUtils.readLongLE(key, i + 8);
|
|
|
+
|
|
|
+ k1 *= C1;
|
|
|
+ k1 = Long.rotateLeft(k1, 31);
|
|
|
+ k1 *= C2;
|
|
|
+ h1 ^= k1;
|
|
|
+
|
|
|
+ h1 = Long.rotateLeft(h1, 27);
|
|
|
+ h1 += h2;
|
|
|
+ h1 = h1 * 5 + 0x52dce729;
|
|
|
+
|
|
|
+ k2 *= C2;
|
|
|
+ k2 = Long.rotateLeft(k2, 33);
|
|
|
+ k2 *= C1;
|
|
|
+ h2 ^= k2;
|
|
|
+
|
|
|
+ h2 = Long.rotateLeft(h2, 31);
|
|
|
+ h2 += h1;
|
|
|
+ h2 = h2 * 5 + 0x38495ab5;
|
|
|
+ }
|
|
|
|
|
|
- h2 = Long.rotateLeft(h2, 31);
|
|
|
- h2 += h1;
|
|
|
- h2 = h2 * 5 + 0x38495ab5;
|
|
|
- }
|
|
|
+ // Advance offset to the unprocessed tail of the data.
|
|
|
+ offset = end;
|
|
|
|
|
|
- // Advance offset to the unprocessed tail of the data.
|
|
|
- offset = end;
|
|
|
- }
|
|
|
+ return new IntermediateResult(offset, h1, h2);
|
|
|
+ }
|
|
|
|
|
|
+ @SuppressWarnings("fallthrough") // Intentionally uses fallthrough to implement a well known hashing algorithm
|
|
|
+ static Hash128 finalizeHash(Hash128 hash, byte[] remainder, int offset, int length, long h1, long h2) {
|
|
|
long k1 = 0;
|
|
|
long k2 = 0;
|
|
|
|
|
|
switch (length & 15) {
|
|
|
case 15:
|
|
|
- k2 ^= (key[offset + 14] & 0xFFL) << 48;
|
|
|
+ k2 ^= (remainder[offset + 14] & 0xFFL) << 48;
|
|
|
case 14:
|
|
|
- k2 ^= (key[offset + 13] & 0xFFL) << 40;
|
|
|
+ k2 ^= (remainder[offset + 13] & 0xFFL) << 40;
|
|
|
case 13:
|
|
|
- k2 ^= (key[offset + 12] & 0xFFL) << 32;
|
|
|
+ k2 ^= (remainder[offset + 12] & 0xFFL) << 32;
|
|
|
case 12:
|
|
|
- k2 ^= (key[offset + 11] & 0xFFL) << 24;
|
|
|
+ k2 ^= (remainder[offset + 11] & 0xFFL) << 24;
|
|
|
case 11:
|
|
|
- k2 ^= (key[offset + 10] & 0xFFL) << 16;
|
|
|
+ k2 ^= (remainder[offset + 10] & 0xFFL) << 16;
|
|
|
case 10:
|
|
|
- k2 ^= (key[offset + 9] & 0xFFL) << 8;
|
|
|
+ k2 ^= (remainder[offset + 9] & 0xFFL) << 8;
|
|
|
case 9:
|
|
|
- k2 ^= (key[offset + 8] & 0xFFL) << 0;
|
|
|
+ k2 ^= (remainder[offset + 8] & 0xFFL) << 0;
|
|
|
k2 *= C2;
|
|
|
k2 = Long.rotateLeft(k2, 33);
|
|
|
k2 *= C1;
|
|
|
h2 ^= k2;
|
|
|
|
|
|
case 8:
|
|
|
- k1 ^= (key[offset + 7] & 0xFFL) << 56;
|
|
|
+ k1 ^= (remainder[offset + 7] & 0xFFL) << 56;
|
|
|
case 7:
|
|
|
- k1 ^= (key[offset + 6] & 0xFFL) << 48;
|
|
|
+ k1 ^= (remainder[offset + 6] & 0xFFL) << 48;
|
|
|
case 6:
|
|
|
- k1 ^= (key[offset + 5] & 0xFFL) << 40;
|
|
|
+ k1 ^= (remainder[offset + 5] & 0xFFL) << 40;
|
|
|
case 5:
|
|
|
- k1 ^= (key[offset + 4] & 0xFFL) << 32;
|
|
|
+ k1 ^= (remainder[offset + 4] & 0xFFL) << 32;
|
|
|
case 4:
|
|
|
- k1 ^= (key[offset + 3] & 0xFFL) << 24;
|
|
|
+ k1 ^= (remainder[offset + 3] & 0xFFL) << 24;
|
|
|
case 3:
|
|
|
- k1 ^= (key[offset + 2] & 0xFFL) << 16;
|
|
|
+ k1 ^= (remainder[offset + 2] & 0xFFL) << 16;
|
|
|
case 2:
|
|
|
- k1 ^= (key[offset + 1] & 0xFFL) << 8;
|
|
|
+ k1 ^= (remainder[offset + 1] & 0xFFL) << 8;
|
|
|
case 1:
|
|
|
- k1 ^= (key[offset] & 0xFFL);
|
|
|
+ k1 ^= (remainder[offset] & 0xFFL);
|
|
|
k1 *= C1;
|
|
|
k1 = Long.rotateLeft(k1, 31);
|
|
|
k1 *= C2;
|