package cassandra.metadata;
import cassandra.cql.type.CQL3Type;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Constructor;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public abstract class Partitioner {
public static Partitioner getPartitioner(String partitioner) {
if (partitioner == null) {
return null;
}
if (partitioner.endsWith(".Murmur3Partitioner")) {
return Murmur3Partitioner.INSTANCE;
} else if (partitioner.endsWith(".RandomPartitioner")) {
return RandomPartitioner.INSTANCE;
} else if (partitioner.endsWith(".ByteOrderedPartitioner")) {
return ByteOrderedPartitioner.INSTANCE;
} else if (partitioner.endsWith(".OrderPreservingPartitioner")) {
return OrderPreservingPartitioner.INSTANCE;
} else {
return null;
}
}
public static abstract class Token implements Comparable<Token> {
// Tag
}
public abstract Token getToken(ByteBuffer partitionKey);
public abstract Token getToken(String token);
public static class Murmur3Partitioner extends Partitioner {
public static final Murmur3Partitioner INSTANCE = new Murmur3Partitioner();
public static final LongToken MINIMUM = new LongToken(Long.MIN_VALUE);
@Override
public LongToken getToken(ByteBuffer partitionKey) {
if (partitionKey.remaining() == 0) {
return MINIMUM;
}
long hash = MurmurHash.hash3_x64_128(partitionKey, partitionKey.position(), partitionKey.remaining(), 0)[0];
if (hash == Long.MIN_VALUE) {
return new LongToken(Long.MAX_VALUE);
}
return new LongToken(hash);
}
@Override
public LongToken getToken(String token) {
return new LongToken(new Long(token));
}
public static class LongToken extends Token {
final Long value;
public LongToken(Long value) {
if (value == null) {
throw new NullPointerException("value");
}
this.value = value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
return this == o || o != null && (getClass() == o.getClass()) && value.equals(((LongToken)o).value);
}
@Override
public String toString() {
return value.toString();
}
@Override
public int compareTo(Token token) {
return value.compareTo(((LongToken)token).value);
}
}
}
public static class RandomPartitioner extends Partitioner {
public static final RandomPartitioner INSTANCE = new RandomPartitioner();
public static final BigIntegerToken MINIMUM = new BigIntegerToken(new BigInteger("-1"));
private final ThreadLocal<MessageDigest> md5 = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
};
@Override
public BigIntegerToken getToken(ByteBuffer partitionKey) {
if (partitionKey.remaining() == 0) {
return MINIMUM;
}
MessageDigest digest = md5.get();
digest.reset();
digest.update(partitionKey.duplicate());
return new BigIntegerToken(new BigInteger(digest.digest()).abs());
}
@Override
public BigIntegerToken getToken(String token) {
return new BigIntegerToken(new BigInteger(token));
}
public static class BigIntegerToken extends Token {
final BigInteger value;
public BigIntegerToken(BigInteger value) {
if (value == null) {
throw new NullPointerException("value");
}
this.value = value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
return this == o || o != null && (getClass() == o.getClass()) && value.equals(((BigIntegerToken)o).value);
}
@Override
public String toString() {
return value.toString();
}
@Override
public int compareTo(Token token) {
return value.compareTo(((BigIntegerToken)token).value);
}
}
}
public static class ByteOrderedPartitioner extends Partitioner {
public static final ByteOrderedPartitioner INSTANCE = new ByteOrderedPartitioner();
public static final BytesToken MINIMUM = new BytesToken(new byte[0]);
@Override
public BytesToken getToken(ByteBuffer partitionKey) {
if (partitionKey.remaining() == 0) {
return MINIMUM;
}
byte[] bytes;
int length = partitionKey.remaining();
if (partitionKey.hasArray()) {
int offset = partitionKey.arrayOffset() + partitionKey.position();
if (offset == 0 && length == partitionKey.array().length)
bytes = partitionKey.array();
else
bytes = Arrays.copyOfRange(partitionKey.array(), offset, offset + length);
} else {
bytes = new byte[length];
partitionKey.duplicate().get(bytes);
}
return new BytesToken(bytes);
}
@Override
public BytesToken getToken(String token) {
return new BytesToken(Hex.hexToBytes(token));
}
public static class BytesToken extends Token {
final byte[] value;
public BytesToken(byte[] value) {
if (value == null) {
throw new NullPointerException("value");
}
this.value = value;
}
@Override
public int hashCode() {
return 31 + Arrays.hashCode(value);
}
@Override
public boolean equals(Object o) {
return this == o || o != null && (getClass() == o.getClass()) && Arrays.equals(value, ((BytesToken)o).value);
}
@Override
public String toString() {
return Hex.bytesToHex(value);
}
public int compareTo(Token token) {
return ByteBufUtil.compare(Unpooled.wrappedBuffer(value), Unpooled.wrappedBuffer(((BytesToken)token).value));
}
}
}
public static class OrderPreservingPartitioner extends Partitioner {
public static final OrderPreservingPartitioner INSTANCE = new OrderPreservingPartitioner();
@Override
public StringToken getToken(ByteBuffer partitionKey) {
try {
return new StringToken(CQL3Type.StringCodec.UTF8_INSTANCE.decode(partitionKey));
} catch (Exception e) {
return new StringToken(Hex.bytesToHex(partitionKey));
}
}
@Override
public StringToken getToken(String token) {
return new StringToken(token);
}
public static class StringToken extends Token {
final String value;
public StringToken(String value) {
if (value == null) {
throw new NullPointerException("value");
}
this.value = value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
return this == o || o != null && (getClass() == o.getClass()) && value.equals(((StringToken)o).value);
}
@Override
public int compareTo(Token token) {
return value.compareTo(((StringToken)token).value);
}
@Override
public String toString() {
return value;
}
}
}
//
public static class Hex {
@SuppressWarnings("unchecked")
private static final Constructor<String> stringConstructor = getProtectedConstructor(String.class, int.class, int.class, char[].class);
private static final byte[] charToByte = new byte[256];
private static final char[] byteToChar = new char[16];
static {
for (char c = 0; c < charToByte.length; ++c) {
if (c >= '0' && c <= '9')
charToByte[c] = (byte)(c - '0');
else if (c >= 'A' && c <= 'F')
charToByte[c] = (byte)(c - 'A' + 10);
else if (c >= 'a' && c <= 'f')
charToByte[c] = (byte)(c - 'a' + 10);
else
charToByte[c] = (byte)-1;
}
for (int i = 0; i < 16; ++i) {
byteToChar[i] = Integer.toHexString(i).charAt(0);
}
}
public static byte[] hexToBytes(String str) {
if (str.length() % 2 == 1)
throw new NumberFormatException("An hex string representing bytes must have an even length");
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < bytes.length; i++) {
byte halfByte1 = charToByte[str.charAt(i * 2)];
byte halfByte2 = charToByte[str.charAt(i * 2 + 1)];
if (halfByte1 == -1 || halfByte2 == -1) {
throw new NumberFormatException("Non-hex characters in " + str);
}
bytes[i] = (byte)((halfByte1 << 4) | halfByte2);
}
return bytes;
}
public static String bytesToHex(byte... bytes) {
char[] c = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
int bint = bytes[i];
c[i * 2] = byteToChar[(bint & 0xf0) >> 4];
c[1 + i * 2] = byteToChar[bint & 0x0f];
}
return wrapCharArray(c);
}
public static String bytesToHex(ByteBuffer bytes) {
final int offset = bytes.position();
final int size = bytes.remaining();
final char[] c = new char[size * 2];
for (int i = 0; i < size; i++) {
final int bint = bytes.get(i + offset);
c[i * 2] = Hex.byteToChar[(bint & 0xf0) >> 4];
c[1 + i * 2] = Hex.byteToChar[bint & 0x0f];
}
return Hex.wrapCharArray(c);
}
public static String wrapCharArray(char[] c) {
if (c == null)
return null;
String s = null;
if (stringConstructor != null) {
try {
s = stringConstructor.newInstance(0, c.length, c);
} catch (Exception e) {
// Swallowing as we'll just use a copying constructor
}
}
return s == null ? new String(c) : s;
}
@SuppressWarnings("unchecked")
public static Constructor getProtectedConstructor(Class klass, Class... paramTypes) {
Constructor c;
try {
c = klass.getDeclaredConstructor(paramTypes);
c.setAccessible(true);
return c;
} catch (Exception e) {
return null;
}
}
}
public static class MurmurHash {
public static long[] hash3_x64_128(ByteBuffer key, int offset, int length, long seed) {
final int nblocks = length >> 4;
long h1 = seed;
long h2 = seed;
long c1 = 0x87c37b91114253d5L;
long c2 = 0x4cf5ad432745937fL;
for (int i = 0; i < nblocks; i++) {
long k1 = getblock(key, offset, i * 2);
long k2 = getblock(key, offset, i * 2 + 1);
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
h1 ^= k1;
h1 = rotl64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
h2 ^= k2;
h2 = rotl64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}
offset += nblocks * 16;
long k1 = 0;
long k2 = 0;
switch (length & 15) {
case 15:
k2 ^= ((long)key.get(offset + 14)) << 48;
case 14:
k2 ^= ((long)key.get(offset + 13)) << 40;
case 13:
k2 ^= ((long)key.get(offset + 12)) << 32;
case 12:
k2 ^= ((long)key.get(offset + 11)) << 24;
case 11:
k2 ^= ((long)key.get(offset + 10)) << 16;
case 10:
k2 ^= ((long)key.get(offset + 9)) << 8;
case 9:
k2 ^= ((long)key.get(offset + 8)) << 0;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
h2 ^= k2;
case 8:
k1 ^= ((long)key.get(offset + 7)) << 56;
case 7:
k1 ^= ((long)key.get(offset + 6)) << 48;
case 6:
k1 ^= ((long)key.get(offset + 5)) << 40;
case 5:
k1 ^= ((long)key.get(offset + 4)) << 32;
case 4:
k1 ^= ((long)key.get(offset + 3)) << 24;
case 3:
k1 ^= ((long)key.get(offset + 2)) << 16;
case 2:
k1 ^= ((long)key.get(offset + 1)) << 8;
case 1:
k1 ^= ((long)key.get(offset));
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
h1 ^= k1;
}
h1 ^= length;
h2 ^= length;
h1 += h2;
h2 += h1;
h1 = fmix(h1);
h2 = fmix(h2);
h1 += h2;
h2 += h1;
return (new long[]{h1, h2});
}
protected static long getblock(ByteBuffer key, int offset, int index) {
int i_8 = index << 3;
int blockOffset = offset + i_8;
return ((long)key.get(blockOffset) & 0xff) + (((long)key.get(blockOffset + 1) & 0xff) << 8) +
(((long)key.get(blockOffset + 2) & 0xff) << 16) + (((long)key.get(blockOffset + 3) & 0xff) << 24) +
(((long)key.get(blockOffset + 4) & 0xff) << 32) + (((long)key.get(blockOffset + 5) & 0xff) << 40) +
(((long)key.get(blockOffset + 6) & 0xff) << 48) + (((long)key.get(blockOffset + 7) & 0xff) << 56);
}
protected static long rotl64(long v, int n) {
return ((v << n) | (v >>> (64 - n)));
}
protected static long fmix(long k) {
k ^= k >>> 33;
k *= 0xff51afd7ed558ccdL;
k ^= k >>> 33;
k *= 0xc4ceb9fe1a85ec53L;
k ^= k >>> 33;
return k;
}
}
}