diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java
new file mode 100644
index 000000000..44088b816
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java
@@ -0,0 +1,140 @@
+/*
+ * 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.nio.ByteOrder;
+
+/**
+ * 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
+ * ordered byte sequence each {@code T} instance represents. For example, if some {@code
+ * Access} implementation returns {@link ByteOrder#LITTLE_ENDIAN} on {@link #byteOrder(Object)
+ * byteOrder(input)} call, the following expressions should always have the same value:
+ *
{@code getXXX(input, offset)} methods could throw unchecked exceptions when requested bytes
+ * range is outside of the bounds of the byte sequence, represented by the given {@code input}.
+ * However, they could omit checks for better performance.
+ *
+ *
{@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
+ * are accessed, but this should be awkward, and hashing using such {@code Access} is expected to
+ * be slow.
+ *
+ * @param the type of the object to access
+ */
+public interface Access {
+
+ /**
+ * Reads {@code [offset, offset + 7]} bytes of the byte sequence represented by the given
+ * {@code input} as a single {@code long} value.
+ *
+ * @param input the object to access
+ * @param offset offset to the first byte to read within the byte sequence represented
+ * by the given object
+ * @return eight bytes as a {@code long} value, in {@linkplain #byteOrder(Object) the expected
+ * order}
+ */
+ long getLong(T input, long offset);
+
+ /**
+ * Shortcut for {@code getInt(input, offset) & 0xFFFFFFFFL}. Could be implemented more
+ * efficiently.
+ *
+ * @param input the object to access
+ * @param offset offset to the first byte to read within the byte sequence represented
+ * by the given object
+ * @return four bytes as an unsigned int value, in {@linkplain #byteOrder(Object) the expected
+ * order}
+ */
+ long getUnsignedInt(T input, long offset);
+
+ /**
+ * Reads {@code [offset, offset + 3]} bytes of the byte sequence represented by the given
+ * {@code input} as a single {@code int} value.
+ *
+ * @param input the object to access
+ * @param offset offset to the first byte to read within the byte sequence represented
+ * by the given object
+ * @return four bytes as an {@code int} value, in {@linkplain #byteOrder(Object) the expected
+ * order}
+ */
+ int getInt(T input, long offset);
+
+ /**
+ * Shortcut for {@code getShort(input, offset) & 0xFFFF}. Could be implemented more
+ * efficiently.
+ *
+ * @param input the object to access
+ * @param offset offset to the first byte to read within the byte sequence represented
+ * by the given object
+ * @return two bytes as an unsigned short value, in {@linkplain #byteOrder(Object) the expected
+ * order}
+ */
+ int getUnsignedShort(T input, long offset);
+
+ /**
+ * Reads {@code [offset, offset + 1]} bytes of the byte sequence represented by the given
+ * {@code input} as a single {@code short} value, returned widened to {@code int}.
+ *
+ * @param input the object to access
+ * @param offset offset to the first byte to read within the byte sequence represented
+ * by the given object
+ * @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);
+
+ /**
+ * Shortcut for {@code getByte(input, offset) & 0xFF}. Could be implemented more efficiently.
+ *
+ * @param input the object to access
+ * @param offset offset to the byte to read within the byte sequence represented
+ * by the given object
+ * @return a byte by the given {@code offset}, interpreted as unsigned
+ */
+ int getUnsignedByte(T input, long offset);
+
+ /**
+ * Reads a single byte at the given {@code offset} in the byte sequence represented by the given
+ * {@code input}, returned widened to {@code int}.
+ *
+ * @param input the object to access
+ * @param offset offset to the byte to read within the byte sequence represented
+ * by the given object
+ * @return a byte by the given {@code offset}, widened to {@code int}
+ */
+ int getByte(T input, long offset);
+
+ /**
+ * The byte order in which all multi-byte {@code getXXX()} reads from the given {@code input}
+ * are performed.
+ *
+ * @param input the accessed object
+ * @return the byte order of all multi-byte reads from the given {@code input}
+ */
+ 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
new file mode 100644
index 000000000..d9b71abd5
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+import java.nio.ByteOrder;
+
+/**
+ * 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
new file mode 100644
index 000000000..703111dff
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java
@@ -0,0 +1,64 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+enum ByteBufferAccess implements Access {
+ INSTANCE;
+
+ @Override
+ public long getLong(ByteBuffer input, long offset) {
+ return input.getLong((int) offset);
+ }
+
+ @Override
+ public long getUnsignedInt(ByteBuffer input, long offset) {
+ return Primitives.unsignedInt(getInt(input, offset));
+ }
+
+ @Override
+ public int getInt(ByteBuffer input, long offset) {
+ return input.getInt((int) offset);
+ }
+
+ @Override
+ public int getUnsignedShort(ByteBuffer input, long offset) {
+ return Primitives.unsignedShort(getShort(input, offset));
+ }
+
+ @Override
+ public int getShort(ByteBuffer input, long offset) {
+ return input.getShort((int) offset);
+ }
+
+ @Override
+ public int getUnsignedByte(ByteBuffer input, long offset) {
+ return Primitives.unsignedByte(getByte(input, offset));
+ }
+
+ @Override
+ public int getByte(ByteBuffer input, long offset) {
+ return input.get((int) offset);
+ }
+
+ @Override
+ public ByteOrder byteOrder(ByteBuffer input) {
+ return input.order();
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java
new file mode 100644
index 000000000..4e67b40f8
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ByteOrder;
+
+enum BytesAccess implements Access {
+ INSTANCE;
+
+ @Override
+ public long getLong(Bytes input, long offset) {
+ return input.readLong(offset);
+ }
+
+ @Override
+ public long getUnsignedInt(Bytes input, long offset) {
+ return input.readUnsignedInt(offset);
+ }
+
+ @Override
+ public int getInt(Bytes input, long offset) {
+ return input.readInt(offset);
+ }
+
+ @Override
+ public int getUnsignedShort(Bytes input, long offset) {
+ return input.readUnsignedShort(offset);
+ }
+
+ @Override
+ public int getShort(Bytes input, long offset) {
+ return input.readShort(offset);
+ }
+
+ @Override
+ public int getUnsignedByte(Bytes input, long offset) {
+ return input.readUnsignedByte(offset);
+ }
+
+ @Override
+ public int getByte(Bytes input, long offset) {
+ return input.readByte(offset);
+ }
+
+ @Override
+ public ByteOrder byteOrder(Bytes input) {
+ return input.byteOrder();
+ }
+
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java b/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java
new file mode 100644
index 000000000..a7c97393c
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java
@@ -0,0 +1,194 @@
+/*
+ * 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);
+ 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/Primitives.java b/src/main/java/net/openhft/chronicle/hash/hashing/Primitives.java
new file mode 100644
index 000000000..ba1ac75a9
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/Primitives.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+final class Primitives {
+
+ private Primitives() {}
+
+ static long unsignedInt(int i) {
+ return i & 0xFFFFFFFFL;
+ }
+
+ static int unsignedShort(int s) {
+ return s & 0xFFFF;
+ }
+
+ static int unsignedByte(int b) {
+ return b & 0xFF;
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java
new file mode 100644
index 000000000..36d54a1cb
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java
@@ -0,0 +1,93 @@
+/*
+ * 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 sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.nio.ByteOrder;
+
+import static net.openhft.chronicle.hash.hashing.Primitives.unsignedByte;
+import static net.openhft.chronicle.hash.hashing.Primitives.unsignedInt;
+import static net.openhft.chronicle.hash.hashing.Primitives.unsignedShort;
+
+enum UnsafeAccess implements Access