diff --git a/pom.xml b/pom.xml index 30828ab28..2d6fde82c 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 3.0.0a-SNAPSHOT OpenHFT/Chronicle-Map Chronicle-Map + bundle @@ -47,7 +48,7 @@ net.openhft lang - 6.5.1-SNAPSHOT + 6.6-SNAPSHOT @@ -218,6 +219,60 @@ + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId}.${project.artifactId} + OpenHFT :: ${project.artifactId} + ${project.version} + + com.thoughtworks.xstream;resolution:=optional, + org.jetbrains.annotations;resolution:=optional, + org.intellij.lang.annotations;resolution:=optional, + * + + + net.openhft.chronicle.map.*, + net.openhft.chronicle.set.*, + net.openhft.chronicle.hash.* + + + + + + + + manifest + + + + diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java index 44088b816..6f896bd7f 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/Access.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/Access.java @@ -16,8 +16,13 @@ package net.openhft.chronicle.hash.hashing; +import net.openhft.lang.io.Bytes; + +import java.nio.ByteBuffer; import java.nio.ByteOrder; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + /** * Strategy of reading bytes, defines the abstraction of {@code T} class instances as ordered byte * sequence. All {@code getXXX(input, offset)} should be consistent to each other in terms of @@ -38,6 +43,10 @@ import java.nio.ByteOrder; * range is outside of the bounds of the byte sequence, represented by the given {@code input}. * However, they could omit checks for better performance. * + *

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

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

Usage example:

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

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

Usage example:

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

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

Usage example:

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

Usage example:

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

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

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

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

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

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

This method doesn't alter the state (position or limit) of the given {@code Bytes}. + * + * @param input the {@code Bytes} to read bytes from + * @param off index of the first {@code byte} in the subsequence to hash + * @param len length of the subsequence to hash + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.capacity()} + * or {@code len < 0} + */ + public long hashBytes(Bytes input, long off, long len) { + if (len < 0 || off < 0 || off + len > input.capacity() || off + len < 0) + throw new IndexOutOfBoundsException(); + if (input instanceof NativeBytes) { + return hashMemory(input.address() + off, len); + } else { + return hash(input, toBytes(), off, len); + } + } + + /** + * Returns the hash code of bytes of the wild memory from the given address. Use with caution. + * + * @param address the address of the first byte to hash + * @param len length of the byte sequence to hash + * @return hash code for the specified byte sequence + */ + public long hashMemory(long address, long len) { + return unsafeHash(null, address, len); + } + + /** + * Shortcut for {@link #hashChars(char[], int, int) hashChars(input, 0, input.length)}. + */ + public long hashChars(@NotNull char[] input) { + return hashChars(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code char} array. + * + * @param input the array to read data from + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashChars(@NotNull char[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, CHAR_BASE + (off * 2L), len * 2L); + } + + /** + * Shortcut for {@link #hashChars(String, int, int) hashChars(input, 0, input.length())}.* + */ + public long hashChars(@NotNull String input) { + return stringHash.longHash(input, this, 0, input.length()); + } + + /** + * Returns the hash code for bytes of the specified subsequence of the given {@code String}'s + * underlying {@code char} array. + * + * @param input the string which bytes to hash + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return the hash code of the given {@code String}'s bytes + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()} + * or {@code len < 0} + */ + public long hashChars(@NotNull String input, int off, int len) { + checkArrayOffs(input.length(), off, len); + return stringHash.longHash(input, this, off, len); + } + + /** + * Shortcut for {@link #hashChars(StringBuilder, int, int) hashChars(input, 0, input.length())}. + */ + public long hashChars(@NotNull StringBuilder input) { + return hashNativeChars(input); + } + + /** + * Returns the hash code for bytes of the specified subsequence of the given + * {@code StringBuilder}'s underlying {@code char} array. + * + * @param input the string builder which bytes to hash + * @param off index of the first {@code char} in the subsequence to hash + * @param len length of the subsequence to hash, in chars (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return the hash code of the given {@code String}'s bytes + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length()} + * or {@code len < 0} + */ + public long hashChars(@NotNull StringBuilder input, int off, int len) { + return hashNativeChars(input, off, len); + } + + long hashNativeChars(CharSequence input) { + return hashNativeChars(input, 0, input.length()); + } + + long hashNativeChars(CharSequence input, int off, int len) { + checkArrayOffs(input.length(), off, len); + return hash(input, nativeCharSequenceAccess(), off * 2L, len * 2L); + } + + /** + * Shortcut for {@link #hashShorts(short[], int, int) hashShorts(input, 0, input.length)}. + */ + public long hashShorts(@NotNull short[] input) { + return hashShorts(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code short} array. + * + * @param input the array to read data from + * @param off index of the first {@code short} in the subsequence to hash + * @param len length of the subsequence to hash, in shorts (i. e. the length of the bytes + * sequence to hash is {@code len * 2L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashShorts(@NotNull short[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, SHORT_BASE + (off * 2L), len * 2L); + } + + /** + * Shortcut for {@link #hashInts(int[], int, int) hashInts(input, 0, input.length)}. + */ + public long hashInts(@NotNull int[] input) { + return hashInts(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code int} array. + * + * @param input the array to read data from + * @param off index of the first {@code int} in the subsequence to hash + * @param len length of the subsequence to hash, in ints (i. e. the length of the bytes + * sequence to hash is {@code len * 4L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashInts(@NotNull int[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, INT_BASE + (off * 4L), len * 4L); + } + + /** + * Shortcut for {@link #hashLongs(long[], int, int) hashLongs(input, 0, input.length)}. + */ + public long hashLongs(@NotNull long[] input) { + return hashLongs(input, 0, input.length); + } + + /** + * Returns the hash code for bytes, as they lay in memory, of the specified subsequence + * of the given {@code long} array. + * + * @param input the array to read data from + * @param off index of the first {@code long} in the subsequence to hash + * @param len length of the subsequence to hash, in longs (i. e. the length of the bytes + * sequence to hash is {@code len * 8L}) + * @return hash code for the specified subsequence + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > input.length} + * or {@code len < 0} + */ + public long hashLongs(@NotNull long[] input, int off, int len) { + checkArrayOffs(input.length, off, len); + return unsafeHash(input, LONG_BASE + (off * 8L), len * 8L); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java new file mode 100644 index 000000000..923491527 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/ModernHotSpotStringHash.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +import java.lang.reflect.Field; + +enum ModernHotSpotStringHash implements StringHash { + INSTANCE; + + private static final long valueOffset; + + static { + try { + Field valueField = String.class.getDeclaredField("value"); + valueOffset = UnsafeAccess.UNSAFE.objectFieldOffset(valueField); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); + } + } + + @Override + public long longHash(String s, LongHashFunction hashFunction, int off, int len) { + char[] value = (char[]) UnsafeAccess.UNSAFE.getObject(s, valueOffset); + return hashFunction.hashChars(value, off, len); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java new file mode 100644 index 000000000..acd8e7008 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/StringHash.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +interface StringHash { + + long longHash(String s, LongHashFunction hashFunction, int off, int len); +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java new file mode 100644 index 000000000..d78474cdc --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnknownJvmStringHash.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.hashing; + +enum UnknownJvmStringHash implements StringHash { + INSTANCE; + + @Override + public long longHash(String s, LongHashFunction hashFunction, int off, int len) { + return hashFunction.hashNativeChars(s, off, len); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java index b135be6e9..b66b5f607 100644 --- a/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java +++ b/src/main/java/net/openhft/chronicle/hash/hashing/UnsafeAccess.java @@ -23,10 +23,11 @@ import java.nio.ByteOrder; import static net.openhft.chronicle.hash.hashing.Primitives.*; -enum UnsafeAccess implements Access { - 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 000000000..11356ad3c --- /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 204a49f69..8bb8cdfe8 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 c05ae3b12..c3d31e022 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 @@ package net.openhft.chronicle.hash.serialization; 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 957c2a3dc..bed6c5efa 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 @@ package net.openhft.chronicle.hash.serialization; 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 1c4444a24..24d8b0828 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 8bdb4412b..61c27335f 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,15 +56,15 @@ public abstract class BasicCopyingMetaBytesInterop implements MetaBytesInt } @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; } @Override public void write(W writer, Bytes bytes, E e) { - bytes.write(buffer.buffer); + bytes.write(buffer.buffer, buffer.buffer.position(), size); } } 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 767df0abd..f4d1bddda 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 enum ByteArrayMarshaller implements BytesInterop, BytesReader, 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 enum ByteBufferMarshaller implements BytesInterop, BytesReade } @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 enum ByteBufferMarshaller implements BytesInterop, BytesReade } @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 6ccbaa1e8..5b83e2d0e 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 abstract class ByteableMarshaller return new Default<>(eClass); } + @NotNull @Override public ByteableMarshaller withDeserializationFactory( - ObjectFactory deserializationFactory) { + @NotNull ObjectFactory deserializationFactory) { return new WithCustomFactory<>(tClass, deserializationFactory); } @@ -62,7 +63,7 @@ public abstract class ByteableMarshaller } @Override - public long size(E e) { + public long size(@NotNull E e) { return size; } @@ -92,7 +93,7 @@ public abstract class ByteableMarshaller } @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 abstract class ByteableMarshaller } @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,17 +142,19 @@ public abstract class ByteableMarshaller 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(); - toReuse.bytes(bytes, bytes.position()); + setBytesAndOffset(toReuse, bytes); bytes.skip(size); return toReuse; } catch (Exception ex) { @@ -177,8 +185,15 @@ public abstract class ByteableMarshaller } @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 6c5a59174..4cdc2a49b 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) { - bytes.write(e); + public void write(@NotNull Bytes bytes, @NotNull Bytes e) { + bytes.write(e, e.position(), e.remaining()); } } 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 db1c88600..651280a5d 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 @@ package net.openhft.chronicle.hash.serialization.internal; 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 final class BytesReaders { 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 bdcf3d220..c6e030940 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 enum CharArrayMarshaller implements BytesInterop, BytesReader, BytesReader 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 final class CharSequenceReader 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 9803cd11c..f6684aaa1 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 @@ package net.openhft.chronicle.hash.serialization.internal; 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 final class CharSequenceWriter implements BytesW 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 995eea719..0c82d0f72 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 @@ public abstract class CopyingMetaBytesInterop extends BasicCopyingMetaByte 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/DataValueBytesMarshallers.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java index fcf065a91..c12280146 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java @@ -492,7 +492,8 @@ public final class DataValueBytesMarshallers { sb.append(" if (toReuse == null)\n"); sb.append(" toReuse = getInstance();\n"); sb.append(" if (toReuse instanceof Byteable) {\n"); - sb.append(" ((Byteable) toReuse).bytes(bytes, bytes.position());\n"); + sb.append(" " + + "ByteableMarshaller.setBytesAndOffset(((Byteable) toReuse), bytes);\n"); sb.append(" bytes.skip(size);\n"); sb.append(" return toReuse;\n"); sb.append(" }\n"); 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 87eeb84b5..6e1c4d4c6 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 @@ public abstract class DataValueMetaBytesInterop 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 7342f2c02..befcae372 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 final class DelegatingMetaBytesInterop> } @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 b54ca23db..917cd1ca6 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, 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(@NotNull Bytes bytes, long size, DummyValue toReuse) { return DUMMY_VALUE; } @Override - public DummyValue read(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 b3d9139cd..a20c49a7e 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 enum IntegerMarshaller } @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 enum IntegerMarshaller 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 a17a2f137..0e2403d52 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 enum LongMarshaller } @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 enum LongMarshaller 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 7e947a614..8f391b4ee 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 27ee71429..000000000 --- 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/AbstractChronicleMap.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMap.java index eb38d48db..87c68c5da 100644 --- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMap.java @@ -295,6 +295,7 @@ abstract class AbstractChronicleMap extends AbstractMap c.updateLock().lock(); c.initSegment(); c.hashLookup.forEach(this); + return; } finally { context = null; } @@ -466,7 +467,8 @@ abstract class AbstractChronicleMap extends AbstractMap try { if (!c.containsKey()) // for replicated map return false; - shouldBreak = predicate.test(c); + shouldBreak = !predicate.test(c); + c.closeKey0(); // release all exclusive locks: possibly if context.remove() is performed // in the callback, or acquired manually while (c.writeLockCount > 0) { diff --git a/src/main/java/net/openhft/chronicle/map/ChannelProvider.java b/src/main/java/net/openhft/chronicle/map/ChannelProvider.java index 8e08abe66..5f9ae55db 100644 --- a/src/main/java/net/openhft/chronicle/map/ChannelProvider.java +++ b/src/main/java/net/openhft/chronicle/map/ChannelProvider.java @@ -502,7 +502,7 @@ final class ChannelProvider implements Closeable { @Override public void writeExternalEntry(@NotNull Bytes entry, @NotNull Bytes destination, int na) { - destination.write(entry); + destination.write(entry, entry.position(), entry.remaining()); } @Override diff --git a/src/main/java/net/openhft/chronicle/map/NodeDiscovery.java b/src/main/java/net/openhft/chronicle/map/NodeDiscovery.java index 137bca57d..893f9a4be 100644 --- a/src/main/java/net/openhft/chronicle/map/NodeDiscovery.java +++ b/src/main/java/net/openhft/chronicle/map/NodeDiscovery.java @@ -755,7 +755,7 @@ class DiscoveryNodeBytesMarshallable implements BytesMarshallable { return false; BOOTSTRAP_BYTES.clear(); - out.write(BOOTSTRAP_BYTES); + out.write(BOOTSTRAP_BYTES, BOOTSTRAP_BYTES.position(), BOOTSTRAP_BYTES.remaining()); ourProposedIdentifier.writeMarshallable(out); return true; diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index db864a043..cd5642d1c 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.Maths; 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.Set; 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.*; @@ -289,6 +288,13 @@ final class ReplicatedChronicleMap) m; } + @Override + void totalCheckClosed() { + super.totalCheckClosed(); + assert !replicationStateInit() : "replication state not closed"; + assert !replicationUpdateInit() : "replication update not closed"; + } + ///////////////////////////////////////////////// // Replication state long replicationBytesOffset; @@ -314,11 +320,15 @@ final class ReplicatedChronicleMap 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(); } @@ -493,6 +514,7 @@ final class ReplicatedChronicleMap implements Cloneable, Serializable { } 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 @@ final class SerializationBuilder implements Cloneable, Serializable { 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 ed92edf7c..4a2ce5c0b 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -134,6 +134,7 @@ class VanillaChronicleMap, ///////////////////////////////////////////////// // Bytes Store (essentially, the base address) and serialization-dependent offsets transient BytesStore ms; + transient Bytes bytes; transient long headerSize; transient long segmentHeadersOffset; @@ -272,6 +273,7 @@ class VanillaChronicleMap, final void createMappedStoreAndSegments(BytesStore bytesStore) throws IOException { this.ms = bytesStore; + bytes = ms.bytes(); onHeaderCreated(); @@ -362,6 +364,8 @@ class VanillaChronicleMap, public void close() { if (ms == null) return; + bytes.release(); + bytes = null; ms.free(); ms = null; } @@ -376,7 +380,7 @@ class VanillaChronicleMap, @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()); } @@ -403,12 +407,7 @@ class VanillaChronicleMap, @Override void putDefaultValue(VanillaContext context) { - context.initPutDependencies(); - context.initInstanceValueModel0(); - context.initNewInstanceValue0(defaultValue(context)); - context.put0(); - context.closeValueModel(); - context.closeValue(); + context.doPut(defaultValue(context)); } @Override @@ -433,6 +432,15 @@ class VanillaChronicleMap, "during map building"); } + @Override + V prevValueOnPut(MapKeyContext context) { + return putReturnsNull ? null : super.prevValueOnPut(context); + } + + @Override + V prevValueOnRemove(MapKeyContext context) { + return removeReturnsNull ? null : super.prevValueOnRemove(context); + } @Override public V newValueInstance() { @@ -500,7 +508,6 @@ class VanillaChronicleMap, try { if (!c.containsKey()) { c.doPut(defaultValue(c)); - c.closeValue(); } V value = c.getUsing(usingValue); if (value != usingValue) { diff --git a/src/main/java/net/openhft/chronicle/map/VanillaContext.java b/src/main/java/net/openhft/chronicle/map/VanillaContext.java index bed81d417..9db64f540 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; @@ -154,6 +155,10 @@ class VanillaContext, // Map VanillaChronicleMap m; + boolean mapInit() { + return m != null; + } + void initMap(VanillaChronicleMap map) { initMapDependencies(); initMap0(map); @@ -168,12 +173,12 @@ class VanillaContext, } void checkMapInit() { - if (m == null) + if (!mapInit()) throw new IllegalStateException("Map should be init"); } void closeMap() { - if (m == null) + if (!mapInit()) return; closeMapDependants(); closeMap0(); @@ -206,6 +211,28 @@ class VanillaContext, void doClose() { closeMap(); used = false; + totalCheckClosed(); + } + + void totalCheckClosed() { + assert !mapInit() : "map not closed"; + assert !mapAndContextLocalsInit() : "map&context locals not closed"; + assert !keyModelInit() : "key model not closed"; + assert !keyReaderInit() : "key reader not closed"; + assert !keyInit() : "key not closed"; + assert !keyHashInit() : "key hash not closed"; + assert !segmentIndexInit() : "segment index not closed"; + assert !segmentHeaderInit() : "segment header not closed"; + assert !locksInit() : "locks not closed"; + assert !segmentInit() : "segment not closed"; + assert !hashLookupInit() : "hash lookup not closed"; + assert !keySearchInit() : "key search not closed"; + assert !valueBytesInit() : "value bytes not closed"; + assert !valueReaderInit() : "value reader not closed"; + assert !valueInit() : "value not closed"; + assert !valueModelInit() : "value model not closed"; + assert !newValueInit() : "new value not closed"; + assert !entrySizeInChunksInit() : "entry size in chunks not closed"; } synchronized void closeFromAnotherThread() { @@ -225,16 +252,11 @@ class VanillaContext, K reusableKey; V reusableValue; MapAndContextLocals(ChronicleMap map) { -// try { -// reusableKey = map.newKeyInstance(); -// } catch (Exception e) { -// // ignore -// } -// try { -// reusableValue = map.newValueInstance(); -// } catch (Exception e) { -// // ignore -// } + // TODO why not using map.newKeyInstance() and newValueInstance() + if (map.keyClass() == CharSequence.class) + reusableKey = (K) new StringBuilder(); + if (map.valueClass() == CharSequence.class) + reusableValue = (V) new StringBuilder(); } } @@ -249,8 +271,12 @@ class VanillaContext, } } + boolean mapAndContextLocalsInit() { + return mapAndContextLocalsReference != null; + } + void initMapAndContextLocals() { - if (mapAndContextLocalsReference != null) + if (mapAndContextLocalsInit()) return; initMapAndContextLocalsDependencies(); initMapAndContextLocals0(); @@ -296,7 +322,7 @@ class VanillaContext, } void closeMapAndContextLocals() { - if (mapAndContextLocalsReference == null) + if (!mapAndContextLocalsInit()) return; closeMapContextAndLocalsDependants(); closeMapAndContextLocals0(); @@ -317,12 +343,16 @@ class VanillaContext, KI keyInterop; void initKeyModel() { - if (keyInterop != null) + if (keyModelInit()) return; initKeyModelDependencies(); initKeyModel0(); } + boolean keyModelInit() { + return keyInterop != null; + } + void initKeyModelDependencies() { checkMapInit(); } @@ -332,7 +362,7 @@ class VanillaContext, } void closeKeyModel() { - if (keyInterop == null) + if (!keyModelInit()) return; closeKeyModelDependants(); closeKeyModel0(); @@ -352,12 +382,16 @@ class VanillaContext, BytesReader keyReader; void initKeyReader() { - if (keyReader != null) + if (keyReaderInit()) return; initKeyReaderDependencies(); initKeyReader0(); } + boolean keyReaderInit() { + return keyReader != null; + } + void initKeyReaderDependencies() { checkMapInit(); } @@ -367,7 +401,7 @@ class VanillaContext, } void closeKeyReader() { - if (keyReader == null) + if (!keyReaderInit()) return; closeKeyReaderDependants(); closeKeyReader0(); @@ -387,12 +421,15 @@ class VanillaContext, MKI metaKeyInterop; long keySize; - void initKey(K key) { initKeyDependencies(); initKey0(key); } + boolean keyInit() { + return key != null; + } + void initKeyDependencies() { initKeyModel(); } @@ -406,12 +443,12 @@ class VanillaContext, } void checkKeyInit() { - if (key == null) + if (!keyInit()) throw new IllegalStateException("Key should be init"); } void closeKey() { - if (key == null) + if (!keyInit()) return; closeKeyDependants(); closeKey0(); @@ -436,7 +473,7 @@ class VanillaContext, @NotNull @Override public K key() { - if (key != null) + if (keyInit()) return key; initKeySearch(); initMapAndContextLocals(); @@ -451,19 +488,23 @@ class VanillaContext, long hash; void initKeyHash() { - if (hash != 0) + if (keyHashInit()) return; initKeyHashDependencies(); initKeyHash0(); } + boolean keyHashInit() { + return hash != 0; + } + void initKeyHashDependencies() { checkKeyInit(); initKeyModel(); } void initKeyHash0() { - hash = metaKeyInterop.hash(keyInterop, key); + hash = metaKeyInterop.hash(keyInterop, LongHashFunction.city_1_1(), key); } void closeKeyHash() { @@ -486,12 +527,16 @@ class VanillaContext, int segmentIndex = -1; void initSegmentIndex() { - if (segmentIndex >= 0) + if (segmentIndexInit()) return; initSegmentIndexDependencies(); initSegmentIndex0(); } + boolean segmentIndexInit() { + return segmentIndex >= 0; + } + void initSegmentIndexDependencies() { initKeyHash(); } @@ -501,7 +546,7 @@ class VanillaContext, } void closeSegmentIndex() { - if (segmentIndex < 0) + if (!segmentIndexInit()) return; closeSegmentIndexDependants(); closeSegmentIndex0(); @@ -522,12 +567,16 @@ class VanillaContext, SegmentHeader segmentHeader; void initSegmentHeader() { - if (segmentHeader != null) + if (segmentHeaderInit()) return; initSegmentHeaderDependencies(); initSegmentHeader0(); } + boolean segmentHeaderInit() { + return segmentHeader != null; + } + void initSegmentHeaderDependencies() { initSegmentIndex(); } @@ -538,7 +587,7 @@ class VanillaContext, } void closeSegmentHeader() { - if (segmentHeader == null) + if (!segmentHeaderInit()) return; closeSegmentHeaderDependants(); closeSegmentHeader0(); @@ -586,12 +635,16 @@ class VanillaContext, final InterProcessLock writeLock = new WriteLock(); void initLocks() { - if (rootContextOnThisSegment != null) + if (locksInit()) return; initLocksDependencies(); initLocks0(); } + boolean locksInit() { + return rootContextOnThisSegment != null; + } + void initLocksDependencies() { initSegmentHeader(); } @@ -615,7 +668,7 @@ class VanillaContext, } void closeLocks() { - if (rootContextOnThisSegment == null) + if (!locksInit()) return; closeLocksDependants(); closeLocks0(); @@ -627,6 +680,7 @@ class VanillaContext, } void closeLocks0() { + // TODO should throw ISE on attempt to close root context when children not closed if (rootContextOnThisSegment == this) { if (totalWriteLockCount > 0) { segmentHeader.writeUnlock(segmentHeaderAddress); @@ -1034,12 +1088,16 @@ class VanillaContext, long entrySpaceOffset; void initSegment() { - if (entrySpaceOffset != 0L) + if (segmentInit()) return; initSegmentDependencies(); initSegment0(); } + boolean segmentInit() { + return entrySpaceOffset != 0; + } + void initSegmentDependencies() { initSegmentHeader(); } @@ -1057,7 +1115,7 @@ class VanillaContext, } void closeSegment() { - if (entrySpaceOffset == 0L) + if (!segmentInit()) return; closeSegmentDependants(); closeSegment0(); @@ -1074,18 +1132,22 @@ class VanillaContext, ///////////////////////////////////////////////// // Hash lookup public void initHashLookup() { - if (hashLookup.isInit()) + if (hashLookupInit()) return; initHashLookupDependencies(); hashLookup.init0(m.hashSplitting.segmentHash(hash)); } + boolean hashLookupInit() { + return hashLookup.isInit(); + } + void initHashLookupDependencies() { initSegment(); } public void closeHashLookup() { - if (!hashLookup.isInit()) + if (!hashLookupInit()) return; closeHashLookupDependants(); hashLookup.close0(); @@ -1109,12 +1171,16 @@ class VanillaContext, MultiStoreBytes entry; void initKeySearch() { - if (state != null) + if (keySearchInit()) return; initKeySearchDependencies(); initKeySearch0(); } + boolean keySearchInit() { + return state != null; + } + void initKeySearchDependencies() { initHashLookup(); initLocks(); @@ -1145,7 +1211,7 @@ class VanillaContext, } void closeKeySearch() { - if (state == null) + if (!keySearchInit()) return; closeKeySearchDependants(); closeKeySearch0(); @@ -1194,12 +1260,16 @@ class VanillaContext, long valueSize; void initValueBytes() { - if (valueSizeOffset != 0L) + if (valueBytesInit()) return; initValueBytesDependencies(); initValueBytes0(); } + boolean valueBytesInit() { + return valueSizeOffset != 0; + } + void initValueBytesDependencies() { initKeySearch(); } @@ -1219,7 +1289,7 @@ class VanillaContext, } void closeValueBytes() { - if (valueSizeOffset == 0L) + if (!valueBytesInit()) return; closeValueBytesDependants(); closeValueBytes0(); @@ -1253,12 +1323,16 @@ class VanillaContext, BytesReader valueReader; void initValueReader() { - if (valueReader != null) + if (valueReaderInit()) return; initValueReaderDependencies(); initValueReader0(); } + boolean valueReaderInit() { + return valueReader != null; + } + void initValueReaderDependencies() { checkMapInit(); } @@ -1268,7 +1342,7 @@ class VanillaContext, } void closeValueReader() { - if (valueReader == null) + if (!valueReaderInit()) return; closeValueReaderDependants(); closeValueReader0(); @@ -1290,7 +1364,7 @@ class VanillaContext, @Override public V get() { checkOnEachPublicOperation(); - if (value != null) + if (valueInit()) return value; initKeySearch(); if (state != PRESENT) @@ -1302,6 +1376,10 @@ class VanillaContext, return value; } + boolean valueInit() { + return value != null; + } + void initValueDependencies() { initValueBytes(); initValueReader(); @@ -1327,7 +1405,7 @@ class VanillaContext, } void closeValue() { - if (value == null) + if (!valueInit()) return; closeValueDependants(); closeValue0(); @@ -1351,12 +1429,16 @@ class VanillaContext, VI valueInterop; void initValueModel() { - if (valueInterop != null) + if (valueModelInit()) return; initValueModelDependencies(); initValueModel0(); } + boolean valueModelInit() { + return valueInterop != null; + } + void initValueModelDependencies() { checkMapInit(); } @@ -1370,7 +1452,7 @@ class VanillaContext, } void closeValueModel() { - if (valueInterop == null) + if (!valueModelInit()) return; closeValueModelDependants(); closeValueModel0(); @@ -1391,12 +1473,14 @@ class VanillaContext, long newValueSize; void initNewValue(V newValue) { - if (this.newValue != null) - return; initNewValueDependencies(); initNewValue0(newValue); } + boolean newValueInit() { + return newValue != null; + } + void initNewValueDependencies() { initValueModel(); } @@ -1414,7 +1498,7 @@ class VanillaContext, } void closeNewValue() { - if (newValue == null) + if (!newValueInit()) return; closeNewValueDependants(); closeNewValue0(); @@ -1434,12 +1518,14 @@ class VanillaContext, checkOnEachPublicOperation(); initValueBytes(); initNewValue(value); - if (newValueSize != valueSize) - return false; - entry.position(valueOffset); - boolean result = metaValueInterop.startsWith(valueInterop, entry, newValue); - closeNewValue(); - return result; + try { + if (newValueSize != valueSize) + return false; + entry.position(valueOffset); + return metaValueInterop.startsWith(valueInterop, entry, newValue); + } finally { + closeNewValue(); + } } @@ -1448,12 +1534,16 @@ class VanillaContext, int entrySizeInChunks; void initEntrySizeInChunks() { - if (entrySizeInChunks != 0) + if (entrySizeInChunksInit()) return; initEntrySizeInChunksDependencies(); initEntrySizeInChunks0(); } + boolean entrySizeInChunksInit() { + return entrySizeInChunks != 0; + } + void initEntrySizeInChunksDependencies() { initValueBytes(); } @@ -1463,7 +1553,7 @@ class VanillaContext, } void closeEntrySizeInChunks() { - if (entrySizeInChunks == 0) + if (!entrySizeInChunksInit()) return; closeEntrySizeInChunksDependants(); closeEntrySizeInChunks0(); @@ -1713,8 +1803,8 @@ class VanillaContext, final MultiStoreBytes reuse(MultiStoreBytes entry, long pos) { long offsetWithinEntrySpace = pos * m.chunkSize; - entry.storePositionAndSize(m.ms, entrySpaceOffset + offsetWithinEntrySpace, - m.segmentEntrySpaceInnerSize - offsetWithinEntrySpace); + entry.setBytesOffset(m.bytes, entrySpaceOffset + offsetWithinEntrySpace); + entry.limit(m.segmentEntrySpaceInnerSize - offsetWithinEntrySpace); entry.position(m.metaDataBytes); return entry; } @@ -1745,8 +1835,7 @@ class VanillaContext, } final int inChunks(long sizeInBytes) { - // TODO optimize for the case when chunkSize is power of 2, that is default (and often) - // now + // TODO optimize for the case when chunkSize is power of 2, that is default (and often) now if (sizeInBytes <= m.chunkSize) return 1; // int division is MUCH faster than long on Intel CPUs diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java index 381e58484..3e2befac3 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.replication.TimeProvider; 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 org.jetbrains.annotations.NotNull; 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 9a8a029e5..350ce7ef7 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 80317c016..7550d21a7 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 @@ package net.openhft.chronicle.set; 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.Collection; 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 61ea12ef1..1822cfd7c 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.ChronicleHashInstanceBuilder; 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/main/java/net/openhft/xstream/converters/StatelessChronicleMapConverter.java b/src/main/java/net/openhft/xstream/converters/StatelessChronicleMapConverter.java index e7752d253..adc1616b2 100644 --- a/src/main/java/net/openhft/xstream/converters/StatelessChronicleMapConverter.java +++ b/src/main/java/net/openhft/xstream/converters/StatelessChronicleMapConverter.java @@ -45,7 +45,8 @@ public class StatelessChronicleMapConverter extends AbstractChronicleMapCo try { - final Method entrySize = o.getClass().getDeclaredMethod("entrySet", MapEntryCallback.class); + final Method entrySize = o.getClass() + .getDeclaredMethod("entrySet", MapEntryCallback.class); entrySize.setAccessible(true); entrySize.invoke(o, diff --git a/src/test/java/net/openhft/chronicle/map/Builder.java b/src/test/java/net/openhft/chronicle/map/Builder.java index 68ac0c64a..aee3866a3 100644 --- a/src/test/java/net/openhft/chronicle/map/Builder.java +++ b/src/test/java/net/openhft/chronicle/map/Builder.java @@ -19,6 +19,8 @@ package net.openhft.chronicle.map; import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig; +import net.openhft.chronicle.hash.serialization.internal.DummyValue; +import net.openhft.chronicle.hash.serialization.internal.DummyValueMarshaller; import java.io.File; import java.io.IOException; @@ -58,12 +60,14 @@ public class Builder { 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, - identifier, serverPort, endpoints).create(); + return (T) newTcpSocketShmBuilder(Integer.class, DummyValue.class, + identifier, serverPort, endpoints) + .valueMarshallers(DummyValueMarshaller.INSTANCE, DummyValueMarshaller.INSTANCE) + .valueSizeMarshaller(DummyValueMarshaller.INSTANCE).create(); } public static ChronicleMapBuilder newTcpSocketShmBuilder( diff --git a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java index b137cd639..dd98340af 100644 --- a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java @@ -416,7 +416,8 @@ public class CHMUseCasesTest { Arrays.fill(expected, 'X'); ChronicleMapBuilder builder = ChronicleMapBuilder - .of(CharSequence.class, char[].class).entries(1).constantValueSizeBySample(expected); + .of(CharSequence.class, char[].class).entries(1) + .constantValueSizeBySample(expected); try (ChronicleMap map = newInstance(builder)) { map.put("Key", expected); @@ -658,28 +659,6 @@ public class CHMUseCasesTest { } } - @Test - public void testGetUsingWithCharSequenceNoValue() throws IOException { - ChronicleMapBuilder builder = ChronicleMapBuilder - .of(CharSequence.class, CharSequence.class); - - if (typeOfMap == TypeOfMap.STATELESS) - return; // context supported by the STATELESS client - - try (ChronicleMap map = newInstance(builder)) { - - try (MapKeyContext c = map.context("1")) { - CharSequence value = c.get(); - assertTrue(value instanceof StringBuilder); - ((StringBuilder) value).append("Hello World"); - } - - assertEquals(null, map.get("1")); - mapChecks(); - } - } - - @Test public void testGetUsingWithIntValueNoValue() throws IOException { @@ -701,32 +680,6 @@ public class CHMUseCasesTest { } } - - @Test(expected = IllegalStateException.class) - public void testAcquireUsingWithIntValueNoValue() throws IOException { - - if (typeOfMap == TypeOfMap.STATELESS) - throw new IllegalStateException(); // acquireContext supported by the STATELESS - - - ChronicleMapBuilder builder = ChronicleMapBuilder - .of(CharSequence.class, IntValue.class); - - try (ChronicleMap map = newInstance(builder)) { - - try (MapKeyContext c = map.context("1")) { - c.updateLock().lock(); - IntValue value = c.get(); - assertTrue(value instanceof IntValue); - value.setValue(1); - } - - assertEquals(null, map.get("1")); - mapChecks(); - } - } - - @Test(expected = IllegalArgumentException.class) public void testAcquireUsingImmutableUsing() throws IOException { diff --git a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java index 67c0be5bc..774774f00 100755 --- a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java @@ -592,9 +592,12 @@ public class ChronicleMapTest { CharSequence userCS = getUserCharSequence(i); if (j > 1) { - assertNotNull(userCS.toString(), map.getUsing(userCS, value)); + LongValue v = map.getUsing(userCS, value); + assertNotNull(userCS.toString(), v); + assertTrue(userCS.toString(), v == value); } else { - map.acquireUsing(userCS, value); + LongValue v = map.acquireUsing(userCS, value); + assertTrue(userCS.toString(), v == value); } if (i >= 1) assertTrue(userCS.toString(), map.containsKey(getUserCharSequence(1))); diff --git a/src/test/java/net/openhft/chronicle/map/MarkTest.java b/src/test/java/net/openhft/chronicle/map/MarkTest.java index 825f5025c..6d2ca96f9 100644 --- a/src/test/java/net/openhft/chronicle/map/MarkTest.java +++ b/src/test/java/net/openhft/chronicle/map/MarkTest.java @@ -45,7 +45,10 @@ public class MarkTest { @Test(timeout = 25000) public void persistedTest() { - final File db = Paths.get(System.getProperty("java.io.tmpdir"), "mark").toFile(); + int rnd = new Random().nextInt(); + final File db = Paths.get(System.getProperty("java.io.tmpdir"), "mark" + rnd).toFile(); + if (db.exists()) + db.delete(); try { test(new Function, ChronicleMap>() { diff --git a/src/test/java/net/openhft/chronicle/map/ReplicatedChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/ReplicatedChronicleMapTest.java index eaee51f2c..14f5d7517 100644 --- a/src/test/java/net/openhft/chronicle/map/ReplicatedChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/ReplicatedChronicleMapTest.java @@ -37,11 +37,15 @@ import static org.junit.Assert.*; public class ReplicatedChronicleMapTest extends JSR166TestCase { ChronicleMap newShmIntString() throws IOException { return ChronicleMapBuilder.of(Integer.class, CharSequence.class) + .replication((byte) 1) + .entries(1000) .create(); } ChronicleMap newShmStringString() throws IOException { return ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) + .replication((byte) 1) + .entries(1000) .create(); } diff --git a/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java b/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java index 9613ba244..96404d9b6 100644 --- a/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java +++ b/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java @@ -300,7 +300,7 @@ public class StatelessClientTest { serverMap.put("hello", "world"); - statelessMap.replace("hello", "world", "hello"); + assertTrue(statelessMap.replace("hello", "world", "hello")); assertEquals("hello", statelessMap.get("hello")); assertEquals(1, statelessMap.size()); diff --git a/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java b/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3VoidValueTest.java index 781bdccbd..91edcc2d3 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.io.IOException; 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 @@ import static org.junit.Assert.assertTrue; 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 class TCPSocketReplication3VoidValueTest { @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 class TCPSocketReplication3VoidValueTest { @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 class TCPSocketReplication3VoidValueTest { @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/TCPSocketReplication3wayPutReturnsNullTest.java b/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3wayPutReturnsNullTest.java index d00671ba4..07ef6b673 100755 --- a/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3wayPutReturnsNullTest.java +++ b/src/test/java/net/openhft/chronicle/map/TCPSocketReplication3wayPutReturnsNullTest.java @@ -107,7 +107,7 @@ public class TCPSocketReplication3wayPutReturnsNullTest { assertEquals(null, map1.put(1, "EXAMPLE-1")); assertEquals(null, map1.put(2, "EXAMPLE-2")); - assertEquals("EXAMPLE-2", map1.put(2, "EXAMPLE-1")); + assertEquals(null, map1.put(2, "EXAMPLE-1")); assertEquals(null, map2.put(5, "EXAMPLE-2")); assertEquals(null, map2.put(6, "EXAMPLE-2")); diff --git a/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTest.java b/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTest.java index 338c4fb54..7aba61fa5 100644 --- a/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTest.java +++ b/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTest.java @@ -114,7 +114,7 @@ public class TimeBasedReplicationTest extends JSR166TestCase { // should NOT throw LateUpdateException because the value is present and it is just // returned. - assertEquals(map.putIfAbsent("key-1", "value-2"), null); + assertEquals("value-1", map.putIfAbsent("key-1", "value-2")); // we'll now flip the time back to the current in order to do the read the result current(timeProvider); 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 25173b946..6e9444db0 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 @@ package net.openhft.chronicle.map.fromdocs; 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 32268bae4..321a99b6a 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 @@ package net.openhft.chronicle.map.fromdocs; 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 88928ee77..1122d4c14 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 class StatelessChronicleMapTest extends JSR166TestCase { c.get(null); shouldThrow(); } catch (NullPointerException success) { + } catch (IllegalArgumentException success) { } } @@ -716,6 +717,7 @@ public class StatelessChronicleMapTest extends JSR166TestCase { c.containsKey(null); shouldThrow(); } catch (NullPointerException success) { + } catch (IllegalArgumentException success) { } }