/* * Copyright (C) 2012, 2016 higherfrequencytrading.com * Copyright (C) 2016 Roman Leventov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.chronicle.hash.serialization.impl; import net.openhft.chronicle.bytes.Byteable; import net.openhft.chronicle.bytes.BytesMarshallable; import net.openhft.chronicle.core.OS; import net.openhft.chronicle.core.util.ReadResolvable; import net.openhft.chronicle.hash.serialization.*; import net.openhft.chronicle.values.ValueModel; import net.openhft.chronicle.values.Values; import net.openhft.chronicle.wire.Marshallable; import java.io.Externalizable; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import static net.openhft.chronicle.hash.serialization.SizeMarshaller.constant; import static net.openhft.chronicle.hash.serialization.SizeMarshaller.stopBit; public final class SerializationBuilder<T> implements Cloneable { public final Class<T> tClass; public final boolean sizeIsStaticallyKnown; private SizeMarshaller sizeMarshaller = stopBit(); private SizedReader<T> reader; private DataAccess<T> dataAccess; @SuppressWarnings("unchecked") public SerializationBuilder(Class<T> tClass) { this.tClass = tClass; configureByDefault(tClass); sizeIsStaticallyKnown = constantSizeMarshaller(); } private static boolean concreteClass(Class c) { return !c.isInterface() && !Modifier.isAbstract(c.getModifiers()); } private static void checkNonMarshallableEnum(Class c) { if (Enum.class.isAssignableFrom(c) && (Marshallable.class.isAssignableFrom(c) || ReadResolvable.class.isAssignableFrom(c))) { throw new IllegalArgumentException(c + ": since Chronicle Map 3.9.0, enum marshaller " + "shouldn't be a Java enum and implement " + Marshallable.class.getName() + " or " + ReadResolvable.class.getName() + ". There are problems with " + "serializing/deserializing them in Chronicle Map header. Emulate enums by " + "static final fields"); } } @SuppressWarnings("unchecked") private void configureByDefault(Class<T> tClass) { if (tClass.isPrimitive()) { throw new IllegalArgumentException( "Chronicle Map's key or value type cannot be primitive, " + tClass + " type is given"); } if (tClass.isInterface() && Values.isValueInterfaceOrImplClass(tClass)) { try { // Acquire a model before assigning readers/writers // if the interface is not a value interface ValueModel valueModel = ValueModel.acquire(tClass); reader((BytesReader<T>) new ValueReader<>(tClass)); dataAccess(new ValueDataAccess<>(tClass)); sizeMarshaller(constant((long) valueModel.sizeInBytes())); return; } catch (Exception e) { try { tClass = Values.nativeClassFor(tClass); } catch (Exception ex) { // ignore, fall through } // ignore, fall through } } if (concreteClass(tClass) && Byteable.class.isAssignableFrom(tClass)) { reader(new ByteableSizedReader<>((Class) tClass)); dataAccess((DataAccess<T>) new ByteableDataAccess<>((Class) tClass)); try { long byteableSize = ((Byteable) OS.memory().allocateInstance(tClass)).maxSize(); sizeMarshaller(constant(byteableSize)); } catch (InstantiationException e) { throw new IllegalStateException(e); } } else if (tClass == CharSequence.class) { reader((SizedReader<T>) CharSequenceSizedReader.INSTANCE); dataAccess((DataAccess<T>) new CharSequenceUtf8DataAccess()); } else if (tClass == StringBuilder.class) { reader((SizedReader<T>) StringBuilderSizedReader.INSTANCE); dataAccess((DataAccess<T>) new StringBuilderUtf8DataAccess()); } else if (tClass == String.class) { reader((SizedReader<T>) new StringSizedReader()); dataAccess((DataAccess<T>) new StringUtf8DataAccess()); } else if (tClass == Boolean.class) { reader((SizedReader<T>) BooleanMarshaller.INSTANCE); notReusingWriter((SizedWriter<T>) BooleanMarshaller.INSTANCE); sizeMarshaller(constant(1)); } else if (tClass == Long.class) { reader((SizedReader<T>) LongMarshaller.INSTANCE); dataAccess((DataAccess<T>) new LongDataAccess()); sizeMarshaller(constant(8)); } else if (tClass == Double.class) { reader((SizedReader<T>) DoubleMarshaller.INSTANCE); dataAccess((DataAccess<T>) new DoubleDataAccess()); sizeMarshaller(constant(8)); } else if (tClass == Integer.class) { reader((SizedReader<T>) IntegerMarshaller.INSTANCE); dataAccess((DataAccess<T>) new IntegerDataAccess_3_13()); sizeMarshaller(constant(4)); } else if (tClass == byte[].class) { reader((SizedReader<T>) ByteArraySizedReader.INSTANCE); dataAccess((DataAccess<T>) new ByteArrayDataAccess()); } else if (tClass == ByteBuffer.class) { reader((SizedReader<T>) ByteBufferSizedReader.INSTANCE); dataAccess((DataAccess<T>) new ByteBufferDataAccess()); } else if (concreteClass(tClass) && BytesMarshallable.class.isAssignableFrom(tClass)) { reader((BytesReader<T>) new BytesMarshallableReader<>((Class) tClass)); dataAccess(new BytesMarshallableDataAccess<>((Class) tClass)); } else if (concreteClass(tClass) && Externalizable.class.isAssignableFrom(tClass)) { reader((BytesReader<T>) new ExternalizableReader<>((Class) tClass)); dataAccess(new ExternalizableDataAccess<>((Class) tClass)); } else { reader((SizedReader<T>) new SerializableReader<>()); dataAccess((DataAccess<T>) new SerializableDataAccess<>()); } } public void reader(SizedReader<T> reader) { checkNonMarshallableEnum(reader.getClass()); this.reader = reader; } public void reader(BytesReader<T> reader) { checkNonMarshallableEnum(reader.getClass()); this.reader = new BytesAsSizedReader<>(reader); } public SizedReader<T> reader() { return StatefulCopyable.copyIfNeeded(reader); } public void dataAccess(DataAccess<T> dataAccess) { checkNonMarshallableEnum(dataAccess.getClass()); this.dataAccess = dataAccess; } public DataAccess<T> dataAccess() { return dataAccess.copy(); } public void writer(SizedWriter<? super T> writer) { checkNonMarshallableEnum(writer.getClass()); dataAccess(new SizedMarshallableDataAccess<>(tClass, reader, writer)); } private void notReusingWriter(SizedWriter<? super T> writer) { checkNonMarshallableEnum(writer.getClass()); dataAccess(new NotReusingSizedMarshallableDataAccess<>(tClass, reader, writer)); } public void writer(BytesWriter<? super T> writer) { checkNonMarshallableEnum(writer.getClass()); dataAccess(new ExternalBytesMarshallableDataAccess<>(tClass, reader, writer)); } public long serializationSize(T sampleObject) { return dataAccess().getData(sampleObject).size(); } public long constantSizeBySample(T sampleObject) { long constantSize = serializationSize(sampleObject); if (constantSizeMarshaller()) { long expectedConstantSize = constantSize(); if (constantSize != expectedConstantSize) { throw new IllegalStateException("Although configuring constant size by sample " + "is not forbidden for types which size we already know statically, they " + "should be the same. For " + tClass + " we know constant size is " + expectedConstantSize + " statically, configured sample is " + sampleObject + " which size in serialized form is " + constantSize); } } sizeMarshaller(constant(constantSize)); return constantSize; } public SizeMarshaller sizeMarshaller() { return sizeMarshaller; } public boolean constantSizeMarshaller() { SizeMarshaller marshaller = sizeMarshaller(); boolean feature1 = marshaller.storingLength(marshaller.maxStorableSize()) == 0; boolean feature2 = marshaller.minStorableSize() == marshaller.maxStorableSize(); if (feature1 && feature2) return true; if (!feature1 && !feature2) return false; throw new IllegalStateException("SizeMarshaller " + marshaller + " has only 1 of 2 " + "constant features: storingLength == 0 and minStorableSize == maxStorableSize." + " Should have both"); } public boolean constantStoringLengthSizeMarshaller() { SizeMarshaller marshaller = sizeMarshaller(); long minStorableSize = marshaller.minStorableSize(); long maxStorableSize = marshaller.maxStorableSize(); return marshaller.minStoringLengthOfSizesInRange(minStorableSize, maxStorableSize) == marshaller.maxStoringLengthOfSizesInRange(minStorableSize, maxStorableSize); } public long constantSize() { if (sizeMarshaller().minStorableSize() != sizeMarshaller().maxStorableSize()) throw new AssertionError(); return sizeMarshaller().minStorableSize(); } public SerializationBuilder<T> sizeMarshaller(SizeMarshaller sizeMarshaller) { checkNonMarshallableEnum(sizeMarshaller.getClass()); this.sizeMarshaller = sizeMarshaller; return this; } @Override public SerializationBuilder<T> clone() { try { //noinspection unchecked return (SerializationBuilder<T>) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } }