diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java
index 44088b816..6f896bd7f 100644
--- a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java
@@ -16,8 +16,13 @@
package net.openhft.chronicle.hash.hashing;
+import net.openhft.lang.io.Bytes;
+
+import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
/**
* Strategy of reading bytes, defines the abstraction of {@code T} class instances as ordered byte
* sequence. All {@code getXXX(input, offset)} should be consistent to each other in terms of
@@ -38,6 +43,10 @@ import java.nio.ByteOrder;
* range is outside of the bounds of the byte sequence, represented by the given {@code input}.
* However, they could omit checks for better performance.
*
+ *
Only {@link #getByte(Object, long)} and {@link #byteOrder(Object)} methods are abstract in
+ * this class, so implementing them is sufficient for valid {@code Access} instance, but for
+ * efficiency your should override methods used by target {@link LongHashFunction} implementation.
+ *
*
{@code Access} API is designed for inputs, that actually represent byte sequences that lay
* continuously in memory. Theoretically {@code Access} strategy could be implemented for
* non-continuous byte sequences, or abstractions which aren't actually present in memory as they
@@ -45,8 +54,108 @@ import java.nio.ByteOrder;
* be slow.
*
* @param the type of the object to access
+ * @see LongHashFunction#hash(Object, Access, long, long)
*/
-public interface Access {
+public abstract class Access {
+
+ /**
+ * Returns the {@code Access} to any {@link Bytes}. This {@code Access} isn't useful in
+ * the user code, because methods {@link LongHashFunction#hashBytes(Bytes)} and
+ * {@link LongHashFunction#hashBytes(Bytes, long, long)} exist. This {@code Access} could be
+ * used in new {@link LongHashFunction} implementations.
+ *
+ * @return the {@code Access} to {@link Bytes} instances
+ */
+ public static Access toBytes() {
+ return BytesAccess.INSTANCE;
+ }
+
+ /**
+ * Returns the {@code Access} delegating {@code getXXX(input, offset)} methods to {@code
+ * sun.misc.Unsafe.getXXX(input, offset)}.
+ *
+ *
Usage example:
{@code
+ * class Pair {
+ * long first, second;
+ *
+ * static final long pairDataOffset =
+ * theUnsafe.objectFieldOffset(Pair.class.getDeclaredField("first"));
+ *
+ * static long hashPair(Pair pair, LongHashFunction hashFunction) {
+ * return hashFunction.hash(pair, Access.unsafe(), pairDataOffset, 16L);
+ * }
+ * }}
+ *
+ *
{@code null} is a valid input, on accepting {@code null} {@code Unsafe} just interprets
+ * the given offset as a wild memory address. Note that for hashing memory by address there is
+ * a shortcut {@link LongHashFunction#hashMemory(long, long) hashMemory(address, len)} method.
+ *
+ * @param the type of objects to access
+ * @return the unsafe memory {@code Access}
+ */
+ public static Access unsafe() {
+ return (Access) UnsafeAccess.INSTANCE;
+ }
+
+ /**
+ * Returns the {@code Access} to any {@link ByteBuffer}. This {@code Access} isn't useful in
+ * the user code, because methods {@link LongHashFunction#hashBytes(ByteBuffer)} and
+ * {@link LongHashFunction#hashBytes(ByteBuffer, int, int)} exist. This {@code Access} could be
+ * used in new {@link LongHashFunction} implementations.
+ *
+ * @return the {@code Access} to {@link ByteBuffer}s
+ */
+ public static Access toByteBuffer() {
+ return ByteBufferAccess.INSTANCE;
+ }
+
+ /**
+ * Returns the {@code Access} to {@link CharSequence}s backed by {@linkplain
+ * ByteOrder#nativeOrder() native} {@code char} reads, typically from {@code char[]} array.
+ *
+ *
Usage example:
{@code
+ * static long hashStringBuffer(StringBuffer buffer, LongHashFunction hashFunction) {
+ * return hashFunction.hash(buffer, Access.toNativeCharSequence(),
+ * // * 2L because length is passed in bytes, not chars
+ * 0L, buffer.length() * 2L);
+ * }}
+ *
+ *
This method is a shortcut for {@code Access.toCharSequence(ByteOrder.nativeOrder())}.
+ *
+ * @param the {@code CharSequence} subtype (backed by native {@code char reads}) to access
+ * @return the {@code Access} to {@link CharSequence}s backed by native {@code char} reads
+ * @see #toCharSequence(ByteOrder)
+ */
+ public static Access toNativeCharSequence() {
+ return (Access) CharSequenceAccess.nativeCharSequenceAccess();
+ }
+
+ /**
+ * Returns the {@code Access} to {@link CharSequence}s backed by {@code char} reads made in
+ * the specified byte order.
+ *
+ *
Usage example:
{@code
+ * static long hashCharBuffer(CharBuffer buffer, LongHashFunction hashFunction) {
+ * return hashFunction.hash(buffer, Access.toCharSequence(buffer.order()),
+ * // * 2L because length is passed in bytes, not chars
+ * 0L, buffer.length() * 2L);
+ * }}
+ *
+ * @param backingOrder the byte order of {@code char} reads backing
+ * {@code CharSequences} to access
+ * @return the {@code Access} to {@link CharSequence}s backed by {@code char} reads made in
+ * the specified byte order
+ * @param the {@code CharSequence} subtype to access
+ * @see #toNativeCharSequence()
+ */
+ public static Access toCharSequence(ByteOrder backingOrder) {
+ return (Access) CharSequenceAccess.charSequenceAccess(backingOrder);
+ }
+
+ /**
+ * Constructor for use in subclasses.
+ */
+ protected Access() {}
/**
* Reads {@code [offset, offset + 7]} bytes of the byte sequence represented by the given
@@ -58,7 +167,13 @@ public interface Access {
* @return eight bytes as a {@code long} value, in {@linkplain #byteOrder(Object) the expected
* order}
*/
- long getLong(T input, long offset);
+ public long getLong(T input, long offset) {
+ if (byteOrder(input) == LITTLE_ENDIAN) {
+ return getUnsignedInt(input, offset) | (getUnsignedInt(input, offset + 4L) << 32);
+ } else {
+ return getUnsignedInt(input, offset + 4L) | (getUnsignedInt(input, offset) << 32);
+ }
+ }
/**
* Shortcut for {@code getInt(input, offset) & 0xFFFFFFFFL}. Could be implemented more
@@ -70,7 +185,9 @@ public interface Access {
* @return four bytes as an unsigned int value, in {@linkplain #byteOrder(Object) the expected
* order}
*/
- long getUnsignedInt(T input, long offset);
+ public long getUnsignedInt(T input, long offset) {
+ return ((long) getInt(input, offset)) & 0xFFFFFFFFL;
+ }
/**
* Reads {@code [offset, offset + 3]} bytes of the byte sequence represented by the given
@@ -82,7 +199,13 @@ public interface Access {
* @return four bytes as an {@code int} value, in {@linkplain #byteOrder(Object) the expected
* order}
*/
- int getInt(T input, long offset);
+ public int getInt(T input, long offset) {
+ if (byteOrder(input) == LITTLE_ENDIAN) {
+ return getUnsignedShort(input, offset) | (getUnsignedShort(input, offset + 2L) << 16);
+ } else {
+ return getUnsignedShort(input, offset + 2L) | (getUnsignedShort(input, offset) << 16);
+ }
+ }
/**
* Shortcut for {@code getShort(input, offset) & 0xFFFF}. Could be implemented more
@@ -94,7 +217,13 @@ public interface Access {
* @return two bytes as an unsigned short value, in {@linkplain #byteOrder(Object) the expected
* order}
*/
- int getUnsignedShort(T input, long offset);
+ public int getUnsignedShort(T input, long offset) {
+ if (byteOrder(input) == LITTLE_ENDIAN) {
+ return getUnsignedByte(input, offset) | (getUnsignedByte(input, offset + 1L) << 8);
+ } else {
+ return getUnsignedByte(input, offset + 1L) | (getUnsignedByte(input, offset) << 8);
+ }
+ }
/**
* Reads {@code [offset, offset + 1]} bytes of the byte sequence represented by the given
@@ -106,7 +235,9 @@ public interface Access {
* @return two bytes as a {@code short} value, in {@linkplain #byteOrder(Object) the expected
* order}, widened to {@code int}
*/
- int getShort(T input, long offset);
+ public int getShort(T input, long offset) {
+ return (int) (short) getUnsignedShort(input, offset);
+ }
/**
* Shortcut for {@code getByte(input, offset) & 0xFF}. Could be implemented more efficiently.
@@ -116,7 +247,9 @@ public interface Access {
* by the given object
* @return a byte by the given {@code offset}, interpreted as unsigned
*/
- int getUnsignedByte(T input, long offset);
+ public int getUnsignedByte(T input, long offset) {
+ return getByte(input, offset) & 0xFF;
+ }
/**
* Reads a single byte at the given {@code offset} in the byte sequence represented by the given
@@ -127,7 +260,7 @@ public interface Access {
* by the given object
* @return a byte by the given {@code offset}, widened to {@code int}
*/
- int getByte(T input, long offset);
+ public abstract int getByte(T input, long offset);
/**
* The byte order in which all multi-byte {@code getXXX()} reads from the given {@code input}
@@ -136,5 +269,5 @@ public interface Access {
* @param input the accessed object
* @return the byte order of all multi-byte reads from the given {@code input}
*/
- ByteOrder byteOrder(T input);
+ public abstract ByteOrder byteOrder(T input);
}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java b/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java
deleted file mode 100644
index ac67cf2ea..000000000
--- a/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
- *
- * 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 net.openhft.chronicle.hash.hashing;
-
-import net.openhft.lang.io.Bytes;
-
-import java.nio.ByteBuffer;
-
-/**
- * Static methods returning useful {@link Access} implementations.
- */
-public final class Accesses {
-
- /**
- * Returns the {@code Access} delegating {@code getXXX(input, offset)} methods to {@code
- * sun.misc.Unsafe.getXXX(input, offset)}.
- *
- *
Usage example:
{@code
- * class Pair {
- * long first, second;
- *
- * static final long pairDataOffset =
- * theUnsafe.objectFieldOffset(Pair.class.getDeclaredField("first"));
- *
- * static long hashPair(Pair pair, LongHashFunction hashFunction) {
- * return hashFunction.hash(pair, Accesses.unsafe(), pairDataOffset, 16L);
- * }
- * }}
- *
- *
{@code null} is a valid input, on accepting {@code null} {@code Unsafe} just interprets
- * the given offset as a wild memory address.
- *
- * @param the type of objects to access
- * @return the unsafe memory {@code Access}
- */
- public static Access unsafe() {
- return (Access) UnsafeAccess.INSTANCE;
- }
-
- /**
- * Returns the {@code Access} to any {@link ByteBuffer}.
- *
- * @return the {@code Access} to {@link ByteBuffer}s
- */
- public static Access toByteBuffer() {
- return ByteBufferAccess.INSTANCE;
- }
-
- public static Access toBytes() {
- return BytesAccess.INSTANCE;
- }
-
- private Accesses() {}
-}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java
index 703111dff..d6e05dde6 100644
--- a/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java
@@ -19,8 +19,10 @@ package net.openhft.chronicle.hash.hashing;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-enum ByteBufferAccess implements Access {
- INSTANCE;
+final class ByteBufferAccess extends Access {
+ public static final ByteBufferAccess INSTANCE = new ByteBufferAccess();
+
+ private ByteBufferAccess() {}
@Override
public long getLong(ByteBuffer input, long offset) {
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java
index 4e67b40f8..ad08c3649 100644
--- a/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java
@@ -20,8 +20,10 @@ import net.openhft.lang.io.Bytes;
import java.nio.ByteOrder;
-enum BytesAccess implements Access {
- INSTANCE;
+public final class BytesAccess extends Access {
+ public static final BytesAccess INSTANCE = new BytesAccess();
+
+ private BytesAccess() {}
@Override
public long getLong(Bytes input, long offset) {
@@ -62,5 +64,4 @@ enum BytesAccess implements Access {
public ByteOrder byteOrder(Bytes input) {
return input.byteOrder();
}
-
}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java
new file mode 100644
index 000000000..24ac156fc
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+import net.openhft.chronicle.hash.hashing.Access;
+
+import java.nio.ByteOrder;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
+abstract class CharSequenceAccess extends Access {
+
+ public static CharSequenceAccess charSequenceAccess(ByteOrder order) {
+ return order == LITTLE_ENDIAN ?
+ LittleEndianCharSequenceAccess.INSTANCE :
+ BigEndianCharSequenceAccess.INSTANCE;
+ }
+
+ public static CharSequenceAccess nativeCharSequenceAccess() {
+ return charSequenceAccess(ByteOrder.nativeOrder());
+ }
+
+ private static int ix(long offset) {
+ return (int) (offset >> 1);
+ }
+
+ static long getLong(CharSequence input, long offset,
+ int char0Off, int char1Off, int char2Off, int char3Off) {
+ int base = ix(offset);
+ long char0 = input.charAt(base + char0Off);
+ long char1 = input.charAt(base + char1Off);
+ long char2 = input.charAt(base + char2Off);
+ long char3 = input.charAt(base + char3Off);
+ return char0 | (char1 << 16) | (char2 << 32) | (char3 << 48);
+ }
+
+ static long getUnsignedInt(CharSequence input, long offset,
+ int char0Off, int char1Off) {
+ int base = ix(offset);
+ long char0 = input.charAt(base + char0Off);
+ long char1 = input.charAt(base + char1Off);
+ return char0 | (char1 << 16);
+ }
+
+ private CharSequenceAccess() {}
+
+ @Override
+ public int getInt(CharSequence input, long offset) {
+ return (int) getUnsignedInt(input, offset);
+ }
+
+ @Override
+ public int getUnsignedShort(CharSequence input, long offset) {
+ return input.charAt(ix(offset));
+ }
+
+ @Override
+ public int getShort(CharSequence input, long offset) {
+ return (int) (short) input.charAt(ix(offset));
+ }
+
+ static int getUnsignedByte(CharSequence input, long offset, int shift) {
+ return Primitives.unsignedByte(input.charAt(ix(offset)) >> shift);
+ }
+
+ @Override
+ public int getByte(CharSequence input, long offset) {
+ return (int) (byte) getUnsignedByte(input, offset);
+ }
+
+ private static class LittleEndianCharSequenceAccess extends CharSequenceAccess {
+ private static final CharSequenceAccess INSTANCE = new LittleEndianCharSequenceAccess();
+
+ private LittleEndianCharSequenceAccess() {}
+
+ @Override
+ public long getLong(CharSequence input, long offset) {
+ return getLong(input, offset, 0, 1, 2, 3);
+ }
+
+ @Override
+ public long getUnsignedInt(CharSequence input, long offset) {
+ return getUnsignedInt(input, offset, 0, 1);
+ }
+
+ @Override
+ public int getUnsignedByte(CharSequence input, long offset) {
+ return getUnsignedByte(input, offset, ((int) offset & 1) << 3);
+ }
+
+ @Override
+ public ByteOrder byteOrder(CharSequence input) {
+ return LITTLE_ENDIAN;
+ }
+ }
+
+ private static class BigEndianCharSequenceAccess extends CharSequenceAccess {
+ private static final CharSequenceAccess INSTANCE = new BigEndianCharSequenceAccess();
+
+ private BigEndianCharSequenceAccess() {}
+
+ @Override
+ public long getLong(CharSequence input, long offset) {
+ return getLong(input, offset, 3, 2, 1, 0);
+ }
+
+ @Override
+ public long getUnsignedInt(CharSequence input, long offset) {
+ return getUnsignedInt(input, offset, 1, 0);
+ }
+
+ @Override
+ public int getUnsignedByte(CharSequence input, long offset) {
+ return getUnsignedByte(input, offset, (((int) offset & 1) ^ 1) << 3);
+ }
+
+ @Override
+ public ByteOrder byteOrder(CharSequence input) {
+ return BIG_ENDIAN;
+ }
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java b/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java
new file mode 100644
index 000000000..009cd52f1
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+import static java.lang.Long.reverseBytes;
+import static java.lang.Long.rotateRight;
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+import static net.openhft.chronicle.hash.hashing.LongHashFunction.NATIVE_LITTLE_ENDIAN;
+
+/**
+ * Adapted from the C++ CityHash implementation from Google at
+ * http://code.google.com/p/cityhash/source/browse/trunk/src/city.cc.
+ */
+class CityHash_1_1 {
+ private static final CityHash_1_1 INSTANCE = new CityHash_1_1();
+
+ private static final CityHash_1_1 NATIVE_CITY = NATIVE_LITTLE_ENDIAN ?
+ CityHash_1_1.INSTANCE : BigEndian.INSTANCE;
+
+ private CityHash_1_1() {}
+
+ private static final long K0 = 0xc3a5c85c97cb3127L;
+ private static final long K1 = 0xb492b66fbe98f273L;
+ private static final long K2 = 0x9ae16a3b2f90404fL;
+
+ private static long shiftMix(long val) {
+ return val ^ (val >>> 47);
+ }
+
+ private static long hashLen16(long u, long v) {
+ return hashLen16(u, v, K_MUL);
+ }
+
+ private static final long K_MUL = 0x9ddfea08eb382d69L;
+
+ private static long hashLen16(long u, long v, long mul) {
+ long a = shiftMix((u ^ v) * mul);
+ return shiftMix((v ^ a) * mul) * mul;
+ }
+
+ private static long mul(long len) {
+ return K2 + (len << 1);
+ }
+
+ private static long hash1To3Bytes(int len, int firstByte, int midOrLastByte, int lastByte) {
+ int y = firstByte + (midOrLastByte << 8);
+ int z = len + (lastByte << 2);
+ return shiftMix((((long) y) * K2) ^ (((long) z) * K0)) * K2;
+ }
+
+ private static long hash4To7Bytes(long len, long first4Bytes, long last4Bytes) {
+ long mul = mul(len);
+ return hashLen16(len + (first4Bytes << 3), last4Bytes, mul);
+ }
+
+ private static long hash8To16Bytes(long len, long first8Bytes, long last8Bytes) {
+ long mul = mul(len);
+ long a = first8Bytes + K2;
+ long c = rotateRight(last8Bytes, 37) * mul + a;
+ long d = (rotateRight(a, 25) + last8Bytes) * mul;
+ return hashLen16(c, d, mul);
+ }
+
+ long fetch64(Access access, T in, long off) {
+ return access.getLong(in, off);
+ }
+
+ int fetch32(Access access, T in, long off) {
+ return access.getInt(in, off);
+ }
+
+ long toLittleEndian(long v) {
+ return v;
+ }
+
+ int toLittleEndian(int v) {
+ return v;
+ }
+
+ private long hashLen0To16(Access access, T in, long off, long len) {
+ if (len >= 8L) {
+ long a = fetch64(access, in, off);
+ long b = fetch64(access, in, off + len - 8L);
+ return hash8To16Bytes(len, a, b);
+ } else if (len >= 4L) {
+ long a = Primitives.unsignedInt(fetch32(access, in, off));
+ long b = Primitives.unsignedInt(fetch32(access, in, off + len - 4L));
+ return hash4To7Bytes(len, a, b);
+ } else if (len > 0L) {
+ int a = access.getUnsignedByte(in, off);
+ int b = access.getUnsignedByte(in, off + (len >> 1));
+ int c = access.getUnsignedByte(in, off + len - 1L);
+ return hash1To3Bytes((int) len, a, b, c);
+ }
+ return K2;
+ }
+
+ private long hashLen17To32(Access access, T in, long off, long len) {
+ long mul = mul(len);
+ long a = fetch64(access, in, off) * K1;
+ long b = fetch64(access, in, off + 8L);
+ long c = fetch64(access, in, off + len - 8L) * mul;
+ long d = fetch64(access, in, off + len - 16L) * K2;
+ return hashLen16(rotateRight(a + b, 43) + rotateRight(c, 30) + d,
+ a + rotateRight(b + K2, 18) + c, mul);
+ }
+
+ private long hashLen33To64(Access access, T in, long off, long len) {
+ long mul = mul(len);
+ long a = fetch64(access, in, off) * K2;
+ long b = fetch64(access, in, off + 8L);
+ long c = fetch64(access, in, off + len - 24L);
+ long d = fetch64(access, in, off + len - 32L);
+ long e = fetch64(access, in, off + 16L) * K2;
+ long f = fetch64(access, in, off + 24L) * 9L;
+ long g = fetch64(access, in, off + len - 8L);
+ long h = fetch64(access, in, off + len - 16L) * mul;
+ long u = rotateRight(a + g, 43) + (rotateRight(b, 30) + c) * 9L;
+ long v = ((a + g) ^ d) + f + 1L;
+ long w = reverseBytes((u + v) * mul) + h;
+ long x = rotateRight(e + f, 42) + c;
+ long y = (reverseBytes((v + w) * mul) + g) * mul;
+ long z = e + f + c;
+ a = reverseBytes((x + z) * mul + y) + b;
+ b = shiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+ }
+
+ long cityHash64(Access access, T in, long off, long len) {
+ if (len <= 32L) {
+ if (len <= 16L) {
+ return hashLen0To16(access, in, off, len);
+ } else {
+ return hashLen17To32(access, in, off, len);
+ }
+ } else if (len <= 64L) {
+ return hashLen33To64(access, in, off, len);
+ }
+
+ long x = fetch64(access, in, off + len - 40L);
+ long y = fetch64(access, in, off + len - 16L) + fetch64(access, in, off + len - 56L);
+ long z = hashLen16(fetch64(access, in, off + len - 48L) + len,
+ fetch64(access, in, off + len - 24L));
+
+ long vFirst, vSecond, wFirst, wSecond;
+
+ // This and following 3 blocks are produced by a single-click inline-function refactoring.
+ // IntelliJ IDEA ftw
+ // WeakHashLen32WithSeeds
+ long a3 = len;
+ long b3 = z;
+ long w4 = fetch64(access, in, off + len - 64L);
+ long x4 = fetch64(access, in, off + len - 64L + 8L);
+ long y4 = fetch64(access, in, off + len - 64L + 16L);
+ long z4 = fetch64(access, in, off + len - 64L + 24L);
+ a3 += w4;
+ b3 = rotateRight(b3 + a3 + z4, 21);
+ long c3 = a3;
+ a3 += x4 + y4;
+ b3 += rotateRight(a3, 44);
+ vFirst = a3 + z4;
+ vSecond = b3 + c3;
+
+ // WeakHashLen32WithSeeds
+ long a2 = y + K1;
+ long b2 = x;
+ long w3 = fetch64(access, in, off + len - 32L);
+ long x3 = fetch64(access, in, off + len - 32L + 8L);
+ long y3 = fetch64(access, in, off + len - 32L + 16L);
+ long z3 = fetch64(access, in, off + len - 32L + 24L);
+ a2 += w3;
+ b2 = rotateRight(b2 + a2 + z3, 21);
+ long c2 = a2;
+ a2 += x3 + y3;
+ b2 += rotateRight(a2, 44);
+ wFirst = a2 + z3;
+ wSecond = b2 + c2;
+
+ x = x * K1 + fetch64(access, in, off);
+
+ len = (len - 1L) & (~63L);
+ do {
+ x = rotateRight(x + y + vFirst + fetch64(access, in, off + 8L), 37) * K1;
+ y = rotateRight(y + vSecond + fetch64(access, in, off + 48L), 42) * K1;
+ x ^= wSecond;
+ y += vFirst + fetch64(access, in, off + 40L);
+ z = rotateRight(z + wFirst, 33) * K1;
+
+ // WeakHashLen32WithSeeds
+ long a1 = vSecond * K1;
+ long b1 = x + wFirst;
+ long w2 = fetch64(access, in, off);
+ long x2 = fetch64(access, in, off + 8L);
+ long y2 = fetch64(access, in, off + 16L);
+ long z2 = fetch64(access, in, off + 24L);
+ a1 += w2;
+ b1 = rotateRight(b1 + a1 + z2, 21);
+ long c1 = a1;
+ a1 += x2 + y2;
+ b1 += rotateRight(a1, 44);
+ vFirst = a1 + z2;
+ vSecond = b1 + c1;
+
+ // WeakHashLen32WithSeeds
+ long a = z + wSecond;
+ long b = y + fetch64(access, in, off + 16L);
+ long w1 = fetch64(access, in, off + 32L);
+ long x1 = fetch64(access, in, off + 32L + 8L);
+ long y1 = fetch64(access, in, off + 32L + 16L);
+ long z1 = fetch64(access, in, off + 32L + 24L);
+ a += w1;
+ b = rotateRight(b + a + z1, 21);
+ long c = a;
+ a += x1 + y1;
+ b += rotateRight(a, 44);
+ wFirst = a + z1;
+ wSecond = b + c;
+
+ long tmp = x;
+ x = z;
+ z = tmp;
+
+ len -= 64L;
+ off += 64L;
+ } while (len != 0);
+ return hashLen16(hashLen16(vFirst, wFirst) + shiftMix(y) * K1 + z,
+ hashLen16(vSecond, wSecond) + x);
+ }
+
+ private static class BigEndian extends CityHash_1_1 {
+ private static final BigEndian INSTANCE = new BigEndian();
+ private BigEndian() {}
+
+ @Override
+ long fetch64(Access access, T in, long off) {
+ return reverseBytes(super.fetch64(access, in, off));
+ }
+
+ @Override
+ int fetch32(Access access, T in, long off) {
+ return Integer.reverseBytes(super.fetch32(access, in, off));
+ }
+
+ @Override
+ long toLittleEndian(long v) {
+ return reverseBytes(v);
+ }
+
+ @Override
+ int toLittleEndian(int v) {
+ return Integer.reverseBytes(v);
+ }
+ }
+
+ private static class AsLongHashFunction extends LongHashFunction {
+ public static final AsLongHashFunction INSTANCE = new AsLongHashFunction();
+ private static final long serialVersionUID = 0L;
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+
+ @Override
+ public long hashLong(long input) {
+ input = NATIVE_CITY.toLittleEndian(input);
+ long hash = hash8To16Bytes(8L, input, input);
+ return finalize(hash);
+ }
+
+ @Override
+ public long hashInt(int input) {
+ input = NATIVE_CITY.toLittleEndian(input);
+ long unsignedInt = Primitives.unsignedInt(input);
+ long hash = hash4To7Bytes(4L, unsignedInt, unsignedInt);
+ return finalize(hash);
+ }
+
+ @Override
+ public long hashShort(short input) {
+ return hashChar((char) input);
+ }
+
+ private static final int FIRST_SHORT_BYTE_SHIFT = NATIVE_LITTLE_ENDIAN ? 0 : 8;
+ // JIT could probably optimize & -1 to no-op
+ private static final int FIRST_SHORT_BYTE_MASK = NATIVE_LITTLE_ENDIAN ? 0xFF : -1;
+ private static final int SECOND_SHORT_BYTE_SHIFT = 8 - FIRST_SHORT_BYTE_SHIFT;
+ private static final int SECOND_SHORT_BYTE_MASK = NATIVE_LITTLE_ENDIAN ? -1 : 0xFF;
+
+ @Override
+ public long hashChar(char input) {
+ int unsignedInput = (int) input;
+ int firstByte = (unsignedInput >> FIRST_SHORT_BYTE_SHIFT) & FIRST_SHORT_BYTE_MASK;
+ int secondByte = (unsignedInput >> SECOND_SHORT_BYTE_SHIFT) & SECOND_SHORT_BYTE_MASK;
+ long hash = hash1To3Bytes(2, firstByte, secondByte, secondByte);
+ return finalize(hash);
+ }
+
+ @Override
+ public long hashByte(byte input) {
+ int unsignedByte = Primitives.unsignedByte(input);
+ long hash = hash1To3Bytes(1, unsignedByte, unsignedByte, unsignedByte);
+ return finalize(hash);
+ }
+
+ @Override
+ public long hashVoid() {
+ return K2;
+ }
+
+ @Override
+ public long hash(T input, Access access, long off, long len) {
+ long hash;
+ if (access.byteOrder(input) == LITTLE_ENDIAN) {
+ hash = CityHash_1_1.INSTANCE.cityHash64(access, input, off, len);
+ } else {
+ hash = BigEndian.INSTANCE.cityHash64(access, input, off, len);
+ }
+ return finalize(hash);
+ }
+
+ long finalize(long hash) {
+ return hash;
+ }
+ }
+
+ public static LongHashFunction asLongHashFunctionWithoutSeed() {
+ return AsLongHashFunction.INSTANCE;
+ }
+
+ private static class AsLongHashFunctionSeeded extends AsLongHashFunction {
+ private static final long serialVersionUID = 0L;
+
+ private final long seed0, seed1;
+ private transient long voidHash;
+
+ private AsLongHashFunctionSeeded(long seed0, long seed1) {
+ this.seed0 = seed0;
+ this.seed1 = seed1;
+ voidHash = finalize(K2);
+ }
+
+ @Override
+ public long hashVoid() {
+ return voidHash;
+ }
+
+ @Override
+ protected long finalize(long hash) {
+ return hashLen16(hash - seed0, seed1);
+ }
+ }
+
+ public static LongHashFunction asLongHashFunctionWithSeed(long seed) {
+ return new AsLongHashFunctionSeeded(K2, seed);
+ }
+
+ public static LongHashFunction asLongHashFunctionWithTwoSeeds(long seed0, long seed1) {
+ return new AsLongHashFunctionSeeded(seed0, seed1);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java b/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java
deleted file mode 100644
index 2548ee39a..000000000
--- a/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2014 Higher Frequency Trading
- *
- * http://www.higherfrequencytrading.com
- *
- * 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 net.openhft.chronicle.hash.hashing;
-
-import net.openhft.lang.io.Bytes;
-
-import static net.openhft.chronicle.hash.hashing.UnsafeAccess.BYTE_BASE;
-import static net.openhft.chronicle.hash.hashing.UnsafeAccess.CHAR_BASE;
-
-public final class Hasher {
-
- // MurmurHash3, derived from
- // https://github.com/google/guava/blob/fa95e381e665d8ee9639543b99ed38020c8de5ef
- // /guava/src/com/google/common/hash/Murmur3_128HashFunction.java
-
-
- private static final long C1 = 0x87c37b91114253d5L;
- private static final long C2 = 0x4cf5ad432745937fL;
-
- /**
- * Returns the hash code for {@code len} continuous bytes of the given {@code input} object,
- * starting from the given offset. The abstraction of input as ordered byte sequence and
- * "offset within the input" is defined by the given {@code access} strategy.
- *
- *
This method doesn't promise to throw a {@code RuntimeException} if {@code
- * [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code
- * access} strategy for the given {@code input}, so use this method with caution.
- *
- * @param input the object to read bytes from
- * @param access access which defines the abstraction of the given input
- * as ordered byte sequence
- * @param offset offset to the first byte of the subsequence to hash
- * @param length length of the subsequence to hash
- * @param the type of the input
- * @return hash code for the specified bytes subsequence
- */
- public static long hash(T input, Access access, long offset, long length) {
- assert offset >= 0L && length >= 0L;
-
- long h1 = 0L;
- long h2 = 0L;
- long remaining = length;
- while (remaining >= 16L) {
- long k1 = access.getLong(input, offset);
- long k2 = access.getLong(input, offset + 8L);
- offset += 16L;
- remaining -= 16L;
- h1 ^= mixK1(k1);
-
- h1 = Long.rotateLeft(h1, 27);
- h1 += h2;
- h1 = h1 * 5L + 0x52dce729L;
-
- h2 ^= mixK2(k2);
-
- h2 = Long.rotateLeft(h2, 31);
- h2 += h1;
- h2 = h2 * 5L + 0x38495ab5L;
- }
-
- if (remaining > 0L) {
- long k1 = 0L;
- long k2 = 0L;
- switch ((int) remaining) {
- case 15:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 14L)) << 48;// fall through
- case 14:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 13L)) << 40;// fall through
- case 13:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 12L)) << 32;// fall through
- case 12:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 11L)) << 24;// fall through
- case 11:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 10L)) << 16;// fall through
- case 10:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 9L)) << 8; // fall through
- case 9:
- k2 ^= ((long) access.getUnsignedByte(input, offset + 8L)); // fall through
- case 8:
- k1 ^= access.getLong(input, offset);
- break;
- case 7:
- k1 ^= ((long) access.getUnsignedByte(input, offset + 6L)) << 48; // fall through
- case 6:
- k1 ^= ((long) access.getUnsignedByte(input, offset + 5L)) << 40; // fall through
- case 5:
- k1 ^= ((long) access.getUnsignedByte(input, offset + 4L)) << 32; // fall through
- case 4:
- k1 ^= access.getUnsignedInt(input, offset);
- break;
- case 3:
- k1 ^= ((long) access.getUnsignedByte(input, offset + 2L)) << 16; // fall through
- case 2:
- k1 ^= ((long) access.getUnsignedByte(input, offset + 1L)) << 8; // fall through
- case 1:
- k1 ^= ((long) access.getUnsignedByte(input, offset));
- case 0:
- break;
- default:
- throw new AssertionError("Should never get here.");
- }
- h1 ^= mixK1(k1);
- h2 ^= mixK2(k2);
- }
-
- return finalize(length, h1, h2);
- }
-
- private static long finalize(long length, long h1, long h2) {
- h1 ^= length;
- h2 ^= length;
-
- h1 += h2;
- h2 += h1;
-
- h1 = fmix64(h1);
- h2 = fmix64(h2);
-
- h1 += h2;
- return h1;
- }
-
-
- private static long fmix64(long k) {
- k ^= k >>> 33;
- k *= 0xff51afd7ed558ccdL;
- k ^= k >>> 33;
- k *= 0xc4ceb9fe1a85ec53L;
- k ^= k >>> 33;
- return k;
- }
-
- private static long mixK1(long k1) {
- k1 *= C1;
- k1 = Long.rotateLeft(k1, 31);
- k1 *= C2;
- return k1;
- }
-
- private static long mixK2(long k2) {
- k2 *= C2;
- k2 = Long.rotateLeft(k2, 33);
- k2 *= C1;
- return k2;
- }
-
- public static long hash(Bytes bytes) {
- return hash(bytes, bytes.position(), bytes.limit());
- }
-
- public static long hash(Bytes bytes, long offset, long limit) {
- return hash(bytes, Accesses.toBytes(), offset, limit - offset);
- }
-
- public static long hash(byte[] array) {
- return hash(array, Accesses.unsafe(), BYTE_BASE, (long) array.length);
- }
-
- public static long hash(char[] array) {
- return hash(array, Accesses.unsafe(), CHAR_BASE, array.length * 2L);
- }
-
- public static long hash(int value) {
- long k1 = Primitives.unsignedInt(value);
- long h1 = 0L ^ mixK1(k1);
- long h2 = 0L;
- return finalize(4L, h1, h2);
- }
-
- public static long hash(long value) {
- long k1 = value;
- long h1 = 0L ^ mixK1(k1);
- long h2 = 0L;
- return finalize(8L, h1, h2);
- }
-
- private Hasher() {}
-}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java
new file mode 100644
index 000000000..145806fb0
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+import java.lang.reflect.Field;
+
+enum HotSpotPrior7u6StringHash implements StringHash {
+ INSTANCE;
+
+ private static final long valueOffset;
+ private static final long offsetOffset;
+
+ static {
+ try {
+ Field valueField = String.class.getDeclaredField("value");
+ valueOffset = UnsafeAccess.UNSAFE.objectFieldOffset(valueField);
+
+ Field offsetField = String.class.getDeclaredField("offset");
+ offsetOffset = UnsafeAccess.UNSAFE.objectFieldOffset(offsetField);
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public long longHash(String s, LongHashFunction hashFunction, int off, int len) {
+ char[] value = (char[]) UnsafeAccess.UNSAFE.getObject(s, valueOffset);
+ int offset = UnsafeAccess.UNSAFE.getInt(s, offsetOffset);
+ return hashFunction.hashChars(value, offset + off, len);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java b/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java
new file mode 100644
index 000000000..864145ba6
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+import net.openhft.chronicle.hash.serialization.BytesInterop;
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.NativeBytes;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+import static java.nio.ByteOrder.nativeOrder;
+import static net.openhft.chronicle.hash.hashing.CharSequenceAccess.nativeCharSequenceAccess;
+import static net.openhft.chronicle.hash.hashing.UnsafeAccess.*;
+
+/**
+ * Hash function producing {@code long}-valued result from byte sequences of any length and
+ * a plenty of different sources which "feels like byte sequences". Except {@link
+ * #hashBytes(byte[])}, {@link #hashBytes(ByteBuffer)} (with their "sliced" versions) and
+ * {@link #hashMemory(long, long)} methods, which actually accept byte sequences, notion of byte
+ * sequence is defined as follows:
+ *
+ *
For methods accepting arrays of Java primitives, {@code String}s and
+ * {@code StringBuilder}s, byte sequence is how the input's bytes are actually lay in memory.
+ *
+ *
For methods accepting single primitive values, byte sequence is how this primitive
+ * would be put into memory with {@link ByteOrder#nativeOrder() native} byte order, or
+ * equivalently, {@code hashXxx(primitive)} has always the same result as {@code
+ * hashXxxs(new xxx[] {primitive})}, where "xxx" is any Java primitive type name.
+ *
For {@link #hash(Object, Access, long, long)} method byte sequence abstraction
+ * is defined by the given {@link Access} strategy to the given object.
+ *
+ *
+ * @see BytesInterop#hash(LongHashFunction, Object)
+ */
+public abstract class LongHashFunction implements Serializable {
+ private static final long serialVersionUID = 0L;
+
+ static final boolean NATIVE_LITTLE_ENDIAN = nativeOrder() == LITTLE_ENDIAN;
+ static final byte TRUE_BYTE_VALUE = UNSAFE.getByte(new boolean[] {true}, BOOLEAN_BASE);
+ static final byte FALSE_BYTE_VALUE = UNSAFE.getByte(new boolean[] {false}, BOOLEAN_BASE);
+
+ /**
+ * Returns a hash function implementing
+ *
+ * CityHash64 algorithm, version 1.1 without seed values. This implementation produce
+ * equal results for equal input on platforms with different {@link ByteOrder}, but is slower
+ * on big-endian platforms than on little-endian.
+ *
+ * @see #city_1_1(long)
+ * @see #city_1_1(long, long)
+ */
+ public static LongHashFunction city_1_1() {
+ return CityHash_1_1.asLongHashFunctionWithoutSeed();
+ }
+
+ /**
+ * Returns a hash function implementing
+ *
+ * CityHash64 algorithm, version 1.1 using the given seed value. This implementation produce
+ * equal results for equal input on platforms with different {@link ByteOrder}, but is slower
+ * on big-endian platforms than on little-endian.
+ *
+ * @see #city_1_1()
+ * @see #city_1_1(long, long)
+ */
+ public static LongHashFunction city_1_1(long seed) {
+ return CityHash_1_1.asLongHashFunctionWithSeed(seed);
+ }
+
+ /**
+ * Returns a hash function implementing
+ *
+ * CityHash64 algorithm, version 1.1 using the two given seed values. This implementation
+ * produce equal results for equal input on platforms with different {@link ByteOrder}, but
+ * is slower on big-endian platforms than on little-endian.
+ *
+ * @see #city_1_1()
+ * @see #city_1_1(long)
+ */
+ public static LongHashFunction city_1_1(long seed0, long seed1) {
+ return CityHash_1_1.asLongHashFunctionWithTwoSeeds(seed0, seed1);
+ }
+
+ private static StringHash stringHash;
+ static {
+ try {
+ if (System.getProperty("java.vm.name").contains("HotSpot")) {
+ if (System.getProperty("java.version").compareTo("1.7.0_06") >= 0) {
+ stringHash = ModernHotSpotStringHash.INSTANCE;
+ } else {
+ stringHash = HotSpotPrior7u6StringHash.INSTANCE;
+ }
+ } else {
+ // try to initialize this version anyway
+ stringHash = HotSpotPrior7u6StringHash.INSTANCE;
+ }
+ } catch (Throwable e) {
+ // ignore
+ } finally {
+ if (stringHash == null)
+ stringHash = UnknownJvmStringHash.INSTANCE;
+ }
+ }
+
+ private static void checkArrayOffs(int arrayLength, int off, int len) {
+ if (len < 0 || off < 0 || off + len > arrayLength || off + len < 0)
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * Constructor for use in subclasses.
+ */
+ protected LongHashFunction() {}
+
+ /**
+ * Returns the hash code for the given {@code long} value; this method is consistent with
+ * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input}
+ * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example,
+ * the result of {@code hashLong(v)} call is identical to the result of
+ * {@code hashLongs(new long[] {v})} call for any {@code long} value.
+ */
+ public abstract long hashLong(long input);
+
+ /**
+ * Returns the hash code for the given {@code int} value; this method is consistent with
+ * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input}
+ * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example,
+ * the result of {@code hashInt(v)} call is identical to the result of
+ * {@code hashInts(new int[] {v})} call for any {@code int} value.
+ */
+ public abstract long hashInt(int input);
+
+ /**
+ * Returns the hash code for the given {@code short} value; this method is consistent with
+ * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input}
+ * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example,
+ * the result of {@code hashShort(v)} call is identical to the result of
+ * {@code hashShorts(new short[] {v})} call for any {@code short} value.
+ * As a consequence, {@code hashShort(v)} call produce always the same result as {@code
+ * hashChar((char) v)}.
+ */
+ public abstract long hashShort(short input);
+
+ /**
+ * Returns the hash code for the given {@code char} value; this method is consistent with
+ * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input}
+ * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example,
+ * the result of {@code hashChar(v)} call is identical to the result of
+ * {@code hashChars(new char[] {v})} call for any {@code char} value.
+ * As a consequence, {@code hashChar(v)} call produce always the same result as {@code
+ * hashShort((short) v)}.
+ */
+ public abstract long hashChar(char input);
+
+ /**
+ * Returns the hash code for the given {@code byte} value. This method is consistent with
+ * {@code LongHashFunction} methods that accept sequences of bytes. For example, the result of
+ * {@code hashByte(v)} call is identical to the result of
+ * {@code hashBytes(new byte[] {v})} call for any {@code byte} value.
+ */
+ public abstract long hashByte(byte input);
+
+ /**
+ * Returns the hash code for the empty (zero-length) bytes sequence,
+ * for example {@code hashBytes(new byte[0])}.
+ */
+ public abstract long hashVoid();
+
+ /**
+ * Returns the hash code for {@code len} continuous bytes of the given {@code input} object,
+ * starting from the given offset. The abstraction of input as ordered byte sequence and
+ * "offset within the input" is defined by the given {@code access} strategy.
+ *
+ *
This method doesn't promise to throw a {@code RuntimeException} if {@code
+ * [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code
+ * access} strategy for the given {@code input}, so use this method with caution.
+ *
+ * @param input the object to read bytes from
+ * @param access access which defines the abstraction of the given input
+ * as ordered byte sequence
+ * @param off offset to the first byte of the subsequence to hash
+ * @param len length of the subsequence to hash
+ * @param the type of the input
+ * @return hash code for the specified bytes subsequence
+ */
+ public abstract long hash(T input, Access access, long off, long len);
+
+ private long unsafeHash(Object input, long off, long len) {
+ return hash(input, UnsafeAccess.INSTANCE, off, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashBooleans(boolean[]) hashBooleans(new boolean[] {input})}.
+ * Note that this is not necessarily equal to {@code hashByte(input ? (byte) 1 : (byte) 0)},
+ * because booleans could be stored differently in this JVM.
+ */
+ public long hashBoolean(boolean input) {
+ return hashByte(input ? TRUE_BYTE_VALUE : FALSE_BYTE_VALUE);
+ }
+
+ /**
+ * Shortcut for {@link #hashBooleans(boolean[], int, int) hashBooleans(input, 0, input.length)}.
+ */
+ public long hashBooleans(@NotNull boolean[] input) {
+ return hashBooleans(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for the specified subsequence of the given {@code boolean} array.
+ *
+ * @param input the array to read data from
+ * @param off index of the first {@code boolean} in the subsequence to hash
+ * @param len length of the subsequence to hash
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashBooleans(@NotNull boolean[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, BOOLEAN_BASE + off, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashBytes(byte[], int, int) hashBytes(input, 0, input.length)}.
+ */
+ public long hashBytes(@NotNull byte[] input) {
+ return hashBytes(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for the specified subsequence of the given {@code byte} array.
+ *
+ * @param input the array to read bytes from
+ * @param off index of the first {@code byte} in the subsequence to hash
+ * @param len length of the subsequence to hash
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashBytes(@NotNull byte[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, BYTE_BASE + off, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashBytes(ByteBuffer, int, int)
+ * hashBytes(input, input.position(), input.remaining())}.
+ */
+ public long hashBytes(ByteBuffer input) {
+ return hashBytes(input, input.position(), input.remaining());
+ }
+
+ /**
+ * Returns the hash code for the specified subsequence of the given {@code ByteBuffer}.
+ *
+ *
This method doesn't alter the state (mark, position, limit or order) of the given
+ * {@code ByteBuffer}.
+ *
+ * @param input the buffer to read bytes from
+ * @param off index of the first {@code byte} in the subsequence to hash
+ * @param len length of the subsequence to hash
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.capacity()}
+ * or {@code len < 0}
+ */
+ public long hashBytes(@NotNull ByteBuffer input, int off, int len) {
+ checkArrayOffs(input.capacity(), off, len);
+ return hash(input, ByteBufferAccess.INSTANCE, off, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashBytes(Bytes, long, long)
+ * hashBytes(input, input.position(), input.remaining())}.
+ */
+ public long hashBytes(Bytes input) {
+ return hashBytes(input, input.position(), input.remaining());
+ }
+
+ /**
+ * Returns the hash code for the specified subsequence of the given {@code Bytes}.
+ *
+ *
This method doesn't alter the state (position or limit) of the given {@code Bytes}.
+ *
+ * @param input the {@code Bytes} to read bytes from
+ * @param off index of the first {@code byte} in the subsequence to hash
+ * @param len length of the subsequence to hash
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.capacity()}
+ * or {@code len < 0}
+ */
+ public long hashBytes(Bytes input, long off, long len) {
+ if (len < 0 || off < 0 || off + len > input.capacity() || off + len < 0)
+ throw new IndexOutOfBoundsException();
+ if (input instanceof NativeBytes) {
+ return hashMemory(input.address() + off, len);
+ } else {
+ return hash(input, toBytes(), off, len);
+ }
+ }
+
+ /**
+ * Returns the hash code of bytes of the wild memory from the given address. Use with caution.
+ *
+ * @param address the address of the first byte to hash
+ * @param len length of the byte sequence to hash
+ * @return hash code for the specified byte sequence
+ */
+ public long hashMemory(long address, long len) {
+ return unsafeHash(null, address, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashChars(char[], int, int) hashChars(input, 0, input.length)}.
+ */
+ public long hashChars(@NotNull char[] input) {
+ return hashChars(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for bytes, as they lay in memory, of the specified subsequence
+ * of the given {@code char} array.
+ *
+ * @param input the array to read data from
+ * @param off index of the first {@code char} in the subsequence to hash
+ * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes
+ * sequence to hash is {@code len * 2L})
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashChars(@NotNull char[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, CHAR_BASE + (off * 2L), len * 2L);
+ }
+
+ /**
+ * Shortcut for {@link #hashChars(String, int, int) hashChars(input, 0, input.length())}.*
+ */
+ public long hashChars(@NotNull String input) {
+ return stringHash.longHash(input, this, 0, input.length());
+ }
+
+ /**
+ * Returns the hash code for bytes of the specified subsequence of the given {@code String}'s
+ * underlying {@code char} array.
+ *
+ * @param input the string which bytes to hash
+ * @param off index of the first {@code char} in the subsequence to hash
+ * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes
+ * sequence to hash is {@code len * 2L})
+ * @return the hash code of the given {@code String}'s bytes
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()}
+ * or {@code len < 0}
+ */
+ public long hashChars(@NotNull String input, int off, int len) {
+ checkArrayOffs(input.length(), off, len);
+ return stringHash.longHash(input, this, off, len);
+ }
+
+ /**
+ * Shortcut for {@link #hashChars(StringBuilder, int, int) hashChars(input, 0, input.length())}.
+ */
+ public long hashChars(@NotNull StringBuilder input) {
+ return hashNativeChars(input);
+ }
+
+ /**
+ * Returns the hash code for bytes of the specified subsequence of the given
+ * {@code StringBuilder}'s underlying {@code char} array.
+ *
+ * @param input the string builder which bytes to hash
+ * @param off index of the first {@code char} in the subsequence to hash
+ * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes
+ * sequence to hash is {@code len * 2L})
+ * @return the hash code of the given {@code String}'s bytes
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()}
+ * or {@code len < 0}
+ */
+ public long hashChars(@NotNull StringBuilder input, int off, int len) {
+ return hashNativeChars(input, off, len);
+ }
+
+ long hashNativeChars(CharSequence input) {
+ return hashNativeChars(input, 0, input.length());
+ }
+
+ long hashNativeChars(CharSequence input, int off, int len) {
+ checkArrayOffs(input.length(), off, len);
+ return hash(input, nativeCharSequenceAccess(), off * 2L, len * 2L);
+ }
+
+ /**
+ * Shortcut for {@link #hashShorts(short[], int, int) hashShorts(input, 0, input.length)}.
+ */
+ public long hashShorts(@NotNull short[] input) {
+ return hashShorts(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for bytes, as they lay in memory, of the specified subsequence
+ * of the given {@code short} array.
+ *
+ * @param input the array to read data from
+ * @param off index of the first {@code short} in the subsequence to hash
+ * @param len length of the subsequence to hash, in shorts (i. e. the length of the bytes
+ * sequence to hash is {@code len * 2L})
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashShorts(@NotNull short[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, SHORT_BASE + (off * 2L), len * 2L);
+ }
+
+ /**
+ * Shortcut for {@link #hashInts(int[], int, int) hashInts(input, 0, input.length)}.
+ */
+ public long hashInts(@NotNull int[] input) {
+ return hashInts(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for bytes, as they lay in memory, of the specified subsequence
+ * of the given {@code int} array.
+ *
+ * @param input the array to read data from
+ * @param off index of the first {@code int} in the subsequence to hash
+ * @param len length of the subsequence to hash, in ints (i. e. the length of the bytes
+ * sequence to hash is {@code len * 4L})
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashInts(@NotNull int[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, INT_BASE + (off * 4L), len * 4L);
+ }
+
+ /**
+ * Shortcut for {@link #hashLongs(long[], int, int) hashLongs(input, 0, input.length)}.
+ */
+ public long hashLongs(@NotNull long[] input) {
+ return hashLongs(input, 0, input.length);
+ }
+
+ /**
+ * Returns the hash code for bytes, as they lay in memory, of the specified subsequence
+ * of the given {@code long} array.
+ *
+ * @param input the array to read data from
+ * @param off index of the first {@code long} in the subsequence to hash
+ * @param len length of the subsequence to hash, in longs (i. e. the length of the bytes
+ * sequence to hash is {@code len * 8L})
+ * @return hash code for the specified subsequence
+ * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length}
+ * or {@code len < 0}
+ */
+ public long hashLongs(@NotNull long[] input, int off, int len) {
+ checkArrayOffs(input.length, off, len);
+ return unsafeHash(input, LONG_BASE + (off * 8L), len * 8L);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java
new file mode 100644
index 000000000..923491527
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+import java.lang.reflect.Field;
+
+enum ModernHotSpotStringHash implements StringHash {
+ INSTANCE;
+
+ private static final long valueOffset;
+
+ static {
+ try {
+ Field valueField = String.class.getDeclaredField("value");
+ valueOffset = UnsafeAccess.UNSAFE.objectFieldOffset(valueField);
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public long longHash(String s, LongHashFunction hashFunction, int off, int len) {
+ char[] value = (char[]) UnsafeAccess.UNSAFE.getObject(s, valueOffset);
+ return hashFunction.hashChars(value, off, len);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java
new file mode 100644
index 000000000..acd8e7008
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+interface StringHash {
+
+ long longHash(String s, LongHashFunction hashFunction, int off, int len);
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java
new file mode 100644
index 000000000..d78474cdc
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * 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 net.openhft.chronicle.hash.hashing;
+
+enum UnknownJvmStringHash implements StringHash {
+ INSTANCE;
+
+ @Override
+ public long longHash(String s, LongHashFunction hashFunction, int off, int len) {
+ return hashFunction.hashNativeChars(s, off, len);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java
index b135be6e9..b66b5f607 100644
--- a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java
@@ -23,10 +23,11 @@ import java.nio.ByteOrder;
import static net.openhft.chronicle.hash.hashing.Primitives.*;
-enum UnsafeAccess implements Access