From 68697831b8ac21f74d0ac2511e345c8428488314 Mon Sep 17 00:00:00 2001 From: leventov Date: Mon, 26 Jan 2015 13:21:27 +0700 Subject: [PATCH] Moved to CityHash (proven to be faster); Made Hashing and BytesInterop API more cohrent; removed Void marshalling because values should never be null --- .../chronicle/hash/hashing/Access.java | 151 +++++- .../chronicle/hash/hashing/Accesses.java | 68 --- .../hash/hashing/ByteBufferAccess.java | 6 +- .../chronicle/hash/hashing/BytesAccess.java | 7 +- .../hash/hashing/CharSequenceAccess.java | 137 +++++ .../chronicle/hash/hashing/CityHash_1_1.java | 374 ++++++++++++++ .../chronicle/hash/hashing/Hasher.java | 194 ------- .../hashing/HotSpotPrior7u6StringHash.java | 45 ++ .../hash/hashing/LongHashFunction.java | 481 ++++++++++++++++++ .../hash/hashing/ModernHotSpotStringHash.java | 40 ++ .../chronicle/hash/hashing/StringHash.java | 22 + .../hash/hashing/UnknownJvmStringHash.java | 26 + .../chronicle/hash/hashing/UnsafeAccess.java | 8 +- .../chronicle/hash/hashing/package-info.java | 38 ++ .../hash/serialization/BytesInterop.java | 36 +- .../hash/serialization/BytesReader.java | 7 +- .../hash/serialization/BytesWriter.java | 5 +- ...izationFactoryConfigurableBytesReader.java | 4 +- .../BasicCopyingMetaBytesInterop.java | 6 +- .../internal/ByteArrayMarshaller.java | 26 +- .../internal/ByteBufferMarshaller.java | 25 +- .../internal/ByteableMarshaller.java | 37 +- .../internal/BytesBytesInterop.java | 26 +- .../serialization/internal/BytesReaders.java | 7 +- .../internal/CharArrayMarshaller.java | 26 +- .../internal/CharSequenceReader.java | 6 +- .../internal/CharSequenceWriter.java | 5 +- .../internal/CopyingMetaBytesInterop.java | 7 + .../internal/DataValueMetaBytesInterop.java | 7 + .../internal/DelegatingMetaBytesInterop.java | 11 +- .../internal/DoubleMarshaller.java | 24 +- .../serialization/internal}/DummyValue.java | 4 +- .../internal}/DummyValueMarshaller.java | 27 +- .../internal/IntegerMarshaller.java | 24 +- .../internal/LongMarshaller.java | 24 +- .../internal/MetaBytesInterop.java | 7 +- .../internal/VoidMarshaller.java | 91 ---- .../chronicle/map/ReplicatedChronicleMap.java | 12 +- .../chronicle/map/SerializationBuilder.java | 6 - .../chronicle/map/VanillaChronicleMap.java | 2 +- .../openhft/chronicle/map/VanillaContext.java | 3 +- .../chronicle/set/ChronicleSetBuilder.java | 3 +- .../ChronicleSetStatelessClientBuilder.java | 1 + .../net/openhft/chronicle/set/SetFromMap.java | 3 +- .../chronicle/set/SetInstanceBuilder.java | 1 + .../net/openhft/chronicle/map/Builder.java | 5 +- .../net/openhft/chronicle/map/HasherTest.java | 38 -- .../TCPSocketReplication3VoidValueTest.java | 38 +- .../map/fromdocs/LongPairArrayReader.java | 7 +- .../map/fromdocs/LongPairArrayWriter.java | 5 +- .../jsr166/map/StatelessChronicleMapTest.java | 2 + 51 files changed, 1619 insertions(+), 546 deletions(-) delete mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java delete mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java create mode 100644 src/main/java/net/openhft/chronicle/hash/hashing/package-info.java rename src/main/java/net/openhft/chronicle/{set => hash/serialization/internal}/DummyValue.java (88%) rename src/main/java/net/openhft/chronicle/{set => hash/serialization/internal}/DummyValueMarshaller.java (66%) delete mode 100644 src/main/java/net/openhft/chronicle/hash/serialization/internal/VoidMarshaller.java delete mode 100644 src/test/java/net/openhft/chronicle/map/HasherTest.java diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java index 44088b816f..6f896bd7fb 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java @@ -16,8 +16,13 @@ package net.openhft.chronicle.hash.hashing; +import net.openhft.lang.io.Bytes; + +import java.nio.ByteBuffer; import java.nio.ByteOrder; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + /** * Strategy of reading bytes, defines the abstraction of {@code T} class instances as ordered byte * sequence. All {@code getXXX(input, offset)} should be consistent to each other in terms of @@ -38,6 +43,10 @@ * range is outside of the bounds of the byte sequence, represented by the given {@code input}. * However, they could omit checks for better performance. * + *

Only {@link #getByte(Object, long)} and {@link #byteOrder(Object)} methods are abstract in + * this class, so implementing them is sufficient for valid {@code Access} instance, but for + * efficiency your should override methods used by target {@link LongHashFunction} implementation. + * *

{@code Access} API is designed for inputs, that actually represent byte sequences that lay * continuously in memory. Theoretically {@code Access} strategy could be implemented for * non-continuous byte sequences, or abstractions which aren't actually present in memory as they @@ -45,8 +54,108 @@ * be slow. * * @param the type of the object to access + * @see LongHashFunction#hash(Object, Access, long, long) */ -public interface Access { +public abstract class Access { + + /** + * Returns the {@code Access} to any {@link Bytes}. This {@code Access} isn't useful in + * the user code, because methods {@link LongHashFunction#hashBytes(Bytes)} and + * {@link LongHashFunction#hashBytes(Bytes, long, long)} exist. This {@code Access} could be + * used in new {@link LongHashFunction} implementations. + * + * @return the {@code Access} to {@link Bytes} instances + */ + public static Access toBytes() { + return BytesAccess.INSTANCE; + } + + /** + * Returns the {@code Access} delegating {@code getXXX(input, offset)} methods to {@code + * sun.misc.Unsafe.getXXX(input, offset)}. + * + *

Usage example:

{@code
+     * class Pair {
+     *     long first, second;
+     *
+     *     static final long pairDataOffset =
+     *         theUnsafe.objectFieldOffset(Pair.class.getDeclaredField("first"));
+     *
+     *     static long hashPair(Pair pair, LongHashFunction hashFunction) {
+     *         return hashFunction.hash(pair, Access.unsafe(), pairDataOffset, 16L);
+     *     }
+     * }}
+ * + *

{@code null} is a valid input, on accepting {@code null} {@code Unsafe} just interprets + * the given offset as a wild memory address. Note that for hashing memory by address there is + * a shortcut {@link LongHashFunction#hashMemory(long, long) hashMemory(address, len)} method. + * + * @param the type of objects to access + * @return the unsafe memory {@code Access} + */ + public static Access unsafe() { + return (Access) UnsafeAccess.INSTANCE; + } + + /** + * Returns the {@code Access} to any {@link ByteBuffer}. This {@code Access} isn't useful in + * the user code, because methods {@link LongHashFunction#hashBytes(ByteBuffer)} and + * {@link LongHashFunction#hashBytes(ByteBuffer, int, int)} exist. This {@code Access} could be + * used in new {@link LongHashFunction} implementations. + * + * @return the {@code Access} to {@link ByteBuffer}s + */ + public static Access toByteBuffer() { + return ByteBufferAccess.INSTANCE; + } + + /** + * Returns the {@code Access} to {@link CharSequence}s backed by {@linkplain + * ByteOrder#nativeOrder() native} {@code char} reads, typically from {@code char[]} array. + * + *

Usage example:

{@code
+     * static long hashStringBuffer(StringBuffer buffer, LongHashFunction hashFunction) {
+     *     return hashFunction.hash(buffer, Access.toNativeCharSequence(),
+     *         // * 2L because length is passed in bytes, not chars
+     *         0L, buffer.length() * 2L);
+     * }}
+ * + *

This method is a shortcut for {@code Access.toCharSequence(ByteOrder.nativeOrder())}. + * + * @param the {@code CharSequence} subtype (backed by native {@code char reads}) to access + * @return the {@code Access} to {@link CharSequence}s backed by native {@code char} reads + * @see #toCharSequence(ByteOrder) + */ + public static Access toNativeCharSequence() { + return (Access) CharSequenceAccess.nativeCharSequenceAccess(); + } + + /** + * Returns the {@code Access} to {@link CharSequence}s backed by {@code char} reads made in + * the specified byte order. + * + *

Usage example:

{@code
+     * static long hashCharBuffer(CharBuffer buffer, LongHashFunction hashFunction) {
+     *     return hashFunction.hash(buffer, Access.toCharSequence(buffer.order()),
+     *         // * 2L because length is passed in bytes, not chars
+     *         0L, buffer.length() * 2L);
+     * }}
+ * + * @param backingOrder the byte order of {@code char} reads backing + * {@code CharSequences} to access + * @return the {@code Access} to {@link CharSequence}s backed by {@code char} reads made in + * the specified byte order + * @param the {@code CharSequence} subtype to access + * @see #toNativeCharSequence() + */ + public static Access toCharSequence(ByteOrder backingOrder) { + return (Access) CharSequenceAccess.charSequenceAccess(backingOrder); + } + + /** + * Constructor for use in subclasses. + */ + protected Access() {} /** * Reads {@code [offset, offset + 7]} bytes of the byte sequence represented by the given @@ -58,7 +167,13 @@ public interface Access { * @return eight bytes as a {@code long} value, in {@linkplain #byteOrder(Object) the expected * order} */ - long getLong(T input, long offset); + public long getLong(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedInt(input, offset) | (getUnsignedInt(input, offset + 4L) << 32); + } else { + return getUnsignedInt(input, offset + 4L) | (getUnsignedInt(input, offset) << 32); + } + } /** * Shortcut for {@code getInt(input, offset) & 0xFFFFFFFFL}. Could be implemented more @@ -70,7 +185,9 @@ public interface Access { * @return four bytes as an unsigned int value, in {@linkplain #byteOrder(Object) the expected * order} */ - long getUnsignedInt(T input, long offset); + public long getUnsignedInt(T input, long offset) { + return ((long) getInt(input, offset)) & 0xFFFFFFFFL; + } /** * Reads {@code [offset, offset + 3]} bytes of the byte sequence represented by the given @@ -82,7 +199,13 @@ public interface Access { * @return four bytes as an {@code int} value, in {@linkplain #byteOrder(Object) the expected * order} */ - int getInt(T input, long offset); + public int getInt(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedShort(input, offset) | (getUnsignedShort(input, offset + 2L) << 16); + } else { + return getUnsignedShort(input, offset + 2L) | (getUnsignedShort(input, offset) << 16); + } + } /** * Shortcut for {@code getShort(input, offset) & 0xFFFF}. Could be implemented more @@ -94,7 +217,13 @@ public interface Access { * @return two bytes as an unsigned short value, in {@linkplain #byteOrder(Object) the expected * order} */ - int getUnsignedShort(T input, long offset); + public int getUnsignedShort(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedByte(input, offset) | (getUnsignedByte(input, offset + 1L) << 8); + } else { + return getUnsignedByte(input, offset + 1L) | (getUnsignedByte(input, offset) << 8); + } + } /** * Reads {@code [offset, offset + 1]} bytes of the byte sequence represented by the given @@ -106,7 +235,9 @@ public interface Access { * @return two bytes as a {@code short} value, in {@linkplain #byteOrder(Object) the expected * order}, widened to {@code int} */ - int getShort(T input, long offset); + public int getShort(T input, long offset) { + return (int) (short) getUnsignedShort(input, offset); + } /** * Shortcut for {@code getByte(input, offset) & 0xFF}. Could be implemented more efficiently. @@ -116,7 +247,9 @@ public interface Access { * by the given object * @return a byte by the given {@code offset}, interpreted as unsigned */ - int getUnsignedByte(T input, long offset); + public int getUnsignedByte(T input, long offset) { + return getByte(input, offset) & 0xFF; + } /** * Reads a single byte at the given {@code offset} in the byte sequence represented by the given @@ -127,7 +260,7 @@ public interface Access { * by the given object * @return a byte by the given {@code offset}, widened to {@code int} */ - int getByte(T input, long offset); + public abstract int getByte(T input, long offset); /** * The byte order in which all multi-byte {@code getXXX()} reads from the given {@code input} @@ -136,5 +269,5 @@ public interface Access { * @param input the accessed object * @return the byte order of all multi-byte reads from the given {@code input} */ - ByteOrder byteOrder(T input); + public abstract ByteOrder byteOrder(T input); } diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java b/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java deleted file mode 100644 index ac67cf2eaa..0000000000 --- a/src/main/java/net/openhft/chronicle/hash/hashing/Accesses.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.openhft.chronicle.hash.hashing; - -import net.openhft.lang.io.Bytes; - -import java.nio.ByteBuffer; - -/** - * Static methods returning useful {@link Access} implementations. - */ -public final class Accesses { - - /** - * Returns the {@code Access} delegating {@code getXXX(input, offset)} methods to {@code - * sun.misc.Unsafe.getXXX(input, offset)}. - * - *

Usage example:

{@code
-     * class Pair {
-     *     long first, second;
-     *
-     *     static final long pairDataOffset =
-     *         theUnsafe.objectFieldOffset(Pair.class.getDeclaredField("first"));
-     *
-     *     static long hashPair(Pair pair, LongHashFunction hashFunction) {
-     *         return hashFunction.hash(pair, Accesses.unsafe(), pairDataOffset, 16L);
-     *     }
-     * }}
- * - *

{@code null} is a valid input, on accepting {@code null} {@code Unsafe} just interprets - * the given offset as a wild memory address. - * - * @param the type of objects to access - * @return the unsafe memory {@code Access} - */ - public static Access unsafe() { - return (Access) UnsafeAccess.INSTANCE; - } - - /** - * Returns the {@code Access} to any {@link ByteBuffer}. - * - * @return the {@code Access} to {@link ByteBuffer}s - */ - public static Access toByteBuffer() { - return ByteBufferAccess.INSTANCE; - } - - public static Access toBytes() { - return BytesAccess.INSTANCE; - } - - private Accesses() {} -} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java index 703111dffd..d6e05dde68 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/ByteBufferAccess.java @@ -19,8 +19,10 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -enum ByteBufferAccess implements Access { - INSTANCE; +final class ByteBufferAccess extends Access { + public static final ByteBufferAccess INSTANCE = new ByteBufferAccess(); + + private ByteBufferAccess() {} @Override public long getLong(ByteBuffer input, long offset) { diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java index 4e67b40f8d..ad08c3649d 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/BytesAccess.java @@ -20,8 +20,10 @@ import java.nio.ByteOrder; -enum BytesAccess implements Access { - INSTANCE; +public final class BytesAccess extends Access { + public static final BytesAccess INSTANCE = new BytesAccess(); + + private BytesAccess() {} @Override public long getLong(Bytes input, long offset) { @@ -62,5 +64,4 @@ public int getByte(Bytes input, long offset) { public ByteOrder byteOrder(Bytes input) { return input.byteOrder(); } - } diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java new file mode 100644 index 0000000000..24ac156fc3 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/CharSequenceAccess.java @@ -0,0 +1,137 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import net.openhft.chronicle.hash.hashing.Access; + +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.BIG_ENDIAN; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +abstract class CharSequenceAccess extends Access { + + public static CharSequenceAccess charSequenceAccess(ByteOrder order) { + return order == LITTLE_ENDIAN ? + LittleEndianCharSequenceAccess.INSTANCE : + BigEndianCharSequenceAccess.INSTANCE; + } + + public static CharSequenceAccess nativeCharSequenceAccess() { + return charSequenceAccess(ByteOrder.nativeOrder()); + } + + private static int ix(long offset) { + return (int) (offset >> 1); + } + + static long getLong(CharSequence input, long offset, + int char0Off, int char1Off, int char2Off, int char3Off) { + int base = ix(offset); + long char0 = input.charAt(base + char0Off); + long char1 = input.charAt(base + char1Off); + long char2 = input.charAt(base + char2Off); + long char3 = input.charAt(base + char3Off); + return char0 | (char1 << 16) | (char2 << 32) | (char3 << 48); + } + + static long getUnsignedInt(CharSequence input, long offset, + int char0Off, int char1Off) { + int base = ix(offset); + long char0 = input.charAt(base + char0Off); + long char1 = input.charAt(base + char1Off); + return char0 | (char1 << 16); + } + + private CharSequenceAccess() {} + + @Override + public int getInt(CharSequence input, long offset) { + return (int) getUnsignedInt(input, offset); + } + + @Override + public int getUnsignedShort(CharSequence input, long offset) { + return input.charAt(ix(offset)); + } + + @Override + public int getShort(CharSequence input, long offset) { + return (int) (short) input.charAt(ix(offset)); + } + + static int getUnsignedByte(CharSequence input, long offset, int shift) { + return Primitives.unsignedByte(input.charAt(ix(offset)) >> shift); + } + + @Override + public int getByte(CharSequence input, long offset) { + return (int) (byte) getUnsignedByte(input, offset); + } + + private static class LittleEndianCharSequenceAccess extends CharSequenceAccess { + private static final CharSequenceAccess INSTANCE = new LittleEndianCharSequenceAccess(); + + private LittleEndianCharSequenceAccess() {} + + @Override + public long getLong(CharSequence input, long offset) { + return getLong(input, offset, 0, 1, 2, 3); + } + + @Override + public long getUnsignedInt(CharSequence input, long offset) { + return getUnsignedInt(input, offset, 0, 1); + } + + @Override + public int getUnsignedByte(CharSequence input, long offset) { + return getUnsignedByte(input, offset, ((int) offset & 1) << 3); + } + + @Override + public ByteOrder byteOrder(CharSequence input) { + return LITTLE_ENDIAN; + } + } + + private static class BigEndianCharSequenceAccess extends CharSequenceAccess { + private static final CharSequenceAccess INSTANCE = new BigEndianCharSequenceAccess(); + + private BigEndianCharSequenceAccess() {} + + @Override + public long getLong(CharSequence input, long offset) { + return getLong(input, offset, 3, 2, 1, 0); + } + + @Override + public long getUnsignedInt(CharSequence input, long offset) { + return getUnsignedInt(input, offset, 1, 0); + } + + @Override + public int getUnsignedByte(CharSequence input, long offset) { + return getUnsignedByte(input, offset, (((int) offset & 1) ^ 1) << 3); + } + + @Override + public ByteOrder byteOrder(CharSequence input) { + return BIG_ENDIAN; + } + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java b/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java new file mode 100644 index 0000000000..009cd52f10 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/CityHash_1_1.java @@ -0,0 +1,374 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import static java.lang.Long.reverseBytes; +import static java.lang.Long.rotateRight; +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static net.openhft.chronicle.hash.hashing.LongHashFunction.NATIVE_LITTLE_ENDIAN; + +/** + * Adapted from the C++ CityHash implementation from Google at + * http://code.google.com/p/cityhash/source/browse/trunk/src/city.cc. + */ +class CityHash_1_1 { + private static final CityHash_1_1 INSTANCE = new CityHash_1_1(); + + private static final CityHash_1_1 NATIVE_CITY = NATIVE_LITTLE_ENDIAN ? + CityHash_1_1.INSTANCE : BigEndian.INSTANCE; + + private CityHash_1_1() {} + + private static final long K0 = 0xc3a5c85c97cb3127L; + private static final long K1 = 0xb492b66fbe98f273L; + private static final long K2 = 0x9ae16a3b2f90404fL; + + private static long shiftMix(long val) { + return val ^ (val >>> 47); + } + + private static long hashLen16(long u, long v) { + return hashLen16(u, v, K_MUL); + } + + private static final long K_MUL = 0x9ddfea08eb382d69L; + + private static long hashLen16(long u, long v, long mul) { + long a = shiftMix((u ^ v) * mul); + return shiftMix((v ^ a) * mul) * mul; + } + + private static long mul(long len) { + return K2 + (len << 1); + } + + private static long hash1To3Bytes(int len, int firstByte, int midOrLastByte, int lastByte) { + int y = firstByte + (midOrLastByte << 8); + int z = len + (lastByte << 2); + return shiftMix((((long) y) * K2) ^ (((long) z) * K0)) * K2; + } + + private static long hash4To7Bytes(long len, long first4Bytes, long last4Bytes) { + long mul = mul(len); + return hashLen16(len + (first4Bytes << 3), last4Bytes, mul); + } + + private static long hash8To16Bytes(long len, long first8Bytes, long last8Bytes) { + long mul = mul(len); + long a = first8Bytes + K2; + long c = rotateRight(last8Bytes, 37) * mul + a; + long d = (rotateRight(a, 25) + last8Bytes) * mul; + return hashLen16(c, d, mul); + } + + long fetch64(Access access, T in, long off) { + return access.getLong(in, off); + } + + int fetch32(Access access, T in, long off) { + return access.getInt(in, off); + } + + long toLittleEndian(long v) { + return v; + } + + int toLittleEndian(int v) { + return v; + } + + private long hashLen0To16(Access access, T in, long off, long len) { + if (len >= 8L) { + long a = fetch64(access, in, off); + long b = fetch64(access, in, off + len - 8L); + return hash8To16Bytes(len, a, b); + } else if (len >= 4L) { + long a = Primitives.unsignedInt(fetch32(access, in, off)); + long b = Primitives.unsignedInt(fetch32(access, in, off + len - 4L)); + return hash4To7Bytes(len, a, b); + } else if (len > 0L) { + int a = access.getUnsignedByte(in, off); + int b = access.getUnsignedByte(in, off + (len >> 1)); + int c = access.getUnsignedByte(in, off + len - 1L); + return hash1To3Bytes((int) len, a, b, c); + } + return K2; + } + + private long hashLen17To32(Access access, T in, long off, long len) { + long mul = mul(len); + long a = fetch64(access, in, off) * K1; + long b = fetch64(access, in, off + 8L); + long c = fetch64(access, in, off + len - 8L) * mul; + long d = fetch64(access, in, off + len - 16L) * K2; + return hashLen16(rotateRight(a + b, 43) + rotateRight(c, 30) + d, + a + rotateRight(b + K2, 18) + c, mul); + } + + private long hashLen33To64(Access access, T in, long off, long len) { + long mul = mul(len); + long a = fetch64(access, in, off) * K2; + long b = fetch64(access, in, off + 8L); + long c = fetch64(access, in, off + len - 24L); + long d = fetch64(access, in, off + len - 32L); + long e = fetch64(access, in, off + 16L) * K2; + long f = fetch64(access, in, off + 24L) * 9L; + long g = fetch64(access, in, off + len - 8L); + long h = fetch64(access, in, off + len - 16L) * mul; + long u = rotateRight(a + g, 43) + (rotateRight(b, 30) + c) * 9L; + long v = ((a + g) ^ d) + f + 1L; + long w = reverseBytes((u + v) * mul) + h; + long x = rotateRight(e + f, 42) + c; + long y = (reverseBytes((v + w) * mul) + g) * mul; + long z = e + f + c; + a = reverseBytes((x + z) * mul + y) + b; + b = shiftMix((z + a) * mul + d + h) * mul; + return b + x; + } + + long cityHash64(Access access, T in, long off, long len) { + if (len <= 32L) { + if (len <= 16L) { + return hashLen0To16(access, in, off, len); + } else { + return hashLen17To32(access, in, off, len); + } + } else if (len <= 64L) { + return hashLen33To64(access, in, off, len); + } + + long x = fetch64(access, in, off + len - 40L); + long y = fetch64(access, in, off + len - 16L) + fetch64(access, in, off + len - 56L); + long z = hashLen16(fetch64(access, in, off + len - 48L) + len, + fetch64(access, in, off + len - 24L)); + + long vFirst, vSecond, wFirst, wSecond; + + // This and following 3 blocks are produced by a single-click inline-function refactoring. + // IntelliJ IDEA ftw + // WeakHashLen32WithSeeds + long a3 = len; + long b3 = z; + long w4 = fetch64(access, in, off + len - 64L); + long x4 = fetch64(access, in, off + len - 64L + 8L); + long y4 = fetch64(access, in, off + len - 64L + 16L); + long z4 = fetch64(access, in, off + len - 64L + 24L); + a3 += w4; + b3 = rotateRight(b3 + a3 + z4, 21); + long c3 = a3; + a3 += x4 + y4; + b3 += rotateRight(a3, 44); + vFirst = a3 + z4; + vSecond = b3 + c3; + + // WeakHashLen32WithSeeds + long a2 = y + K1; + long b2 = x; + long w3 = fetch64(access, in, off + len - 32L); + long x3 = fetch64(access, in, off + len - 32L + 8L); + long y3 = fetch64(access, in, off + len - 32L + 16L); + long z3 = fetch64(access, in, off + len - 32L + 24L); + a2 += w3; + b2 = rotateRight(b2 + a2 + z3, 21); + long c2 = a2; + a2 += x3 + y3; + b2 += rotateRight(a2, 44); + wFirst = a2 + z3; + wSecond = b2 + c2; + + x = x * K1 + fetch64(access, in, off); + + len = (len - 1L) & (~63L); + do { + x = rotateRight(x + y + vFirst + fetch64(access, in, off + 8L), 37) * K1; + y = rotateRight(y + vSecond + fetch64(access, in, off + 48L), 42) * K1; + x ^= wSecond; + y += vFirst + fetch64(access, in, off + 40L); + z = rotateRight(z + wFirst, 33) * K1; + + // WeakHashLen32WithSeeds + long a1 = vSecond * K1; + long b1 = x + wFirst; + long w2 = fetch64(access, in, off); + long x2 = fetch64(access, in, off + 8L); + long y2 = fetch64(access, in, off + 16L); + long z2 = fetch64(access, in, off + 24L); + a1 += w2; + b1 = rotateRight(b1 + a1 + z2, 21); + long c1 = a1; + a1 += x2 + y2; + b1 += rotateRight(a1, 44); + vFirst = a1 + z2; + vSecond = b1 + c1; + + // WeakHashLen32WithSeeds + long a = z + wSecond; + long b = y + fetch64(access, in, off + 16L); + long w1 = fetch64(access, in, off + 32L); + long x1 = fetch64(access, in, off + 32L + 8L); + long y1 = fetch64(access, in, off + 32L + 16L); + long z1 = fetch64(access, in, off + 32L + 24L); + a += w1; + b = rotateRight(b + a + z1, 21); + long c = a; + a += x1 + y1; + b += rotateRight(a, 44); + wFirst = a + z1; + wSecond = b + c; + + long tmp = x; + x = z; + z = tmp; + + len -= 64L; + off += 64L; + } while (len != 0); + return hashLen16(hashLen16(vFirst, wFirst) + shiftMix(y) * K1 + z, + hashLen16(vSecond, wSecond) + x); + } + + private static class BigEndian extends CityHash_1_1 { + private static final BigEndian INSTANCE = new BigEndian(); + private BigEndian() {} + + @Override + long fetch64(Access access, T in, long off) { + return reverseBytes(super.fetch64(access, in, off)); + } + + @Override + int fetch32(Access access, T in, long off) { + return Integer.reverseBytes(super.fetch32(access, in, off)); + } + + @Override + long toLittleEndian(long v) { + return reverseBytes(v); + } + + @Override + int toLittleEndian(int v) { + return Integer.reverseBytes(v); + } + } + + private static class AsLongHashFunction extends LongHashFunction { + public static final AsLongHashFunction INSTANCE = new AsLongHashFunction(); + private static final long serialVersionUID = 0L; + + private Object readResolve() { + return INSTANCE; + } + + @Override + public long hashLong(long input) { + input = NATIVE_CITY.toLittleEndian(input); + long hash = hash8To16Bytes(8L, input, input); + return finalize(hash); + } + + @Override + public long hashInt(int input) { + input = NATIVE_CITY.toLittleEndian(input); + long unsignedInt = Primitives.unsignedInt(input); + long hash = hash4To7Bytes(4L, unsignedInt, unsignedInt); + return finalize(hash); + } + + @Override + public long hashShort(short input) { + return hashChar((char) input); + } + + private static final int FIRST_SHORT_BYTE_SHIFT = NATIVE_LITTLE_ENDIAN ? 0 : 8; + // JIT could probably optimize & -1 to no-op + private static final int FIRST_SHORT_BYTE_MASK = NATIVE_LITTLE_ENDIAN ? 0xFF : -1; + private static final int SECOND_SHORT_BYTE_SHIFT = 8 - FIRST_SHORT_BYTE_SHIFT; + private static final int SECOND_SHORT_BYTE_MASK = NATIVE_LITTLE_ENDIAN ? -1 : 0xFF; + + @Override + public long hashChar(char input) { + int unsignedInput = (int) input; + int firstByte = (unsignedInput >> FIRST_SHORT_BYTE_SHIFT) & FIRST_SHORT_BYTE_MASK; + int secondByte = (unsignedInput >> SECOND_SHORT_BYTE_SHIFT) & SECOND_SHORT_BYTE_MASK; + long hash = hash1To3Bytes(2, firstByte, secondByte, secondByte); + return finalize(hash); + } + + @Override + public long hashByte(byte input) { + int unsignedByte = Primitives.unsignedByte(input); + long hash = hash1To3Bytes(1, unsignedByte, unsignedByte, unsignedByte); + return finalize(hash); + } + + @Override + public long hashVoid() { + return K2; + } + + @Override + public long hash(T input, Access access, long off, long len) { + long hash; + if (access.byteOrder(input) == LITTLE_ENDIAN) { + hash = CityHash_1_1.INSTANCE.cityHash64(access, input, off, len); + } else { + hash = BigEndian.INSTANCE.cityHash64(access, input, off, len); + } + return finalize(hash); + } + + long finalize(long hash) { + return hash; + } + } + + public static LongHashFunction asLongHashFunctionWithoutSeed() { + return AsLongHashFunction.INSTANCE; + } + + private static class AsLongHashFunctionSeeded extends AsLongHashFunction { + private static final long serialVersionUID = 0L; + + private final long seed0, seed1; + private transient long voidHash; + + private AsLongHashFunctionSeeded(long seed0, long seed1) { + this.seed0 = seed0; + this.seed1 = seed1; + voidHash = finalize(K2); + } + + @Override + public long hashVoid() { + return voidHash; + } + + @Override + protected long finalize(long hash) { + return hashLen16(hash - seed0, seed1); + } + } + + public static LongHashFunction asLongHashFunctionWithSeed(long seed) { + return new AsLongHashFunctionSeeded(K2, seed); + } + + public static LongHashFunction asLongHashFunctionWithTwoSeeds(long seed0, long seed1) { + return new AsLongHashFunctionSeeded(seed0, seed1); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java b/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java deleted file mode 100644 index 2548ee39a1..0000000000 --- a/src/main/java/net/openhft/chronicle/hash/hashing/Hasher.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2014 Higher Frequency Trading - * - * http://www.higherfrequencytrading.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.openhft.chronicle.hash.hashing; - -import net.openhft.lang.io.Bytes; - -import static net.openhft.chronicle.hash.hashing.UnsafeAccess.BYTE_BASE; -import static net.openhft.chronicle.hash.hashing.UnsafeAccess.CHAR_BASE; - -public final class Hasher { - - // MurmurHash3, derived from - // https://github.com/google/guava/blob/fa95e381e665d8ee9639543b99ed38020c8de5ef - // /guava/src/com/google/common/hash/Murmur3_128HashFunction.java - - - private static final long C1 = 0x87c37b91114253d5L; - private static final long C2 = 0x4cf5ad432745937fL; - - /** - * Returns the hash code for {@code len} continuous bytes of the given {@code input} object, - * starting from the given offset. The abstraction of input as ordered byte sequence and - * "offset within the input" is defined by the given {@code access} strategy. - * - *

This method doesn't promise to throw a {@code RuntimeException} if {@code - * [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code - * access} strategy for the given {@code input}, so use this method with caution. - * - * @param input the object to read bytes from - * @param access access which defines the abstraction of the given input - * as ordered byte sequence - * @param offset offset to the first byte of the subsequence to hash - * @param length length of the subsequence to hash - * @param the type of the input - * @return hash code for the specified bytes subsequence - */ - public static long hash(T input, Access access, long offset, long length) { - assert offset >= 0L && length >= 0L; - - long h1 = 0L; - long h2 = 0L; - long remaining = length; - while (remaining >= 16L) { - long k1 = access.getLong(input, offset); - long k2 = access.getLong(input, offset + 8L); - offset += 16L; - remaining -= 16L; - h1 ^= mixK1(k1); - - h1 = Long.rotateLeft(h1, 27); - h1 += h2; - h1 = h1 * 5L + 0x52dce729L; - - h2 ^= mixK2(k2); - - h2 = Long.rotateLeft(h2, 31); - h2 += h1; - h2 = h2 * 5L + 0x38495ab5L; - } - - if (remaining > 0L) { - long k1 = 0L; - long k2 = 0L; - switch ((int) remaining) { - case 15: - k2 ^= ((long) access.getUnsignedByte(input, offset + 14L)) << 48;// fall through - case 14: - k2 ^= ((long) access.getUnsignedByte(input, offset + 13L)) << 40;// fall through - case 13: - k2 ^= ((long) access.getUnsignedByte(input, offset + 12L)) << 32;// fall through - case 12: - k2 ^= ((long) access.getUnsignedByte(input, offset + 11L)) << 24;// fall through - case 11: - k2 ^= ((long) access.getUnsignedByte(input, offset + 10L)) << 16;// fall through - case 10: - k2 ^= ((long) access.getUnsignedByte(input, offset + 9L)) << 8; // fall through - case 9: - k2 ^= ((long) access.getUnsignedByte(input, offset + 8L)); // fall through - case 8: - k1 ^= access.getLong(input, offset); - break; - case 7: - k1 ^= ((long) access.getUnsignedByte(input, offset + 6L)) << 48; // fall through - case 6: - k1 ^= ((long) access.getUnsignedByte(input, offset + 5L)) << 40; // fall through - case 5: - k1 ^= ((long) access.getUnsignedByte(input, offset + 4L)) << 32; // fall through - case 4: - k1 ^= access.getUnsignedInt(input, offset); - break; - case 3: - k1 ^= ((long) access.getUnsignedByte(input, offset + 2L)) << 16; // fall through - case 2: - k1 ^= ((long) access.getUnsignedByte(input, offset + 1L)) << 8; // fall through - case 1: - k1 ^= ((long) access.getUnsignedByte(input, offset)); - case 0: - break; - default: - throw new AssertionError("Should never get here."); - } - h1 ^= mixK1(k1); - h2 ^= mixK2(k2); - } - - return finalize(length, h1, h2); - } - - private static long finalize(long length, long h1, long h2) { - h1 ^= length; - h2 ^= length; - - h1 += h2; - h2 += h1; - - h1 = fmix64(h1); - h2 = fmix64(h2); - - h1 += h2; - return h1; - } - - - private static long fmix64(long k) { - k ^= k >>> 33; - k *= 0xff51afd7ed558ccdL; - k ^= k >>> 33; - k *= 0xc4ceb9fe1a85ec53L; - k ^= k >>> 33; - return k; - } - - private static long mixK1(long k1) { - k1 *= C1; - k1 = Long.rotateLeft(k1, 31); - k1 *= C2; - return k1; - } - - private static long mixK2(long k2) { - k2 *= C2; - k2 = Long.rotateLeft(k2, 33); - k2 *= C1; - return k2; - } - - public static long hash(Bytes bytes) { - return hash(bytes, bytes.position(), bytes.limit()); - } - - public static long hash(Bytes bytes, long offset, long limit) { - return hash(bytes, Accesses.toBytes(), offset, limit - offset); - } - - public static long hash(byte[] array) { - return hash(array, Accesses.unsafe(), BYTE_BASE, (long) array.length); - } - - public static long hash(char[] array) { - return hash(array, Accesses.unsafe(), CHAR_BASE, array.length * 2L); - } - - public static long hash(int value) { - long k1 = Primitives.unsignedInt(value); - long h1 = 0L ^ mixK1(k1); - long h2 = 0L; - return finalize(4L, h1, h2); - } - - public static long hash(long value) { - long k1 = value; - long h1 = 0L ^ mixK1(k1); - long h2 = 0L; - return finalize(8L, h1, h2); - } - - private Hasher() {} -} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java new file mode 100644 index 0000000000..145806fb0d --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/HotSpotPrior7u6StringHash.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import java.lang.reflect.Field; + +enum HotSpotPrior7u6StringHash implements StringHash { + INSTANCE; + + private static final long valueOffset; + private static final long offsetOffset; + + static { + try { + Field valueField = String.class.getDeclaredField("value"); + valueOffset = UnsafeAccess.UNSAFE.objectFieldOffset(valueField); + + Field offsetField = String.class.getDeclaredField("offset"); + offsetOffset = UnsafeAccess.UNSAFE.objectFieldOffset(offsetField); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); + } + } + + @Override + public long longHash(String s, LongHashFunction hashFunction, int off, int len) { + char[] value = (char[]) UnsafeAccess.UNSAFE.getObject(s, valueOffset); + int offset = UnsafeAccess.UNSAFE.getInt(s, offsetOffset); + return hashFunction.hashChars(value, offset + off, len); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java b/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java new file mode 100644 index 0000000000..864145ba6e --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/LongHashFunction.java @@ -0,0 +1,481 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import net.openhft.chronicle.hash.serialization.BytesInterop; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static java.nio.ByteOrder.nativeOrder; +import static net.openhft.chronicle.hash.hashing.CharSequenceAccess.nativeCharSequenceAccess; +import static net.openhft.chronicle.hash.hashing.UnsafeAccess.*; + +/** + * Hash function producing {@code long}-valued result from byte sequences of any length and + * a plenty of different sources which "feels like byte sequences". Except {@link + * #hashBytes(byte[])}, {@link #hashBytes(ByteBuffer)} (with their "sliced" versions) and + * {@link #hashMemory(long, long)} methods, which actually accept byte sequences, notion of byte + * sequence is defined as follows: + *

    + *
  • For methods accepting arrays of Java primitives, {@code String}s and + * {@code StringBuilder}s, byte sequence is how the input's bytes are actually lay in memory. + *
  • + *
  • For methods accepting single primitive values, byte sequence is how this primitive + * would be put into memory with {@link ByteOrder#nativeOrder() native} byte order, or + * equivalently, {@code hashXxx(primitive)} has always the same result as {@code + * hashXxxs(new xxx[] {primitive})}, where "xxx" is any Java primitive type name.
  • + *
  • For {@link #hash(Object, Access, long, long)} method byte sequence abstraction + * is defined by the given {@link Access} strategy to the given object.
  • + *
+ * + * @see BytesInterop#hash(LongHashFunction, Object) + */ +public abstract class LongHashFunction implements Serializable { + private static final long serialVersionUID = 0L; + + static final boolean NATIVE_LITTLE_ENDIAN = nativeOrder() == LITTLE_ENDIAN; + static final byte TRUE_BYTE_VALUE = UNSAFE.getByte(new boolean[] {true}, BOOLEAN_BASE); + static final byte FALSE_BYTE_VALUE = UNSAFE.getByte(new boolean[] {false}, BOOLEAN_BASE); + + /** + * Returns a hash function implementing + * + * CityHash64 algorithm, version 1.1 without seed values. This implementation produce + * equal results for equal input on platforms with different {@link ByteOrder}, but is slower + * on big-endian platforms than on little-endian. + * + * @see #city_1_1(long) + * @see #city_1_1(long, long) + */ + public static LongHashFunction city_1_1() { + return CityHash_1_1.asLongHashFunctionWithoutSeed(); + } + + /** + * Returns a hash function implementing + * + * CityHash64 algorithm, version 1.1 using the given seed value. This implementation produce + * equal results for equal input on platforms with different {@link ByteOrder}, but is slower + * on big-endian platforms than on little-endian. + * + * @see #city_1_1() + * @see #city_1_1(long, long) + */ + public static LongHashFunction city_1_1(long seed) { + return CityHash_1_1.asLongHashFunctionWithSeed(seed); + } + + /** + * Returns a hash function implementing + * + * CityHash64 algorithm, version 1.1 using the two given seed values. This implementation + * produce equal results for equal input on platforms with different {@link ByteOrder}, but + * is slower on big-endian platforms than on little-endian. + * + * @see #city_1_1() + * @see #city_1_1(long) + */ + public static LongHashFunction city_1_1(long seed0, long seed1) { + return CityHash_1_1.asLongHashFunctionWithTwoSeeds(seed0, seed1); + } + + private static StringHash stringHash; + static { + try { + if (System.getProperty("java.vm.name").contains("HotSpot")) { + if (System.getProperty("java.version").compareTo("1.7.0_06") >= 0) { + stringHash = ModernHotSpotStringHash.INSTANCE; + } else { + stringHash = HotSpotPrior7u6StringHash.INSTANCE; + } + } else { + // try to initialize this version anyway + stringHash = HotSpotPrior7u6StringHash.INSTANCE; + } + } catch (Throwable e) { + // ignore + } finally { + if (stringHash == null) + stringHash = UnknownJvmStringHash.INSTANCE; + } + } + + private static void checkArrayOffs(int arrayLength, int off, int len) { + if (len < 0 || off < 0 || off + len > arrayLength || off + len < 0) + throw new IndexOutOfBoundsException(); + } + + /** + * Constructor for use in subclasses. + */ + protected LongHashFunction() {} + + /** + * Returns the hash code for the given {@code long} value; this method is consistent with + * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input} + * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example, + * the result of {@code hashLong(v)} call is identical to the result of + * {@code hashLongs(new long[] {v})} call for any {@code long} value. + */ + public abstract long hashLong(long input); + + /** + * Returns the hash code for the given {@code int} value; this method is consistent with + * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input} + * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example, + * the result of {@code hashInt(v)} call is identical to the result of + * {@code hashInts(new int[] {v})} call for any {@code int} value. + */ + public abstract long hashInt(int input); + + /** + * Returns the hash code for the given {@code short} value; this method is consistent with + * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input} + * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example, + * the result of {@code hashShort(v)} call is identical to the result of + * {@code hashShorts(new short[] {v})} call for any {@code short} value. + * As a consequence, {@code hashShort(v)} call produce always the same result as {@code + * hashChar((char) v)}. + */ + public abstract long hashShort(short input); + + /** + * Returns the hash code for the given {@code char} value; this method is consistent with + * {@code LongHashFunction} methods that accept sequences of bytes, assuming the {@code input} + * value is interpreted in {@linkplain ByteOrder#nativeOrder() native} byte order. For example, + * the result of {@code hashChar(v)} call is identical to the result of + * {@code hashChars(new char[] {v})} call for any {@code char} value. + * As a consequence, {@code hashChar(v)} call produce always the same result as {@code + * hashShort((short) v)}. + */ + public abstract long hashChar(char input); + + /** + * Returns the hash code for the given {@code byte} value. This method is consistent with + * {@code LongHashFunction} methods that accept sequences of bytes. For example, the result of + * {@code hashByte(v)} call is identical to the result of + * {@code hashBytes(new byte[] {v})} call for any {@code byte} value. + */ + public abstract long hashByte(byte input); + + /** + * Returns the hash code for the empty (zero-length) bytes sequence, + * for example {@code hashBytes(new byte[0])}. + */ + public abstract long hashVoid(); + + /** + * Returns the hash code for {@code len} continuous bytes of the given {@code input} object, + * starting from the given offset. The abstraction of input as ordered byte sequence and + * "offset within the input" is defined by the given {@code access} strategy. + * + *

This method doesn't promise to throw a {@code RuntimeException} if {@code + * [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code + * access} strategy for the given {@code input}, so use this method with caution. + * + * @param input the object to read bytes from + * @param access access which defines the abstraction of the given input + * as ordered byte sequence + * @param off offset to the first byte of the subsequence to hash + * @param len length of the subsequence to hash + * @param the type of the input + * @return hash code for the specified bytes subsequence + */ + public abstract long hash(T input, Access access, long off, long len); + + private long unsafeHash(Object input, long off, long len) { + return hash(input, UnsafeAccess.INSTANCE, off, len); + } + + /** + * Shortcut for {@link #hashBooleans(boolean[]) hashBooleans(new boolean[] {input})}. + * Note that this is not necessarily equal to {@code hashByte(input ? (byte) 1 : (byte) 0)}, + * because booleans could be stored differently in this JVM. + */ + public long hashBoolean(boolean input) { + return hashByte(input ? TRUE_BYTE_VALUE : FALSE_BYTE_VALUE); + } + + /** + * Shortcut for {@link #hashBooleans(boolean[], int, int) hashBooleans(input, 0, input.length)}. + */ + public long hashBooleans(@NotNull boolean[] input) { + return hashBooleans(input, 0, input.length); + } + + /** + * Returns the hash code for the specified subsequence of the given {@code boolean} array. + * + * @param input the array to read data from + * @param off index of the first {@code boolean} in the subsequence to hash + * @param len length of the subsequence to hash + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashBooleans(@NotNull boolean[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, BOOLEAN_BASE + off, len); + } + + /** + * Shortcut for {@link #hashBytes(byte[], int, int) hashBytes(input, 0, input.length)}. + */ + public long hashBytes(@NotNull byte[] input) { + return hashBytes(input, 0, input.length); + } + + /** + * Returns the hash code for the specified subsequence of the given {@code byte} array. + * + * @param input the array to read bytes from + * @param off index of the first {@code byte} in the subsequence to hash + * @param len length of the subsequence to hash + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashBytes(@NotNull byte[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, BYTE_BASE + off, len); + } + + /** + * Shortcut for {@link #hashBytes(ByteBuffer, int, int) + * hashBytes(input, input.position(), input.remaining())}. + */ + public long hashBytes(ByteBuffer input) { + return hashBytes(input, input.position(), input.remaining()); + } + + /** + * Returns the hash code for the specified subsequence of the given {@code ByteBuffer}. + * + *

This method doesn't alter the state (mark, position, limit or order) of the given + * {@code ByteBuffer}. + * + * @param input the buffer to read bytes from + * @param off index of the first {@code byte} in the subsequence to hash + * @param len length of the subsequence to hash + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.capacity()} + * or {@code len < 0} + */ + public long hashBytes(@NotNull ByteBuffer input, int off, int len) { + checkArrayOffs(input.capacity(), off, len); + return hash(input, ByteBufferAccess.INSTANCE, off, len); + } + + /** + * Shortcut for {@link #hashBytes(Bytes, long, long) + * hashBytes(input, input.position(), input.remaining())}. + */ + public long hashBytes(Bytes input) { + return hashBytes(input, input.position(), input.remaining()); + } + + /** + * Returns the hash code for the specified subsequence of the given {@code Bytes}. + * + *

This method doesn't alter the state (position or limit) of the given {@code Bytes}. + * + * @param input the {@code Bytes} to read bytes from + * @param off index of the first {@code byte} in the subsequence to hash + * @param len length of the subsequence to hash + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.capacity()} + * or {@code len < 0} + */ + public long hashBytes(Bytes input, long off, long len) { + if (len < 0 || off < 0 || off + len > input.capacity() || off + len < 0) + throw new IndexOutOfBoundsException(); + if (input instanceof NativeBytes) { + return hashMemory(input.address() + off, len); + } else { + return hash(input, toBytes(), off, len); + } + } + + /** + * Returns the hash code of bytes of the wild memory from the given address. Use with caution. + * + * @param address the address of the first byte to hash + * @param len length of the byte sequence to hash + * @return hash code for the specified byte sequence + */ + public long hashMemory(long address, long len) { + return unsafeHash(null, address, len); + } + + /** + * Shortcut for {@link #hashChars(char[], int, int) hashChars(input, 0, input.length)}. + */ + public long hashChars(@NotNull char[] input) { + return hashChars(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code char} array. + * + * @param input the array to read data from + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashChars(@NotNull char[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, CHAR_BASE + (off * 2L), len * 2L); + } + + /** + * Shortcut for {@link #hashChars(String, int, int) hashChars(input, 0, input.length())}.* + */ + public long hashChars(@NotNull String input) { + return stringHash.longHash(input, this, 0, input.length()); + } + + /** + * Returns the hash code for bytes of the specified subsequence of the given {@code String}'s + * underlying {@code char} array. + * + * @param input the string which bytes to hash + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return the hash code of the given {@code String}'s bytes + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()} + * or {@code len < 0} + */ + public long hashChars(@NotNull String input, int off, int len) { + checkArrayOffs(input.length(), off, len); + return stringHash.longHash(input, this, off, len); + } + + /** + * Shortcut for {@link #hashChars(StringBuilder, int, int) hashChars(input, 0, input.length())}. + */ + public long hashChars(@NotNull StringBuilder input) { + return hashNativeChars(input); + } + + /** + * Returns the hash code for bytes of the specified subsequence of the given + * {@code StringBuilder}'s underlying {@code char} array. + * + * @param input the string builder which bytes to hash + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return the hash code of the given {@code String}'s bytes + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()} + * or {@code len < 0} + */ + public long hashChars(@NotNull StringBuilder input, int off, int len) { + return hashNativeChars(input, off, len); + } + + long hashNativeChars(CharSequence input) { + return hashNativeChars(input, 0, input.length()); + } + + long hashNativeChars(CharSequence input, int off, int len) { + checkArrayOffs(input.length(), off, len); + return hash(input, nativeCharSequenceAccess(), off * 2L, len * 2L); + } + + /** + * Shortcut for {@link #hashShorts(short[], int, int) hashShorts(input, 0, input.length)}. + */ + public long hashShorts(@NotNull short[] input) { + return hashShorts(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code short} array. + * + * @param input the array to read data from + * @param off index of the first {@code short} in the subsequence to hash + * @param len length of the subsequence to hash, in shorts (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashShorts(@NotNull short[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, SHORT_BASE + (off * 2L), len * 2L); + } + + /** + * Shortcut for {@link #hashInts(int[], int, int) hashInts(input, 0, input.length)}. + */ + public long hashInts(@NotNull int[] input) { + return hashInts(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code int} array. + * + * @param input the array to read data from + * @param off index of the first {@code int} in the subsequence to hash + * @param len length of the subsequence to hash, in ints (i. e. the length of the bytes + * sequence to hash is {@code len * 4L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashInts(@NotNull int[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, INT_BASE + (off * 4L), len * 4L); + } + + /** + * Shortcut for {@link #hashLongs(long[], int, int) hashLongs(input, 0, input.length)}. + */ + public long hashLongs(@NotNull long[] input) { + return hashLongs(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code long} array. + * + * @param input the array to read data from + * @param off index of the first {@code long} in the subsequence to hash + * @param len length of the subsequence to hash, in longs (i. e. the length of the bytes + * sequence to hash is {@code len * 8L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashLongs(@NotNull long[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, LONG_BASE + (off * 8L), len * 8L); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java new file mode 100644 index 0000000000..9234915274 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import java.lang.reflect.Field; + +enum ModernHotSpotStringHash implements StringHash { + INSTANCE; + + private static final long valueOffset; + + static { + try { + Field valueField = String.class.getDeclaredField("value"); + valueOffset = UnsafeAccess.UNSAFE.objectFieldOffset(valueField); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); + } + } + + @Override + public long longHash(String s, LongHashFunction hashFunction, int off, int len) { + char[] value = (char[]) UnsafeAccess.UNSAFE.getObject(s, valueOffset); + return hashFunction.hashChars(value, off, len); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java new file mode 100644 index 0000000000..acd8e70089 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +interface StringHash { + + long longHash(String s, LongHashFunction hashFunction, int off, int len); +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java new file mode 100644 index 0000000000..d78474cdcf --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +enum UnknownJvmStringHash implements StringHash { + INSTANCE; + + @Override + public long longHash(String s, LongHashFunction hashFunction, int off, int len) { + return hashFunction.hashNativeChars(s, off, len); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java index b135be6e9c..b66b5f607b 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java @@ -23,10 +23,11 @@ import static net.openhft.chronicle.hash.hashing.Primitives.*; -enum UnsafeAccess implements Access { - INSTANCE; +final class UnsafeAccess extends Access { + public static final UnsafeAccess INSTANCE = new UnsafeAccess(); static final Unsafe UNSAFE; + static final long BOOLEAN_BASE; static final long BYTE_BASE; static final long CHAR_BASE; static final long SHORT_BASE; @@ -38,6 +39,7 @@ enum UnsafeAccess implements Access { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); + BOOLEAN_BASE = UNSAFE.arrayBaseOffset(boolean[].class); BYTE_BASE = UNSAFE.arrayBaseOffset(byte[].class); CHAR_BASE = UNSAFE.arrayBaseOffset(char[].class); SHORT_BASE = UNSAFE.arrayBaseOffset(short[].class); @@ -48,6 +50,8 @@ enum UnsafeAccess implements Access { } } + private UnsafeAccess() {} + @Override public long getLong(Object input, long offset) { return UNSAFE.getLong(input, offset); diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/package-info.java b/src/main/java/net/openhft/chronicle/hash/hashing/package-info.java new file mode 100644 index 0000000000..11356ad3c8 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/package-info.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * API for hashing sequential data and zero-allocation, pretty fast implementations + * of non-cryptographic hash functions. + * + *

Available implementations: + *

    + *
  • {@code long}-valued functions: see + * {@link net.openhft.chronicle.hash.hashing.LongHashFunction} + *
      + *
    • CityHash 1.1: + * {@linkplain net.openhft.chronicle.hash.hashing.LongHashFunction#city_1_1() without + * seeds}, + * {@linkplain net.openhft.chronicle.hash.hashing.LongHashFunction#city_1_1(long) + * with one seed}, + * {@linkplain net.openhft.chronicle.hash.hashing.LongHashFunction#city_1_1(long, long) + * with two seeds}. + *
    • + *
    + *
  • + *
+ */ +package net.openhft.chronicle.hash.hashing; \ No newline at end of file diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/BytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/BytesInterop.java index 204a49f690..8bb8cdfe83 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/BytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/BytesInterop.java @@ -18,8 +18,10 @@ package net.openhft.chronicle.hash.serialization; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.lang.io.Bytes; import net.openhft.lang.model.Byteable; +import org.jetbrains.annotations.NotNull; /** * Writer for objects, which themselves are bytes sequence in some sense: @@ -46,7 +48,37 @@ public interface BytesInterop extends BytesWriter { * @param e the object to serialize virtually and compare with the given {@code bytes} * @return if the given {@code bytes} starts with the given {@code e} object's serialized form */ - boolean startsWith(Bytes bytes, E e); + boolean startsWith(@NotNull Bytes bytes, @NotNull E e); - long hash(E e); + /** + * Returns {@code true} if the given objects are serialized to the same sequence of bytes by + * {@link #write(Bytes, Object)} method, without actual serialization. Normally this method + * should always return the same result as {@code a.equals(b)} call, unless some fields of the + * serialized objects, accounted in {@code equals()} implementation, are ignored + * on serialization. + * + * @param a first object to compare + * @param b second object to compare + * @return if the given objects are serialized to the same sequence of bytes by this + * {@code BytesInterop} strategy + */ + boolean equivalent(@NotNull E a, @NotNull E b); + + /** + * Returns hash code for the byte sequence the given object {@linkplain #write(Bytes, Object) + * is serialized} to, without actual serialization. Formally speaking, implementation of this + * method should be equivalent to
{@code
+     * public long hash(LongHashFunction hashFunction, E e) {
+     *     long size = size(e);
+     *     long addr = theUnsafe.allocateMemory(size);
+     *     Bytes bytes = new NativeBytes(addr, size);
+     *     write(bytes, e);
+     *     return hashFunction.hashBytes(bytes, 0, size);
+     * }}
except that it should perform actual serialization and allocate memory, of cause. + * + * @param hashFunction the hash function to use for hash code computation + * @param e the object to hash + * @return hash code for the bytes sequence the given object is serialized to + */ + long hash(@NotNull LongHashFunction hashFunction, @NotNull E e); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/BytesReader.java b/src/main/java/net/openhft/chronicle/hash/serialization/BytesReader.java index c05ae3b12c..c3d31e0228 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/BytesReader.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/BytesReader.java @@ -20,6 +20,7 @@ import net.openhft.chronicle.hash.ChronicleHash; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.Serializable; @@ -85,7 +86,8 @@ public interface BytesReader extends Serializable { * @see BytesWriter#write(Bytes, Object) * @see #read(Bytes, long, Object) */ - E read(Bytes bytes, long size); + @NotNull + E read(@NotNull Bytes bytes, long size); /** * Similar to {@link #read(Bytes, long)}, but should attempt to reuse the given object, i. e. @@ -101,5 +103,6 @@ public interface BytesReader extends Serializable { * @return the object read from the bytes, either reused or newly created * @see #read(Bytes, long) */ - E read(Bytes bytes, long size, @Nullable E toReuse); + @NotNull + E read(@NotNull Bytes bytes, long size, @Nullable E toReuse); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/BytesWriter.java b/src/main/java/net/openhft/chronicle/hash/serialization/BytesWriter.java index 957c2a3dc2..bed6c5efab 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/BytesWriter.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/BytesWriter.java @@ -20,6 +20,7 @@ import net.openhft.chronicle.hash.ChronicleHash; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import java.io.Serializable; @@ -63,7 +64,7 @@ public interface BytesWriter extends Serializable { * @param e the object which serialized form length should be returned * @return the length (in bytes) of the serialized form of the given object */ - long size(E e); + long size(@NotNull E e); /** * Serializes the given object to the given {@code bytes}, without writing the length of the @@ -79,5 +80,5 @@ public interface BytesWriter extends Serializable { * @param e the object to serialize to the given bytes * @see BytesReader#read(Bytes, long) */ - void write(Bytes bytes, E e); + void write(@NotNull Bytes bytes, @NotNull E e); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java b/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java index 1c4444a248..24d8b08282 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java @@ -17,10 +17,12 @@ package net.openhft.chronicle.hash.serialization; import net.openhft.lang.io.serialization.ObjectFactory; +import org.jetbrains.annotations.NotNull; public interface DeserializationFactoryConfigurableBytesReader> extends BytesReader { - R withDeserializationFactory(ObjectFactory deserializationFactory); + @NotNull + R withDeserializationFactory(@NotNull ObjectFactory deserializationFactory); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BasicCopyingMetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BasicCopyingMetaBytesInterop.java index 8bdb4412be..d1899f024c 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BasicCopyingMetaBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BasicCopyingMetaBytesInterop.java @@ -16,7 +16,7 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.lang.io.Bytes; import net.openhft.lang.threadlocal.Provider; import net.openhft.lang.threadlocal.ThreadLocalCopies; @@ -56,10 +56,10 @@ public boolean startsWith(W writer, Bytes bytes, E e) { } @Override - public long hash(W writer, E e) { + public long hash(W writer, LongHashFunction hashFunction, E e) { long h; if ((h = hash) == 0L) - return hash = Hasher.hash(buffer.buffer); + return hash = hashFunction.hashBytes(buffer.buffer); return h; } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteArrayMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteArrayMarshaller.java index 767df0abd8..f4d1bddda9 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteArrayMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteArrayMarshaller.java @@ -18,21 +18,24 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; public enum ByteArrayMarshaller implements BytesInterop, BytesReader { INSTANCE; @Override - public long size(byte[] ba) { + public long size(@NotNull byte[] ba) { return ba.length; } @Override - public boolean startsWith(Bytes bytes, byte[] ba) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull byte[] ba) { if (bytes.capacity() - bytes.position() < (long) ba.length) return false; long pos = bytes.position(); @@ -44,24 +47,31 @@ public boolean startsWith(Bytes bytes, byte[] ba) { } @Override - public long hash(byte[] ba) { - return Hasher.hash(ba); + public boolean equivalent(@NotNull byte[] a, @NotNull byte[] b) { + return Arrays.equals(a, b); + } + + @Override + public long hash(@NotNull LongHashFunction hashFunction, @NotNull byte[] ba) { + return hashFunction.hashBytes(ba); } @Override - public void write(Bytes bytes, byte[] ba) { + public void write(@NotNull Bytes bytes, @NotNull byte[] ba) { bytes.write(ba); } + @NotNull @Override - public byte[] read(Bytes bytes, long size) { + public byte[] read(@NotNull Bytes bytes, long size) { byte[] ba = new byte[resLen(size)]; bytes.read(ba); return ba; } + @NotNull @Override - public byte[] read(Bytes bytes, long size, byte[] toReuse) { + public byte[] read(@NotNull Bytes bytes, long size, byte[] toReuse) { int resLen = resLen(size); if (toReuse == null || resLen != toReuse.length) toReuse = new byte[resLen]; diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteBufferMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteBufferMarshaller.java index faffb78762..2a9b35d788 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteBufferMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteBufferMarshaller.java @@ -16,11 +16,11 @@ 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.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; @@ -29,7 +29,7 @@ public enum ByteBufferMarshaller implements BytesInterop, BytesReade INSTANCE; @Override - public boolean startsWith(Bytes bytes, ByteBuffer bb) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull ByteBuffer bb) { int inputRemaining = bb.remaining(); if(bytes.capacity() - bytes.position() < (long) inputRemaining) return false; @@ -55,17 +55,24 @@ public boolean startsWith(Bytes bytes, ByteBuffer bb) { } @Override - public long hash(ByteBuffer bb) { - return Hasher.hash(bb, Accesses.toByteBuffer(), 0L, (long) bb.remaining()); + public boolean equivalent(@NotNull ByteBuffer a, @NotNull ByteBuffer b) { + return a.equals(b); } @Override - public ByteBuffer read(Bytes bytes, long size) { + public long hash(@NotNull LongHashFunction hashFunction, @NotNull ByteBuffer bb) { + return hashFunction.hashBytes(bb); + } + + @NotNull + @Override + public ByteBuffer read(@NotNull Bytes bytes, long size) { return read(bytes, size, null); } + @NotNull @Override - public ByteBuffer read(Bytes bytes, long size, @Nullable ByteBuffer toReuse) { + public ByteBuffer read(@NotNull 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?"); @@ -82,12 +89,12 @@ public ByteBuffer read(Bytes bytes, long size, @Nullable ByteBuffer toReuse) { } @Override - public long size(ByteBuffer bb) { + public long size(@NotNull ByteBuffer bb) { return (long) bb.remaining(); } @Override - public void write(Bytes bytes, ByteBuffer bb) { + public void write(@NotNull Bytes bytes, @NotNull ByteBuffer bb) { int position = bb.position(); bytes.write(bb); bb.position(position); diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java index 3358adf89a..5b83e2d0e3 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java @@ -18,7 +18,7 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.DeserializationFactoryConfigurableBytesReader; import net.openhft.chronicle.hash.serialization.SizeMarshaller; @@ -39,9 +39,10 @@ public static ByteableMarshaller of(@NotNull Class eC return new Default<>(eClass); } + @NotNull @Override public ByteableMarshaller withDeserializationFactory( - ObjectFactory deserializationFactory) { + @NotNull ObjectFactory deserializationFactory) { return new WithCustomFactory<>(tClass, deserializationFactory); } @@ -62,7 +63,7 @@ void initSize() { } @Override - public long size(E e) { + public long size(@NotNull E e) { return size; } @@ -92,7 +93,7 @@ public void writeSize(Bytes bytes, long size) { } @Override - public boolean startsWith(Bytes bytes, E e) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull E e) { if (bytes.capacity() - bytes.position() < size) return false; Bytes input = e.bytes(); @@ -111,12 +112,17 @@ public boolean startsWith(Bytes bytes, E e) { } @Override - public long hash(E e) { - return Hasher.hash(e.bytes(), e.offset(), e.offset() + size); + public boolean equivalent(@NotNull E a, @NotNull E b) { + return a.bytes().compare(a.offset(), b.bytes(), b.offset(), size); } @Override - public void write(Bytes bytes, E e) { + public long hash(@NotNull LongHashFunction hashFunction, @NotNull E e) { + return hashFunction.hashBytes(e.bytes(), e.offset(), size); + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull E e) { Bytes eBytes = e.bytes(); if (eBytes != null) { if (eBytes != bytes || bytes.position() != e.offset()) { @@ -136,13 +142,15 @@ public long readSize(Bytes bytes) { return size; } + @NotNull @Override - public E read(Bytes bytes, long size) { + public E read(@NotNull Bytes bytes, long size) { return read(bytes, size, null); } + @NotNull @Override - public E read(Bytes bytes, long size, E toReuse) { + public E read(@NotNull Bytes bytes, long size, E toReuse) { try { if (toReuse == null) toReuse = getInstance(); @@ -177,8 +185,15 @@ public boolean startsWith(Object interop, Bytes bytes, E e) { } @Override - public long hash(Object interop, E e) { - return hash(e); + public boolean equivalent( + Object interop, E e, + MetaBytesInterop otherMetaInterop, I2 otherInterop, E other) { + return equivalent(e, other); + } + + @Override + public long hash(Object interop, LongHashFunction hashFunction, E e) { + return hash(hashFunction, e); } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesBytesInterop.java index 6c5a59174f..c1abb7c1bc 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesBytesInterop.java @@ -16,34 +16,38 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; public enum BytesBytesInterop implements BytesInterop { INSTANCE; @Override - public boolean startsWith(Bytes bytes, Bytes e) { - long limit = bytes.limit(); - bytes.limit(bytes.capacity()); - boolean result = bytes.startsWith(e); - bytes.limit(limit); - return result; + public boolean startsWith(@NotNull Bytes bytes, @NotNull Bytes e) { + return bytes.compare(bytes.position(), e, e.position(), e.remaining()); } @Override - public long hash(Bytes e) { - return Hasher.hash(e); + public boolean equivalent(@NotNull Bytes a, @NotNull Bytes b) { + if (a.remaining() != b.remaining()) + return false; + return a.compare(a.position(), b, b.position(), b.remaining()); } @Override - public long size(Bytes e) { + public long hash(@NotNull LongHashFunction hashFunction, @NotNull Bytes e) { + return hashFunction.hashBytes(e); + } + + @Override + public long size(@NotNull Bytes e) { return e.remaining(); } @Override - public void write(Bytes bytes, Bytes e) { + public void write(@NotNull Bytes bytes, @NotNull Bytes e) { bytes.write(e); } } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java index db1c886007..651280a5df 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java @@ -21,6 +21,7 @@ import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshaller; +import org.jetbrains.annotations.NotNull; /** * Utility methods returning {@link BytesReader} implementations. @@ -62,13 +63,15 @@ public SimpleBytesReader(BytesMarshaller marshaller) { this.marshaller = marshaller; } + @NotNull @Override - public E read(Bytes bytes, long size) { + public E read(@NotNull Bytes bytes, long size) { return (E) marshaller.read(bytes); } + @NotNull @Override - public E read(Bytes bytes, long size, E toReuse) { + public E read(@NotNull Bytes bytes, long size, E toReuse) { return (E) marshaller.read(bytes, toReuse); } } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharArrayMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharArrayMarshaller.java index bdcf3d220b..c6e0309403 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharArrayMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharArrayMarshaller.java @@ -18,16 +18,19 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; public enum CharArrayMarshaller implements BytesInterop, BytesReader { INSTANCE; @Override - public boolean startsWith(Bytes bytes, char[] chars) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull char[] chars) { if (bytes.capacity() - bytes.position() < chars.length * 2L) return false; long pos = bytes.position(); @@ -39,19 +42,26 @@ public boolean startsWith(Bytes bytes, char[] chars) { } @Override - public long hash(char[] chars) { - return Hasher.hash(chars); + public boolean equivalent(@NotNull char[] a, @NotNull char[] b) { + return Arrays.equals(a, b); + } + + @Override + public long hash(@NotNull LongHashFunction hashFunction, @NotNull char[] chars) { + return hashFunction.hashChars(chars); } + @NotNull @Override - public char[] read(Bytes bytes, long size) { + public char[] read(@NotNull Bytes bytes, long size) { char[] chars = new char[resLen(size)]; bytes.readFully(chars); return chars; } + @NotNull @Override - public char[] read(Bytes bytes, long size, char[] toReuse) { + public char[] read(@NotNull Bytes bytes, long size, char[] toReuse) { int resLen = resLen(size); if (toReuse == null || toReuse.length != resLen) toReuse = new char[resLen]; @@ -68,12 +78,12 @@ private int resLen(long size) { } @Override - public long size(char[] chars) { + public long size(@NotNull char[] chars) { return chars.length * 2L; } @Override - public void write(Bytes bytes, char[] chars) { + public void write(@NotNull Bytes bytes, @NotNull char[] chars) { bytes.write(chars); } } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java index 60eb7b5168..32e608f492 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java @@ -81,8 +81,9 @@ private CharSequenceReader(@NotNull CharSequenceInterner interner, this.identity = identity; } + @NotNull @Override - public S read(Bytes bytes, long size) { + public S read(@NotNull Bytes bytes, long size) { sb.setLength(0); try { AbstractBytes.readUTF0(bytes, sb, (int) size); @@ -92,8 +93,9 @@ public S read(Bytes bytes, long size) { return interner.intern(sb); } + @NotNull @Override - public S read(Bytes bytes, long size, S toReuse) { + public S read(@NotNull Bytes bytes, long size, S toReuse) { Appendable appendable; if (toReuse instanceof Appendable) { appendable = (Appendable) toReuse; diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceWriter.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceWriter.java index 9803cd11c8..f6684aaa1f 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceWriter.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceWriter.java @@ -21,6 +21,7 @@ import net.openhft.chronicle.hash.serialization.BytesWriter; import net.openhft.lang.io.AbstractBytes; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import java.io.ObjectStreamException; @@ -35,12 +36,12 @@ public static CharSequenceWriter instance() { private CharSequenceWriter() {} @Override - public long size(CS s) { + public long size(@NotNull CS s) { return AbstractBytes.findUTFLength(s, s.length()); } @Override - public void write(Bytes bytes, CS s) { + public void write(@NotNull Bytes bytes, @NotNull CS s) { AbstractBytes.writeUTF0(bytes, s, s.length()); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java index 995eea7190..0c82d0f721 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java @@ -48,6 +48,13 @@ protected CopyingMetaBytesInterop(DirectBytesBuffer buffer) { super(buffer); } + @Override + public boolean equivalent( + W interop, E e, MetaBytesInterop otherMetaInterop, I2 otherInterop, E other) { + return otherMetaInterop.size(otherInterop, other) == size(interop, e) && + otherMetaInterop.startsWith(otherInterop, buffer.buffer, other); + } + DirectBytesBuffer buffer() { return buffer; } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueMetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueMetaBytesInterop.java index 87eeb84b5c..6e1c4d4c6c 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueMetaBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueMetaBytesInterop.java @@ -33,6 +33,13 @@ protected DataValueMetaBytesInterop(DirectBytesBuffer buffer) { super(buffer); } + @Override + public boolean equivalent( + BytesWriter interop, E e, MetaBytesInterop otherMetaInterop, I2 otherInterop, + E other) { + return e.equals(other); + } + void init(BytesWriter writer, E e, long size) { Bytes buffer = this.buffer.obtain(size, false); writer.write(buffer, e); diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DelegatingMetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DelegatingMetaBytesInterop.java index 7342f2c020..befcae3721 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DelegatingMetaBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DelegatingMetaBytesInterop.java @@ -18,6 +18,7 @@ package net.openhft.chronicle.hash.serialization.internal; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.lang.io.Bytes; @@ -46,8 +47,14 @@ public boolean startsWith(I interop, Bytes bytes, E e) { } @Override - public long hash(I interop, E e) { - return interop.hash(e); + public boolean equivalent( + I interop, E e, MetaBytesInterop otherMetaInterop, I2 otherInterop, E other) { + return interop.equivalent(e, other); + } + + @Override + public long hash(I interop, LongHashFunction hashFunction, E e) { + return interop.hash(hashFunction, e); } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DoubleMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DoubleMarshaller.java index b54ca23dbe..917cd1ca6c 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DoubleMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DoubleMarshaller.java @@ -18,11 +18,12 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.chronicle.hash.serialization.SizeMarshaller; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import static java.lang.Double.doubleToLongBits; @@ -30,7 +31,7 @@ public enum DoubleMarshaller implements BytesInterop, BytesReader, BytesReader, SizeMarshaller { INSTANCE; @Override - public boolean startsWith(Bytes bytes, DummyValue dummyValue) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull DummyValue dummyValue) { return true; } @Override - public long hash(DummyValue dummyValue) { + public boolean equivalent(@NotNull DummyValue a, @NotNull DummyValue b) { + return true; + } + + @Override + public long hash(@NotNull LongHashFunction hashFunction, @NotNull DummyValue dummyValue) { throw new UnsupportedOperationException(); } + @NotNull @Override - public DummyValue read(Bytes bytes, long size) { + public DummyValue read(@NotNull Bytes bytes, long size) { return DUMMY_VALUE; } + @NotNull @Override - public DummyValue read(Bytes bytes, long size, DummyValue toReuse) { + public DummyValue read(@NotNull Bytes bytes, long size, DummyValue toReuse) { return DUMMY_VALUE; } @Override - public long size(DummyValue dummyValue) { + public long size(@NotNull DummyValue dummyValue) { return 0L; } @Override - public void write(Bytes bytes, DummyValue dummyValue) { + public void write(@NotNull Bytes bytes, @NotNull DummyValue dummyValue) { // do nothing } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/IntegerMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/IntegerMarshaller.java index b3d9139cd8..a20c49a7e3 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/IntegerMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/IntegerMarshaller.java @@ -18,18 +18,19 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.chronicle.hash.serialization.SizeMarshaller; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; public enum IntegerMarshaller implements BytesInterop, BytesReader, SizeMarshaller { INSTANCE; @Override - public long size(Integer e) { + public long size(@NotNull Integer e) { return 4L; } @@ -59,17 +60,22 @@ public void writeSize(Bytes bytes, long size) { } @Override - public boolean startsWith(Bytes bytes, Integer e) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull Integer e) { return e == bytes.readInt(bytes.position()); } @Override - public long hash(Integer e) { - return Hasher.hash(e); + public boolean equivalent(@NotNull Integer a, @NotNull Integer b) { + return a.intValue() == b.intValue(); } @Override - public void write(Bytes bytes, Integer e) { + public long hash(@NotNull LongHashFunction hashFunction, @NotNull Integer e) { + return hashFunction.hashInt(e); + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull Integer e) { bytes.writeInt(e); } @@ -78,13 +84,15 @@ public long readSize(Bytes bytes) { return 4L; } + @NotNull @Override - public Integer read(Bytes bytes, long size) { + public Integer read(@NotNull Bytes bytes, long size) { return bytes.readInt(); } + @NotNull @Override - public Integer read(Bytes bytes, long size, Integer toReuse) { + public Integer read(@NotNull Bytes bytes, long size, Integer toReuse) { return bytes.readInt(); } } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongMarshaller.java index a17a2f1379..0e2403d525 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongMarshaller.java @@ -18,18 +18,19 @@ package net.openhft.chronicle.hash.serialization.internal; -import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.serialization.BytesInterop; import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.chronicle.hash.serialization.SizeMarshaller; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; public enum LongMarshaller implements BytesInterop, BytesReader, SizeMarshaller { INSTANCE; @Override - public long size(Long e) { + public long size(@NotNull Long e) { return 8L; } @@ -59,17 +60,22 @@ public void writeSize(Bytes bytes, long size) { } @Override - public boolean startsWith(Bytes bytes, Long e) { + public boolean startsWith(@NotNull Bytes bytes, @NotNull Long e) { return e == bytes.readLong(bytes.position()); } @Override - public long hash(Long e) { - return Hasher.hash(e); + public boolean equivalent(@NotNull Long a, @NotNull Long b) { + return a.longValue() == b.longValue(); } @Override - public void write(Bytes bytes, Long e) { + public long hash(@NotNull LongHashFunction hashFunction, @NotNull Long e) { + return hashFunction.hashLong(e); + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull Long e) { bytes.writeLong(e); } @@ -78,13 +84,15 @@ public long readSize(Bytes bytes) { return 8L; } + @NotNull @Override - public Long read(Bytes bytes, long size) { + public Long read(@NotNull Bytes bytes, long size) { return bytes.readLong(); } + @NotNull @Override - public Long read(Bytes bytes, long size, Long toReuse) { + public Long read(@NotNull Bytes bytes, long size, Long toReuse) { return bytes.readLong(); } } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/MetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/MetaBytesInterop.java index 7e947a6145..8f391b4eea 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/MetaBytesInterop.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/MetaBytesInterop.java @@ -18,12 +18,15 @@ package net.openhft.chronicle.hash.serialization.internal; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.lang.io.Bytes; public interface MetaBytesInterop extends MetaBytesWriter { boolean startsWith(I interop, Bytes bytes, E e); + + boolean equivalent(I interop, E e, + MetaBytesInterop otherMetaInterop, I2 otherInterop, E other); - long hash(I interop, E e); - + long hash(I interop, LongHashFunction hashFunction, E e); } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/VoidMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/VoidMarshaller.java deleted file mode 100644 index 27ee71429a..0000000000 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/VoidMarshaller.java +++ /dev/null @@ -1,91 +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.internal; - -import net.openhft.chronicle.hash.serialization.BytesInterop; -import net.openhft.chronicle.hash.serialization.BytesReader; -import net.openhft.chronicle.hash.serialization.SizeMarshaller; -import net.openhft.lang.io.Bytes; - -public enum VoidMarshaller - implements BytesInterop, BytesReader, SizeMarshaller { - INSTANCE; - - @Override - public long size(Void e) { - return 0L; - } - - @Override - public int sizeEncodingSize(long size) { - return 0; - } - - @Override - public long minEncodableSize() { - return 0L; - } - - @Override - public int minSizeEncodingSize() { - return 0; - } - - @Override - public int maxSizeEncodingSize() { - return 0; - } - - @Override - public void writeSize(Bytes bytes, long size) { - // do nothing - } - - @Override - public boolean startsWith(Bytes bytes, Void e) { - return false; - } - - @Override - public long hash(Void e) { - throw new UnsupportedOperationException(); - } - - @Override - public void write(Bytes bytes, Void e) { - // do nothing; - } - - @Override - public long readSize(Bytes bytes) { - return 0L; - } - - @Override - public Void read(Bytes bytes, long size) { - // Void nothing; - return null; - } - - @Override - public Void read(Bytes bytes, long size, Void toReuse) { - return null; - } - -} diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index 38ee2aaf73..e41181abe6 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -18,6 +18,7 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.replication.AbstractReplication; import net.openhft.chronicle.hash.replication.LateUpdateException; import net.openhft.chronicle.hash.replication.TimeProvider; @@ -30,7 +31,6 @@ import net.openhft.lang.collection.ATSDirectBitSet; import net.openhft.lang.collection.SingleThreadedDirectBitSet; import net.openhft.lang.io.Bytes; -import net.openhft.lang.io.MultiStoreBytes; import net.openhft.lang.threadlocal.ThreadLocalCopies; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -43,7 +43,6 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicReferenceArray; -import static net.openhft.chronicle.hash.hashing.Hasher.hash; import static net.openhft.chronicle.map.VanillaContext.SearchState.DELETED; import static net.openhft.chronicle.map.VanillaContext.SearchState.PRESENT; import static net.openhft.lang.MemoryUnit.*; @@ -464,7 +463,14 @@ public boolean startsWith(Object interop, Bytes bytes, Object o) { } @Override - public long hash(Object interop, Object o) { + public boolean equivalent( + Object interop, Object o, + MetaBytesInterop otherMetaInterop, I2 otherInterop, Object other) { + throw new UnsupportedOperationException(); + } + + @Override + public long hash(Object interop, LongHashFunction hashFunction, Object o) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java index 58f574af64..04f5a8c646 100644 --- a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java @@ -138,10 +138,6 @@ private void configureByDefault(Class eClass, Role role) { } else if (eClass == StringBuilder.class) { reader((BytesReader) CharSequenceReader.ofStringBuilder()); writer((BytesWriter) CharSequenceWriter.instance()); - } else if (eClass == Void.class) { - sizeMarshaller(VoidMarshaller.INSTANCE); - reader((BytesReader) VoidMarshaller.INSTANCE); - interop((BytesInterop) VoidMarshaller.INSTANCE); } else if (eClass == Long.class) { sizeMarshaller(LongMarshaller.INSTANCE); reader((BytesReader) LongMarshaller.INSTANCE); @@ -176,8 +172,6 @@ private void configureByDefault(Class eClass, Role role) { boolean possibleOffHeapReferences() { if (reader instanceof CharSequenceReader) return false; - if (reader instanceof VoidMarshaller) - return false; // exclude some known classes, most notably boxed primitive types if (!instancesAreMutable(eClass)) return false; diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 58e103a2dd..4a2ce5c0b2 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -380,7 +380,7 @@ final void checkKey(Object key) { @Override final void checkValue(Object value) { - if (vClass != Void.class && !vClass.isInstance(value)) { + if (!vClass.isInstance(value)) { throw new ClassCastException("Value must be a " + vClass.getName() + " but was a " + value.getClass()); } diff --git a/src/main/java/net/openhft/chronicle/map/VanillaContext.java b/src/main/java/net/openhft/chronicle/map/VanillaContext.java index 02b87c8486..9db64f540f 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaContext.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaContext.java @@ -16,6 +16,7 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.hash.hashing.LongHashFunction; import net.openhft.chronicle.hash.locks.IllegalInterProcessLockStateException; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.chronicle.hash.serialization.BytesReader; @@ -503,7 +504,7 @@ void initKeyHashDependencies() { } void initKeyHash0() { - hash = metaKeyInterop.hash(keyInterop, key); + hash = metaKeyInterop.hash(keyInterop, LongHashFunction.city_1_1(), key); } void closeKeyHash() { diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java index 381e584843..3e2befac38 100755 --- a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java @@ -26,6 +26,8 @@ import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.chronicle.hash.serialization.BytesWriter; import net.openhft.chronicle.hash.serialization.SizeMarshaller; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; +import net.openhft.chronicle.hash.serialization.internal.DummyValueMarshaller; import net.openhft.chronicle.map.ChronicleMap; import net.openhft.chronicle.map.ChronicleMapBuilder; import net.openhft.lang.io.serialization.BytesMarshaller; @@ -36,7 +38,6 @@ import java.io.File; import java.io.IOException; -import java.util.concurrent.TimeUnit; /** * {@code ChronicleSetBuilder} manages the whole set of {@link ChronicleSet} configurations, could diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetStatelessClientBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetStatelessClientBuilder.java index 9a8a029e5b..350ce7ef7c 100644 --- a/src/main/java/net/openhft/chronicle/set/ChronicleSetStatelessClientBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSetStatelessClientBuilder.java @@ -17,6 +17,7 @@ package net.openhft.chronicle.set; import net.openhft.chronicle.hash.ChronicleHashStatelessClientBuilder; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; import net.openhft.chronicle.map.ChronicleMapBuilder; import net.openhft.chronicle.map.ChronicleMapStatelessClientBuilder; diff --git a/src/main/java/net/openhft/chronicle/set/SetFromMap.java b/src/main/java/net/openhft/chronicle/set/SetFromMap.java index 80317c0168..7550d21a7b 100644 --- a/src/main/java/net/openhft/chronicle/set/SetFromMap.java +++ b/src/main/java/net/openhft/chronicle/set/SetFromMap.java @@ -21,6 +21,7 @@ import net.openhft.chronicle.hash.KeyContext; import net.openhft.chronicle.hash.function.Consumer; import net.openhft.chronicle.hash.function.Predicate; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; import net.openhft.chronicle.map.ChronicleMap; import java.io.File; @@ -31,7 +32,7 @@ import java.util.Iterator; import java.util.Set; -import static net.openhft.chronicle.set.DummyValue.DUMMY_VALUE; +import static net.openhft.chronicle.hash.serialization.internal.DummyValue.DUMMY_VALUE; class SetFromMap extends AbstractSet implements ChronicleSet, Serializable { diff --git a/src/main/java/net/openhft/chronicle/set/SetInstanceBuilder.java b/src/main/java/net/openhft/chronicle/set/SetInstanceBuilder.java index 61ea12ef1b..1822cfd7c1 100644 --- a/src/main/java/net/openhft/chronicle/set/SetInstanceBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/SetInstanceBuilder.java @@ -20,6 +20,7 @@ import net.openhft.chronicle.hash.replication.ReplicationChannel; import net.openhft.chronicle.hash.replication.SingleChronicleHashReplication; import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; import net.openhft.chronicle.map.ChronicleMap; import java.io.File; diff --git a/src/test/java/net/openhft/chronicle/map/Builder.java b/src/test/java/net/openhft/chronicle/map/Builder.java index 68ac0c64a6..5ebdc6661f 100644 --- a/src/test/java/net/openhft/chronicle/map/Builder.java +++ b/src/test/java/net/openhft/chronicle/map/Builder.java @@ -19,6 +19,7 @@ package net.openhft.chronicle.map; import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; import java.io.File; import java.io.IOException; @@ -58,11 +59,11 @@ using setwritable() and then releasing RandomRW lock adds the file to JVM exit c return file; } - public static > T newMapVoid( + public static > T newMapDummyValue( final byte identifier, final int serverPort, final InetSocketAddress... endpoints) throws IOException { - return (T) newTcpSocketShmBuilder(Integer.class, Void.class, + return (T) newTcpSocketShmBuilder(Integer.class, DummyValue.class, identifier, serverPort, endpoints).create(); } diff --git a/src/test/java/net/openhft/chronicle/map/HasherTest.java b/src/test/java/net/openhft/chronicle/map/HasherTest.java deleted file mode 100644 index f883a04c0a..0000000000 --- a/src/test/java/net/openhft/chronicle/map/HasherTest.java +++ /dev/null @@ -1,38 +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.map; - -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; -import net.openhft.chronicle.hash.hashing.Hasher; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class HasherTest { - @Test - public void test() { - HashFunction hashFunction = Hashing.murmur3_128(); - for (int i=0;i<10;i++) - { - String key = "010758403"+String.format("%06d",i)+"S-INJFIX_SLE"; - long hashCode = Hasher.hash(key.getBytes()); - long guavaHashCode = hashFunction.hashBytes(key.getBytes()).asLong(); - assertEquals(guavaHashCode, hashCode); - } - } -} diff --git a/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java b/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java index 781bdccbde..91edcc2d33 100755 --- a/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java +++ b/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java @@ -18,6 +18,7 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -27,7 +28,8 @@ import java.net.InetSocketAddress; import java.util.Set; -import static net.openhft.chronicle.map.Builder.newMapVoid; +import static net.openhft.chronicle.hash.serialization.internal.DummyValue.DUMMY_VALUE; +import static net.openhft.chronicle.map.Builder.newMapDummyValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -39,16 +41,16 @@ public class TCPSocketReplication3VoidValueTest { - private ChronicleMap map1; - private ChronicleMap map2; - private ChronicleMap map3; + private ChronicleMap map1; + private ChronicleMap map2; + private ChronicleMap map3; @Before public void setup() throws IOException { - map1 = newMapVoid((byte) 1, 8036, new InetSocketAddress("localhost", 8037), + map1 = newMapDummyValue((byte) 1, 8036, new InetSocketAddress("localhost", 8037), new InetSocketAddress("localhost", 8039)); - map2 = newMapVoid((byte) 2, 8037, new InetSocketAddress("localhost", 8039)); - map3 = newMapVoid((byte) 3, 8039); + map2 = newMapDummyValue((byte) 2, 8037, new InetSocketAddress("localhost", 8039)); + map3 = newMapDummyValue((byte) 3, 8039); } @After @@ -78,7 +80,7 @@ public void checkThreadsShutdown() { @Test public void test3() throws IOException, InterruptedException { - assertEquals(null, map3.put(5, null)); + assertEquals(null, map3.put(5, DUMMY_VALUE)); // allow time for the recompilation to resolve assertTrue(waitTillEqual(15000)); @@ -90,16 +92,16 @@ public void test3() throws IOException, InterruptedException { @Test public void test() throws IOException, InterruptedException { - assertEquals(null, map1.put(1, null)); - assertEquals(null, map1.put(2, null)); + assertEquals(null, map1.put(1, DUMMY_VALUE)); + assertEquals(null, map1.put(2, DUMMY_VALUE)); - assertEquals(null, map2.put(5, null)); - assertEquals(null, map2.put(6, null)); + assertEquals(null, map2.put(5, DUMMY_VALUE)); + assertEquals(null, map2.put(6, DUMMY_VALUE)); map1.remove(2); map2.remove(3); map1.remove(3); - map2.put(5, null); + map2.put(5, DUMMY_VALUE); // allow time for the recompilation to resolve assertTrue(waitTillEqual(5000)); @@ -111,15 +113,15 @@ public void test() throws IOException, InterruptedException { @Test public void testClear() throws IOException, InterruptedException { - assertEquals(null, map1.put(1, null)); - assertEquals(null, map1.put(2, null)); + assertEquals(null, map1.put(1, DUMMY_VALUE)); + assertEquals(null, map1.put(2, DUMMY_VALUE)); - assertEquals(null, map2.put(5, null)); - assertEquals(null, map2.put(6, null)); + assertEquals(null, map2.put(5, DUMMY_VALUE)); + assertEquals(null, map2.put(6, DUMMY_VALUE)); map1.clear(); - map2.put(5, null); + map2.put(5, DUMMY_VALUE); // allow time for the recompilation to resolve assertTrue("test timed out", waitTillEqual(15000)); diff --git a/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayReader.java b/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayReader.java index 25173b946f..6e9444db00 100644 --- a/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayReader.java +++ b/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayReader.java @@ -18,19 +18,22 @@ import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; enum LongPairArrayReader implements BytesReader { INSTANCE; + @NotNull @Override - public LongPair[] read(Bytes bytes, long size) { + public LongPair[] read(@NotNull Bytes bytes, long size) { return read(bytes, size, null); } + @NotNull @Override - public LongPair[] read(Bytes bytes, long size, LongPair[] toReuse) { + public LongPair[] read(@NotNull Bytes bytes, long size, LongPair[] toReuse) { if (size > Integer.MAX_VALUE * 16L) throw new IllegalStateException("LongPair[] size couldn't be " + (size / 16L)); int resLen = (int) (size / 16L); diff --git a/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayWriter.java b/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayWriter.java index 32268bae44..321a99b6a7 100644 --- a/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayWriter.java +++ b/src/test/java/net/openhft/chronicle/map/fromdocs/LongPairArrayWriter.java @@ -18,17 +18,18 @@ import net.openhft.chronicle.hash.serialization.BytesWriter; import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; enum LongPairArrayWriter implements BytesWriter { INSTANCE; @Override - public long size(LongPair[] longPairs) { + public long size(@NotNull LongPair[] longPairs) { return longPairs.length * 16L; } @Override - public void write(Bytes bytes, LongPair[] longPairs) { + public void write(@NotNull Bytes bytes, @NotNull LongPair[] longPairs) { for (LongPair pair : longPairs) { bytes.writeLong(pair.first); bytes.writeLong(pair.second); diff --git a/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java index 88928ee776..1122d4c147 100755 --- a/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java @@ -704,6 +704,7 @@ public void testGet_NullPointerException() throws IOException { c.get(null); shouldThrow(); } catch (NullPointerException success) { + } catch (IllegalArgumentException success) { } } @@ -716,6 +717,7 @@ public void testContainsKey_NullPointerException() throws IOException { c.containsKey(null); shouldThrow(); } catch (NullPointerException success) { + } catch (IllegalArgumentException success) { } }