diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java similarity index 71% rename from src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java rename to src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java index 8f9b93bcd..cf67d2d27 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java @@ -23,12 +23,24 @@ import net.openhft.chronicle.hash.serialization.BytesReader; import net.openhft.chronicle.hash.serialization.Hasher; import net.openhft.chronicle.hash.serialization.SizeMarshaller; import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.Byteable; import net.openhft.lang.model.DataValueClasses; import net.openhft.lang.values.LongValue; -public enum LongValueMarshaller +public class LongValueKeyMarshaller implements BytesInterop, BytesReader, SizeMarshaller { - INSTANCE; + private static final long serialVersionUID = 0L; + + public static final LongValueKeyMarshaller INSTANCE = new LongValueKeyMarshaller(); + + private static final Class heapLongValueClass = + DataValueClasses.heapClassFor(LongValue.class); + + LongValueKeyMarshaller() {} + + private Object readResolve() { + return INSTANCE; + } @Override public long size(LongValue e) { @@ -67,11 +79,19 @@ public enum LongValueMarshaller @Override public LongValue read(Bytes bytes, long size) { - return read(bytes, size, DataValueClasses.newInstance(LongValue.class)); + return read(bytes, size, null); } @Override public LongValue read(Bytes bytes, long size, LongValue toReuse) { + if (toReuse == null || + (toReuse instanceof Byteable && ((Byteable) toReuse).bytes() == null)) { + try { + toReuse = heapLongValueClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new AssertionError(e); + } + } toReuse.setValue(bytes.readLong()); return toReuse; } diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java new file mode 100644 index 000000000..3473e584b --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java @@ -0,0 +1,52 @@ +/* + * 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.lang.io.Bytes; +import net.openhft.lang.model.Byteable; +import net.openhft.lang.model.DataValueClasses; +import net.openhft.lang.values.LongValue; + +public final class LongValueValueMarshaller extends LongValueKeyMarshaller { + private static final long serialVersionUID = 0L; + + public static final LongValueValueMarshaller INSTANCE = new LongValueValueMarshaller(); + + private static final Class directLongValueClass = + DataValueClasses.directClassFor(LongValue.class); + + private LongValueValueMarshaller() {} + + private Object readResolve() { + return INSTANCE; + } + + @Override + public LongValue read(Bytes bytes, long size, LongValue toReuse) { + if (!(toReuse instanceof Byteable)) { + try { + toReuse = directLongValueClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + Byteable value = (Byteable) toReuse; + ByteableMarshaller.setBytesAndOffset(value, bytes); + bytes.skip(8L); + return toReuse; + } +} diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java index f54953bf1..92d42a2b6 100644 --- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java @@ -39,6 +39,7 @@ 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; @@ -143,6 +144,8 @@ public abstract class AbstractChronicleMapBuilder keyClass, Class valueClass) { keyBuilder = new SerializationBuilder(keyClass, SerializationBuilder.Role.KEY); valueBuilder = new SerializationBuilder(valueClass, SerializationBuilder.Role.VALUE); + if (valueClass == LongValue.class) + prepareValueBytesOnAcquire(new ZeroOutValueBytes(valueSize())); } protected static boolean offHeapReference(Class valueClass) { @@ -721,6 +724,8 @@ public abstract class AbstractChronicleMapBuilder prepareValueBytesAsWriter() { + if (prepareValueBytes == null) + return null; return new PrepareValueBytesAsWriter<>(prepareValueBytes, valueSize()); } diff --git a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java index 99adb577c..7c69e0718 100644 --- a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java @@ -29,6 +29,7 @@ import net.openhft.lang.io.serialization.ObjectFactory; import net.openhft.lang.io.serialization.ObjectSerializer; import net.openhft.lang.io.serialization.impl.*; import net.openhft.lang.model.Byteable; +import net.openhft.lang.values.LongValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +40,7 @@ import java.lang.reflect.Modifier; import static net.openhft.chronicle.map.Objects.hash; import static net.openhft.chronicle.hash.serialization.SizeMarshallers.stopBit; +import static net.openhft.chronicle.map.SerializationBuilder.Role.KEY; final class SerializationBuilder implements Cloneable { @@ -83,34 +85,40 @@ final class SerializationBuilder implements Cloneable { if (concreteClass(eClass) && Byteable.class.isAssignableFrom(eClass)) { ByteableMarshaller byteableMarshaller = ByteableMarshaller.of((Class) eClass); - sizeMarshaller(byteableMarshaller) - .reader(byteableMarshaller) - .interop(byteableMarshaller); + sizeMarshaller(byteableMarshaller); + reader(byteableMarshaller); + interop(byteableMarshaller); } else if (eClass == CharSequence.class || eClass == String.class) { reader((BytesReader) CharSequenceReader.of()); writer((BytesWriter) CharSequenceWriter.instance()); } else if (eClass == Void.class) { - sizeMarshaller(VoidMarshaller.INSTANCE) - .reader((BytesReader) VoidMarshaller.INSTANCE) - .interop((BytesInterop) VoidMarshaller.INSTANCE); + sizeMarshaller(VoidMarshaller.INSTANCE); + reader((BytesReader) VoidMarshaller.INSTANCE); + interop((BytesInterop) VoidMarshaller.INSTANCE); } else if (eClass == Long.class) { - sizeMarshaller(LongMarshaller.INSTANCE) - .reader((BytesReader) LongMarshaller.INSTANCE) - .interop((BytesInterop) LongMarshaller.INSTANCE); + sizeMarshaller(LongMarshaller.INSTANCE); + reader((BytesReader) LongMarshaller.INSTANCE); + interop((BytesInterop) LongMarshaller.INSTANCE); } else if (eClass == Double.class) { - sizeMarshaller(DoubleMarshaller.INSTANCE) - .reader((BytesReader) DoubleMarshaller.INSTANCE) - .interop((BytesInterop) DoubleMarshaller.INSTANCE); + sizeMarshaller(DoubleMarshaller.INSTANCE); + reader((BytesReader) DoubleMarshaller.INSTANCE); + interop((BytesInterop) DoubleMarshaller.INSTANCE); } else if (eClass == Integer.class) { - sizeMarshaller(IntegerMarshaller.INSTANCE) - .reader((BytesReader) IntegerMarshaller.INSTANCE) - .interop((BytesInterop) IntegerMarshaller.INSTANCE); + sizeMarshaller(IntegerMarshaller.INSTANCE); + reader((BytesReader) IntegerMarshaller.INSTANCE); + interop((BytesInterop) IntegerMarshaller.INSTANCE); } else if (eClass == byte[].class) { reader((BytesReader) ByteArrayMarshaller.INSTANCE); interop((BytesInterop) ByteArrayMarshaller.INSTANCE); } else if (eClass == char[].class) { reader((BytesReader) CharArrayMarshaller.INSTANCE); interop((BytesInterop) CharArrayMarshaller.INSTANCE); + } else if (eClass == LongValue.class) { + LongValueKeyMarshaller marshaller = role == KEY ? + LongValueKeyMarshaller.INSTANCE : LongValueValueMarshaller.INSTANCE; + sizeMarshaller(marshaller); + reader((BytesReader) marshaller); + interop((BytesInterop) marshaller); } else if (concreteClass(eClass)) { BytesMarshaller marshaller = chooseMarshaller(eClass, eClass); if (marshaller != null) diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index f9d34dcb2..192e76cc6 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -881,8 +881,14 @@ class VanillaChronicleMap, offset = putEntry(metaKeyInterop, keyInterop, key, keySize, metaValueWriter, valueWriter, defaultValue); } else { - offset = putEntry(metaKeyInterop, keyInterop, key, keySize, - prepareValueBytesAsWriter, null, key); + if (prepareValueBytesAsWriter != null) { + offset = putEntry(metaKeyInterop, keyInterop, key, keySize, + prepareValueBytesAsWriter, null, key); + } else { + throw new IllegalStateException("To call acquire*() methods, " + + "you should specify either default value, default value provider " + + "or prepareBytes strategy during map building"); + } } return onKeyAbsentOnAcquire(copies, key, keySize, usingValue, offset); } diff --git a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java index ec2c994f4..cb4d827cf 100644 --- a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java @@ -1495,7 +1495,7 @@ public class ChronicleMapTest { tmpFile.deleteOnExit(); final ChronicleMap map = builder.createPersistedTo(tmpFile); - LongValue value = DataValueClasses.newInstance(LongValue.class); + LongValue value = DataValueClasses.newDirectReference(LongValue.class); try (ReadContext context = map.getUsingLocked("one", value)) { @@ -1503,19 +1503,10 @@ public class ChronicleMapTest { assertEquals(null, context.value()); } - // this wont add the entry try (WriteContext context = map.acquireUsingLocked("one", value)) { - context.value().addValue(1); - context.dontPutOnClose(); + value.setValue(10); } - try (ReadContext context = map.getUsingLocked("one", value)) { - assertEquals(false, context.present()); - assertEquals(null, context.value()); - } - - value.setValue(10); - // this will add the entry try (WriteContext context = map.acquireUsingLocked("one", value)) { LongValue value1 = context.value(); @@ -1540,12 +1531,12 @@ public class ChronicleMapTest { assertEquals(null, context.value()); } - value.setValue(1); - try (WriteContext context = map.acquireUsingLocked("one", value)) { - assertEquals(1, context.value().getValue()); + assertEquals(0, context.value().getValue()); } + value.setValue(1); + try (ReadContext lockedEntry = map.getUsingLocked("one", value)) { assertEquals(1, lockedEntry.value().getValue()); }