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 new file mode 100644 index 000000000..8bdb4412b --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BasicCopyingMetaBytesInterop.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.serialization.internal; + +import net.openhft.chronicle.hash.hashing.Hasher; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.threadlocal.Provider; +import net.openhft.lang.threadlocal.ThreadLocalCopies; + +public abstract class BasicCopyingMetaBytesInterop implements MetaBytesInterop { + private static final long serialVersionUID = 0L; + + static final Provider provider = + Provider.of(DirectBytesBuffer.class); + + static abstract class BasicCopyingMetaBytesInteropProvider> implements MetaProvider { + private static final long serialVersionUID = 0L; + + @Override + public ThreadLocalCopies getCopies(ThreadLocalCopies copies) { + return provider.getCopies(copies); + } + } + + final DirectBytesBuffer buffer; + transient long size; + transient long hash; + + protected BasicCopyingMetaBytesInterop(DirectBytesBuffer buffer) { + this.buffer = buffer; + } + + @Override + public long size(W writer, E e) { + return size; + } + + @Override + public boolean startsWith(W writer, Bytes bytes, E e) { + return bytes.startsWith(buffer.buffer); + } + + @Override + public long hash(W writer, E e) { + long h; + if ((h = hash) == 0L) + return hash = Hasher.hash(buffer.buffer); + return h; + } + + @Override + public void write(W writer, Bytes bytes, E e) { + bytes.write(buffer.buffer); + } +} 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 0f32d5c03..29a1c5623 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 @@ -28,7 +28,7 @@ import net.openhft.lang.model.Byteable; import org.jetbrains.annotations.NotNull; public abstract class ByteableMarshaller - implements BytesInterop, + implements BytesInterop, MetaBytesInterop, DeserializationFactoryConfigurableBytesReader>, SizeMarshaller { private static final long serialVersionUID = 0L; @@ -160,6 +160,26 @@ public abstract class ByteableMarshaller return (E) NativeBytes.UNSAFE.allocateInstance(tClass); } + @Override + public boolean startsWith(Object interop, Bytes bytes, E e) { + return startsWith(bytes, e); + } + + @Override + public long hash(Object interop, E e) { + return hash(e); + } + + @Override + public long size(Object writer, E e) { + return size(e); + } + + @Override + public void write(Object writer, Bytes bytes, E e) { + write(bytes, e); + } + private static class Default extends ByteableMarshaller { private static final long serialVersionUID = 0L; 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 fa631cd04..2b7022e84 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 @@ -19,23 +19,15 @@ package net.openhft.chronicle.hash.serialization.internal; import net.openhft.chronicle.hash.serialization.BytesWriter; -import net.openhft.chronicle.hash.hashing.Hasher; import net.openhft.lang.MemoryUnit; import net.openhft.lang.io.Bytes; -import net.openhft.lang.io.DirectBytes; -import net.openhft.lang.io.DirectStore; import net.openhft.lang.io.serialization.BytesMarshaller; -import net.openhft.lang.io.serialization.JDKObjectSerializer; -import net.openhft.lang.threadlocal.Provider; -import net.openhft.lang.threadlocal.StatefulCopyable; import net.openhft.lang.threadlocal.ThreadLocalCopies; -import java.io.IOException; -import java.io.ObjectInputStream; import java.io.Serializable; -public abstract class CopyingMetaBytesInterop implements MetaBytesInterop { - private static final long serialVersionUID = 0L; +public abstract class CopyingMetaBytesInterop extends BasicCopyingMetaBytesInterop { + private static final long serialVersionUID = 1L; private static final long MAX_REASONABLE_SERIALIZED_SIZE = MemoryUnit.MEGABYTES.toBytes(512L); @@ -50,14 +42,11 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< } } - final DirectBytesBuffer buffer; - transient long size; - transient long hash; transient W writer; transient E cur; - private CopyingMetaBytesInterop(DirectBytesBuffer buffer) { - this.buffer = buffer; + protected CopyingMetaBytesInterop(DirectBytesBuffer buffer) { + super(buffer); } void init(W writer, E e, boolean mutable, long maxSize) { @@ -82,29 +71,6 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< abstract void innerWrite(W writer, Bytes bytes, E e); - @Override - public long size(W writer, E e) { - return size; - } - - @Override - public boolean startsWith(W writer, Bytes bytes, E e) { - return bytes.startsWith(buffer.buffer); - } - - @Override - public long hash(W writer, E e) { - long h; - if ((h = hash) == 0L) - return hash = Hasher.hash(buffer.buffer); - return h; - } - - @Override - public void write(W writer, Bytes bytes, E e) { - bytes.write(buffer.buffer); - } - DirectBytesBuffer buffer() { return buffer; } @@ -119,20 +85,6 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< return new DirectBytesBuffer(bufferIdentity).forBytesWriter; } - private static final Provider provider = - Provider.of(DirectBytesBuffer.class); - - private static abstract class AbstractCopyingMetaBytesInteropProvider> - implements MetaProvider { - private static final long serialVersionUID = 0L; - - @Override - public ThreadLocalCopies getCopies(ThreadLocalCopies copies) { - return provider.getCopies(copies); - } - } - public static > MetaProvider> providerForBytesMarshaller(boolean mutable, long maxSize) { @@ -141,7 +93,7 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< private static class BytesMarshallerCopyingMetaBytesInteropProvider> - extends AbstractCopyingMetaBytesInteropProvider> { + extends BasicCopyingMetaBytesInteropProvider> { private static final long serialVersionUID = 0L; private final boolean mutable; private final long maxSize; @@ -169,7 +121,7 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< } private static class BytesWriterCopyingMetaBytesInteropProvider> - extends AbstractCopyingMetaBytesInteropProvider> { + extends BasicCopyingMetaBytesInteropProvider> { private static final long serialVersionUID = 0L; private final boolean mutable; private final long maxSize; @@ -189,83 +141,4 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop< return forBytesWriter; } } - - private static class DirectBytesBuffer - implements StatefulCopyable, Serializable { - private static final long serialVersionUID = 0L; - private final Serializable identity; - private transient DirectBytes buffer; - - private transient ForBytesMarshaller forBytesMarshaller; - private transient ForBytesWriter forBytesWriter; - - DirectBytesBuffer(Serializable identity) { - this.identity = identity; - initTransients(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - initTransients(); - } - - private void initTransients() { - forBytesMarshaller = new ForBytesMarshaller(); - forBytesWriter = new ForBytesWriter(); - } - - Bytes obtain(long maxSize) { - DirectBytes buf; - if ((buf = buffer) != null) { - if (maxSize <= buf.capacity()) { - return buf.clear(); - } else { - DirectStore store = (DirectStore) buf.store(); - store.resize(maxSize, false); - return buffer = store.bytes(); - } - } else { - buffer = new DirectStore(JDKObjectSerializer.INSTANCE, maxSize, false).bytes(); - return buffer; - } - } - - @Override - public Object stateIdentity() { - return identity; - } - - @Override - public DirectBytesBuffer copy() { - return new DirectBytesBuffer(identity); - } - - private class ForBytesMarshaller> - extends CopyingMetaBytesInterop { - private static final long serialVersionUID = 0L; - - private ForBytesMarshaller() { - super(DirectBytesBuffer.this); - } - - @Override - void innerWrite(M writer, Bytes bytes, E e) { - writer.write(bytes, e); - } - } - - private class ForBytesWriter> - extends CopyingMetaBytesInterop { - private static final long serialVersionUID = 0L; - - private ForBytesWriter() { - super(DirectBytesBuffer.this); - } - - @Override - void innerWrite(W writer, Bytes bytes, E e) { - writer.write(bytes, e); - } - } - } } 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 new file mode 100644 index 000000000..3c4425869 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueMetaBytesInterop.java @@ -0,0 +1,92 @@ +/* + * 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.BytesWriter; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.*; +import net.openhft.lang.threadlocal.ThreadLocalCopies; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +public abstract class DataValueMetaBytesInterop + extends BasicCopyingMetaBytesInterop> { + private static final long serialVersionUID = 0L; + + protected DataValueMetaBytesInterop(DirectBytesBuffer buffer) { + super(buffer); + } + + void init(BytesWriter writer, E e, long size) { + Bytes buffer = this.buffer.obtain(size); + writer.write(buffer, e); + buffer.flip(); + this.size = size; + hash = 0L; + } + + public static MetaBytesInterop> forIdentity(Serializable bufferIdentity) { + return new DirectBytesBuffer(bufferIdentity).forDataValueWriter; + } + + public static MetaProvider, MetaBytesInterop>> + interopProvider(Class eClass) { + return new InteropProvider<>(eClass); + } + + private static class InteropProvider extends BasicCopyingMetaBytesInteropProvider< + E, BytesWriter, MetaBytesInterop>> { + private static final long serialVersionUID = 0L; + + private final Class eClass; + transient long size; + transient MetaBytesInterop metaByteableInterop; + + private InteropProvider(Class eClass) { + this.eClass = eClass; + initTransients(); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + initTransients(); + } + + private void initTransients() { + DataValueModel model = DataValueModels.acquireModel(eClass); + size = DataValueGenerator.computeNonScalarOffset(model, eClass); + // TODO make this pick up configured deserializationFactory somehow + metaByteableInterop = ByteableMarshaller.of( + (Class) DataValueClasses.directClassFor(eClass)); + } + + @Override + public MetaBytesInterop> get( + ThreadLocalCopies copies, MetaBytesInterop> originalMetaWriter, + BytesWriter writer, E e) { + if (e instanceof Byteable) + return metaByteableInterop; + DirectBytesBuffer.ForDataValueWriter forDataValueWriter = + provider.get(copies, ((DataValueMetaBytesInterop)originalMetaWriter).buffer) + .forDataValueWriter; + forDataValueWriter.init(writer, e, size); + return forDataValueWriter; + } + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DirectBytesBuffer.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DirectBytesBuffer.java new file mode 100644 index 000000000..5a78d6dfa --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DirectBytesBuffer.java @@ -0,0 +1,117 @@ +/* + * 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.BytesWriter; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.JDKObjectSerializer; +import net.openhft.lang.threadlocal.StatefulCopyable; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +class DirectBytesBuffer + implements StatefulCopyable, Serializable { + private static final long serialVersionUID = 0L; + private final Serializable identity; + transient DirectBytes buffer; + + transient ForBytesMarshaller forBytesMarshaller; + transient ForBytesWriter forBytesWriter; + transient ForDataValueWriter forDataValueWriter; + + DirectBytesBuffer(Serializable identity) { + this.identity = identity; + initTransients(); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + initTransients(); + } + + private void initTransients() { + forBytesMarshaller = new ForBytesMarshaller(); + forBytesWriter = new ForBytesWriter(); + forDataValueWriter = new ForDataValueWriter(); + } + + Bytes obtain(long maxSize) { + DirectBytes buf; + if ((buf = buffer) != null) { + if (maxSize <= buf.capacity()) { + return buf.clear(); + } else { + DirectStore store = (DirectStore) buf.store(); + store.resize(maxSize, false); + return buffer = store.bytes(); + } + } else { + buffer = new DirectStore(JDKObjectSerializer.INSTANCE, maxSize, false).bytes(); + return buffer; + } + } + + @Override + public Object stateIdentity() { + return identity; + } + + @Override + public DirectBytesBuffer copy() { + return new DirectBytesBuffer(identity); + } + + class ForBytesMarshaller> + extends CopyingMetaBytesInterop { + private static final long serialVersionUID = 0L; + + private ForBytesMarshaller() { + super(DirectBytesBuffer.this); + } + + @Override + void innerWrite(M writer, Bytes bytes, E e) { + writer.write(bytes, e); + } + } + + class ForBytesWriter> + extends CopyingMetaBytesInterop { + private static final long serialVersionUID = 0L; + + private ForBytesWriter() { + super(DirectBytesBuffer.this); + } + + @Override + void innerWrite(W writer, Bytes bytes, E e) { + writer.write(bytes, e); + } + } + + class ForDataValueWriter extends DataValueMetaBytesInterop { + + private ForDataValueWriter() { + super(DirectBytesBuffer.this); + } + } +} diff --git a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java index 77ba08d36..e91bec845 100644 --- a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java @@ -108,7 +108,10 @@ final class SerializationBuilder implements Cloneable, Serializable { DataValueModel model = DataValueModels.acquireModel(eClass); int size = DataValueGenerator.computeNonScalarOffset(model, eClass); reader(reader); - writer(writer); + copyingInterop(null); + setInterop(writer); + metaInterop((MetaBytesInterop) DataValueMetaBytesInterop.forIdentity(role)); + metaInteropProvider(DataValueMetaBytesInterop.interopProvider(eClass)); sizeMarshaller(constant((long) size)); return; } catch (Exception e) { diff --git a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java index 9b7e5c26c..c664e2b44 100755 --- a/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java @@ -1483,7 +1483,7 @@ public class ChronicleMapTest { } - @Test(expected = IllegalStateException.class) + @Test public void testPutLongValue() throws IOException { final ChronicleMapBuilder builder = ChronicleMapBuilder .of(CharSequence.class, LongValue.class) @@ -1493,7 +1493,14 @@ public class ChronicleMapTest { final ChronicleMap map = builder.create(); LongValue value = nativeLongValue(); - map.put("x", value); + try { + map.put("x", value); + } catch (IllegalStateException | NullPointerException e) { + // ok + return; + } + throw new AssertionError("Should throw either IllegalStateException or " + + "NullPointerException, but succeed"); } @Test