All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.hash.serialization.internal.DataValueBytesMarshallers Maven / Gradle / Ivy

There is a newer version: 3.27ea0
Show newest version
/*
 *     Copyright (C) 2015  higherfrequencytrading.com
 *
 *     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 .
 */

package net.openhft.chronicle.hash.serialization.internal;

import net.openhft.chronicle.hash.serialization.BytesReader;
import net.openhft.chronicle.hash.serialization.BytesWriter;
import net.openhft.chronicle.hash.serialization.DeserializationFactoryConfigurableBytesReader;
import net.openhft.compiler.CompilerUtils;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.ObjectFactory;
import net.openhft.lang.model.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;

import static net.openhft.lang.model.DataValueGenerator.*;

public final class DataValueBytesMarshallers {

    private static final boolean dumpCode = Boolean.getBoolean("dvg.dumpCode");
    private static final Map readersClassMap =
            new ConcurrentHashMap<>();
    private static final Map readersWithCustomFactoriesClassMap =
            new ConcurrentHashMap<>();
    private static final Map writerClassMap =
            new ConcurrentHashMap<>();

    public static  BytesReader acquireBytesReader(Class tClass) {
        Class readerClass = acquireReaderClass(tClass);
        try {
            Field instanceField = readerClass.getDeclaredField("INSTANCE");
            Object reader = instanceField.get(null);
            return (BytesReader) reader;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    public static  BytesWriter acquireBytesWriter(Class tClass) {
        Class readerClass = acquireWriterClass(tClass);
        try {
            Field instanceField = readerClass.getDeclaredField("INSTANCE");
            Object reader = instanceField.get(null);
            return (BytesWriter) reader;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }


    public static  Class acquireReaderClass(Class tClass) {
        DataValueClasses.directClassFor(tClass);
        Class readerClass = readersClassMap.get(tClass);
        if (readerClass != null)
            return readerClass;
        DataValueModel dvmodel = DataValueModels.acquireModel(tClass);
        for (Class clazz : dvmodel.nestedModels()) {
            // touch them to make sure they are loaded.
            Class clazz2 = acquireReaderClass(clazz);
        }
        String actual = generateBytesReader(tClass);
        if (dumpCode)
            LoggerFactory.getLogger(DataValueGenerator.class).info(actual);
        ClassLoader classLoader = tClass.getClassLoader();
        String className = bytesReaderName(tClass, false);
        try {
            readerClass = classLoader.loadClass(className);
        } catch (ClassNotFoundException ignored) {
            try {
                readerClass = CompilerUtils.CACHED_COMPILER
                        .loadFromJava(classLoader, className, actual);
            } catch (ClassNotFoundException e) {
                throw new AssertionError(e);
            }
        }
        readersClassMap.put(tClass, readerClass);
        acquireReaderWithCustomFactory(tClass);
        return readerClass;
    }

    public static  Class acquireWriterClass(Class tClass) {
        Class writerClass = writerClassMap.get(tClass);
        if (writerClass != null)
            return writerClass;
        DataValueModel dvmodel = DataValueModels.acquireModel(tClass);
        for (Class clazz : dvmodel.nestedModels()) {
            // touch them to make sure they are loaded.
            Class clazz2 = acquireWriterClass(clazz);
        }
        String actual = generateBytesWriter(tClass);
        if (dumpCode)
            LoggerFactory.getLogger(DataValueGenerator.class).info(actual);
        ClassLoader classLoader = tClass.getClassLoader();
        String className = bytesWriterName(tClass, false);
        try {
            writerClass = classLoader.loadClass(className);
        } catch (ClassNotFoundException ignored) {
            try {
                writerClass = CompilerUtils.CACHED_COMPILER
                        .loadFromJava(classLoader, className, actual);
            } catch (ClassNotFoundException e) {
                throw new AssertionError(e);
            }
        }
        writerClassMap.put(tClass, writerClass);
        return writerClass;
    }

    public static  Class acquireReaderWithCustomFactory(Class tClass) {
        Class c = readersWithCustomFactoriesClassMap.get(tClass);
        if (c != null)
            return c;
        acquireReaderClass(tClass);
        String actual = generateWithCustomFactoryClass(tClass);
        if (dumpCode)
            LoggerFactory.getLogger(DataValueGenerator.class).info(actual);
        ClassLoader classLoader = tClass.getClassLoader();
        String className = withCustomFactoryName(tClass);
        try {
            c = classLoader.loadClass(className);
        } catch (ClassNotFoundException ignored) {
            try {
                c = CompilerUtils.CACHED_COMPILER
                        .loadFromJava(classLoader, className, actual);
            } catch (ClassNotFoundException e) {
                throw new AssertionError(e);
            }
        }
        readersWithCustomFactoriesClassMap.put(tClass, c);
        return c;
    }

    private static  String withCustomFactoryName(Class tClass) {
        return bytesReaderName(tClass, false) + "$WithCustomFactory";
    }

    private static String generateWithCustomFactoryClass(Class tClass) {
        DataValueModel dvModel = DataValueModels.acquireModel(tClass);

        SortedSet imported = newImported();
        imported.add(ObjectFactory.class);
        imported.add(NotNull.class);
        imported.add(dvModel.type());

        String simpleName = tClass.getSimpleName();

        String bytesReaderName = bytesReaderName(simpleName(tClass));
        String name = bytesReaderName + "$WithCustomFactory";
        String withCustomFactoryClass =
                "public final class " + name + " extends " + bytesReaderName + " {\n" +
                        "    private static final long serialVersionUID = 0L;\n" +
                        "\n" +
                        "    @NotNull\n" +
                        "    private final ObjectFactory<" + simpleName + "> factory;\n" +
                        "\n" +
                        "    " + name + "(@NotNull ObjectFactory<" +
                        simpleName + "> factory) {\n" +
                        "        this.factory = factory;\n" +
                        "    }\n" +
                        "\n" +
                        "    @Override\n" +
                        "    " + simpleName + " getInstance() throws Exception {\n" +
                        "        return factory.create();\n" +
                        "    }\n" +
                        "}\n";

        StringBuilder sb = new StringBuilder();
        appendPackage(dvModel, sb);
        appendImported(imported, sb);
        sb.append(withCustomFactoryClass);
        return sb.toString();
    }


    private static String bytesReaderName(Class type, boolean simple) {
        return (simple ? type.getSimpleName() : type.getName()) + "$$BytesReader";
    }

    private static String bytesReaderName(String className) {
        return className + "$$BytesReader";
    }

    public static String bytesWriterName(Class type, boolean simple) {
        return (simple ? simpleName(type) : type.getName()) + "$$BytesWriter";
    }

    private static String generateBytesReader(Class tClass) {
        DataValueModel dvModel = DataValueModels.acquireModel(tClass);

        SortedSet imported = newImported();
        imported.add(BytesReader.class);
        imported.add(DeserializationFactoryConfigurableBytesReader.class);
        imported.add(ObjectFactory.class);
        imported.add(Bytes.class);
        imported.add(ByteableMarshaller.class);
        imported.add(Byteable.class);
        imported.add(dvModel.type());
        imported.add(InvocationTargetException.class);
        imported.add(Constructor.class);

        StringBuilder read = generateReadBody(dvModel, imported);

        StringBuilder sb = new StringBuilder();
        appendPackage(dvModel, sb);
        appendImported(imported, sb);
        String simpleName = tClass.getSimpleName();
        String bytesReaderName = bytesReaderName(tClass, false);
        String simpleReaderName = bytesReaderName(simpleName(tClass));
        sb.append("\n@SuppressWarnings(\"unchecked\") public class ").append(simpleReaderName)
                .append(" implements DeserializationFactoryConfigurableBytesReader<")
                .append(simpleName).append(", ").append(simpleReaderName).append("> {\n");
        declareSerialVersionUID(sb);
        declareStaticInstance(sb, tClass);
        generatePrivateConstructor(sb, simpleReaderName);
        generateWithDeserializationFactory(tClass, dvModel, sb, simpleName, simpleReaderName);
        generateGetInstance(sb, tClass);
        generateDelegatingRead(sb, simpleName);
        generateRead(read, sb, simpleName, tClass);
        sb.append("}\n");
        return sb.toString();
    }

    private static void generateWithDeserializationFactory(Class tClass,
                                                           DataValueModel dvModel,
                                                           StringBuilder sb, String simpleName,
                                                           String simpleReaderName) {
        appendOverride(sb);
        sb.append("    public ").append(simpleReaderName)
                .append(" withDeserializationFactory(ObjectFactory<").append(simpleName)
                .append("> factory) {\n");
        sb.append("        try {\n");
        sb.append("            ")
                .append("Class cfc = Class.forName(\"")
                .append(getPackage(dvModel))
                .append(".").append(withCustomFactoryName(tClass)).append("\");\n");
        sb.append(
                "            Constructor constructor = cfc.getConstructor(ObjectFactory.class);\n" +
                        "            Object reader = constructor.newInstance(factory);\n" +
                        "            return (" + simpleReaderName + ") reader;\n" +
                        "        } catch (ClassNotFoundException | IllegalAccessException | " +
                        "NoSuchMethodException | InstantiationException |\n" +
                        "                InvocationTargetException e) {\n" +
                        "            throw new AssertionError(e);\n" +
                        "        }\n");
        sb.append("    }\n\n");
    }

    private static StringBuilder generateReadBody(DataValueModel dvModel,
                                                  SortedSet imported) {
        StringBuilder read = new StringBuilder();

        Map.Entry[] entries =
                DataValueGenerator.heapSizeOrderedFieldsGrouped(dvModel);
        for (Map.Entry entry : entries) {
            String name = entry.getKey();
            FieldModel model = entry.getValue();
            Class type = model.type();
            if (shouldImport(type))
                imported.add(type);

            Method setter = getSetter(model);
            Method getter = getGetter(model);

            Method orderedSetter = getOrderedSetter(model);
            Method volatileGetter = getVolatileGetter(model);

            Method defaultSetter = setter != null ? setter : orderedSetter;
            Method defaultGetter = getter != null ? getter : volatileGetter;
            if (!dvModel.isScalar(type)) {
                if (model.isArray()) {
                    read.append("        for (int i = 0; i < ")
                            .append(model.indexSize().value()).append("; i++) {\n");
                    read.append("            toReuse.")
                            .append(defaultSetter.getName()).append("(i, ")
                            .append(bytesReaderName(type, false))
                            .append(".INSTANCE").append(".read(bytes, ")
                            .append(computeNonScalarOffset(dvModel, type)).append(", ")
                            .append("toReuse.").append(defaultGetter.getName()).append("(i)));\n");
                    read.append("        }\n");
                } else {
                    read.append("        toReuse.")
                            .append(defaultSetter.getName()).append("(")
                            .append(bytesReaderName(type, false))
                            .append(".INSTANCE").append(".read(bytes, ")
                            .append(computeNonScalarOffset(dvModel, type)).append(", ")
                            .append("toReuse.").append(defaultGetter.getName()).append("()));\n");
                }
            } else {
                if (model.isArray()) {
                    read.append("        for (int i = 0; i < ")
                            .append(model.indexSize().value()).append("; i++) {\n");
                    saveCharSequencePosition(read, type);
                    read.append("            toReuse.").append(defaultSetter.getName())
                            .append("(i, bytes.read").append(bytesType(type)).append("());\n");
                    updateCharSequencePosition(read, model, type);
                    read.append("        }\n");
                } else {
                    read.append("        {\n");
                    saveCharSequencePosition(read, type);
                    read.append("            toReuse.").append(defaultSetter.getName())
                            .append("(bytes.read").append(bytesType(type)).append("());\n");
                    updateCharSequencePosition(read, model, type);
                    read.append("        }\n");
                }
            }
        }
        return read;
    }

    private static void updateCharSequencePosition(StringBuilder read, FieldModel model,
                                                   Class type) {
        if (CharSequence.class.isAssignableFrom(type))
            read.append("            bytes.position(pos + ")
                    .append(fieldSize(model)).append(");\n");
    }

    private static String generateBytesWriter(Class tClass) {
        DataValueModel dvModel = DataValueModels.acquireModel(tClass);

        SortedSet imported = newImported();
        imported.add(BytesWriter.class);
        imported.add(Bytes.class);
        imported.add(Byteable.class);
        imported.add(dvModel.type());

        String write = generateWriteBody(dvModel, imported);

        StringBuilder sb = new StringBuilder();
        appendPackage(dvModel, sb);
        appendImported(imported, sb);
        sb.append("public enum ").append(bytesWriterName(tClass, true))
                .append(" implements BytesWriter<").append(tClass.getSimpleName()).append("> {\n");
        sb.append("    INSTANCE;\n\n");
        generateSize(tClass, dvModel, sb);
        generateWrite(tClass, dvModel, write, sb);
        generateShouldNotBeNull(tClass, sb);
        sb.append("}\n");
        return sb.toString();
    }

    private static void generateShouldNotBeNull(Class tClass, StringBuilder sb) {
        sb.append("    private static void shouldNotBeNull() {\n");
        sb.append("        throw new NullPointerException(\"Sub-members of " +
                tClass.getSimpleName() + " shouldn't be null for this writer. You should specify" +
                " custom writer (e. g. using keyMarshallers()/valueMarshallers() methods of" +
                "ChronicleMapBuilder, if you want to support null fields/array elements.\");\n");
        sb.append("    }\n");
    }

    private static void generateWrite(Class tClass, DataValueModel dvModel, String write,
                                      StringBuilder sb) {
        appendOverride(sb);
        sb.append("    public void write(Bytes bytes, ")
                .append(tClass.getSimpleName()).append(" e) {\n");
        int size = computeNonScalarOffset(dvModel, tClass);
        if (size > 16) {
            sb.append("        if (e instanceof Byteable) {\n");
            sb.append(
                    "            Bytes eBytes = ((Byteable) e).bytes();\n" +
                            "            if (eBytes != null) {\n" +
                            "                bytes.write(eBytes, ((Byteable) e).offset(), " +
                            size + ");\n" +
                            "            } else {\n" +
                            "                throw new NullPointerException(" +
                            "\"You are trying to write a byteable object of \" +\n" +
                            "                        e.getClass() + \", \" +\n" +
                            "                        \"which bytes are not assigned. I. e. most likely " +
                            "the object is uninitialized.\");\n" +
                            "            }\n");
            sb.append("            return;\n");
            sb.append("        }\n");
        }
        sb.append(write);
        sb.append("    }\n\n");
    }

    private static void generateSize(Class tClass, DataValueModel dvModel, StringBuilder sb) {
        appendOverride(sb);
        sb.append("    public long size(").append(tClass.getSimpleName()).append(" e) {\n");
        sb.append("        return ").append(computeNonScalarOffset(dvModel, tClass)).append(";\n");
        sb.append("    }\n\n");
    }

    private static String generateWriteBody(DataValueModel dvModel, SortedSet imported) {
        StringBuilder write = new StringBuilder();

        Map.Entry[] entries =
                heapSizeOrderedFieldsGrouped(dvModel);
        for (Map.Entry entry : entries) {
            String name = entry.getKey();
            FieldModel model = entry.getValue();
            Class type = model.type();
            if (shouldImport(type))
                imported.add(type);

            Method setter = getSetter(model);
            Method getter = getGetter(model);

            Method orderedSetter = getOrderedSetter(model);
            Method volatileGetter = getVolatileGetter(model);

            Method defaultSetter = setter != null ? setter : orderedSetter;
            Method defaultGetter = getter != null ? getter : volatileGetter;
            if (!dvModel.isScalar(type)) {
                if (model.isArray()) {
                    write.append("        for (int i = 0; i < ")
                            .append(model.indexSize().value()).append("; i++) {\n");
                    write.append("            ")
                            .append(normalize(type)).append(" $ = e.")
                            .append(defaultGetter.getName()).append("(i);\n");
                    write.append("            ")
                            .append("if ($ == null) shouldNotBeNull();\n");
                    write.append("            ")
                            .append(bytesWriterName(type, false))
                            .append(".INSTANCE").append(".write(bytes, $);\n");
                    write.append("        }\n");
                } else {
                    write.append("        {")
                            .append(normalize(type)).append(" $ = e.")
                            .append(defaultGetter.getName()).append("();\n");
                    write.append("        ")
                            .append("if ($ == null) shouldNotBeNull();\n");
                    write.append("        ")
                            .append(bytesWriterName(type, false))
                            .append(".INSTANCE").append(".write(bytes, $);}\n");
                }
            } else {
                if (model.isArray()) {
                    write.append("        for (int i = 0; i < ")
                            .append(model.indexSize().value()).append("; i++) {\n");
                    saveCharSequencePosition(write, type);
                    write.append("            bytes.write").append(bytesType(type))
                            .append("(e.").append(defaultGetter.getName()).append("(i));\n");
                    zeroOutRemainingCharSequenceBytesAndUpdatePosition(write, model, type);
                    write.append("        }\n");
                } else {
                    write.append("        {\n");
                    saveCharSequencePosition(write, type);
                    write.append("            bytes.write").append(bytesType(type))
                            .append("(e.").append(defaultGetter.getName()).append("());\n");
                    zeroOutRemainingCharSequenceBytesAndUpdatePosition(write, model, type);
                    write.append("        }\n");
                }
            }
        }
        return write.toString();
    }

    private static void saveCharSequencePosition(StringBuilder write, Class type) {
        if (CharSequence.class.isAssignableFrom(type))
            write.append("            long pos = bytes.position();\n");
    }

    private static void zeroOutRemainingCharSequenceBytesAndUpdatePosition(
            StringBuilder write, FieldModel model, Class type) {
        if (CharSequence.class.isAssignableFrom(type)) {
            write.append("            long newPos = pos + ").append(fieldSize(model))
                    .append(";\n");
            write.append("            bytes.zeroOut(bytes.position(), newPos);\n");
            write.append("            bytes.position(newPos);\n");
        }
    }

    private static void generateRead(StringBuilder read, StringBuilder sb, String simpleName,
                                     Class cl) {
        appendOverride(sb);
        sb.append("    public ").append(simpleName).append(" read(Bytes bytes, long size, ")
                .append(simpleName).append(" toReuse) {\n");
        sb.append("    try {\n");
        sb.append("        if (toReuse == null)\n");
        sb.append("            toReuse = getInstance();\n");
        sb.append("        if (toReuse instanceof Byteable) {\n");
        sb.append("            " +
                "ByteableMarshaller.setBytesAndOffset(((Byteable) toReuse), bytes);\n");
        sb.append("            bytes.skip(size);\n");
        sb.append("            return toReuse;\n");
        sb.append("        }\n");
        sb.append(read);
        sb.append("        return toReuse;\n");
        sb.append("    } catch (Exception e) {\n");
        sb.append("        throw new IllegalStateException(e);\n");
        sb.append("    }\n");
        sb.append("}\n\n");
    }

    private static void generateDelegatingRead(StringBuilder sb, String simpleName) {
        appendOverride(sb);
        sb.append("    public ").append(simpleName).append(" read(Bytes bytes, long size) {\n");
        sb.append("        return read(bytes, size, null);\n");
        sb.append("    }\n\n");
    }

    private static void appendOverride(StringBuilder sb) {
        sb.append("    @Override\n");
    }

    private static void generatePrivateConstructor(StringBuilder sb, String bytesReaderName) {
        sb.append("    ").append(bytesReaderName).append("() {}\n\n");
    }

    private static void declareStaticInstance(StringBuilder sb, Class cl) {
        String bytesReaderName = bytesReaderName(simpleName(cl));
        sb.append("    public static final ").append(bytesReaderName).append(" INSTANCE = new ")
                .append(bytesReaderName).append("();\n\n");
    }

    private static StringBuilder declareSerialVersionUID(StringBuilder sb) {
        return sb.append("    private static final long serialVersionUID = 0L;\n\n");
    }

    private static void generateGetInstance(StringBuilder sb, Class cl) {
        sb.append("    ").append(cl.getCanonicalName())
                .append(" getInstance() throws Exception {\n");
        sb.append("        ").append("return new ").append(cl.getName()).append("$$Native();\n");
        sb.append("    ").append("}\n\n");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy