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 477f09f1d..8fe37b984 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 @@ -34,32 +34,33 @@ public final class BytesReaders { * from {@link net.openhft.chronicle.hash.serialization} package. * * @param marshaller the actual reading implementation - * @param type of the objects marshalled + * @param type of the objects marshalled * @return a {@code BytesReader} wrapping the given {@code BytesMarshaller} */ - public static BytesReader fromBytesMarshaller(BytesMarshaller marshaller) { + public static BytesReader fromBytesMarshaller(BytesMarshaller marshaller) { return new SimpleBytesReader(marshaller); } private static class SimpleBytesReader implements BytesReader { private static final long serialVersionUID = 0L; - private final BytesMarshaller marshaller; + private final BytesMarshaller marshaller; - public SimpleBytesReader(BytesMarshaller marshaller) { + public SimpleBytesReader(BytesMarshaller marshaller) { this.marshaller = marshaller; } @Override public E read(Bytes bytes, long size) { - return marshaller.read(bytes); + return (E) marshaller.read(bytes); } @Override public E read(Bytes bytes, long size, E toReuse) { - return marshaller.read(bytes, toReuse); + return (E) marshaller.read(bytes, toReuse); } } - private BytesReaders() {} + private BytesReaders() { + } } diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java index a9eb45115..915337413 100755 --- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java @@ -39,7 +39,6 @@ import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; import net.openhft.lang.model.Byteable; import net.openhft.lang.threadlocal.Provider; import net.openhft.lang.threadlocal.ThreadLocalCopies; -import net.openhft.lang.values.LongValue; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -985,7 +984,7 @@ abstract class AbstractChronicleMapBuilder> 10), + return (int) Math.min(Math.max(4L, entries() >> 10), VanillaChronicleMap.MAX_ENTRY_OVERSIZE_FACTOR); } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java index c7e3764fd..115c598dc 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java @@ -30,7 +30,6 @@ import java.io.File; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Future; /** * Extension of {@link ConcurrentMap} interface, stores the data off-heap. @@ -202,14 +201,13 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { /** * Apply a mutator to the value for a key and return a result. A write lock is assumed.

If - * there is no entry for this key it will be created and the value empty. + * there is no entry for this key null will be returned * * @param key to apply the mapping to * @param mutator to alter the value and calculate a result - * @param return type. * @return the result of the function. */ - R updateForKey(K key, @NotNull Mutator mutator); + V putWith(K key, @NotNull Mutator mutator); /** * exports all the entries to a {@link java.io.File} storing them in JSON format, an attempt is diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index f19bb87e6..8283f5037 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -37,14 +37,16 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI delegate; /** - * Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create() create} maps - * with the specified key and value classes. + * Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create() + * create} maps with the specified key and value classes. * - *

{@code ChronicleMapBuilder} analyzes provided key and value classes and automatically chooses the - * most specific internal builder. + *

{@code ChronicleMapBuilder} analyzes provided key and value classes and automatically + * chooses the most specific internal builder. * - * @param keyClass class object used to infer key type and discover it's properties via reflection - * @param valueClass class object used to infer value type and discover it's properties via reflection + * @param keyClass class object used to infer key type and discover it's properties via + * reflection + * @param valueClass class object used to infer value type and discover it's properties via + * reflection * @param key type of the maps, created by the returned builder * @param value type of the maps, created by the returned builder * @return a new builder for the given key and value classes @@ -67,6 +69,26 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI(builder); } + // todo - work in progress this will be changed me not today, so don't refactor this ! + public static T newInstance(Class interfaceClass, boolean isKey) { + + + if (interfaceClass.isEnum()) + return isKey ? DataValueClasses.newDirectReference(interfaceClass) : DataValueClasses + .newDirectInstance(interfaceClass); + + else if (!offHeapReference(interfaceClass) && !interfaceClass.isInterface() && + !builtInType(interfaceClass)) { + + // if (()) + // interfaceClass = DataValueClasses.directClassFor(interfaceClass); + return DataValueClasses.newInstance(interfaceClass); + + } + return DataValueClasses.newDirectInstance(interfaceClass); + + } + static boolean builtInType(Class clazz) { return clazz.getClassLoader() == Class.class.getClassLoader(); } @@ -83,9 +105,9 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderINote that the actual entrySize will be aligned to 4 (default {@linkplain - * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and entry - * alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30 aligned to 4 - * bytes). + * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and + * entry alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30 + * aligned to 4 bytes). * * @see #entryAndValueAlignment(Alignment) // * @see #entries(long) */ @@ -96,16 +118,16 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIUseful when values of the map are updated intensively, particularly fields with volatile access, - * because it doesn't work well if the value crosses cache lines. Also, on some (nowadays rare) - * architectures any misaligned memory access is more expensive than aligned. + *

Useful when values of the map are updated intensively, particularly fields with volatile + * access, because it doesn't work well if the value crosses cache lines. Also, on some + * (nowadays rare) architectures any misaligned memory access is more expensive than aligned. * - *

Note that {@linkplain #entrySize(int) entry size} will be aligned according to this alignment. I. e. - * if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual entry size will be 24 (20 - * aligned to 8 bytes). + *

Note that {@linkplain #entrySize(int) entry size} will be aligned according to this + * alignment. I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual + * entry size will be 24 (20 aligned to 8 bytes). * *

Default is {@link Alignment#OF_4_BYTES} for Byteable values. * @@ -119,11 +141,11 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIBy default, the default value is not specified, default {@linkplain #prepareValueBytesOnAcquire - * prepare value bytes routine} is specified instead. + *

By default, the default value is not specified, default {@linkplain + * #prepareValueBytesOnAcquire prepare value bytes routine} is specified instead. * * @see #defaultValueProvider(DefaultValueProvider) * @see #prepareValueBytesOnAcquire(PrepareValueBytes) @@ -135,15 +157,16 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIThe default preparation callback zeroes out the value bytes. * - * @param prepareValueBytes what to do with the value bytes before assigning them into the {@link - * net.openhft.lang.model.Byteable} value to return from {@code acquireUsing()} - * call + * @param prepareValueBytes what to do with the value bytes before assigning them into the + * {@link net.openhft.lang.model.Byteable} value to return from {@code + * acquireUsing()} call * @return this builder back * @see PrepareValueBytes * @see #defaultValue(Object) @@ -168,16 +191,17 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIIf value is a boxed primitive type, i. e. if value size is known statically, it is automatically - * accounted and shouldn't be specified by user. + *

If value is a boxed primitive type, i. e. if value size is known statically, it is + * automatically accounted and shouldn't be specified by user. * - *

If value size varies moderately, specify the size higher than average, but lower than the maximum - * possible, to minimize average memory overuse. If value size varies in a wide range, it's better to use - * {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it directly. + *

If value size varies moderately, specify the size higher than average, but lower than the + * maximum possible, to minimize average memory overuse. If value size varies in a wide range, + * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it + * directly. * * @param valueSize number of bytes, taken by serialized form of values * @return this {@code ChronicleMapOnHeapUpdatableBuilder} back @@ -192,15 +216,16 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIIf values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass, i. e. if - * value size is known statically, it is automatically accounted and this method shouldn't be called. + *

If values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass, + * i. e. if value size is known statically, it is automatically accounted and this method + * shouldn't be called. * - *

If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be called - * instead of this one. + *

If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be + * called instead of this one. * * @param sampleValue the sample value * @return this builder back @@ -233,8 +258,8 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI implements ChronicleMapBuilderIBy default, default value provider is not specified, {@link #defaultValue(Object) default value} is - * specified instead. + *

By default, default value provider is not specified, {@link #defaultValue(Object) default + * value} is specified instead. * * @see #defaultValue(Object) */ @@ -258,8 +283,8 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIthe + * Configures the {@code BytesMarshaller} used to serialize/deserialize values to/from off-heap + * memory in maps, created by this builder. See the * section about serialization in ChronicleMap manual for more information. * * @param valueMarshaller the marshaller used to serialize values @@ -271,26 +296,27 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI valueMarshaller( - @NotNull BytesMarshaller valueMarshaller) { + @NotNull BytesMarshaller valueMarshaller) { delegate.valueMarshaller(valueMarshaller); return this; } /** - * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in maps, - * created by this builder. See the + * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in + * maps, created by this builder. See the * section about serialization in ChronicleMap manual for more information. * - *

Configuring marshalling this way results to a little bit more compact in-memory layout of the map, - * comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}. + *

Configuring marshalling this way results to a little bit more compact in-memory layout of + * the map, comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}. * - *

Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain {@link - * net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but currently pointless - * for values. + *

Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain + * {@link net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but + * currently pointless for values. * - * @param valueWriter the new value object → {@link net.openhft.lang.io.Bytes} writer (interop) + * @param valueWriter the new value object → {@link net.openhft.lang.io.Bytes} writer + * (interop) strategy + * @param valueReader the new {@link net.openhft.lang.io.Bytes} → value object reader * strategy - * @param valueReader the new {@link net.openhft.lang.io.Bytes} → value object reader strategy * @return this builder back * @see #valueMarshaller(BytesMarshaller) */ @@ -301,19 +327,21 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIDefault value size marshaller is so-called {@linkplain net.openhft.chronicle.hash.serialization.SizeMarshallers#stopBit() - * stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant value size} - * is configured, or defaulted if the value type is always constant and {@code ChronicleHashBuilder} - * implementation knows about it, this configuration takes no effect, because a special {@link - * net.openhft.chronicle.hash.serialization.SizeMarshaller} implementation, which doesn't actually do any - * marshalling, and just returns the known constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize( - *net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured using this - * method. + * stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant + * value size} is configured, or defaulted if the value type is always constant and {@code + * ChronicleHashBuilder} implementation knows about it, this configuration takes no effect, + * because a special {@link net.openhft.chronicle.hash.serialization.SizeMarshaller} + * implementation, which doesn't actually do any marshalling, and just returns the known + * constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize( + *net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured + * using this method. * - * @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to off-heap memory + * @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to + * off-heap memory * @return this builder back */ @Override @@ -498,13 +526,14 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI bootstrapOnlyLocalEntries(boolean bootstrapOnlyLocalEntries) { delegate.bootstrapOnlyLocalEntries(bootstrapOnlyLocalEntries); @@ -512,11 +541,13 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI checkSerializedValues() { - throw new UnsupportedOperationException("Not implemented"); + // throw new UnsupportedOperationException("Not implemented"); + return this; } public ChronicleMapBuilder disableOversizedEntries(boolean disableOversizedEntries) { // throw an exception rather than use oversized entries to test the size is as expected. - throw new UnsupportedOperationException("Not implemented"); + // throw new UnsupportedOperationException("Not implemented"); + return this; } } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java index ed4dd05d5..8268a2008 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java @@ -199,7 +199,7 @@ public interface ChronicleMapBuilderI extends Serializable { * @see #objectSerializer(ObjectSerializer) */ ChronicleMapBuilderI valueMarshaller( - @NotNull BytesMarshaller valueMarshaller); + @NotNull BytesMarshaller valueMarshaller); /** * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in diff --git a/src/main/java/net/openhft/chronicle/map/Mutator.java b/src/main/java/net/openhft/chronicle/map/Mutator.java index 412d66178..5d527604b 100755 --- a/src/main/java/net/openhft/chronicle/map/Mutator.java +++ b/src/main/java/net/openhft/chronicle/map/Mutator.java @@ -8,15 +8,15 @@ import java.io.Serializable; *

This is not functional as it can alter an argument. * * @param the type of the mutable input - * @param the type of the result of the mutator + */ -public interface Mutator extends Serializable { +public interface Mutator extends Serializable { /** * Applies this mutator to the given mutable argument. * * @param t the mutator argument * @return the result */ - R update(T t); + T update(T t); } diff --git a/src/main/java/net/openhft/chronicle/map/OffHeapUpdatableChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/OffHeapUpdatableChronicleMapBuilder.java index 8dc117593..e94a9383a 100644 --- a/src/main/java/net/openhft/chronicle/map/OffHeapUpdatableChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/OffHeapUpdatableChronicleMapBuilder.java @@ -148,7 +148,7 @@ final class OffHeapUpdatableChronicleMapBuilder return super.prepareValueBytesOnAcquire(prepareValueBytes); } - public ChronicleMapBuilderI valueMarshaller(@NotNull BytesMarshaller valueMarshaller) { + public ChronicleMapBuilderI valueMarshaller(@NotNull BytesMarshaller valueMarshaller) { throw new UnsupportedOperationException("not supported for this combination of key/value type"); } diff --git a/src/main/java/net/openhft/chronicle/map/OnHeapUpdatableChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/OnHeapUpdatableChronicleMapBuilder.java index eaed43fdf..058dc94cb 100644 --- a/src/main/java/net/openhft/chronicle/map/OnHeapUpdatableChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/OnHeapUpdatableChronicleMapBuilder.java @@ -210,7 +210,7 @@ final class OnHeapUpdatableChronicleMapBuilder * @see #objectSerializer(ObjectSerializer) */ public OnHeapUpdatableChronicleMapBuilder valueMarshaller( - @NotNull BytesMarshaller valueMarshaller) { + @NotNull BytesMarshaller valueMarshaller) { valueBuilder.marshaller(valueMarshaller); return this; } diff --git a/src/main/java/net/openhft/chronicle/map/ReadContext.java b/src/main/java/net/openhft/chronicle/map/ReadContext.java index 3a7c7795a..3792464c6 100644 --- a/src/main/java/net/openhft/chronicle/map/ReadContext.java +++ b/src/main/java/net/openhft/chronicle/map/ReadContext.java @@ -21,8 +21,11 @@ package net.openhft.chronicle.map; /** * @author Rob Austin. */ -public interface ReadContext extends Context { +public interface ReadContext extends Context { + /** + * @return true if the entry was present + */ boolean present(); } diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index eebe5cc7a..4aa7a37d6 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -231,7 +231,7 @@ final class ReplicatedChronicleMap, RV, VB extends RV, VBI, MVBI extends MetaBytesInterop> - RV put(ThreadLocalCopies copies, SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - GetValueInterops getValueInterops, VB value, - InstanceOrBytesToInstance toValue, - boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { + RV put2(ThreadLocalCopies copies, SegmentState segmentState, + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + GetValueInterops getValueInterops, VB value, + InstanceOrBytesToInstance toValue, + boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { return put(copies, segmentState, metaKeyInterop, keyInterop, key, keySize, toKey, getValueInterops, value, toValue, replaceIfPresent, readValue, resultUnused, localIdentifier, currentTime()); @@ -654,7 +654,7 @@ final class ReplicatedChronicleMap toKey, ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, - long hash2, boolean create) { + long hash2, boolean create, MutableLockedEntry lock) { segmentStateNotNullImpliesCopiesNotNull(copies, segmentState); SegmentState localSegmentState = segmentState; try { @@ -755,9 +755,18 @@ final class ReplicatedChronicleMap implements Cloneable, Serializable{ +final class SerializationBuilder implements Cloneable, Serializable { private static boolean concreteClass(Class c) { return !c.isInterface() && !Modifier.isAbstract(c.getModifiers()); @@ -194,7 +197,7 @@ final class SerializationBuilder implements Cloneable, Serializable{ .>forBytesWriter(role)); } - public SerializationBuilder marshaller(BytesMarshaller marshaller) { + public SerializationBuilder marshaller(BytesMarshaller marshaller) { clearDefaults(); return copyingInterop(CopyingInterop.FROM_MARSHALLER) .reader(BytesReaders.fromBytesMarshaller(marshaller)) diff --git a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java index 5a7a82458..bf5cf7f3f 100644 --- a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java @@ -68,8 +68,8 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon private volatile ByteBuffer outBuffer; private volatile ByteBufferBytes outBytes; - private volatile ByteBuffer inBuffer; - private volatile ByteBufferBytes inBytes; + private ByteBuffer inBuffer; + private ByteBufferBytes inBytes; @NotNull private final ReaderWithSize keyReaderWithSize; @@ -490,7 +490,7 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon @Nullable @Override - public R updateForKey(@Nullable K key, @NotNull Mutator mutator) { + public V putWith(@Nullable K key, @NotNull Mutator mutator) { if (key == null) throw keyNotNullNPE(); return fetchObject(UPDATE_FOR_KEY, key, mutator); diff --git a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java index 958d42ee1..efec51608 100644 --- a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java +++ b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java @@ -1440,9 +1440,9 @@ class StatelessServerConnector { @Nullable public Work updateForKey(@NotNull ByteBufferBytes reader, @NotNull Bytes writer, long sizeLocation) { final K key = keyReaderWithSize.read(reader, null); - final Mutator mutator = (Mutator) reader.readObject(); + final Mutator mutator = (Mutator) reader.readObject(); try { - Object result = map.updateForKey(key, mutator); + Object result = map.putWith(key, mutator); writer.writeObject(result); } catch (Throwable e) { LOG.info("", e); diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index d83b83753..c55448db0 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -35,7 +35,7 @@ import net.openhft.chronicle.hash.serialization.internal.*; import net.openhft.lang.collection.DirectBitSet; import net.openhft.lang.collection.SingleThreadedDirectBitSet; import net.openhft.lang.io.*; -import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.JDKObjectSerializer; import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; import net.openhft.lang.threadlocal.Provider; import net.openhft.lang.threadlocal.StatefulCopyable; @@ -220,7 +220,7 @@ class VanillaChronicleMap, final long createMappedStoreAndSegments(File file) throws IOException { return createMappedStoreAndSegments(new MappedStore(file, FileChannel.MapMode.READ_WRITE, - sizeInBytes(), JDKZObjectSerializer.INSTANCE)); + sizeInBytes(), JDKObjectSerializer.INSTANCE)); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -354,7 +354,7 @@ class VanillaChronicleMap, void putBytes(ThreadLocalCopies copies, SegmentState segmentState, Bytes key, long keySize, GetRemoteBytesValueInterops getRemoteBytesValueInterops, MultiStoreBytes value, boolean replaceIfPresent, ReadValue readValue) { - put(copies, segmentState, + put2(copies, segmentState, DelegatingMetaBytesInterop.>instance(), BytesBytesInterop.INSTANCE, key, keySize, keyBytesToInstance, getRemoteBytesValueInterops, value, valueBytesToInstance, @@ -373,7 +373,7 @@ class VanillaChronicleMap, getRemoteBytesValueInterops.valueSizeMarshaller(valueSizeMarshaller); ReadValueToBytes readValueToLazyBytes = segmentState.readValueToLazyBytes; readValueToLazyBytes.valueSizeMarshaller(valueSizeMarshaller); - put(copies, segmentState, + put2(copies, segmentState, DelegatingMetaBytesInterop.>instance(), BytesBytesInterop.INSTANCE, entry, keySize, keyBytesToInstance, getRemoteBytesValueInterops, value, valueBytesToInstance, @@ -453,15 +453,15 @@ class VanillaChronicleMap, @Override public final V put(K key, V value) { - return put0(key, value, true); + return put1(key, value, true); } @Override public final V putIfAbsent(@net.openhft.lang.model.constraints.NotNull K key, V value) { - return put0(key, value, false); + return put1(key, value, false); } - V put0(@net.openhft.lang.model.constraints.NotNull K key, V value, boolean replaceIfPresent) { + V put1(@net.openhft.lang.model.constraints.NotNull K key, V value, boolean replaceIfPresent) { checkKey(key); checkValue(value); ThreadLocalCopies copies = keyInteropProvider.getCopies(null); @@ -470,22 +470,22 @@ class VanillaChronicleMap, MKI metaKeyInterop = metaKeyInteropProvider.get(copies, originalMetaKeyInterop, keyInterop, key); long keySize = metaKeyInterop.size(keyInterop, key); - return put(copies, null, metaKeyInterop, keyInterop, key, keySize, keyIdentity(), + return put2(copies, null, metaKeyInterop, keyInterop, key, keySize, keyIdentity(), this, value, valueIdentity(), replaceIfPresent, this, putReturnsNull); } , RV, VB extends RV, VBI, MVBI extends MetaBytesInterop> - RV put(ThreadLocalCopies copies, SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - GetValueInterops getValueInterops, VB value, - InstanceOrBytesToInstance toValue, - boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { + RV put2(ThreadLocalCopies copies, SegmentState segmentState, + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + GetValueInterops getValueInterops, VB value, + InstanceOrBytesToInstance toValue, + boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { long hash = metaKeyInterop.hash(keyInterop, key); int segmentNum = getSegment(hash); long segmentHash = segmentHash(hash); - return segments[segmentNum].put(copies, segmentState, + return segments[segmentNum].put3(copies, segmentState, metaKeyInterop, keyInterop, key, keySize, toKey, getValueInterops, value, toValue, segmentHash, replaceIfPresent, readValue, resultUnused); @@ -521,7 +521,7 @@ class VanillaChronicleMap, DelegatingMetaBytesInterop.>instance(), BytesBytesInterop.INSTANCE, key, keySizeMarshaller.readSize(key), keyBytesToInstance, readValueToOutputBytes, null, outputValueBytesToInstance, - false); + false, null); } private V lookupUsing(K key, V usingValue, boolean create) { @@ -532,7 +532,7 @@ class VanillaChronicleMap, metaKeyInteropProvider.get(copies, originalMetaKeyInterop, keyInterop, key); long keySize = metaKeyInterop.size(keyInterop, key); return lookupUsing(copies, null, metaKeyInterop, keyInterop, key, keySize, keyIdentity(), - this, usingValue, valueIdentity(), create); + this, usingValue, valueIdentity(), create, null); } private , RV> @@ -540,30 +540,52 @@ class VanillaChronicleMap, MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, InstanceOrBytesToInstance toKey, ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, - boolean create) { + boolean create, final MutableLockedEntry lock) { long hash = metaKeyInterop.hash(keyInterop, key); int segmentNum = getSegment(hash); long segmentHash = segmentHash(hash); Segment segment = segments[segmentNum]; return segment.acquire(copies, segmentState, metaKeyInterop, keyInterop, key, keySize, toKey, - readValue, usingValue, toValue, segmentHash, create); + readValue, usingValue, toValue, segmentHash, create, lock); } @Override public R mapForKey(K key, @NotNull Function function) { try (ReadContext entry = lookupUsing(key, null, - LockType.READ_LOCK, false)) { + false, false, LockType.READ_LOCK)) { return entry.present() ? function.apply(entry.value()) : null; } } @Override - public R updateForKey(K key, @NotNull Mutator mutator) { - try (Context entry = lookupUsing(key, null, - LockType.WRITE_LOCK, false)) { - return mutator.update(entry.value()); + public V putWith(K key, @NotNull Mutator mutator) { + + + final V using = (vClass.equals(CharSequence.class)) ? (V) new StringBuilder() : null; + + try (WriteContext entry = lookupUsing(key, using, + false, false, LockType.WRITE_LOCK)) { + + if (CharSequence.class.isAssignableFrom(vClass)) { + CharSequence value = (CharSequence) entry.value(); + if (entry.value() == null || value.length() == 0) + return null; + } + + if (entry.value() == null) + return null; + + V result = mutator.update(entry.value()); + if (entry.value() instanceof StringBuilder) { + ((StringBuilder) using).setLength(0); + ((StringBuilder) using).append((CharSequence) result); + } else + ((MutableLockedEntry) entry).value(result); + return result; } + + } @Override @@ -737,17 +759,17 @@ class VanillaChronicleMap, @NotNull @Override public final WriteContext acquireUsingLocked(@NotNull K key, @NotNull V usingValue) { - return lookupUsing(key, usingValue, LockType.WRITE_LOCK, true); + return lookupUsing(key, usingValue, true, true, LockType.WRITE_LOCK); } @NotNull @Override public final ReadContext getUsingLocked(@NotNull K key, @NotNull V usingValue) { - return lookupUsing(key, usingValue, LockType.READ_LOCK, true); + return lookupUsing(key, usingValue, true, false, LockType.READ_LOCK); } private T lookupUsing( - K key, V usingValue, LockType lockType, boolean mustReuseValue) { + K key, V usingValue, boolean mustReuseValue, final boolean create, final LockType lockType) { checkKey(key); ThreadLocalCopies copies = SegmentState.getCopies(null); @@ -769,7 +791,7 @@ class VanillaChronicleMap, V v = segment.acquireWithoutLock(copies, segmentState, metaKeyInterop, keyInterop, key, keySize, keyIdentity(), this, usingValue, valueIdentity(), - segmentHash, lockType == LockType.WRITE_LOCK); + segmentHash, create, lock); checkReallyUsingValue(usingValue, mustReuseValue, v); @@ -1592,15 +1614,15 @@ class VanillaChronicleMap, , RV> RV acquire(@Nullable ThreadLocalCopies copies, final @Nullable SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, - long hash2, boolean create) { + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, + long hash2, boolean create, MutableLockedEntry lock) { readLock(null); try { return acquireWithoutLock(copies, segmentState, metaKeyInterop, keyInterop, key, keySize, toKey, - readValue, usingValue, toValue, hash2, create); + readValue, usingValue, toValue, hash2, create, lock); } finally { if (segmentState != null) segmentState.close(); @@ -1614,7 +1636,7 @@ class VanillaChronicleMap, MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, InstanceOrBytesToInstance toKey, ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, - long hash2, boolean create) { + long hash2, boolean create, MutableLockedEntry lock) { segmentStateNotNullImpliesCopiesNotNull(copies, segmentState); SegmentState localSegmentState = segmentState; try { @@ -1648,9 +1670,16 @@ class VanillaChronicleMap, } entry = localSegmentState.tmpBytes; } - return createEntryOnAcquire(copies, localSegmentState, + + RV result = createEntryOnAcquire(copies, localSegmentState, metaKeyInterop, keyInterop, key, keySize, toKey, readValue, usingValue, toValue, entry); + + // notify the context that the entry was created + if (lock instanceof WriteLocked) + ((WriteLocked) lock).created(true); + + return result; } finally { if (segmentState == null && localSegmentState != null) localSegmentState.close(); @@ -1729,15 +1758,15 @@ class VanillaChronicleMap, return v; } - private , + , RV, VB extends RV, VBI, MVBI extends MetaBytesInterop> - RV put(@Nullable ThreadLocalCopies copies, final @Nullable SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - GetValueInterops getValueInterops, VB value, - InstanceOrBytesToInstance toValue, - long hash2, boolean replaceIfPresent, - ReadValue readValue, boolean resultUnused) { + RV put3(@Nullable ThreadLocalCopies copies, final @Nullable SegmentState segmentState, + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + GetValueInterops getValueInterops, VB value, + InstanceOrBytesToInstance toValue, + long hash2, boolean replaceIfPresent, + ReadValue readValue, boolean resultUnused) { shouldNotBeCalledFromReplicatedChronicleMap("Segment.put"); writeLock(null); try { @@ -2587,7 +2616,7 @@ class VanillaChronicleMap, } } - private static abstract class MutableLockedEntry< + static abstract class MutableLockedEntry< K, KI, MKI extends MetaBytesInterop, V, VI, MVI extends MetaBytesInterop> implements Context { final SegmentState segmentState; @@ -2602,6 +2631,7 @@ class VanillaChronicleMap, private K key; private V value; + boolean present; MutableLockedEntry(SegmentState segmentState) { this.segmentState = segmentState; @@ -2620,8 +2650,10 @@ class VanillaChronicleMap, } void value(V value) { + this.present = value != null; this.value = value; } + } private static class ReadLocked, @@ -2629,40 +2661,46 @@ class VanillaChronicleMap, extends MutableLockedEntry implements ReadContext { - private boolean present; ReadLocked(SegmentState segmentState) { super(segmentState); } - void value(V value) { - this.present = value != null; - super.value(value); - } - - /** - * @return if the value is not null - */ - @Override - public boolean present() { - return present; - } - @Override public void close() { segmentState.close(); segment.readUnlock(); } + + /** + * @return if the value is not null + */ + public boolean present() { + return present; + } } - private static abstract class WriteLocked, + static abstract class WriteLocked, V, VI, MVI extends MetaBytesInterop> extends MutableLockedEntry implements WriteContext { + private boolean created = false; + WriteLocked(SegmentState segmentState) { super(segmentState); } + + /** + * @return if the entry was created + */ + public boolean created() { + return created; + } + + public void created(boolean created) { + this.created = created; + } } private static class NativeWriteLocked, @@ -2673,6 +2711,7 @@ class VanillaChronicleMap, super(segmentState); } + @Override public void close() { segmentState.close(); @@ -2715,6 +2754,7 @@ class VanillaChronicleMap, segment.writeUnlock(); } + @Override public void dontPutOnClose() { putOnClose = false; diff --git a/src/main/java/net/openhft/chronicle/map/WriteContext.java b/src/main/java/net/openhft/chronicle/map/WriteContext.java index 91019b0de..bbcd49cb0 100644 --- a/src/main/java/net/openhft/chronicle/map/WriteContext.java +++ b/src/main/java/net/openhft/chronicle/map/WriteContext.java @@ -30,9 +30,14 @@ public interface WriteContext extends Context { void dontPutOnClose(); /** - * this is simular by more efficient than calling map.remove("key") as the entry is already available to - * the WriteContext + * this is similar by more efficient than calling map.remove("key") as the entry is already + * available to the WriteContext */ void removeEntry(); + /** + * @return true if the entry was previously present + */ + boolean created(); + } diff --git a/src/test/java/eg/BGChronicleTest.java b/src/test/java/eg/BGChronicleTest.java index b83d73eef..1b65540ef 100644 --- a/src/test/java/eg/BGChronicleTest.java +++ b/src/test/java/eg/BGChronicleTest.java @@ -24,7 +24,7 @@ public class BGChronicleTest { public static final int READ_RATIO = Integer.getInteger("readRatio", 2); // public static final boolean BUSY_WAIT = Boolean.parseBoolean(System.getProperty("busyWait", // "" + (CLIENTS < Runtime.getRuntime().availableProcessors()))); - public static final int REPLICAS = Integer.getInteger("replicas", 2); + public static final int REPLICAS = Integer.getInteger("replicas", 1); public static final int CLIENTS = Integer.getInteger("clients", Math.max(1, Runtime.getRuntime().availableProcessors() - REPLICAS)); public static final long KEYS = Long.getLong("keys", 1000_000L); public static final long MESSAGES = Long.getLong("messages", 1000_000 + KEYS * 2); diff --git a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java index 98eab7796..97ae1c182 100644 --- a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java @@ -1,7 +1,6 @@ package net.openhft.chronicle.map; import net.openhft.lang.io.serialization.impl.*; -import net.openhft.lang.model.DataValueClasses; import net.openhft.lang.values.*; import org.junit.Ignore; import org.junit.Test; @@ -17,14 +16,13 @@ import static org.junit.Assert.*; */ // TODO Test for persisted map. // TODO Test for stateless map. -@Ignore + public class CHMUseCasesTest { /** * String is not as efficient as CharSequence as a key or value but easier to use The key can * only be on heap and variable length serialised. */ @Test - @Ignore("Not yet implemented") public void testStringStringMap() throws ExecutionException, InterruptedException { /* TODO run the same test for multiple types of map stores for(ChronicleMapBuilder chmb : Arrays.asList( @@ -53,17 +51,36 @@ public class CHMUseCasesTest { return "New " + s; } })); - try { - map.updateForKey("Hello", new Mutator() { - @Override - public String update(String s) { - return "New " + s; - } - }); - fail("Operation not supported as value is not mutable"); - } catch (Exception ignored) { - // TODO replace with the specific exception. - } + + + } + } + + + @Test + public void testStringStringMapMutableValue() throws ExecutionException, InterruptedException { +/* TODO run the same test for multiple types of map stores + for(ChronicleMapBuilder chmb : Arrays.asList( + ChronicleMapBuilder.of(String.class, String.class), + ChronicleMapBuilder.of(String.class, String.class).file(TMP_FILE), + ChronicleMapBuilder.of(String.class, String.class).statelessClient(CONFIG) + )) { + try (ChronicleMap map = chmb.create()) { +*/ + try (ChronicleMap map = ChronicleMapBuilder + .of(String.class, String.class) + .checkSerializedValues() // for testing purposes only + .create()) { + map.put("Hello", "World"); + + + map.putWith("Hello", new Mutator() { + @Override + public String update(String s) { + return "New " + s; + } + }); + } } @@ -73,7 +90,6 @@ public class CHMUseCasesTest { * and variable length serialised. */ @Test - @Ignore("Not yet implemented") public void testCharSequenceCharSequenceMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) @@ -92,38 +108,42 @@ public class CHMUseCasesTest { assertEquals("value-1", value.toString()); map.remove("key-1"); assertNull(map.getUsing(key, value)); - map.close(); - assertEquals("New World", map.mapForKey("Hello", new Function() { - @Override - public CharSequence apply(CharSequence s) { - return "New " + s; - } - })); - assertEquals(null, map.mapForKey("No key", new Function() { - @Override - public CharSequence apply(CharSequence s) { - return "New " + s; - } - })); - assertEquals("New World !!", map.updateForKey("Hello", new Mutator() { - @Override - public CharSequence update(CharSequence s) { - ((StringBuilder) s).append("!!"); - return "New " + s; - } - })); + assertEquals("New World", map.mapForKey(new StringBuilder("Hello"), new + Function() { + @Override + public CharSequence apply(CharSequence s) { + return "New " + s; + } + })); + assertEquals(null, map.mapForKey(new StringBuilder("No key"), new + Function() { + @Override + public CharSequence apply(CharSequence s) { + return "New " + s; + } + })); + + assertEquals("New World !!", map.putWith(new StringBuilder("Hello"), new + Mutator() { + @Override + public CharSequence update(CharSequence s) { + ((StringBuilder) s).append(" !!"); + return "New " + s; + } + })); assertEquals("New World !!", map.get("Hello").toString()); - assertEquals(null, map.updateForKey("no-key", new Mutator() { - @Override - public CharSequence update(CharSequence s) { - ((StringBuilder) s).append("!!"); - return "New " + s; - } - })); + assertEquals(null, map.putWith(new StringBuilder("no-key"), new + Mutator() { + @Override + public CharSequence update(CharSequence s) { + ((StringBuilder) s).append("!!"); + return "New " + s; + } + })); } } @@ -132,16 +152,15 @@ public class CHMUseCasesTest { * StringValue represents any bean which contains a String Value */ @Test - @Ignore("Not yet implemented") public void testStringValueStringValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(StringValue.class, StringValue.class) .checkSerializedValues() // for testing purposes only .create()) { - StringValue key1 = DataValueClasses.newDirectInstance(StringValue.class); - StringValue key2 = DataValueClasses.newInstance(StringValue.class); - StringValue value1 = DataValueClasses.newDirectInstance(StringValue.class); - StringValue value2 = DataValueClasses.newInstance(StringValue.class); + StringValue key1 = ChronicleMapBuilder.newInstance(StringValue.class, true); + StringValue key2 = ChronicleMapBuilder.newInstance(StringValue.class, true); + StringValue value1 = ChronicleMapBuilder.newInstance(StringValue.class, false); + StringValue value2 = ChronicleMapBuilder.newInstance(StringValue.class, false); key1.setValue(new StringBuilder("1")); value1.setValue("11"); @@ -224,7 +243,6 @@ public class CHMUseCasesTest { } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntegerIntegerMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(Integer.class, Integer.class) @@ -265,13 +283,12 @@ public class CHMUseCasesTest { })); try { - map.updateForKey(1, new Mutator() { + map.putWith(1, new Mutator() { @Override public Integer update(Integer s) { return s + 1; } }); - fail("Update of Integer not supported"); } catch (Exception todoMoreSpecificException) { } @@ -280,7 +297,6 @@ public class CHMUseCasesTest { } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testLongLongMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(Long.class, Long.class) @@ -310,13 +326,12 @@ public class CHMUseCasesTest { })); try { - map.updateForKey(1L, new Mutator() { + map.putWith(1L, new Mutator() { @Override public Long update(Long s) { return s + 1; } }); - fail("Update of Long not supported"); } catch (Exception todoMoreSpecificException) { } @@ -325,7 +340,6 @@ public class CHMUseCasesTest { } @Test - @Ignore("Not yet implemented") public void testDoubleDoubleMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(Double.class, Double.class) @@ -355,13 +369,13 @@ public class CHMUseCasesTest { })); try { - map.updateForKey(1.0, new Mutator() { + map.putWith(1.0, new Mutator() { @Override public Double update(Double s) { return s + 1; } }); - fail("Update of Double not supported"); + } catch (Exception todoMoreSpecificException) { } @@ -370,7 +384,6 @@ public class CHMUseCasesTest { } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testByteArrayByteArrayMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(byte[].class, byte[].class) @@ -383,9 +396,12 @@ public class CHMUseCasesTest { byte[] value2 = {22, 22, 22, 22}; assertNull(map.put(key1, value1)); assertTrue(Arrays.equals(value1, map.put(key1, value2))); - assertTrue(Arrays.equals(value1, map.get(key1))); + assertTrue(Arrays.equals(value2, map.get(key1))); assertNull(map.get(key2)); + map.put(key1, value1); + + assertTrue(Arrays.equals(new byte[]{11, 11}, map.mapForKey(key1, new Function() { @Override public byte[] apply(byte[] s) { @@ -399,7 +415,7 @@ public class CHMUseCasesTest { } })); - assertTrue(Arrays.equals(new byte[]{12, 10}, map.updateForKey(key1, new Mutator() { + assertTrue(Arrays.equals(new byte[]{12, 10}, map.putWith(key1, new Mutator() { @Override public byte[] update(byte[] s) { s[0]++; @@ -408,16 +424,15 @@ public class CHMUseCasesTest { } }))); - assertTrue(Arrays.equals(new byte[]{12, 10, 11, 11}, map.get(key1))); + byte[] a2 = map.get(key1); + assertTrue(Arrays.equals(new byte[]{12, 10}, a2)); - byte[] key3 = {3, 3, 3, 3}; - byte[] value3 = {4, 4, 4, 4}; } } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") + @Ignore("Not enough available space") public void testByteBufferByteBufferMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(ByteBuffer.class, ByteBuffer.class) @@ -446,7 +461,7 @@ public class CHMUseCasesTest { assertBBEquals(ByteBuffer.wrap(new byte[]{11, 11}), map.mapForKey(key1, function)); assertEquals(null, map.mapForKey(key2, function)); - assertBBEquals(ByteBuffer.wrap(new byte[]{12, 10}), map.updateForKey(key1, new Mutator() { + assertBBEquals(ByteBuffer.wrap(new byte[]{12, 10}), map.putWith(key1, new Mutator() { @Override public ByteBuffer update(ByteBuffer s) { s.put(0, (byte) (s.get(0) + 1)); @@ -510,7 +525,7 @@ public class CHMUseCasesTest { } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") + @Ignore("Not enough available space") public void testByteBufferDirectByteBufferMap() throws ExecutionException, InterruptedException { try (ChronicleMap map = ChronicleMapBuilder .of(ByteBuffer.class, ByteBuffer.class) @@ -541,7 +556,7 @@ public class CHMUseCasesTest { assertBBEquals(ByteBuffer.wrap(new byte[]{11, 11}), map.mapForKey(key1, function)); assertEquals(null, map.mapForKey(key2, function)); - assertBBEquals(ByteBuffer.wrap(new byte[]{12, 10}), map.updateForKey(key1, new Mutator() { + assertBBEquals(ByteBuffer.wrap(new byte[]{12, 10}), map.putWith(key1, new Mutator() { @Override public ByteBuffer update(ByteBuffer s) { s.put(0, (byte) (s.get(0) + 1)); @@ -562,17 +577,16 @@ public class CHMUseCasesTest { } @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueIntValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, IntValue.class) .disableOversizedEntries(true) // disabled for testing purposes only. .entrySize(8) .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - IntValue value1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue value2 = DataValueClasses.newInstance(IntValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue value1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue value2 = ChronicleMapBuilder.newInstance(IntValue.class, false); key1.setValue(1); value1.setValue(11); @@ -645,17 +659,16 @@ public class CHMUseCasesTest { * For unsigned int -> unsigned int entries, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testUnsignedIntValueUnsignedIntValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(UnsignedIntValue.class, UnsignedIntValue.class) .disableOversizedEntries(true) // disabled for testing purposes only. .entrySize(8) .create()) { - UnsignedIntValue key1 = DataValueClasses.newDirectInstance(UnsignedIntValue.class); - UnsignedIntValue key2 = DataValueClasses.newInstance(UnsignedIntValue.class); - UnsignedIntValue value1 = DataValueClasses.newDirectInstance(UnsignedIntValue.class); - UnsignedIntValue value2 = DataValueClasses.newInstance(UnsignedIntValue.class); + UnsignedIntValue key1 = ChronicleMapBuilder.newInstance(UnsignedIntValue.class, true); + UnsignedIntValue key2 = ChronicleMapBuilder.newInstance(UnsignedIntValue.class, true); + UnsignedIntValue value1 = ChronicleMapBuilder.newInstance(UnsignedIntValue.class, false); + UnsignedIntValue value2 = ChronicleMapBuilder.newInstance(UnsignedIntValue.class, false); key1.setValue(1); value1.setValue(11); @@ -728,17 +741,16 @@ public class CHMUseCasesTest { * For int values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueShortValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, ShortValue.class) .disableOversizedEntries(true) // disabled for testing purposes only. .entrySize(6) .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - ShortValue value1 = DataValueClasses.newDirectInstance(ShortValue.class); - ShortValue value2 = DataValueClasses.newInstance(ShortValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + ShortValue value1 = ChronicleMapBuilder.newInstance(ShortValue.class, false); + ShortValue value2 = ChronicleMapBuilder.newInstance(ShortValue.class, false); key1.setValue(1); value1.setValue((short) 11); @@ -811,17 +823,18 @@ public class CHMUseCasesTest { * For int -> unsigned short values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueUnsignedShortValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, UnsignedShortValue.class) .entrySize(6) .disableOversizedEntries(true) // disabled for testing purposes only. .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - UnsignedShortValue value1 = DataValueClasses.newDirectInstance(UnsignedShortValue.class); - UnsignedShortValue value2 = DataValueClasses.newInstance(UnsignedShortValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + UnsignedShortValue value1 = ChronicleMapBuilder.newInstance(UnsignedShortValue.class, false); + UnsignedShortValue value2 = ChronicleMapBuilder.newInstance(UnsignedShortValue.class, false); + + //key direct instance, rather direct ref key1.setValue(1); value1.setValue(11); @@ -894,17 +907,16 @@ public class CHMUseCasesTest { * For int values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueCharValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, CharValue.class) .disableOversizedEntries(true) // disabled for testing purposes only. .entrySize(6) .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - CharValue value1 = DataValueClasses.newDirectInstance(CharValue.class); - CharValue value2 = DataValueClasses.newInstance(CharValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + CharValue value1 = ChronicleMapBuilder.newInstance(CharValue.class, false); + CharValue value2 = ChronicleMapBuilder.newInstance(CharValue.class, false); key1.setValue(1); value1.setValue((char) 11); @@ -977,17 +989,16 @@ public class CHMUseCasesTest { * For int-> byte entries, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueUnsignedByteMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, UnsignedByteValue.class) .entrySize(5) .disableOversizedEntries(true) // disabled for testing purposes only. .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - UnsignedByteValue value1 = DataValueClasses.newDirectInstance(UnsignedByteValue.class); - UnsignedByteValue value2 = DataValueClasses.newInstance(UnsignedByteValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + UnsignedByteValue value1 = ChronicleMapBuilder.newInstance(UnsignedByteValue.class, false); + UnsignedByteValue value2 = ChronicleMapBuilder.newInstance(UnsignedByteValue.class, false); key1.setValue(1); value1.setValue(11); @@ -1060,17 +1071,16 @@ public class CHMUseCasesTest { * For int values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testIntValueBooleanValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(IntValue.class, BooleanValue.class) .disableOversizedEntries(true) // disabled for testing purposes only. .entrySize(5) .create()) { - IntValue key1 = DataValueClasses.newDirectInstance(IntValue.class); - IntValue key2 = DataValueClasses.newInstance(IntValue.class); - BooleanValue value1 = DataValueClasses.newDirectInstance(BooleanValue.class); - BooleanValue value2 = DataValueClasses.newInstance(BooleanValue.class); + IntValue key1 = ChronicleMapBuilder.newInstance(IntValue.class, false); + IntValue key2 = ChronicleMapBuilder.newInstance(IntValue.class, false); + BooleanValue value1 = ChronicleMapBuilder.newInstance(BooleanValue.class, false); + BooleanValue value2 = ChronicleMapBuilder.newInstance(BooleanValue.class, false); key1.setValue(1); value1.setValue(true); @@ -1143,17 +1153,16 @@ public class CHMUseCasesTest { * For float values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testFloatValueFloatValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(FloatValue.class, FloatValue.class) .entrySize(8) .disableOversizedEntries(true) // disabled for testing purposes only. .create()) { - FloatValue key1 = DataValueClasses.newDirectInstance(FloatValue.class); - FloatValue key2 = DataValueClasses.newInstance(FloatValue.class); - FloatValue value1 = DataValueClasses.newDirectInstance(FloatValue.class); - FloatValue value2 = DataValueClasses.newInstance(FloatValue.class); + FloatValue key1 = ChronicleMapBuilder.newInstance(FloatValue.class, false); + FloatValue key2 = ChronicleMapBuilder.newInstance(FloatValue.class, false); + FloatValue value1 = ChronicleMapBuilder.newInstance(FloatValue.class, false); + FloatValue value2 = ChronicleMapBuilder.newInstance(FloatValue.class, false); key1.setValue(1); value1.setValue(11); @@ -1226,17 +1235,16 @@ public class CHMUseCasesTest { * For double values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-225 Add support for heap and native object in the same map") public void testDoubleValueDoubleValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(DoubleValue.class, DoubleValue.class) .entrySize(16) .create()) { - DoubleValue key1 = DataValueClasses.newDirectInstance(DoubleValue.class); - DoubleValue key2 = DataValueClasses.newInstance(DoubleValue.class); - DoubleValue value1 = DataValueClasses.newDirectInstance(DoubleValue.class); - DoubleValue value2 = DataValueClasses.newInstance(DoubleValue.class); + DoubleValue key1 = ChronicleMapBuilder.newInstance(DoubleValue.class, false); + DoubleValue key2 = ChronicleMapBuilder.newInstance(DoubleValue.class, false); + DoubleValue value1 = ChronicleMapBuilder.newInstance(DoubleValue.class, false); + DoubleValue value2 = ChronicleMapBuilder.newInstance(DoubleValue.class, false); key1.setValue(1); @@ -1312,21 +1320,22 @@ public class CHMUseCasesTest { * For long values, the key can be on heap or off heap. */ @Test - @Ignore("HCOLL-226 implement disableOversizedEntries()") public void testLongValueLongValueMap() { try (ChronicleMap map = ChronicleMapBuilder .of(LongValue.class, LongValue.class) .entrySize(16) .disableOversizedEntries(true) // disabled for testing purposes only. .create()) { - LongValue key1 = DataValueClasses.newDirectInstance(LongValue.class); - LongValue key2 = DataValueClasses.newInstance(LongValue.class); - LongValue value1 = DataValueClasses.newDirectInstance(LongValue.class); - LongValue value2 = DataValueClasses.newInstance(LongValue.class); + LongValue key1 = ChronicleMapBuilder.newInstance(LongValue.class, false); + LongValue key2 = ChronicleMapBuilder.newInstance(LongValue.class, false); + LongValue value1 = ChronicleMapBuilder.newInstance(LongValue.class, false); + LongValue value2 = ChronicleMapBuilder.newInstance(LongValue.class, false); + key1.setValue(1); value1.setValue(11); - assertEquals(value1, map.get(key1)); + assertEquals(null, map.get(key1)); + map.put(key1, value1); key2.setValue(2); value2.setValue(22); @@ -1408,25 +1417,35 @@ public class CHMUseCasesTest { List list1 = new ArrayList<>(); try (WriteContext wc = map.acquireUsingLocked("1", list1)) { - assertEquals(Collections.EMPTY_LIST, list1); + list1.add("one"); + assertEquals(Arrays.asList("one"), list1); } List list2 = new ArrayList<>(); + try (ReadContext rc = map.getUsingLocked("1", list2)) { + assertTrue(rc.present()); + assertEquals(Arrays.asList("one"), list2); + } + try (ReadContext rc = map.getUsingLocked("2", list2)) { assertTrue(rc.present()); list2.add("two-B"); // this is not written as it only a read context assertEquals(Arrays.asList("two-A", "two-B"), list2); } + try (WriteContext wc = map.acquireUsingLocked("2", list1)) { + list1.add("two-C"); + assertEquals(Arrays.asList("two-A", "two-C"), list1); + } + try (ReadContext rc = map.getUsingLocked("2", list2)) { assertTrue(rc.present()); - assertEquals(Arrays.asList("two-A"), list2); + assertEquals(Arrays.asList("two-A", "two-C"), list2); } } } @Test - @Ignore("Fix LinkedHashSet Marshaller") public void testSetValue() { try (ChronicleMap> map = ChronicleMapBuilder .of(String.class, (Class>) (Class) Set.class) @@ -1447,7 +1466,7 @@ public class CHMUseCasesTest { } try (WriteContext wc = map.acquireUsingLocked("2", list1)) { list1.add("three"); - assertEquals(new LinkedHashSet(Arrays.asList("three")), list1); + assertEquals(new LinkedHashSet(Arrays.asList("one", "three")), list1); } try (ReadContext rc = map.getUsingLocked("2", list2)) { assertTrue(rc.present()); diff --git a/src/test/java/net/openhft/chronicle/map/LargeEntriesTest.java b/src/test/java/net/openhft/chronicle/map/LargeEntriesTest.java new file mode 100644 index 000000000..12b73c8c2 --- /dev/null +++ b/src/test/java/net/openhft/chronicle/map/LargeEntriesTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.serialization.impl.SnappyStringMarshaller; +import org.junit.Ignore; +import org.junit.Test; + +import java.beans.XMLEncoder; +import java.io.ByteArrayOutputStream; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Created by peter on 06/12/14. + */ +public class LargeEntriesTest { + + static final int ENTRIES = 640; + static final int ENTRY_SIZE = 1024; + + @Test + @Ignore + public void testLargeStrings() throws ExecutionException, InterruptedException { + final ChronicleMap map = ChronicleMapBuilder + .of(String.class, String.class) + .valueMarshaller(SnappyStringMarshaller.INSTANCE) + .actualSegments(1) // to force an error. + .entries(ENTRIES * 2) + .entrySize(ENTRY_SIZE / 4) + .putReturnsNull(true) + .create(); + { + int threads = 2; //Runtime.getRuntime().availableProcessors(); + ExecutorService es = Executors.newFixedThreadPool(threads); + final int block = ENTRIES / threads; + for (int i = 0; i < 3; i++) { + long start = System.currentTimeMillis(); + List> futureList = new ArrayList<>(); + for (int t = 0; t < threads; t++) { + final int finalT = t; + final int finalI = i; + futureList.add(es.submit(new Runnable() { + @Override + public void run() { + exerciseLargeStrings(map, finalI, finalT * block, finalT * block + block, ENTRY_SIZE); + } + })); + } + for (Future future : futureList) { + future.get(); + } + long time = System.currentTimeMillis() - start; + long operations = 3; + System.out.printf("Put/Get rate was %.1f MB/s%n", operations * ENTRIES * ENTRY_SIZE / 1e6 / (time / 1e3)); + } + es.shutdown(); + if (es.isTerminated()) + map.close(); + } + } + + void exerciseLargeStrings(ChronicleMap map, int run, int start, int finish, int entrySize) { +/* + final Thread thisThread = Thread.currentThread(); + Thread monitor = new Thread(new Runnable() { + @Override + public void run() { + StringBuilder sb = new StringBuilder(); + sb.append(thisThread).append("\n"); + try { + while (!Thread.currentThread().isInterrupted()) { + Thread.sleep(50); + StackTraceElement[] stackTrace = thisThread.getStackTrace(); + for (int i = 0; i < 4 && i < stackTrace.length; i++) + sb.append("\tat ").append(stackTrace[i]).append("\n"); + sb.append("\n"); + } + } catch (InterruptedException e) { + } + System.out.println(sb); + } + }); + monitor.start(); +*/ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XMLEncoder xml = new XMLEncoder(baos); + Map map2 = new HashMap<>(); + Random rand = new Random(1); + for (int i = 0; i < entrySize / 80; i++) + map2.put("key-" + i, "value-" + rand.nextInt(1000)); + xml.writeObject(map2); + xml.close(); + String value = baos.toString().substring(0, entrySize); + // warmup to compression. + if (run == 0) { + DirectBytes bytes = DirectStore.allocate(entrySize).bytes(); + SnappyStringMarshaller.INSTANCE.write(bytes, value); + bytes.flip(); + SnappyStringMarshaller.INSTANCE.read(bytes); + bytes.release(); + } + + for (int i = start; i < finish; i++) { + String key = "key-" + i; + map.put(key, value); + String object = map.get(key); + + assertNotNull(key, object); + assertEquals(key, entrySize, object.length()); + } +// monitor.interrupt(); + + for (int i = start; i < finish; i++) { +// System.out.println(i); + String key = "key-" + i; + String object = map.get(key); + + assertNotNull(key, object); + assertEquals(key, entrySize, object.length()); + } + } +} diff --git a/src/test/java/net/openhft/chronicle/map/RecursiveRefereneChMapTest.java b/src/test/java/net/openhft/chronicle/map/RecursiveRefereneChMapTest.java index 4d50854ad..790b84265 100644 --- a/src/test/java/net/openhft/chronicle/map/RecursiveRefereneChMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/RecursiveRefereneChMapTest.java @@ -18,7 +18,7 @@ package net.openhft.chronicle.map; -import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.JDKObjectSerializer; import org.junit.Test; import java.io.*; @@ -35,7 +35,7 @@ public class RecursiveRefereneChMapTest { file.deleteOnExit(); Map map = ChronicleMapBuilder.of(String.class, StupidCycle.class) .entries(64) - .objectSerializer(JDKZObjectSerializer.INSTANCE).create(); + .objectSerializer(JDKObjectSerializer.INSTANCE).create(); map.put("Test", new StupidCycle()); map.put("Test2", new StupidCycle2()); StupidCycle cycle = (StupidCycle) map.get("Test"); diff --git a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java index e70843d33..757a728c5 100644 --- a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java +++ b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java @@ -223,8 +223,8 @@ public class ReplicationCheckingMap implements ChronicleMap { } @Override - public R updateForKey(K key, @NotNull Mutator mutator) { - return map1.updateForKey(key, mutator); + public V putWith(K key, @NotNull Mutator mutator) { + return map1.putWith(key, mutator); } @Override diff --git a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java index 1c487acf1..5d7a9551c 100755 --- a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java @@ -175,8 +175,8 @@ public class StatelessChronicleMapTest extends JSR166TestCase { } @Override - public R updateForKey(K key, @NotNull Mutator mutator) { - return d.updateForKey(key, mutator); + public V putWith(K key, @NotNull Mutator mutator) { + return d.putWith(key, mutator); } @Override