/* * Copyright 2016 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.lang.model; import net.openhft.compiler.CompilerUtils; import net.openhft.lang.Compare; import net.openhft.lang.Maths; import net.openhft.lang.MemoryUnit; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshallable; import net.openhft.lang.model.constraints.Group; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static net.openhft.lang.MemoryUnit.BYTES; import static net.openhft.lang.model.DataValueModelImpl.heapSize; public class DataValueGenerator { private static final Comparator<Class> COMPARATOR = new Comparator<Class>() { @Override public int compare(Class o1, Class o2) { return o1.getName().compareTo(o2.getName()); } }; private static final Comparator<Map.Entry<String, FieldModel>> COMPARE_BY_GROUP_THEN_HEAP_SIZE = new Comparator<Map.Entry<String, FieldModel>>() { @Override public int compare(Map.Entry<String, FieldModel> o1, Map.Entry<String, FieldModel> o2) { // descending FieldModel model1 = o1.getValue(); FieldModel model2 = o2.getValue(); Group group1 = model1.group(); Group group2 = model2.group(); int group = Integer.compare( group1 == null ? Integer.MIN_VALUE : model1.group().value(), group2 == null ? Integer.MIN_VALUE : model2.group().value()); if (group != 0) return group; int cmp = -Maths.compare(model1.heapSize(), model2.heapSize()); if (cmp != 0) return cmp; Class firstPrimitiveFieldType1 = null; if (!model1.type().isPrimitive() && !CharSequence.class.isAssignableFrom(model1.type())) { firstPrimitiveFieldType1 = firstPrimitiveFieldType(model1.type()); } Class firstPrimitiveFieldType2 = null; if (!model2.type().isPrimitive() && !CharSequence.class.isAssignableFrom(model2.type())) { firstPrimitiveFieldType2 = firstPrimitiveFieldType(model2.type()); } if (firstPrimitiveFieldType1 != null && firstPrimitiveFieldType2 == null) return -1; if (firstPrimitiveFieldType1 == null && firstPrimitiveFieldType2 != null) return 1; if (firstPrimitiveFieldType1 != null && firstPrimitiveFieldType2 != null) { return -Maths.compare(heapSize(firstPrimitiveFieldType1), heapSize(firstPrimitiveFieldType2)); } return o1.getKey().compareTo(o2.getKey()); } }; private final Map<Class, Class> heapClassMap = new ConcurrentHashMap<Class, Class>(); private final Map<Class, Class> nativeClassMap = new ConcurrentHashMap<Class, Class>(); private boolean dumpCode = Boolean.getBoolean("dvg.dumpCode"); public static Class firstPrimitiveFieldType(Class valueClass) { if (valueClass.getClassLoader() == null) return null; try { DataValueModel valueModel; if (valueClass.isInterface()) { valueModel = DataValueModels.acquireModel(valueClass); } else { String valueClassName = valueClass.getName(); String $$Native = "$$Native"; if (valueClassName.endsWith($$Native)) { valueClassName = valueClassName.substring(0, valueClassName.length() - $$Native.length()); valueModel = DataValueModels.acquireModel(Class.forName(valueClassName)); } else { return null; } } Map.Entry<String, FieldModel>[] fields = DataValueGenerator.heapSizeOrderedFieldsGrouped(valueModel); if (fields.length == 0) return null; Class firstFieldType = fields[0].getValue().type(); if (firstFieldType.isPrimitive()) return firstFieldType; return firstPrimitiveFieldType(firstFieldType); } catch (Exception e) { return null; } } public static String bytesType(Class type) { if (type.isPrimitive()) return Character.toUpperCase(type.getName().charAt(0)) + type.getName().substring(1); if (CharSequence.class.isAssignableFrom(type)) return "UTFΔ"; if (Enum.class.isAssignableFrom(type)) return "Enum"; return "Object"; } static String generateHeapObject(DataValueModel<?> dvmodel) { SortedSet<Class> imported = newImported(); imported.add(BytesMarshallable.class); imported.add(Bytes.class); imported.add(IOException.class); imported.add(Copyable.class); imported.add(dvmodel.type()); StringBuilder fieldDeclarations = new StringBuilder(); StringBuilder getterSetters = new StringBuilder(); StringBuilder writeMarshal = new StringBuilder(); StringBuilder readMarshal = new StringBuilder(); StringBuilder copy = new StringBuilder(); Map.Entry<String, FieldModel>[] entries = heapSizeOrderedFieldsGrouped(dvmodel); for (Map.Entry<String, ? extends FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); Class type = model.type(); if (shouldImport(type)) imported.add(type); heapFieldDeclarations(fieldDeclarations, type, name, model); Method setter = getSetter(model); Method getter = getGetter(model); Method getUsing = getUsing(model); boolean bothVolatileAndPlain = false; final Method orderedSetter = getOrderedSetter(model); final Method volatileGetter = getVolatileGetter(model); if (getter != null && volatileGetter != null) { bothVolatileAndPlain = true; } if (setter == null && orderedSetter != null) { setter = orderedSetter; } if (getter == null && volatileGetter != null) { getter = volatileGetter; } if (setter == null) { if (getter != null) copy.append(" ((Copyable) ").append(getter.getName()).append("()).copyFrom(from.").append(getter.getName()).append("());\n"); } else { methodCopy(copy, getter, setter, model); methodHeapSet(getterSetters, setter, name, type, model); } if (getter != null) methodHeapGet(getterSetters, getter, name, type, model); if (getUsing != null && type == String.class && !model.isArray()) { methodHeapGetUsingWithStringBuilder(getterSetters, getUsing, name, type, model); // we have to add in the getter method as its required for the equals() and hashCode() if (getter == null && volatileGetter == null) { String getterName = getterName(getUsing); methodHeapGet(getterSetters, name, type, getterName); } } //In the case where there are both volatile and plain gets and sets they need to be written here //If there is just a volatile get and set it would have been written above. if (bothVolatileAndPlain) { methodHeapGet(getterSetters, volatileGetter, name, type, model); methodHeapSet(getterSetters, orderedSetter, name, type, model); } Method adder = model.adder(); if (adder != null) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(adder.getName()) .append("(").append(adder.getParameterTypes()[0].getName()).append(" $) {\n") .append(" return _").append(name).append(" += $;\n") .append(" }"); } Method sizeOf = model.sizeOf(); if (sizeOf != null) { getterSetters.append(" public int ").append(sizeOf.getName()) .append("() {\n") .append(" return ").append(model.indexSize().value()).append(";\n") .append(" }\n\n"); } Method atomicAdder = model.atomicAdder(); if (atomicAdder != null) { getterSetters.append(" public synchronized ").append(normalize(type)).append(' ').append(atomicAdder.getName()) .append("(").append(atomicAdder.getParameterTypes()[0].getName()).append(" $) {\n") .append(" return _").append(name).append(" += $;\n") .append(" }\n\n"); } Method cas = model.cas(); if (cas != null) { getterSetters.append(" public synchronized boolean ").append(cas.getName()).append("(") .append(normalize(type)).append(" _1, ") .append(normalize(type)).append(" _2) {\n") .append(" if (_").append(name).append(" == _1) {\n") .append(" _").append(name).append(" = _2;\n") .append(" return true;\n") .append(" }\n") .append(" return false;\n") .append(" }\n"); } Method tryLockNanos = model.tryLockNanos(); if (tryLockNanos != null) { getterSetters.append(" public boolean ").append(tryLockNanos.getName()).append("(long nanos) {\n") .append(" throw new UnsupportedOperationException();\n") .append(" }"); } Method tryLock = model.tryLock(); if (tryLock != null) { getterSetters.append(" public boolean ").append(tryLock.getName()).append("() {\n") .append(" throw new UnsupportedOperationException();\n") .append(" }"); } Method unlock = model.unlock(); if (unlock != null) { getterSetters.append(" public void ").append(unlock.getName()).append("() {\n") .append(" throw new UnsupportedOperationException();\n") .append(" }"); } Method busyLock = model.busyLock(); if (busyLock != null) { getterSetters.append(" public void ").append(busyLock.getName()).append("() {\n") .append(" throw new UnsupportedOperationException();\n") .append(" }"); } methodWriteMarshall(writeMarshal, getter, setter, type, model); methodHeapReadMarshall(readMarshal, name, type, model); } StringBuilder sb = new StringBuilder(); appendPackage(dvmodel, sb); sb.append("import static ").append(Compare.class.getName()).append(".*;\n"); for (Class aClass : imported) { sb.append("import ").append(normalize(aClass)).append(";\n"); } String className = simpleName(dvmodel.type()); sb.append("\npublic class ").append(className) .append("$$Heap implements ").append(dvmodel.type().getSimpleName()) .append(", BytesMarshallable, Copyable<").append(normalize(dvmodel.type())).append("> {\n"); sb.append(fieldDeclarations).append('\n'); sb.append(getterSetters); sb.append(" @SuppressWarnings(\"unchecked\")\n" + " public void copyFrom(").append(normalize(dvmodel.type())).append(" from) {\n"); sb.append(copy); sb.append(" }\n\n"); sb.append(" public void writeMarshallable(Bytes out) {\n"); sb.append(writeMarshal); sb.append(" }\n"); sb.append(" public void readMarshallable(Bytes in) {\n"); sb.append(readMarshal); sb.append(" }\n"); if (Byteable.class.isAssignableFrom(dvmodel.type())) { sb.append(" public void bytes(Bytes bytes, long l) {\n"); sb.append(" throw new UnsupportedOperationException();\n"); sb.append(" }\n"); sb.append(" public Bytes bytes() {\n"); sb.append(" return null;\n"); sb.append(" }\n"); sb.append(" public long offset() {\n"); sb.append(" return 0L;\n"); sb.append(" }\n"); sb.append(" public int maxSize() {\n"); sb.append(" throw new UnsupportedOperationException();\n"); sb.append(" }\n"); } generateObjectMethods(sb, dvmodel, entries, false); sb.append("}\n"); // System.out.println(sb); return sb.toString(); } public static String simpleName(Class<?> type) { String name = type.getName(); return name.substring(name.lastIndexOf('.') + 1); } public static CharSequence normalize(Class aClass) { return aClass.getName().replace('$', '.'); } private static void generateObjectMethods(StringBuilder sb, DataValueModel<?> dvmodel, Map.Entry<String, FieldModel>[] entries, boolean offHeap) { int count = 0; StringBuilder hashCode = new StringBuilder(); StringBuilder equals = new StringBuilder(); StringBuilder equalsGetUsing = new StringBuilder(); StringBuilder toStringGetUsing = new StringBuilder(); StringBuilder getUsingEquals = new StringBuilder(); StringBuilder toString = new StringBuilder(); for (Map.Entry<String, FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); Method getter = getGetter(model); Method getUsing = getUsing(model); if (getter == null) getter = getVolatileGetter(model); if (getter != null || getUsing != null) { String getterName = (getter == null) ? getterName(getUsing) : getter.getName(); methodLongHashCode(hashCode, getterName, model, count); if (getter != null) { methodEquals(equals, getterName, model, simpleName(dvmodel.type())); methodToString(toString, getterName, name, model); } else { methodEqualsGetUsing(getUsingEquals, getUsing.getName()); methodToStringGetUsing(toStringGetUsing, getUsing.getName(), name, model); } count++; } Bytes b; if (model.isArray()) { String nameWithUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); if (model.isVolatile()) nameWithUpper = "Volatile" + nameWithUpper; sb.append("\n public long longHashCode_" + name + "() {\n" + " long hc = 0;\n" + " for (int i = 0; i < " + model.indexSize().value() + "; i++) {\n" + " hc += calcLongHashCode(get" + nameWithUpper + "At(i));\n" + " }\n" + " return hc;\n" + " }\n\n"); } } sb.append(" public int hashCode() {\n" + " long lhc = longHashCode();\n" + " return (int) ((lhc >>> 32) ^ lhc);\n" + " }\n" + "\n" + " public long longHashCode() {\n" + " return "); for (int i = 1; i < count; i++) sb.append('('); sb.append(hashCode); CharSequence simpleName = simpleName(dvmodel.type()).replace('$', '.'); sb.append(";\n") .append(" }\n") .append("\n"); sb.append(" public boolean equals(Object o) {\n") .append(" if (this == o) return true;\n") .append(" if (!(o instanceof ").append(simpleName).append(")) return false;\n") .append(" ").append(simpleName).append(" that = (").append(simpleName).append(") o;\n") .append("\n") .append(equals) .append(equalsGetUsing) .append(" return true;\n") .append(" }\n") .append("\n"); sb.append(" public String toString() {\n") .append(offHeap ? " if (_bytes == null) return \"bytes is null\";\n" : "") .append(" StringBuilder sb = new StringBuilder();\n") .append(" sb.append(\"").append(simpleName).append("{ \");\n") .append(toString) .append(toStringGetUsing) .append(" sb.append(\" }\");\n") .append(" return sb.toString();\n") .append(" }\n"); } private static Method getUsing(FieldModel model) { Method getUsing = model.getUsing(); return getUsing; } public static Method getGetter(FieldModel model) { Method getter = model.getter(); if (getter == null) getter = model.indexedGetter(); return getter; } public static Method getVolatileGetter(FieldModel model) { Method getter = model.volatileGetter(); if (getter == null) getter = model.volatileIndexedGetter(); return getter; } public static Method getSetter(FieldModel model) { Method setter = model.setter(); if (setter == null) setter = model.indexedSetter(); return setter; } public static Method getOrderedSetter(FieldModel model) { Method setter = model.orderedSetter(); if (setter == null) setter = model.orderedIndexedSetter(); return setter; } private static void methodCopy( StringBuilder copy, Method getter, Method setter, FieldModel model) { if (!model.isArray()) { if (model.setter() != null && getter != null) { copy.append(" ").append(setter.getName()); copy.append("(from.").append(getter.getName()).append("());\n"); } } else { copy.append(" for (int i = 0; i < ").append(model.indexSize().value()) .append("; i++){"); copy.append("\n ").append(setter.getName()).append("(i, from.") .append(getter.getName()).append("(i));\n"); copy.append(" }\n"); } } private static void methodWriteMarshall(StringBuilder writeMarshal, Method getter, Method setter, Class type, FieldModel model) { if (!model.isArray()) { if (getter != null && setter != null) { writeMarshal.append(" {\n"); // saveCharSequencePosition(writeMarshal, type, "out"); writeMarshal.append(" out.write").append(bytesType(type)).append("(") .append(getter.getName()).append("());\n"); // zeroOutRemainingCharSequenceBytesAndUpdatePosition( // writeMarshal, model, type, "out"); writeMarshal.append(" }\n"); } // otherwise skip. } else { writeMarshal.append(" for (int i = 0; i < ") .append(model.indexSize().value()).append("; i++){\n"); // saveCharSequencePosition(writeMarshal, type, "out"); writeMarshal.append(" out.write").append(bytesType(type)).append("(") .append(getter.getName()).append("(i));\n"); // zeroOutRemainingCharSequenceBytesAndUpdatePosition( // writeMarshal, model, type, "out"); writeMarshal.append(" }\n"); } } private static void saveCharSequencePosition(StringBuilder write, Class type, String bytes) { if (CharSequence.class.isAssignableFrom(type)) write.append(" long pos = " + bytes + ".position();\n"); } private static void zeroOutRemainingCharSequenceBytesAndUpdatePosition( StringBuilder write, FieldModel model, Class type, String bytes) { 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 methodHeapReadMarshall(StringBuilder readMarshal, String name, Class type, FieldModel model) { if(model.type() == Date.class){ readMarshal.append(" _").append(name).append(" = new Date(in.readLong());\n"); } else { String str = bytesType(type); if (!model.isArray()) { readMarshal.append(" _").append(name).append(" = in.read").append(str).append("("); if ("Object".equals(str) || "Enum".equals(str)) readMarshal.append(normalize(type)).append(".class"); readMarshal.append(");\n"); } else { readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); readMarshal.append(" _").append(name).append("[i] = in.read").append(str).append("("); if ("Object".equals(str) || "Enum".equals(str)) readMarshal.append(normalize(type)).append(".class"); readMarshal.append(");\n"); readMarshal.append(" }\n"); } } } private static void methodLongHashCode(StringBuilder hashCode, String getterName, FieldModel model, int count) { if (count > 0) hashCode.append(") * 10191 +\n "); if (!model.isArray()) { hashCode.append("calcLongHashCode(").append(getterName).append("())"); } else { hashCode.append("longHashCode_").append(model.name()).append("()"); } } private static void methodEqualsGetUsing(StringBuilder equals, String getterName) { equals.append(" if(!isEqual(").append(getterName).append("(new StringBuilder()).toString(), that.").append(getterName).append("new StringBuilder().toString())) return false;\n"); } private static void methodEquals(StringBuilder equals, String getterName, FieldModel model, String className) { if (!model.isArray()) { equals.append(" if(!isEqual(").append(getterName).append("(), that.").append(getterName).append("())) return false;\n"); } else { equals.append(" for (int i = 0; i <" + model.indexSize().value() + "; i++) {\n"); equals.append(" if(!isEqual(").append(getterName).append("(i), that.").append(getterName).append("(i))) return false;\n"); equals.append(" }\n"); } } private static void methodToStringGetUsing(StringBuilder toString, String getterName, String name, FieldModel model) { toString.append(" sb.append(\"").append(name).append("= \").append(").append(getterName).append("(new StringBuilder()));\n"); } private static void methodToString(StringBuilder toString, String getterName, String name, FieldModel model) { if (toString.length() > 2) toString.append("sb.append(\", \")\n;"); if (!model.isArray()) { toString.append(" sb.append(\"").append(name).append("= \").append(").append(getterName).append("());\n"); } else { toString .append(" sb.append(\"").append(name).append("\").append(\"= [\");") .append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++) {\n") .append(" if (i > 0) sb.append(\", \") ;\n") .append(" sb.append(").append(getterName).append("(i));\n") .append(" }\n") .append(" sb.append(\"]\");\n"); } } private static String nullAwareToString(String var) { return "(" + var + " != null ? " + var + ".toString() : null)"; } private static void methodHeapSet(StringBuilder getterSetters, Method setter, String name, Class type, FieldModel model) { Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; if (!model.isArray()) { getterSetters.append(" public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); if (type == String.class && setterType != String.class) getterSetters.append(" _").append(name).append(" = " + nullAwareToString("$") + ";\n"); else getterSetters.append(" _").append(name).append(" = $;\n"); } else { getterSetters.append(" public void ").append(setter.getName()).append("(int i, ").append(normalize(setterType)).append(" $) {\n"); getterSetters.append(boundsCheck(model.indexSize().value())); if (type == String.class && setterType != String.class) getterSetters.append(" _").append(name).append("[i] = " + nullAwareToString("$") + ";\n"); else getterSetters.append(" _").append(name).append("[i] = $;\n"); } getterSetters.append(" }\n\n"); } private static void methodHeapGet(StringBuilder getterSetters, Method getter, String name, Class type, FieldModel model) { if (!model.isArray()) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); getterSetters.append(" return _").append(name).append(";\n"); } else { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); getterSetters.append(boundsCheck(model.indexSize().value())); getterSetters.append(" return _").append(name).append("[i];\n"); } getterSetters.append(" }\n\n"); } private static void methodHeapGet(StringBuilder getterSetters, String name, Class type, String getterName) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getterName).append("() {\n"); getterSetters.append(" return _").append(name).append(";\n"); getterSetters.append(" }\n\n"); } private static void methodHeapGetUsingWithStringBuilder(StringBuilder result, Method method, String name, Class type, FieldModel model) { final CharSequence returnType = method.getReturnType() == void.class ? "void" : normalize(method .getReturnType()); if (!type.equals(String.class) || method.getParameterTypes().length != 1) return; if (!StringBuilder.class.equals(method.getParameterTypes()[0])) return; result.append(" public ").append(returnType).append(' ').append(method .getName()) .append("(StringBuilder builder){\n"); result.append(" builder.append(_" + name + ");\n"); if (method.getReturnType() != void.class) result.append(" return builder;\n"); result.append(" }\n\n"); } private static void heapFieldDeclarations(StringBuilder fieldDeclarations, Class type, String name, FieldModel model) { String vol = ""; if (model.isVolatile()) vol = "volatile "; if (!model.isArray()) { fieldDeclarations.append(" private ").append(vol).append(normalize(type)).append(" _").append(name).append(";\n"); } else { fieldDeclarations.append(" private ").append(vol).append(normalize(type)).append("[] _").append(name) .append(" = new ").append(normalize(type)).append("[").append(model.indexSize().value()).append("];\n"); if (!type.isPrimitive()) { fieldDeclarations.append(" {\n") .append(" for (int i = 0; i < _").append(name).append(".length; i++)\n") .append(" _").append(name).append("[i] = new ").append(type.getName()); if (type.isInterface()) { fieldDeclarations.append("$$Heap();\n"); } else { fieldDeclarations.append("();\n"); } fieldDeclarations.append(" }"); } } } private static String boundsCheck(int check) { return " if(i<0) throw new ArrayIndexOutOfBoundsException(i + \" must be greater than 0\");\n" + " if(i>=" + check + ") throw new ArrayIndexOutOfBoundsException(i + \" must be less than " + check + "\");\n"; } public static void appendImported(SortedSet<Class> imported, StringBuilder sb) { for (Class aClass : imported) { sb.append("import ").append(aClass.getName().replace('$', '.')).append(";\n"); } } public static void appendPackage(DataValueModel<?> dvmodel, StringBuilder sb) { sb.append("package ").append(getPackage(dvmodel)).append(";\n\n"); } public static String getPackage(DataValueModel<?> dvmodel) { return dvmodel.type().getPackage().getName(); } public static int fieldSize(FieldModel model) { return computeOffset( (int) BYTES.alignAndConvert((long) model.nativeSize(), MemoryUnit.BITS), model); } public static TreeSet<Class> newImported() { return new TreeSet<Class>(COMPARATOR); } public static boolean shouldImport(Class type) { return !type.isPrimitive() && !type.getPackage().getName().equals("java.lang"); } public static Map.Entry<String, FieldModel>[] heapSizeOrderedFieldsGrouped(DataValueModel<?> dvmodel) { Map<String, ? extends FieldModel> fieldMap = dvmodel.fieldMap(); Map.Entry<String, FieldModel>[] entries = fieldMap.entrySet().toArray(new Map.Entry[fieldMap.size()]); Arrays.sort(entries, COMPARE_BY_GROUP_THEN_HEAP_SIZE); return entries; } /** * gets the getter name based on the getUsing */ private static String getterName(Method getUsingMethod) { String name = getUsingMethod.getName(); if (!name.startsWith("getUsing")) throw new IllegalArgumentException("expected the getUsingXX method to start with the text 'getUsing'."); return "get" + name.substring("getUsing".length()); } private static void methodGetUsingWithStringBuilder(StringBuilder result, Method method, Class type, boolean isVolatile, String name) { String read = "read"; if (isVolatile) read = "readVolatile"; if (method.getParameterTypes().length != 1) return; if (!StringBuilder.class.equals(method.getParameterTypes()[0])) return; if (type != String.class) return; final CharSequence returnType = method.getReturnType() == void.class ? "void" : normalize(method .getReturnType()); result.append(" public ").append(returnType).append(' ').append(method .getName()) .append("(StringBuilder builder){\n"); result.append(" _bytes.position(_offset + ").append(name.toUpperCase()).append(");\n"); result.append(" _bytes.").append(read).append(bytesType(type)).append("(builder);\n"); if (method.getReturnType() != void.class) { result.append(" return builder;\n"); } result.append(" }\n\n"); } public static int computeOffset(int offset, FieldModel model) { if (model.indexSize() == null) { return offset; } else { return model.indexSize().value() * offset; } } public static int computeNonScalarOffset(DataValueModel dvmodel, Class type) { int offset = 0; DataValueModel dvmodel2 = dvmodel.nestedModel(type); Map.Entry<String, FieldModel>[] entries2 = heapSizeOrderedFieldsGrouped(dvmodel2); for (Map.Entry<String, ? extends FieldModel> entry2 : entries2) { FieldModel model2 = entry2.getValue(); int add; if (dvmodel2.isScalar(model2.type())) { add = fieldSize(model2); } else { add = computeNonScalarOffset(dvmodel2, model2.type()); if (model2.isArray()) add *= model2.indexSize().value(); } offset += add; } return offset; } public <T> T heapInstance(Class<T> tClass) { try { //noinspection ClassNewInstance return (T) acquireHeapClass(tClass).newInstance(); } catch (Exception e) { throw new AssertionError(e); } } public <T> Class acquireHeapClass(Class<T> tClass) { Class heapClass = heapClassMap.get(tClass); if (heapClass != null) return heapClass; ClassLoader classLoader = tClass.getClassLoader(); String className = tClass.getName() + "$$Heap"; try { heapClass = classLoader.loadClass(className); } catch (ClassNotFoundException ignored) { try { String actual = generateHeapObject(tClass); if (dumpCode) LoggerFactory.getLogger(DataValueGenerator.class).info(actual); heapClass = CompilerUtils.CACHED_COMPILER.loadFromJava(classLoader, className, actual); } catch (ClassNotFoundException e) { throw new AssertionError(e); } } heapClassMap.put(tClass, heapClass); return heapClass; } String generateHeapObject(Class<?> tClass) { DataValueModel<?> dvmodel = DataValueModels.acquireModel(tClass); for (FieldModel fieldModel : dvmodel.fieldMap().values()) { if (fieldModel.isArray() && !fieldModel.type().isPrimitive()) acquireHeapClass(fieldModel.type()); } return generateHeapObject(dvmodel); } public <T> T nativeInstance(Class<T> tClass) { try { //noinspection ClassNewInstance return (T) acquireNativeClass(tClass).newInstance(); } catch (Exception e) { throw new AssertionError(e); } } public <T> Class acquireNativeClass(Class<T> tClass) { if (!tClass.isInterface()) return tClass; Class nativeClass = nativeClassMap.get(tClass); if (nativeClass != null) return nativeClass; DataValueModel<T> dvmodel = DataValueModels.acquireModel(tClass); for (Class clazz : dvmodel.nestedModels()) { // touch them to make sure they are loaded. Class clazz2 = acquireNativeClass(clazz); } ClassLoader classLoader = tClass.getClassLoader(); String className = tClass.getName() + "$$Native"; try { nativeClass = classLoader.loadClass(className); } catch (ClassNotFoundException ignored) { String actual = new DataValueGenerator().generateNativeObject(dvmodel); if (dumpCode) LoggerFactory.getLogger(DataValueGenerator.class).info(actual); try { nativeClass = CompilerUtils.CACHED_COMPILER.loadFromJava(classLoader, className, actual); } catch (ClassNotFoundException e) { throw new AssertionError(e); } } nativeClassMap.put(tClass, nativeClass); return nativeClass; } public String generateNativeObject(Class<?> tClass) { return generateNativeObject(DataValueModels.acquireModel(tClass)); } public String generateNativeObject(DataValueModel<?> dvmodel) { SortedSet<Class> imported = newImported(); imported.add(BytesMarshallable.class); imported.add(ObjectOutput.class); imported.add(ObjectInput.class); imported.add(IOException.class); imported.add(Copyable.class); imported.add(Byteable.class); imported.add(Bytes.class); StringBuilder staticFieldDeclarations = new StringBuilder(); StringBuilder fieldDeclarations = new StringBuilder(); StringBuilder getterSetters = new StringBuilder(); StringBuilder writeMarshal = new StringBuilder(); StringBuilder readMarshal = new StringBuilder(); StringBuilder copy = new StringBuilder(); StringBuilder nestedBytes = new StringBuilder(); Map.Entry<String, FieldModel>[] entries = heapSizeOrderedFieldsGrouped(dvmodel); int offset = 0; for (Map.Entry<String, ? extends FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); Class type = model.type(); if (shouldImport(type)) imported.add(type); String NAME = "_offset + " + name.toUpperCase(); final Method setter = getSetter(model); final Method getter = getGetter(model); final Method getUsing = getUsing(model); final Method orderedSetter = getOrderedSetter(model); final Method volatileGetter = getVolatileGetter(model); final Method defaultSetter = setter != null ? setter : orderedSetter; final Method defaultGetter = getter != null ? getter : volatileGetter; if (dvmodel.isScalar(type)) { staticFieldDeclarations.append(" private static final int ").append(name.toUpperCase()).append(" = ").append(offset).append(";\n"); methodCopy(copy, defaultGetter, defaultSetter, model); if (setter != null) methodSet(getterSetters, setter, type, NAME, model, false); if (getter != null) methodGet(getterSetters, getter, type, NAME, model, false); if (getUsing != null) { methodGetUsingWithStringBuilder(getterSetters, getUsing, type, false, name); // we have to add in the getter method as its required for the equals() and hashCode() if (getter == null && volatileGetter == null) { String getterName = getterName(getUsing); methodGet(getterSetters, type, NAME, false, getterName); } } if (orderedSetter != null) methodSet(getterSetters, orderedSetter, type, NAME, model, true); if (volatileGetter != null) methodGet(getterSetters, volatileGetter, type, NAME, model, true); Method adder = model.adder(); if (adder != null) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(adder.getName()) .append("(").append(adder.getParameterTypes()[0].getName()).append(" $) {\n") .append(" return _bytes.add").append(bytesType(type)).append("(").append(NAME).append(", $);\n") .append(" }"); } Method atomicAdder = model.atomicAdder(); if (atomicAdder != null) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(atomicAdder.getName()) .append("(").append(atomicAdder.getParameterTypes()[0].getName()).append(" $) {\n") .append(" return _bytes.addAtomic").append(bytesType(type)).append("(").append(NAME).append(", $);\n") .append(" }"); } Method sizeOf = model.sizeOf(); if (sizeOf != null) { getterSetters.append(" public int ").append(sizeOf.getName()) .append("() {\n").append(" return ").append(model.indexSize().value()).append(";\n") .append(" }\n\n"); } Method cas = model.cas(); if (cas != null) { getterSetters.append(" public boolean ").append(cas.getName()).append("(") .append(normalize(type)).append(" _1, ") .append(normalize(type)).append(" _2) {\n") .append(" return _bytes.compareAndSwap").append(bytesType(type)).append('(').append(NAME).append(", _1, _2);\n") .append(" }"); } Method tryLockNanos = model.tryLockNanos(); if (tryLockNanos != null) { getterSetters.append(" public boolean ").append(tryLockNanos.getName()).append("(long nanos) {\n") .append(" return _bytes.tryLockNanos").append(bytesType(type)).append('(').append(NAME).append(", nanos);\n") .append(" }"); } Method tryLock = model.tryLock(); if (tryLock != null) { getterSetters.append(" public boolean ").append(tryLock.getName()).append("() {\n") .append(" return _bytes.tryLock").append(bytesType(type)).append('(').append(NAME).append(");\n") .append(" }"); } Method unlock = model.unlock(); if (unlock != null) { getterSetters.append(" public void ").append(unlock.getName()).append("() {\n") .append(" _bytes.unlock").append(bytesType(type)).append('(').append(NAME).append(");\n") .append(" }"); } Method busyLock = model.busyLock(); if (busyLock != null) { getterSetters.append(" public void ").append(busyLock.getName()).append("() throws InterruptedException {\n") .append(" _bytes.busyLock").append(bytesType(type)).append('(').append(NAME).append(");\n") .append(" }"); } methodWriteMarshall(writeMarshal, defaultGetter, defaultSetter, type, model); methodReadMarshall(readMarshal, defaultGetter, defaultSetter, type, model); if(!Enum.class.isAssignableFrom(type)) offset += fieldSize(model); } else { staticFieldDeclarations.append(" private static final int ").append(name.toUpperCase()).append(" = ").append(offset).append(";\n"); nonScalarFieldDeclaration(staticFieldDeclarations, type, name, model); if (defaultSetter == null) { copy.append(" _").append(name).append(".copyFrom(from.").append(getter.getName()).append("());\n"); } else { methodCopy(copy, defaultGetter, defaultSetter, model); methodNonScalarSet(getterSetters, defaultSetter, name, type, model); } int size = computeNonScalarOffset(dvmodel, type); methodNonScalarGet(getterSetters, getter, name, type, model); methodNonScalarWriteMarshall(writeMarshal, name, model); methodNonScalarReadMarshall(readMarshal, name, model); methodNonScalarBytes(nestedBytes, name, NAME, size, model); offset += computeOffset(size, model); } } fieldDeclarations.append("\n") .append(" private Bytes _bytes;\n") .append(" private long _offset;\n"); StringBuilder sb = new StringBuilder(); appendPackage(dvmodel, sb); sb.append("import static ").append(Compare.class.getName()).append(".*;\n"); appendImported(imported, sb); sb.append("\npublic class ").append(simpleName(dvmodel.type())) .append("$$Native implements ").append(simpleName(dvmodel.type()).replace('$', '.')) .append(", BytesMarshallable, Byteable, Copyable<").append(normalize(dvmodel.type())).append("> {\n"); sb.append(staticFieldDeclarations).append('\n'); sb.append(fieldDeclarations).append('\n'); sb.append(getterSetters); sb.append(" @Override\n") .append(" public void copyFrom(").append(normalize(dvmodel.type())).append(" from) {\n") .append(copy) .append(" }\n\n"); sb.append(" @Override\n") .append(" public void writeMarshallable(Bytes out) {\n") .append(writeMarshal) .append(" }\n"); sb.append(" @Override\n") .append(" public void readMarshallable(Bytes in) {\n") .append(readMarshal) .append(" }\n"); sb.append(" @Override\n") .append(" public void bytes(Bytes bytes, long offset) {\n") .append(" this._bytes = bytes;\n") .append(" this._offset = offset;\n") .append(nestedBytes) .append(" }\n"); sb.append(" @Override\n") .append(" public Bytes bytes() {\n") .append(" return _bytes;\n") .append(" }\n"); sb.append(" @Override\n") .append(" public long offset() {\n") .append(" return _offset;\n") .append(" }\n"); sb.append(" @Override\n") .append(" public int maxSize() {\n") .append(" return ").append(offset).append(";\n") .append(" }\n"); generateObjectMethods(sb, dvmodel, entries, true); sb.append("}\n"); // System.out.println(sb); return sb.toString(); } public boolean isDumpCode() { return dumpCode; } public void setDumpCode(boolean dumpCode) { this.dumpCode = dumpCode; } private void methodSet(StringBuilder getterSetters, Method setter, Class type, String NAME, FieldModel model, boolean isVolatile) { Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; String write = "write"; if (isVolatile) write = "writeOrdered"; if (model.type() == Date.class) { getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); getterSetters.append(" _bytes.").append(write).append("Long").append("(").append(NAME).append(", "); } else if(Enum.class.isAssignableFrom(type)){ getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); getterSetters.append(" _bytes.").append(write).append("Enum").append("("); } else if (!model.isArray()) { getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); getterSetters.append(" _bytes.").append(write).append(bytesType(type)).append("(").append(NAME).append(", "); } else { getterSetters.append(" public void ").append(setter.getName()).append("(int i, "); getterSetters.append(normalize(setterType)).append(" $) {\n"); getterSetters.append(boundsCheck(model.indexSize().value())); getterSetters.append(" _bytes.").append(write).append(bytesType(type)).append("(").append(NAME); getterSetters.append(" + i * ").append((model.nativeSize() + 7) >> 3).append(", "); } if (CharSequence.class.isAssignableFrom(type)) getterSetters.append(model.size().value()).append(", "); if (model.type() == Date.class) { getterSetters.append("$.getTime());\n"); } else { getterSetters.append("$);\n"); } getterSetters.append(" }\n\n"); } private void methodGet(StringBuilder getterSetters, Class type, String NAME, boolean isVolatile, String name) { String read = "read"; if (isVolatile) read = "readVolatile"; getterSetters.append(" public ").append(normalize(type)).append(' ').append(name).append("() {\n"); getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME).append(");\n"); getterSetters.append(" }\n\n"); } private void methodGet(StringBuilder getterSetters, Method getter, Class type, String NAME, FieldModel model, boolean isVolatile) { String read = "read"; if (isVolatile) read = "readVolatile"; if(model.type() == Date.class){ getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); getterSetters.append(" return new Date( _bytes.").append(read).append("Long").append("(").append(NAME).append("));\n"); } else if (Enum.class.isAssignableFrom(model.type())) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); //getterSetters.append(" return _bytes.").append(read).append("Enum").append("(").append(NAME).append(");\n"); getterSetters.append(" return _bytes.").append(read).append("Enum").append("(").append(type.getName()).append(".class);\n"); } else if (!model.isArray()) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME).append(");\n"); } else { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); getterSetters.append(boundsCheck(model.indexSize().value())); getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME); getterSetters.append(" + i * ").append((model.nativeSize() + 7) >> 3); getterSetters.append(");\n"); } getterSetters.append(" }\n\n"); } private void methodNonScalarWriteMarshall(StringBuilder writeMarshal, String name, FieldModel model) { if (!model.isArray()) { writeMarshal.append(" _").append(name).append(".writeMarshallable(out);\n"); } else { writeMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); writeMarshal.append(" _").append(name).append("[i].writeMarshallable(out);\n"); writeMarshal.append(" }\n"); } } private void methodReadMarshall(StringBuilder readMarshal, Method getter, Method setter, Class type, FieldModel model) { if(model.type() == Date.class){ if (getter != null && setter != null) readMarshal.append(" ").append(setter.getName()).append("((Date)in.read").append(bytesType(type)).append("());\n"); } else if (Enum.class.isAssignableFrom(model.type())) { if (getter != null && setter != null) readMarshal.append(" ").append(setter.getName()).append("(in.readEnum(" + model.type().getName() + ".class));\n"); } else if (!model.isArray()) { if (getter != null && setter != null) readMarshal.append(" ").append(setter.getName()).append("(in.read").append(bytesType(type)).append("());\n"); } else { readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); readMarshal.append(" ").append(setter.getName()).append("(i, in.read").append(bytesType(type)).append("());\n"); readMarshal.append(" }\n"); } } private void methodNonScalarReadMarshall(StringBuilder readMarshal, String name, FieldModel model) { if (!model.isArray()) { readMarshal.append(" _").append(name).append(".readMarshallable(in);\n"); } else { readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); readMarshal.append(" _").append(name).append("[i].readMarshallable(in);\n"); readMarshal.append(" }\n"); } } private void nonScalarFieldDeclaration(StringBuilder fieldDeclarations, Class type, String name, FieldModel model) { fieldDeclarations.append(" private final ").append(type.getName()).append("$$Native _").append(name); if (!model.isArray()) { fieldDeclarations.append(" = new ").append(type.getName()).append("$$Native();\n"); } else { fieldDeclarations.append("[] = new ").append(type.getName()).append("$$Native[").append(model.indexSize().value()).append("];\n"); fieldDeclarations.append(" {\n") .append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++)\n") .append(" _").append(name).append("[i] = new ").append(type.getName()).append("$$Native();\n") .append(" }\n"); } } private void methodNonScalarSet(StringBuilder getterSetters, Method setter, String name, Class type, FieldModel model) { Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; if (!model.isArray()) { getterSetters.append(" public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); if (type == String.class && setterType != String.class) getterSetters.append(" _").append(name).append(" = " + nullAwareToString("$") + ";\n"); else getterSetters.append(" _").append(name).append(".copyFrom($);\n"); } else { getterSetters.append(" public void ").append(setter.getName()).append("(int i, ").append(normalize(setterType)).append(" $) {\n"); if (type == String.class && setterType != String.class) getterSetters.append(" _").append(name).append("[i] = " + nullAwareToString("$") + ";\n"); else getterSetters.append(" _").append(name).append("[i].copyFrom($);\n"); } getterSetters.append(" }\n\n"); } private void methodNonScalarGet(StringBuilder getterSetters, Method getter, String name, Class type, FieldModel model) { if (!model.isArray()) { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); getterSetters.append(" return _").append(name).append(";\n"); } else { getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); getterSetters.append(" return _").append(name).append("[i];\n"); } getterSetters.append(" }\n\n"); } private void methodNonScalarBytes(StringBuilder nestedBytes, String name, String NAME, int size, FieldModel model) { if (!model.isArray()) { nestedBytes.append(" ((Byteable) _").append(name).append(").bytes(bytes, ").append(NAME).append(");\n"); } else { nestedBytes.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); nestedBytes.append(" ((Byteable) _").append(name).append("[i]).bytes(bytes, ").append(NAME); nestedBytes.append(" + (i * ").append(size).append("));\n"); nestedBytes.append(" }\n"); } } }