mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
HCOLL-171 HCOLL-206 HCOLL-247 improved hashing algorithm (now murmur3), implemented ByteBufferInterop, some improvements in existing marshallers
This commit is contained in:
@@ -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
|
||||
* <i>ordered byte sequence</i> 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:
|
||||
* <ul>
|
||||
* <li>{@code getLong(input, 0)}</li>
|
||||
* <li>{@code getUnsignedInt(input, 0) | (getUnsignedInt(input, 4) << 32)}</li>
|
||||
* <li><pre>{@code getUnsignedInt(input, 0) |
|
||||
* ((long) getUnsignedShort(input, 4) << 32) |
|
||||
* ((long) getUnsignedByte(input, 6) << 48) |
|
||||
* ((long) getUnsignedByte(input, 7) << 56)}</pre></li>
|
||||
* <li>And so on</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>{@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.
|
||||
*
|
||||
* <p>{@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 <T> the type of the object to access
|
||||
*/
|
||||
public interface Access<T> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -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)}.
|
||||
*
|
||||
* <p>Usage example: <pre>{@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);
|
||||
* }
|
||||
* }}</pre>
|
||||
*
|
||||
* <p>{@code null} is a valid input, on accepting {@code null} {@code Unsafe} just interprets
|
||||
* the given offset as a wild memory address.
|
||||
*
|
||||
* @param <T> the type of objects to access
|
||||
* @return the unsafe memory {@code Access}
|
||||
*/
|
||||
public static <T> Access<T> unsafe() {
|
||||
return (Access<T>) UnsafeAccess.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Access} to any {@link ByteBuffer}.
|
||||
*
|
||||
* @return the {@code Access} to {@link ByteBuffer}s
|
||||
*/
|
||||
public static Access<ByteBuffer> toByteBuffer() {
|
||||
return ByteBufferAccess.INSTANCE;
|
||||
}
|
||||
|
||||
public static Access<Bytes> toBytes() {
|
||||
return BytesAccess.INSTANCE;
|
||||
}
|
||||
|
||||
private Accesses() {}
|
||||
}
|
||||
@@ -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<ByteBuffer> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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<Bytes> {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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 <T> the type of the input
|
||||
* @return hash code for the specified bytes subsequence
|
||||
*/
|
||||
public static <T> long hash(T input, Access<T> 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() {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<Object> {
|
||||
INSTANCE;
|
||||
|
||||
static final Unsafe UNSAFE;
|
||||
static final long BYTE_BASE;
|
||||
static final long CHAR_BASE;
|
||||
static final long SHORT_BASE;
|
||||
static final long INT_BASE;
|
||||
static final long LONG_BASE;
|
||||
|
||||
static {
|
||||
try {
|
||||
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
UNSAFE = (Unsafe) theUnsafe.get(null);
|
||||
BYTE_BASE = UNSAFE.arrayBaseOffset(byte[].class);
|
||||
CHAR_BASE = UNSAFE.arrayBaseOffset(char[].class);
|
||||
SHORT_BASE = UNSAFE.arrayBaseOffset(short[].class);
|
||||
INT_BASE = UNSAFE.arrayBaseOffset(int[].class);
|
||||
LONG_BASE = UNSAFE.arrayBaseOffset(long[].class);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(Object input, long offset) {
|
||||
return UNSAFE.getLong(input, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnsignedInt(Object input, long offset) {
|
||||
return unsignedInt(getInt(input, offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(Object input, long offset) {
|
||||
return UNSAFE.getInt(input, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedShort(Object input, long offset) {
|
||||
return unsignedShort(getShort(input, offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShort(Object input, long offset) {
|
||||
return UNSAFE.getShort(input, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedByte(Object input, long offset) {
|
||||
return unsignedByte(getByte(input, offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getByte(Object input, long offset) {
|
||||
return UNSAFE.getByte(input, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteOrder byteOrder(Object input) {
|
||||
return ByteOrder.nativeOrder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +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.serialization;
|
||||
|
||||
import net.openhft.lang.Maths;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.NativeBytes;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static java.nio.ByteOrder.LITTLE_ENDIAN;
|
||||
import static java.nio.ByteOrder.nativeOrder;
|
||||
|
||||
public enum Hasher {
|
||||
;
|
||||
|
||||
private static final long LONG_LEVEL_PRIME_MULTIPLE = 0x9ddfea08eb382d69L;
|
||||
private static final short SHORT_LEVEL_PRIME_MULTIPLE = 0x404f;
|
||||
private static final byte BYTE_LEVEL_PRIME_MULTIPLE = 0x57;
|
||||
|
||||
private static final int INT_HASH_LOW_SHORT_MULTIPLE =
|
||||
nativeOrder() == LITTLE_ENDIAN ? SHORT_LEVEL_PRIME_MULTIPLE : 1;
|
||||
private static final int INT_HASH_HIGH_SHORT_MULTIPLE =
|
||||
nativeOrder() == LITTLE_ENDIAN ? 1 : SHORT_LEVEL_PRIME_MULTIPLE;
|
||||
|
||||
public static long hash(Bytes bytes) {
|
||||
return hash(bytes, bytes.position(), bytes.limit());
|
||||
}
|
||||
|
||||
public static long hash(Bytes bytes, long offset, long limit) {
|
||||
if (limit - offset == 8)
|
||||
return Maths.hash(bytes.readLong(offset));
|
||||
long h = 0;
|
||||
long i = offset;
|
||||
for (; i < limit - 7; i += 8)
|
||||
h = LONG_LEVEL_PRIME_MULTIPLE * h + bytes.readLong(i);
|
||||
for (; i < limit - 1; i += 2)
|
||||
h = SHORT_LEVEL_PRIME_MULTIPLE * h + bytes.readShort(i);
|
||||
if (i < limit)
|
||||
h = BYTE_LEVEL_PRIME_MULTIPLE * h + bytes.readByte(i);
|
||||
return hash(h);
|
||||
}
|
||||
|
||||
public static long hash(Object array, int length) {
|
||||
Unsafe unsafe = NativeBytes.UNSAFE;
|
||||
long base = unsafe.arrayBaseOffset(array.getClass());
|
||||
long scale = unsafe.arrayIndexScale(array.getClass());
|
||||
long h = 0;
|
||||
long i = 0;
|
||||
long limit = ((long) length) * scale;
|
||||
for (; i < limit - 7; i += 8)
|
||||
h = LONG_LEVEL_PRIME_MULTIPLE * h + unsafe.getLong(array, base + i);
|
||||
for (; i < limit - 1; i += 2)
|
||||
h = SHORT_LEVEL_PRIME_MULTIPLE * h + unsafe.getShort(array, base + i);
|
||||
if (i < limit)
|
||||
h = BYTE_LEVEL_PRIME_MULTIPLE * h + unsafe.getByte(array, base + i);
|
||||
return hash(h);
|
||||
}
|
||||
|
||||
public static long hash(int value) {
|
||||
return hash(((long) (((short) (value >>> 16)) * INT_HASH_HIGH_SHORT_MULTIPLE)) +
|
||||
((short) value) * INT_HASH_LOW_SHORT_MULTIPLE);
|
||||
}
|
||||
|
||||
public static long hash(long value) {
|
||||
return Maths.hash(value);
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -20,7 +20,7 @@ package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
public enum ByteArrayMarshaller implements BytesInterop<byte[]>, BytesReader<byte[]> {
|
||||
@@ -33,6 +33,8 @@ public enum ByteArrayMarshaller implements BytesInterop<byte[]>, BytesReader<byt
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Bytes bytes, byte[] ba) {
|
||||
if (bytes.capacity() - bytes.position() < (long) ba.length)
|
||||
return false;
|
||||
long pos = bytes.position();
|
||||
for (int i = 0; i < ba.length; i++) {
|
||||
if (bytes.readByte(pos + i) != ba[i])
|
||||
@@ -43,7 +45,7 @@ public enum ByteArrayMarshaller implements BytesInterop<byte[]>, BytesReader<byt
|
||||
|
||||
@Override
|
||||
public long hash(byte[] ba) {
|
||||
return Hasher.hash(ba, ba.length);
|
||||
return Hasher.hash(ba);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,6 +62,9 @@ public enum ByteArrayMarshaller implements BytesInterop<byte[]>, BytesReader<byt
|
||||
|
||||
@Override
|
||||
public byte[] read(Bytes bytes, long size, byte[] toReuse) {
|
||||
if (size < 0L || size > (long) Integer.MAX_VALUE)
|
||||
throw new IllegalArgumentException("byte[] size should be non-negative int, " +
|
||||
size + " given. Memory corruption?");
|
||||
if (toReuse == null || size != toReuse.length)
|
||||
toReuse = new byte[(int) size];
|
||||
bytes.read(toReuse);
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.hashing.Accesses;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public enum ByteBufferMarshaller implements BytesInterop<ByteBuffer>, BytesReader<ByteBuffer> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Bytes bytes, ByteBuffer bb) {
|
||||
int inputRemaining = bb.remaining();
|
||||
if(bytes.capacity() - bytes.position() < (long) inputRemaining)
|
||||
return false;
|
||||
|
||||
long pos = bytes.position();
|
||||
int inputPos = bb.position();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < inputRemaining - 7; i += 8) {
|
||||
if (bytes.readLong(pos + (long) i) != bb.getLong(inputPos + i))
|
||||
return false;
|
||||
}
|
||||
for(; i < inputRemaining - 3; i += 4) {
|
||||
if (bytes.readInt(pos + (long) i) != bb.getInt(inputPos + i))
|
||||
return false;
|
||||
}
|
||||
for(; i < inputRemaining; i++) {
|
||||
if (bytes.readByte(pos + (long) i) != bb.get(inputPos + i))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long hash(ByteBuffer bb) {
|
||||
return Hasher.hash(bb, Accesses.toByteBuffer(), 0L, (long) bb.remaining());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer read(Bytes bytes, long size) {
|
||||
return read(bytes, size, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer read(Bytes bytes, long size, @Nullable ByteBuffer toReuse) {
|
||||
if (size < 0L || size > (long) Integer.MAX_VALUE)
|
||||
throw new IllegalArgumentException("ByteBuffer size should be non-negative int, " +
|
||||
size + " given. Memory corruption?");
|
||||
int intSize = (int) size;
|
||||
if (toReuse == null || toReuse.capacity() < intSize) {
|
||||
toReuse = ByteBuffer.allocate(intSize);
|
||||
} else {
|
||||
toReuse.position(0);
|
||||
toReuse.limit(intSize);
|
||||
}
|
||||
bytes.read(toReuse);
|
||||
toReuse.flip();
|
||||
return toReuse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size(ByteBuffer bb) {
|
||||
return (long) bb.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Bytes bytes, ByteBuffer bb) {
|
||||
int position = bb.position();
|
||||
bytes.write(bb);
|
||||
bb.position(position);
|
||||
}
|
||||
}
|
||||
+3
@@ -18,6 +18,7 @@
|
||||
|
||||
package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.*;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.MultiStoreBytes;
|
||||
@@ -75,6 +76,8 @@ public abstract class ByteableMarshaller<E extends Byteable>
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Bytes bytes, E e) {
|
||||
if (bytes.capacity() - bytes.position() < size)
|
||||
return false;
|
||||
Bytes input = e.bytes();
|
||||
long pos = bytes.position(), inputPos = e.offset();
|
||||
|
||||
|
||||
+6
-2
@@ -17,7 +17,7 @@
|
||||
package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
public enum BytesBytesInterop implements BytesInterop<Bytes> {
|
||||
@@ -25,7 +25,11 @@ public enum BytesBytesInterop implements BytesInterop<Bytes> {
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Bytes bytes, Bytes e) {
|
||||
return bytes.startsWith(e);
|
||||
long limit = bytes.limit();
|
||||
bytes.limit(bytes.capacity());
|
||||
boolean result = bytes.startsWith(e);
|
||||
bytes.limit(limit);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -20,7 +20,7 @@ package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
public enum CharArrayMarshaller implements BytesInterop<char[]>, BytesReader<char[]> {
|
||||
@@ -28,6 +28,8 @@ public enum CharArrayMarshaller implements BytesInterop<char[]>, BytesReader<cha
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Bytes bytes, char[] chars) {
|
||||
if (bytes.capacity() - bytes.position() < chars.length * 2L)
|
||||
return false;
|
||||
long pos = bytes.position();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (bytes.readChar(pos + (i * 2L)) != chars[i])
|
||||
@@ -38,7 +40,7 @@ public enum CharArrayMarshaller implements BytesInterop<char[]>, BytesReader<cha
|
||||
|
||||
@Override
|
||||
public long hash(char[] chars) {
|
||||
return Hasher.hash(chars, chars.length);
|
||||
return Hasher.hash(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-2
@@ -19,13 +19,12 @@
|
||||
package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesWriter;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.DirectBytes;
|
||||
import net.openhft.lang.io.DirectStore;
|
||||
import net.openhft.lang.io.serialization.BytesMarshaller;
|
||||
import net.openhft.lang.io.serialization.JDKObjectSerializer;
|
||||
import net.openhft.lang.io.serialization.JDKZObjectSerializer;
|
||||
import net.openhft.lang.threadlocal.Provider;
|
||||
import net.openhft.lang.threadlocal.StatefulCopyable;
|
||||
import net.openhft.lang.threadlocal.ThreadLocalCopies;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.*;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package net.openhft.chronicle.hash.serialization.internal;
|
||||
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
@@ -49,7 +50,7 @@ public enum VoidMarshaller
|
||||
|
||||
@Override
|
||||
public long hash(Void e) {
|
||||
return 0;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
import static net.openhft.chronicle.hash.serialization.Hasher.hash;
|
||||
import static net.openhft.chronicle.hash.hashing.Hasher.hash;
|
||||
import static net.openhft.lang.MemoryUnit.*;
|
||||
import static net.openhft.lang.collection.DirectBitSet.NOT_FOUND;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.hash.serialization.*;
|
||||
import net.openhft.chronicle.hash.serialization.internal.*;
|
||||
import net.openhft.chronicle.hash.serialization.internal.ByteBufferMarshaller;
|
||||
import net.openhft.lang.io.ByteBufferBytes;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.serialization.BytesMarshallable;
|
||||
@@ -151,6 +152,9 @@ final class SerializationBuilder<E> implements Cloneable, Serializable {
|
||||
} else if (eClass == char[].class) {
|
||||
reader((BytesReader<E>) CharArrayMarshaller.INSTANCE);
|
||||
interop((BytesInterop<E>) CharArrayMarshaller.INSTANCE);
|
||||
} else if (eClass == ByteBuffer.class) {
|
||||
reader((BytesReader<E>) ByteBufferMarshaller.INSTANCE);
|
||||
writer((BytesWriter<E>) ByteBufferMarshaller.INSTANCE);
|
||||
} else if (concreteClass(eClass)) {
|
||||
BytesMarshaller<E> marshaller = chooseMarshaller(eClass, eClass);
|
||||
if (marshaller != null)
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
|
||||
import net.openhft.chronicle.hash.ChronicleHashErrorListener;
|
||||
import net.openhft.chronicle.hash.serialization.BytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.Hasher;
|
||||
import net.openhft.chronicle.hash.hashing.Hasher;
|
||||
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
import net.openhft.chronicle.hash.serialization.internal.*;
|
||||
import net.openhft.lang.collection.DirectBitSet;
|
||||
|
||||
Reference in New Issue
Block a user