HCOLL-171 HCOLL-206 HCOLL-247 improved hashing algorithm (now murmur3), implemented ByteBufferInterop, some improvements in existing marshallers

This commit is contained in:
leventov
2014-12-13 15:25:00 +07:00
parent 94249b1269
commit c935c4b05f
23 changed files with 876 additions and 99 deletions
@@ -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);
}
}
@@ -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);
@@ -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);
}
}
@@ -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();
@@ -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
@@ -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
@@ -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;
@@ -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;