add support for java 1.5
This commit is contained in:
@@ -3,38 +3,37 @@ package redis.clients.util;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
public interface Hashing {
|
||||
public static final Hashing MURMUR_HASH = new MurmurHash();
|
||||
|
||||
public static final Hashing MD5 = new Hashing() {
|
||||
private MessageDigest md5 = null; // avoid recurring construction
|
||||
|
||||
public long hash(String key) {
|
||||
return hash(key.getBytes(Protocol.UTF8));
|
||||
}
|
||||
private MessageDigest md5 = null; // avoid recurring construction
|
||||
|
||||
public long hash(byte[] key) {
|
||||
if (md5 == null) {
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(
|
||||
"++++ no md5 algorythm found");
|
||||
}
|
||||
}
|
||||
public long hash(String key) {
|
||||
return hash(SafeEncoder.encode(key));
|
||||
}
|
||||
|
||||
md5.reset();
|
||||
md5.update(key);
|
||||
byte[] bKey = md5.digest();
|
||||
long res = ((long) (bKey[3] & 0xFF) << 24)
|
||||
| ((long) (bKey[2] & 0xFF) << 16)
|
||||
| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);
|
||||
return res;
|
||||
}
|
||||
public long hash(byte[] key) {
|
||||
if (md5 == null) {
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(
|
||||
"++++ no md5 algorythm found");
|
||||
}
|
||||
}
|
||||
|
||||
md5.reset();
|
||||
md5.update(key);
|
||||
byte[] bKey = md5.digest();
|
||||
long res = ((long) (bKey[3] & 0xFF) << 24)
|
||||
| ((long) (bKey[2] & 0xFF) << 16)
|
||||
| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
public long hash(String key);
|
||||
|
||||
public long hash(byte[] key);
|
||||
}
|
||||
@@ -20,8 +20,6 @@ package redis.clients.util;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
/**
|
||||
* This is a very fast, non-cryptographic hash suitable for general hash-based
|
||||
* lookup. See http://murmurhash.googlepages.com/ for more details.
|
||||
@@ -42,7 +40,7 @@ public class MurmurHash implements Hashing {
|
||||
* @return The 32 bit hash of the bytes in question.
|
||||
*/
|
||||
public static int hash(byte[] data, int seed) {
|
||||
return hash(ByteBuffer.wrap(data), seed);
|
||||
return hash(ByteBuffer.wrap(data), seed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +57,7 @@ public class MurmurHash implements Hashing {
|
||||
* @return The 32-bit hash of the data in question.
|
||||
*/
|
||||
public static int hash(byte[] data, int offset, int length, int seed) {
|
||||
return hash(ByteBuffer.wrap(data, offset, length), seed);
|
||||
return hash(ByteBuffer.wrap(data, offset, length), seed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,97 +70,97 @@ public class MurmurHash implements Hashing {
|
||||
* @return The 32 bit murmur hash of the bytes in the buffer.
|
||||
*/
|
||||
public static int hash(ByteBuffer buf, int seed) {
|
||||
// save byte order for later restoration
|
||||
ByteOrder byteOrder = buf.order();
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
// save byte order for later restoration
|
||||
ByteOrder byteOrder = buf.order();
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
int m = 0x5bd1e995;
|
||||
int r = 24;
|
||||
int m = 0x5bd1e995;
|
||||
int r = 24;
|
||||
|
||||
int h = seed ^ buf.remaining();
|
||||
int h = seed ^ buf.remaining();
|
||||
|
||||
int k;
|
||||
while (buf.remaining() >= 4) {
|
||||
k = buf.getInt();
|
||||
int k;
|
||||
while (buf.remaining() >= 4) {
|
||||
k = buf.getInt();
|
||||
|
||||
k *= m;
|
||||
k ^= k >>> r;
|
||||
k *= m;
|
||||
k *= m;
|
||||
k ^= k >>> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
}
|
||||
h *= m;
|
||||
h ^= k;
|
||||
}
|
||||
|
||||
if (buf.remaining() > 0) {
|
||||
ByteBuffer finish = ByteBuffer.allocate(4).order(
|
||||
ByteOrder.LITTLE_ENDIAN);
|
||||
// for big-endian version, use this first:
|
||||
// finish.position(4-buf.remaining());
|
||||
finish.put(buf).rewind();
|
||||
h ^= finish.getInt();
|
||||
h *= m;
|
||||
}
|
||||
if (buf.remaining() > 0) {
|
||||
ByteBuffer finish = ByteBuffer.allocate(4).order(
|
||||
ByteOrder.LITTLE_ENDIAN);
|
||||
// for big-endian version, use this first:
|
||||
// finish.position(4-buf.remaining());
|
||||
finish.put(buf).rewind();
|
||||
h ^= finish.getInt();
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ^= h >>> 13;
|
||||
h *= m;
|
||||
h ^= h >>> 15;
|
||||
h ^= h >>> 13;
|
||||
h *= m;
|
||||
h ^= h >>> 15;
|
||||
|
||||
buf.order(byteOrder);
|
||||
return h;
|
||||
buf.order(byteOrder);
|
||||
return h;
|
||||
}
|
||||
|
||||
public static long hash64A(byte[] data, int seed) {
|
||||
return hash64A(ByteBuffer.wrap(data), seed);
|
||||
return hash64A(ByteBuffer.wrap(data), seed);
|
||||
}
|
||||
|
||||
public static long hash64A(byte[] data, int offset, int length, int seed) {
|
||||
return hash64A(ByteBuffer.wrap(data, offset, length), seed);
|
||||
return hash64A(ByteBuffer.wrap(data, offset, length), seed);
|
||||
}
|
||||
|
||||
public static long hash64A(ByteBuffer buf, int seed) {
|
||||
ByteOrder byteOrder = buf.order();
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
ByteOrder byteOrder = buf.order();
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
long m = 0xc6a4a7935bd1e995L;
|
||||
int r = 47;
|
||||
long m = 0xc6a4a7935bd1e995L;
|
||||
int r = 47;
|
||||
|
||||
long h = seed ^ (buf.remaining() * m);
|
||||
long h = seed ^ (buf.remaining() * m);
|
||||
|
||||
long k;
|
||||
while (buf.remaining() >= 8) {
|
||||
k = buf.getLong();
|
||||
long k;
|
||||
while (buf.remaining() >= 8) {
|
||||
k = buf.getLong();
|
||||
|
||||
k *= m;
|
||||
k ^= k >>> r;
|
||||
k *= m;
|
||||
k *= m;
|
||||
k ^= k >>> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
}
|
||||
h ^= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
if (buf.remaining() > 0) {
|
||||
ByteBuffer finish = ByteBuffer.allocate(8).order(
|
||||
ByteOrder.LITTLE_ENDIAN);
|
||||
// for big-endian version, do this first:
|
||||
// finish.position(8-buf.remaining());
|
||||
finish.put(buf).rewind();
|
||||
h ^= finish.getLong();
|
||||
h *= m;
|
||||
}
|
||||
if (buf.remaining() > 0) {
|
||||
ByteBuffer finish = ByteBuffer.allocate(8).order(
|
||||
ByteOrder.LITTLE_ENDIAN);
|
||||
// for big-endian version, do this first:
|
||||
// finish.position(8-buf.remaining());
|
||||
finish.put(buf).rewind();
|
||||
h ^= finish.getLong();
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ^= h >>> r;
|
||||
h *= m;
|
||||
h ^= h >>> r;
|
||||
h ^= h >>> r;
|
||||
h *= m;
|
||||
h ^= h >>> r;
|
||||
|
||||
buf.order(byteOrder);
|
||||
return h;
|
||||
buf.order(byteOrder);
|
||||
return h;
|
||||
}
|
||||
|
||||
public long hash(byte[] key) {
|
||||
return hash64A(key, 0x1234ABCD);
|
||||
return hash64A(key, 0x1234ABCD);
|
||||
}
|
||||
|
||||
|
||||
public long hash(String key) {
|
||||
return hash(key.getBytes(Protocol.UTF8));
|
||||
return hash(SafeEncoder.encode(key));
|
||||
}
|
||||
}
|
||||
28
src/main/java/redis/clients/util/SafeEncoder.java
Normal file
28
src/main/java/redis/clients/util/SafeEncoder.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package redis.clients.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import redis.clients.jedis.JedisException;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
/**
|
||||
* The only reason to have this is to be able to compatible with java 1.5 :(
|
||||
*
|
||||
*/
|
||||
public class SafeEncoder {
|
||||
public static byte[] encode(final String str) {
|
||||
try {
|
||||
return str.getBytes(Protocol.CHARSET);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new JedisException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encode(final byte[] data) {
|
||||
try {
|
||||
return new String(data, Protocol.CHARSET);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new JedisException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package redis.clients.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -22,69 +24,73 @@ public class Sharded<R, S extends ShardInfo<R>> {
|
||||
private Pattern tagPattern = null;
|
||||
// the tag is anything between {}
|
||||
public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern
|
||||
.compile("\\{(.+?)\\}");
|
||||
.compile("\\{(.+?)\\}");
|
||||
|
||||
public Sharded(List<S> shards) {
|
||||
this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works
|
||||
// with 64-bits not 128
|
||||
this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works
|
||||
// with 64-bits not 128
|
||||
}
|
||||
|
||||
public Sharded(List<S> shards, Hashing algo) {
|
||||
this.algo = algo;
|
||||
initialize(shards);
|
||||
this.algo = algo;
|
||||
initialize(shards);
|
||||
}
|
||||
|
||||
public Sharded(List<S> shards, Pattern tagPattern) {
|
||||
this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good
|
||||
// as we works with
|
||||
// 64-bits not 128
|
||||
this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good
|
||||
// as we works with
|
||||
// 64-bits not 128
|
||||
}
|
||||
|
||||
public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
|
||||
this.algo = algo;
|
||||
this.tagPattern = tagPattern;
|
||||
initialize(shards);
|
||||
this.algo = algo;
|
||||
this.tagPattern = tagPattern;
|
||||
initialize(shards);
|
||||
}
|
||||
|
||||
private void initialize(List<S> shards) {
|
||||
nodes = new TreeMap<Long, S>();
|
||||
nodes = new TreeMap<Long, S>();
|
||||
|
||||
int totalWeight = 0;
|
||||
int totalWeight = 0;
|
||||
|
||||
for (ShardInfo<?> shard : shards) {
|
||||
totalWeight += shard.getWeight();
|
||||
}
|
||||
for (ShardInfo<?> shard : shards) {
|
||||
totalWeight += shard.getWeight();
|
||||
}
|
||||
|
||||
long oneForthOfStep = (1L << 62) / totalWeight; // 62 vs 64 to normalize
|
||||
// math in Long
|
||||
long oneForthOfStep = (1L << 62) / totalWeight; // 62 vs 64 to normalize
|
||||
// math in Long
|
||||
|
||||
long floor = Long.MIN_VALUE;
|
||||
for (int i = 0; i != shards.size(); ++i) {
|
||||
final S shardInfo = shards.get(i);
|
||||
shardInfo.initResource();
|
||||
nodes.put(floor, shardInfo);
|
||||
floor += 4 * oneForthOfStep * shardInfo.getWeight(); // *4 to
|
||||
// compensate
|
||||
// 62 vs 64
|
||||
}
|
||||
long floor = Long.MIN_VALUE;
|
||||
for (int i = 0; i != shards.size(); ++i) {
|
||||
final S shardInfo = shards.get(i);
|
||||
shardInfo.initResource();
|
||||
nodes.put(floor, shardInfo);
|
||||
floor += 4 * oneForthOfStep * shardInfo.getWeight(); // *4 to
|
||||
// compensate
|
||||
// 62 vs 64
|
||||
}
|
||||
}
|
||||
|
||||
public R getShard(byte[] key) {
|
||||
return nodes
|
||||
.floorEntry(algo.hash(key))
|
||||
.getValue()
|
||||
.getResource();
|
||||
}
|
||||
public R getShard(byte[] key) {
|
||||
return getShardInfo(key).getResource();
|
||||
}
|
||||
|
||||
public R getShard(String key) {
|
||||
return nodes
|
||||
.floorEntry(algo.hash(getKeyTag(key)))
|
||||
.getValue()
|
||||
.getResource();
|
||||
}
|
||||
public R getShard(String key) {
|
||||
return getShardInfo(key).getResource();
|
||||
}
|
||||
|
||||
private S getShardInfo(byte[] key) {
|
||||
Iterator<Entry<Long, S>> iterator = nodes.headMap(algo.hash(key))
|
||||
.entrySet().iterator();
|
||||
Entry<Long, S> next = iterator.next();
|
||||
if (iterator.hasNext()) {
|
||||
next = iterator.next();
|
||||
}
|
||||
return next.getValue();
|
||||
}
|
||||
|
||||
public S getShardInfo(String key) {
|
||||
return nodes.floorEntry(algo.hash(getKeyTag(key))).getValue();
|
||||
return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,15 +103,15 @@ public class Sharded<R, S extends ShardInfo<R>> {
|
||||
* @return The tag if it exists, or the original key
|
||||
*/
|
||||
public String getKeyTag(String key) {
|
||||
if (tagPattern != null) {
|
||||
Matcher m = tagPattern.matcher(key);
|
||||
if (m.find())
|
||||
return m.group(1);
|
||||
}
|
||||
return key;
|
||||
if (tagPattern != null) {
|
||||
Matcher m = tagPattern.matcher(key);
|
||||
if (m.find())
|
||||
return m.group(1);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public Collection<S> getAllShards() {
|
||||
return Collections.unmodifiableCollection(nodes.values());
|
||||
return Collections.unmodifiableCollection(nodes.values());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user