extends RedissonExpirable implement
private String getLockName(Object key) {
ByteBuf keyState = encodeMapKey(key);
try {
- return suffixName(getName(), Hash.hashToBase64(keyState) + ":key");
+ return suffixName(getName(), Hash.hash128toBase64(keyState) + ":key");
} finally {
keyState.release();
}
}
protected String hash(ByteBuf objectState) {
- return Hash.hashToBase64(objectState);
+ return Hash.hash128toBase64(objectState);
}
protected String hashAndRelease(ByteBuf objectState) {
try {
- return Hash.hashToBase64(objectState);
+ return Hash.hash128toBase64(objectState);
} finally {
objectState.release();
}
diff --git a/redisson/src/main/java/org/redisson/api/RBloomFilter.java b/redisson/src/main/java/org/redisson/api/RBloomFilter.java
index 205400f21..61817b765 100644
--- a/redisson/src/main/java/org/redisson/api/RBloomFilter.java
+++ b/redisson/src/main/java/org/redisson/api/RBloomFilter.java
@@ -16,7 +16,7 @@
package org.redisson.api;
/**
- * Bloom filter based on 64-bit hash derived from 128-bit hash (xxHash + FarmHash).
+ * Bloom filter based on Highway 128-bit hash.
*
* @author Nikita Koksharov
*
diff --git a/redisson/src/main/java/org/redisson/api/RSemaphore.java b/redisson/src/main/java/org/redisson/api/RSemaphore.java
index 12dc64d30..410d87c04 100644
--- a/redisson/src/main/java/org/redisson/api/RSemaphore.java
+++ b/redisson/src/main/java/org/redisson/api/RSemaphore.java
@@ -96,7 +96,7 @@ public interface RSemaphore extends RExpirable, RSemaphoreAsync {
*
* Acquires a permits, if all are available and returns immediately,
* with the value {@code true},
- * reducing the number of available permits by given number of permitss.
+ * reducing the number of available permits by given number of permits.
*
*
If no permits are available then this method will return
* immediately with the value {@code false}.
diff --git a/redisson/src/main/java/org/redisson/jcache/JCache.java b/redisson/src/main/java/org/redisson/jcache/JCache.java
index 70ff33193..735bf1768 100644
--- a/redisson/src/main/java/org/redisson/jcache/JCache.java
+++ b/redisson/src/main/java/org/redisson/jcache/JCache.java
@@ -560,7 +560,7 @@ public class JCache extends RedissonObject implements Cache {
private String getLockName(Object key) {
ByteBuf keyState = encodeMapKey(key);
try {
- return "{" + getName() + "}:" + Hash.hashToBase64(keyState) + ":key";
+ return "{" + getName() + "}:" + Hash.hash128toBase64(keyState) + ":key";
} finally {
keyState.release();
}
diff --git a/redisson/src/main/java/org/redisson/mapreduce/Collector.java b/redisson/src/main/java/org/redisson/mapreduce/Collector.java
index 83a59ada2..cdbf2541d 100644
--- a/redisson/src/main/java/org/redisson/mapreduce/Collector.java
+++ b/redisson/src/main/java/org/redisson/mapreduce/Collector.java
@@ -23,9 +23,9 @@ import org.redisson.api.RListMultimap;
import org.redisson.api.RedissonClient;
import org.redisson.api.mapreduce.RCollector;
import org.redisson.client.codec.Codec;
+import org.redisson.misc.Hash;
import io.netty.buffer.ByteBuf;
-import net.openhft.hashing.LongHashFunction;
/**
*
@@ -57,7 +57,7 @@ public class Collector implements RCollector {
public void emit(K key, V value) {
try {
ByteBuf encodedKey = codec.getValueEncoder().encode(key);
- long hash = LongHashFunction.xx().hashBytes(encodedKey.internalNioBuffer(encodedKey.readerIndex(), encodedKey.readableBytes()));
+ long hash = Hash.hash64(encodedKey);
encodedKey.release();
int part = (int) Math.abs(hash % parts);
String partName = name + ":" + part;
diff --git a/redisson/src/main/java/org/redisson/misc/Hash.java b/redisson/src/main/java/org/redisson/misc/Hash.java
index 13f4292fa..290b01ac3 100644
--- a/redisson/src/main/java/org/redisson/misc/Hash.java
+++ b/redisson/src/main/java/org/redisson/misc/Hash.java
@@ -15,14 +15,10 @@
*/
package org.redisson.misc;
-import java.nio.ByteBuffer;
-
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
-import net.openhft.hashing.LongHashFunction;
/**
*
@@ -31,18 +27,17 @@ import net.openhft.hashing.LongHashFunction;
*/
public class Hash {
+ private static final long[] KEY = {0x9e3779b97f4a7c15L, 0xf39cc0605cedc834L, 0x1082276bf3a27251L, 0xf86c6a11d0c18e95L};
+
private Hash() {
}
-
-
- public static byte[] hash(ByteBuf objectState) {
- ByteBuffer b = objectState.internalNioBuffer(objectState.readerIndex(), objectState.readableBytes());
- long h1 = LongHashFunction.farmUo().hashBytes(b);
- long h2 = LongHashFunction.xx().hashBytes(b);
+
+ public static byte[] hash128toArray(ByteBuf objectState) {
+ long[] hash = hash128(objectState);
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer((2 * Long.SIZE) / Byte.SIZE);
try {
- buf.writeLong(h1).writeLong(h2);
+ buf.writeLong(hash[0]).writeLong(hash[1]);
byte[] dst = new byte[buf.readableBytes()];
buf.readBytes(dst);
return dst;
@@ -50,16 +45,41 @@ public class Hash {
buf.release();
}
}
+
+ public static long hash64(ByteBuf objectState) {
+ HighwayHash h = calcHash(objectState);
+ return h.finalize64();
+ }
+
+ public static long[] hash128(ByteBuf objectState) {
+ HighwayHash h = calcHash(objectState);
+ return h.finalize128();
+ }
+ protected static HighwayHash calcHash(ByteBuf objectState) {
+ HighwayHash h = new HighwayHash(KEY);
+ int i;
+ int length = objectState.readableBytes();
+ int offset = objectState.readerIndex();
+ byte[] data = new byte[32];
+ for (i = 0; i + 32 <= length; i += 32) {
+ objectState.getBytes(offset + i, data);
+ h.updatePacket(data, 0);
+ }
+ if ((length & 31) != 0) {
+ data = new byte[length & 31];
+ objectState.getBytes(offset + i, data);
+ h.updateRemainder(data, 0, length & 31);
+ }
+ return h;
+ }
- public static String hashToBase64(ByteBuf objectState) {
- ByteBuffer bf = objectState.internalNioBuffer(objectState.readerIndex(), objectState.readableBytes());
- long h1 = LongHashFunction.farmUo().hashBytes(bf);
- long h2 = LongHashFunction.xx().hashBytes(bf);
+ public static String hash128toBase64(ByteBuf objectState) {
+ long[] hash = hash128(objectState);
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer((2 * Long.SIZE) / Byte.SIZE);
try {
- buf.writeLong(h1).writeLong(h2);
+ buf.writeLong(hash[0]).writeLong(hash[1]);
ByteBuf b = Base64.encode(buf);
try {
String s = b.toString(CharsetUtil.UTF_8);
diff --git a/redisson/src/main/java/org/redisson/misc/HighwayHash.java b/redisson/src/main/java/org/redisson/misc/HighwayHash.java
new file mode 100644
index 000000000..caf672705
--- /dev/null
+++ b/redisson/src/main/java/org/redisson/misc/HighwayHash.java
@@ -0,0 +1,336 @@
+/**
+ * Copyright 2016 Nikita Koksharov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.redisson.misc;
+
+/**
+ * HighwayHash algorithm. See
+ * HighwayHash on GitHub
+ */
+public final class HighwayHash {
+ private final long[] v0 = new long[4];
+ private final long[] v1 = new long[4];
+ private final long[] mul0 = new long[4];
+ private final long[] mul1 = new long[4];
+ private boolean done = false;
+
+ /**
+ * @param key0 first 8 bytes of the key
+ * @param key1 next 8 bytes of the key
+ * @param key2 next 8 bytes of the key
+ * @param key3 last 8 bytes of the key
+ */
+ public HighwayHash(long key0, long key1, long key2, long key3) {
+ reset(key0, key1, key2, key3);
+ }
+
+ /**
+ * @param key array of size 4 with the key to initialize the hash with
+ */
+ public HighwayHash(long[] key) {
+ if (key.length != 4) {
+ throw new IllegalArgumentException(String.format("Key length (%s) must be 4", key.length));
+ }
+ reset(key[0], key[1], key[2], key[3]);
+ }
+
+ /**
+ * Updates the hash with 32 bytes of data. If you can read 4 long values
+ * from your data efficiently, prefer using update() instead for more speed.
+ * @param packet data array which has a length of at least pos + 32
+ * @param pos position in the array to read the first of 32 bytes from
+ */
+ public void updatePacket(byte[] packet, int pos) {
+ if (pos < 0) {
+ throw new IllegalArgumentException(String.format("Pos (%s) must be positive", pos));
+ }
+ if (pos + 32 > packet.length) {
+ throw new IllegalArgumentException("packet must have at least 32 bytes after pos");
+ }
+ long a0 = read64(packet, pos + 0);
+ long a1 = read64(packet, pos + 8);
+ long a2 = read64(packet, pos + 16);
+ long a3 = read64(packet, pos + 24);
+ update(a0, a1, a2, a3);
+ }
+
+ /**
+ * Updates the hash with 32 bytes of data given as 4 longs. This function is
+ * more efficient than updatePacket when you can use it.
+ * @param a0 first 8 bytes in little endian 64-bit long
+ * @param a1 next 8 bytes in little endian 64-bit long
+ * @param a2 next 8 bytes in little endian 64-bit long
+ * @param a3 last 8 bytes in little endian 64-bit long
+ */
+ public void update(long a0, long a1, long a2, long a3) {
+ if (done) {
+ throw new IllegalStateException("Can compute a hash only once per instance");
+ }
+ v1[0] += mul0[0] + a0;
+ v1[1] += mul0[1] + a1;
+ v1[2] += mul0[2] + a2;
+ v1[3] += mul0[3] + a3;
+ for (int i = 0; i < 4; ++i) {
+ mul0[i] ^= (v1[i] & 0xffffffffL) * (v0[i] >>> 32);
+ v0[i] += mul1[i];
+ mul1[i] ^= (v0[i] & 0xffffffffL) * (v1[i] >>> 32);
+ }
+ v0[0] += zipperMerge0(v1[1], v1[0]);
+ v0[1] += zipperMerge1(v1[1], v1[0]);
+ v0[2] += zipperMerge0(v1[3], v1[2]);
+ v0[3] += zipperMerge1(v1[3], v1[2]);
+ v1[0] += zipperMerge0(v0[1], v0[0]);
+ v1[1] += zipperMerge1(v0[1], v0[0]);
+ v1[2] += zipperMerge0(v0[3], v0[2]);
+ v1[3] += zipperMerge1(v0[3], v0[2]);
+ }
+
+
+ /**
+ * Updates the hash with the last 1 to 31 bytes of the data. You must use
+ * updatePacket first per 32 bytes of the data, if and only if 1 to 31 bytes
+ * of the data are not processed after that, updateRemainder must be used for
+ * those final bytes.
+ * @param bytes data array which has a length of at least pos + size_mod32
+ * @param pos position in the array to start reading size_mod32 bytes from
+ * @param size_mod32 the amount of bytes to read
+ */
+ public void updateRemainder(byte[] bytes, int pos, int size_mod32) {
+ if (pos < 0) {
+ throw new IllegalArgumentException(String.format("Pos (%s) must be positive", pos));
+ }
+ if (size_mod32 < 0 || size_mod32 >= 32) {
+ throw new IllegalArgumentException(
+ String.format("size_mod32 (%s) must be between 0 and 31", size_mod32));
+ }
+ if (pos + size_mod32 > bytes.length) {
+ throw new IllegalArgumentException("bytes must have at least size_mod32 bytes after pos");
+ }
+ int size_mod4 = size_mod32 & 3;
+ int remainder = size_mod32 & ~3;
+ byte[] packet = new byte[32];
+ for (int i = 0; i < 4; ++i) {
+ v0[i] += ((long)size_mod32 << 32) + size_mod32;
+ }
+ rotate32By(size_mod32, v1);
+ for (int i = 0; i < remainder; i++) {
+ packet[i] = bytes[pos + i];
+ }
+ if ((size_mod32 & 16) != 0) {
+ for (int i = 0; i < 4; i++) {
+ packet[28 + i] = bytes[pos + remainder + i + size_mod4 - 4];
+ }
+ } else {
+ if (size_mod4 != 0) {
+ packet[16 + 0] = bytes[pos + remainder + 0];
+ packet[16 + 1] = bytes[pos + remainder + (size_mod4 >>> 1)];
+ packet[16 + 2] = bytes[pos + remainder + (size_mod4 - 1)];
+ }
+ }
+ updatePacket(packet, 0);
+ }
+
+ /**
+ * Computes the hash value after all bytes were processed. Invalidates the
+ * state.
+ *
+ * NOTE: The 64-bit HighwayHash algorithm is declared stable and no longer subject to change.
+ *
+ * @return 64-bit hash
+ */
+ public long finalize64() {
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ done = true;
+ return v0[0] + v1[0] + mul0[0] + mul1[0];
+ }
+
+ /**
+ * Computes the hash value after all bytes were processed. Invalidates the
+ * state.
+ *
+ * NOTE: The 128-bit HighwayHash algorithm is not yet frozen and subject to change.
+ *
+ * @return array of size 2 containing 128-bit hash
+ */
+ public long[] finalize128() {
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ done = true;
+ long[] hash = new long[2];
+ hash[0] = v0[0] + mul0[0] + v1[2] + mul1[2];
+ hash[1] = v0[1] + mul0[1] + v1[3] + mul1[3];
+ return hash;
+ }
+
+ /**
+ * Computes the hash value after all bytes were processed. Invalidates the
+ * state.
+ *
+ * NOTE: The 256-bit HighwayHash algorithm is not yet frozen and subject to change.
+ *
+ * @return array of size 4 containing 256-bit hash
+ */
+ public long[] finalize256() {
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ permuteAndUpdate();
+ done = true;
+ long[] hash = new long[4];
+ modularReduction(v1[1] + mul1[1], v1[0] + mul1[0],
+ v0[1] + mul0[1], v0[0] + mul0[0],
+ hash, 0);
+ modularReduction(v1[3] + mul1[3], v1[2] + mul1[2],
+ v0[3] + mul0[3], v0[2] + mul0[2],
+ hash, 2);
+ return hash;
+ }
+ private void reset(long key0, long key1, long key2, long key3) {
+ mul0[0] = 0xdbe6d5d5fe4cce2fL;
+ mul0[1] = 0xa4093822299f31d0L;
+ mul0[2] = 0x13198a2e03707344L;
+ mul0[3] = 0x243f6a8885a308d3L;
+ mul1[0] = 0x3bd39e10cb0ef593L;
+ mul1[1] = 0xc0acf169b5f18a8cL;
+ mul1[2] = 0xbe5466cf34e90c6cL;
+ mul1[3] = 0x452821e638d01377L;
+ v0[0] = mul0[0] ^ key0;
+ v0[1] = mul0[1] ^ key1;
+ v0[2] = mul0[2] ^ key2;
+ v0[3] = mul0[3] ^ key3;
+ v1[0] = mul1[0] ^ ((key0 >>> 32) | (key0 << 32));
+ v1[1] = mul1[1] ^ ((key1 >>> 32) | (key1 << 32));
+ v1[2] = mul1[2] ^ ((key2 >>> 32) | (key2 << 32));
+ v1[3] = mul1[3] ^ ((key3 >>> 32) | (key3 << 32));
+ }
+
+ private long zipperMerge0(long v1, long v0) {
+ return (((v0 & 0xff000000L) | (v1 & 0xff00000000L)) >>> 24) |
+ (((v0 & 0xff0000000000L) | (v1 & 0xff000000000000L)) >>> 16) |
+ (v0 & 0xff0000L) | ((v0 & 0xff00L) << 32) |
+ ((v1 & 0xff00000000000000L) >>> 8) | (v0 << 56);
+ }
+
+ private long zipperMerge1(long v1, long v0) {
+ return (((v1 & 0xff000000L) | (v0 & 0xff00000000L)) >>> 24) |
+ (v1 & 0xff0000L) | ((v1 & 0xff0000000000L) >>> 16) |
+ ((v1 & 0xff00L) << 24) | ((v0 & 0xff000000000000L) >>> 8) |
+ ((v1 & 0xffL) << 48) | (v0 & 0xff00000000000000L);
+ }
+
+ private long read64(byte[] src, int pos) {
+ // Mask with 0xffL so that it is 0..255 as long (byte can only be -128..127)
+ return (src[pos + 0] & 0xffL) | ((src[pos + 1] & 0xffL) << 8) |
+ ((src[pos + 2] & 0xffL) << 16) | ((src[pos + 3] & 0xffL) << 24) |
+ ((src[pos + 4] & 0xffL) << 32) | ((src[pos + 5] & 0xffL) << 40) |
+ ((src[pos + 6] & 0xffL) << 48) | ((src[pos + 7] & 0xffL) << 56);
+ }
+
+ private void rotate32By(long count, long[] lanes) {
+ for (int i = 0; i < 4; ++i) {
+ long half0 = (lanes[i] & 0xffffffffL);
+ long half1 = (lanes[i] >>> 32) & 0xffffffffL;
+ lanes[i] = ((half0 << count) & 0xffffffffL) | (half0 >>> (32 - count));
+ lanes[i] |= ((long)(((half1 << count) & 0xffffffffL) |
+ (half1 >>> (32 - count)))) << 32;
+ }
+ }
+
+ private void permuteAndUpdate() {
+ update((v0[2] >>> 32) | (v0[2] << 32),
+ (v0[3] >>> 32) | (v0[3] << 32),
+ (v0[0] >>> 32) | (v0[0] << 32),
+ (v0[1] >>> 32) | (v0[1] << 32));
+ }
+
+ private void modularReduction(long a3_unmasked, long a2, long a1,
+ long a0, long[] hash, int pos) {
+ long a3 = a3_unmasked & 0x3FFFFFFFFFFFFFFFL;
+ hash[pos + 1] = a1 ^ ((a3 << 1) | (a2 >>> 63)) ^ ((a3 << 2) | (a2 >>> 62));
+ hash[pos + 0] = a0 ^ (a2 << 1) ^ (a2 << 2);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * NOTE: The 64-bit HighwayHash algorithm is declared stable and no longer subject to change.
+ *
+ * @param data array with data bytes
+ * @param offset position of first byte of data to read from
+ * @param length number of bytes from data to read
+ * @param key array of size 4 with the key to initialize the hash with
+ * @return 64-bit hash for the given data
+ */
+ public static long hash64(byte[] data, int offset, int length, long[] key) {
+ HighwayHash h = new HighwayHash(key);
+ h.processAll(data, offset, length);
+ return h.finalize64();
+ }
+
+ /**
+ * NOTE: The 128-bit HighwayHash algorithm is not yet frozen and subject to change.
+ *
+ * @param data array with data bytes
+ * @param offset position of first byte of data to read from
+ * @param length number of bytes from data to read
+ * @param key array of size 4 with the key to initialize the hash with
+ * @return array of size 2 containing 128-bit hash for the given data
+ */
+ public static long[] hash128(
+ byte[] data, int offset, int length, long[] key) {
+ HighwayHash h = new HighwayHash(key);
+ h.processAll(data, offset, length);
+ return h.finalize128();
+ }
+
+ /**
+ * NOTE: The 256-bit HighwayHash algorithm is not yet frozen and subject to change.
+ *
+ * @param data array with data bytes
+ * @param offset position of first byte of data to read from
+ * @param length number of bytes from data to read
+ * @param key array of size 4 with the key to initialize the hash with
+ * @return array of size 4 containing 256-bit hash for the given data
+ */
+ public static long[] hash256(
+ byte[] data, int offset, int length, long[] key) {
+ HighwayHash h = new HighwayHash(key);
+ h.processAll(data, offset, length);
+ return h.finalize256();
+ }
+
+ private void processAll(byte[] data, int offset, int length) {
+ int i;
+ for (i = 0; i + 32 <= length; i += 32) {
+ updatePacket(data, offset + i);
+ }
+ if ((length & 31) != 0) {
+ updateRemainder(data, offset + i, length & 31);
+ }
+ }
+}
\ No newline at end of file
diff --git a/redisson/src/main/java/org/redisson/reactive/RedissonBaseMultimapReactive.java b/redisson/src/main/java/org/redisson/reactive/RedissonBaseMultimapReactive.java
index c0846cb3b..36f06c95d 100644
--- a/redisson/src/main/java/org/redisson/reactive/RedissonBaseMultimapReactive.java
+++ b/redisson/src/main/java/org/redisson/reactive/RedissonBaseMultimapReactive.java
@@ -152,12 +152,12 @@ abstract class RedissonBaseMultimapReactive extends RedissonExpirableReact
}
protected String hash(ByteBuf objectState) {
- return Hash.hashToBase64(objectState);
+ return Hash.hash128toBase64(objectState);
}
protected String hashAndRelease(ByteBuf objectState) {
try {
- return Hash.hashToBase64(objectState);
+ return Hash.hash128toBase64(objectState);
} finally {
objectState.release();
}