/* * Copyright (C) 2015, 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.values; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import net.openhft.chronicle.bytes.Byteable; import static java.lang.String.format; import static net.openhft.chronicle.values.IntegerFieldModel.NORMAL_ACCESS_TYPE; import static net.openhft.chronicle.values.Utils.capitalize; final class PointerFieldModel extends IntegerBackedFieldModel { private final ValueFieldModel pointedModel; PointerFieldModel(ValueFieldModel pointedModel) { this.pointedModel = pointedModel; } @Override void postProcess() { super.postProcess(); pointedModel.postProcess(); backend.type = long.class; backend.range = RangeImpl.DEFAULT_LONG_RANGE; backend.postProcess(); } @Override void checkState() { super.checkState(); pointedModel.checkState(); } private FieldSpec cachedValue() { return pointedModel.nativeGenerator().cachedValue; } private void initCachedValue( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address) { methodBuilder.addStatement("$N.bytesStore($N, $N, $L)", cachedValue(), valueBuilder.bytesStoreForPointers(), address, pointedModel.sizeInBytes()); } private String extractAddress(MethodSpec.Builder methodBuilder, String value) { String addressVariable = value + "Address"; methodBuilder.addStatement("long $N", addressVariable); methodBuilder.beginControlFlow("if ($N != null)", value); { methodBuilder.beginControlFlow("if (!($N instanceof $T))", value, Byteable.class); String message = "\"$N should be instance of $T, \" + $N.getClass() + \" is given\""; methodBuilder.addStatement("throw new $T(" + message + ")", IllegalArgumentException.class, name, Byteable.class, value); methodBuilder.endControlFlow(); methodBuilder.addStatement( "$N = (($T) $N).bytesStore().address((($T) $N).offset())", addressVariable, Byteable.class, value, Byteable.class, value); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("$N = 0L", addressVariable); } methodBuilder.endControlFlow(); return addressVariable; } private void genWriteMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address, Object value) { String addressVariable = name + "Address"; methodBuilder.addStatement("long $N = $N", addressVariable, address); methodBuilder.beginControlFlow("if ($N != 0)", addressVariable); { initCachedValue(valueBuilder, methodBuilder, address); methodBuilder.addStatement("bytes.writeBoolean(true)"); methodBuilder.addStatement("$N.writeMarshallable(bytes)", value); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("bytes.writeBoolean(false)"); } methodBuilder.endControlFlow(); } private void genReadMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address, Object value, Runnable setNull) { String present = name + "Present"; methodBuilder.addStatement("boolean $N = bytes.readBoolean()", present); methodBuilder.beginControlFlow("if ($N)", present); { String addressVariable = name + "Address"; methodBuilder.addStatement("long $N = $N", addressVariable, address); methodBuilder.beginControlFlow("if ($N != 0)", addressVariable); { initCachedValue(valueBuilder, methodBuilder, address); methodBuilder.addStatement("$N.readMarshallable(bytes)", value); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("throw new $T($S)", IllegalStateException.class, name + " field should be initialized to some pointer when reading " + "non-null value from marshalled bytes"); } methodBuilder.endControlFlow(); } methodBuilder.nextControlFlow("else"); { setNull.run(); } methodBuilder.endControlFlow(); } final MemberGenerator nativeGenerator = new IntegerBackedNativeMemberGenerator(this, backend) { @Override void generateFields(ValueBuilder valueBuilder) { super.generateFields(valueBuilder); pointedModel.nativeGenerator().generateFields(valueBuilder); } @Override void generateArrayElementFields( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder) { generateFields(valueBuilder); } @Override void finishGet( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address) { String addressVariable = name + "Address"; methodBuilder.addStatement("long $N = $N", addressVariable, address); methodBuilder.beginControlFlow("if ($N != 0)", addressVariable); { initCachedValue(valueBuilder, methodBuilder, address); methodBuilder.addStatement("return $N", cachedValue()); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("return null"); } methodBuilder.endControlFlow(); } @Override String startSet(MethodSpec.Builder methodBuilder) { return extractAddress(methodBuilder, varName()); } @Override void generateWriteMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genGet(valueBuilder, NORMAL_ACCESS_TYPE); genWriteMarshallable(valueBuilder, methodBuilder, address, cachedValue()); } @Override void generateReadMarshallable(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genGet(valueBuilder, NORMAL_ACCESS_TYPE); genReadMarshallable(valueBuilder, methodBuilder, address, cachedValue(), () -> backingFieldModel.genSet(valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE, "0L") ); } @Override void generateArrayElementWriteMarshallable( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genArrayElementGet( arrayFieldModel, valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE); genWriteMarshallable(valueBuilder, methodBuilder, address, cachedValue()); } @Override void generateArrayElementReadMarshallable( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genArrayElementGet( arrayFieldModel, valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE); genReadMarshallable(valueBuilder, methodBuilder, address, cachedValue(), () -> backingFieldModel.genArrayElementSet( arrayFieldModel, valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE, "0L") ); } @Override void generateEquals(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String otherValueVariable = "other" + capitalize(name); methodBuilder.addStatement("$T $N = other.$N()", type, otherValueVariable, getOrGetVolatile().getName()); String otherAddress = extractAddress(methodBuilder, otherValueVariable); String thisAddress = backingFieldModel.genGet(valueBuilder, NORMAL_ACCESS_TYPE); methodBuilder.addCode("if ($N != $N) return false;\n", thisAddress, otherAddress); } @Override void generateArrayElementEquals( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String otherValueVariable = "other" + capitalize(name); methodBuilder.addStatement("$T $N = other.$N(index)", type, otherValueVariable, getOrGetVolatile().getName()); String otherAddress = extractAddress(methodBuilder, otherValueVariable); String thisAddress = backingFieldModel.genArrayElementGet( arrayFieldModel, valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE); methodBuilder.addCode("if ($N != $N) return false;\n", thisAddress, otherAddress); } @Override String generateHashCode(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genGet(valueBuilder, NORMAL_ACCESS_TYPE); return format("Long.hashCode(%s)", address); } @Override String generateArrayElementHashCode( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String address = backingFieldModel.genArrayElementGet( arrayFieldModel, valueBuilder, methodBuilder, NORMAL_ACCESS_TYPE); return format("Long.hashCode(%s)", address); } }; @Override MemberGenerator nativeGenerator() { return nativeGenerator; } @Override MemberGenerator createHeapGenerator() { return new PrimitiveBackedHeapMemberGenerator(this, backend.type) { @Override void generateFields(ValueBuilder valueBuilder) { super.generateFields(valueBuilder); pointedModel.nativeGenerator().generateFields(valueBuilder); } @Override void generateArrayElementFields( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder) { super.generateArrayElementFields(arrayFieldModel, valueBuilder); pointedModel.nativeGenerator() .generateArrayElementFields(arrayFieldModel, valueBuilder); } @Override String wrap(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String rawStoredValue) { String result = name + "Result"; methodBuilder.addStatement("$T $N", type, result); methodBuilder.beginControlFlow("if ($N != 0)", rawStoredValue); { initCachedValue(valueBuilder, methodBuilder, rawStoredValue); methodBuilder.addStatement("$N = $N", result, cachedValue()); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("$N = null", result); } methodBuilder.endControlFlow(); return result; } @Override String unwrap(MethodSpec.Builder methodBuilder, String inputValue) { return extractAddress(methodBuilder, inputValue); } private void genWriteMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address) { String addressVariable = name + "Address"; methodBuilder.addStatement("long $N = $N", addressVariable, address); methodBuilder.beginControlFlow("if ($N != 0)", addressVariable); { initCachedValue(valueBuilder, methodBuilder, addressVariable); methodBuilder.addStatement("bytes.writeBoolean(true)"); methodBuilder.addStatement("$N.writeMarshallable(bytes)", cachedValue()); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("bytes.writeBoolean(false)"); } methodBuilder.endControlFlow(); } @Override void generateWriteMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genWriteMarshallable(valueBuilder, methodBuilder, field.name); } @Override void generateArrayElementWriteMarshallable( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genWriteMarshallable(valueBuilder, methodBuilder, field.name + "[index]"); } private void genReadMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String address, Runnable setNull) { String present = name + "Present"; methodBuilder.addStatement("boolean $N = bytes.readBoolean()", present); methodBuilder.beginControlFlow("if ($N)", present); { String addressVariable = name + "Address"; methodBuilder.addStatement("long $N = $N", addressVariable, address); methodBuilder.beginControlFlow("if ($N != 0)", addressVariable); { initCachedValue(valueBuilder, methodBuilder, addressVariable); methodBuilder.addStatement("$N.readMarshallable(bytes)", cachedValue()); } methodBuilder.nextControlFlow("else"); { methodBuilder.addStatement("throw new $T($S)", IllegalStateException.class, name + " field should be initialized to some pointer " + "when reading non-null value from marshalled bytes"); } methodBuilder.endControlFlow(); } methodBuilder.nextControlFlow("else"); { setNull.run(); } methodBuilder.endControlFlow(); } @Override void generateReadMarshallable( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genReadMarshallable(valueBuilder, methodBuilder, field.name, () -> methodBuilder.addStatement("$N = 0L", field)); } @Override void generateArrayElementReadMarshallable( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genReadMarshallable(valueBuilder, methodBuilder, field.name + "[index]", () -> methodBuilder.addStatement("$N[index] = 0L", field)); } @Override String generateHashCode(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { return format("Long.hashCode(%s)", field.name); } @Override String generateArrayElementHashCode( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { return format("Long.hashCode(%s[index])", field.name); } }; } }