/* * 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.MethodSpec; import sun.misc.Unsafe; import static java.lang.String.format; import static net.openhft.chronicle.values.Primitives.boxed; class FloatingFieldModel extends PrimitiveFieldModel { private final MemberGenerator nativeGenerator = new MemberGenerator(FloatingFieldModel.this) { @Override void generateArrayElementFields( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder) { // no fields } private int verifiedByteOffset(ValueBuilder valueBuilder) { int bitOffset = valueBuilder.model.fieldBitOffset(FloatingFieldModel.this); assert bitOffset % 8 == 0; return bitOffset / 8; } private String scaledIndex() { if (type == float.class) { return "(4L * index)"; } else if (type == double.class) { return "(8L * index)"; } else { throw new AssertionError(); } } private void gen( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String ret, String method, String arguments) { methodBuilder.addStatement("$Nbs.$N$N(offset + $L$N)", ret, method, capTypeName(), verifiedByteOffset(valueBuilder), arguments); } private void genArrayElement( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String ret, String method, String arguments) { methodBuilder.addStatement("$Nbs.$N$N(offset + $L + $N$N)", ret, method, capTypeName(), arrayFieldModel.verifiedByteOffset(valueBuilder), scaledIndex(), arguments); } @Override void generateGet(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "return ", "read", ""); } @Override void generateArrayElementGet( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "return ", "read", ""); } @Override void generateGetVolatile(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "return ", "readVolatile", ""); } @Override void generateArrayElementGetVolatile( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "return ", "readVolatile", ""); } @Override void generateSet(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "", "write", ", " + varName()); } @Override void generateArrayElementSet( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "", "write", ", " + varName()); } @Override void generateSetVolatile(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "", "writeVolatile", ", " + varName()); } @Override void generateArrayElementSetVolatile( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "", "writeVolatile", ", " + varName()); } @Override void generateSetOrdered(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "", "writeOrdered", ", " + varName()); } @Override void generateArrayElementSetOrdered( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "", "writeOrdered", ", " + varName()); } @Override void generateAdd(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addStatement("return bs.addAndGet$NNotAtomic(offset + $L, addition)", capTypeName(), verifiedByteOffset(valueBuilder)); } @Override void generateArrayElementAdd( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); methodBuilder.addStatement( "return bs.writeAndGet$NNotAtomic(offset + $L + $N, addition)", capTypeName(), arrayFieldModel.verifiedByteOffset(valueBuilder), scaledIndex()); } @Override void generateAddAtomic(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "return ", "addAndGet", ", addition"); } @Override void generateArrayElementAddAtomic( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "return ", "addAndGet", ", addition"); } @Override void generateCompareAndSwap(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "return ", "compareAndSwap", format(", %s, %s", oldName(), newName())); } @Override void generateArrayElementCompareAndSwap( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "return ", "compareAndSwap", format(", %s, %s", oldName(), newName())); } @Override void generateCopyFrom(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "", "write", format(", from.%s()", getOrGetVolatile().getName())); } @Override void generateArrayElementCopyFrom( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "", "write", format(", from.%s(index)", arrayFieldModel.getOrGetVolatile().getName())); } @Override void generateReadMarshallable(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { gen(valueBuilder, methodBuilder, "", "write", format(", bytes.read%s()", capTypeName())); } @Override void generateArrayElementReadMarshallable( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { genArrayElement(arrayFieldModel, valueBuilder, methodBuilder, "", "write", format(", bytes.read%s()", capTypeName())); } @Override void generateEquals(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addCode("if ($N($N()) != $N(other.$N())) return false;\n", toBits(), getOrGetVolatile().getName(), toBits(), getOrGetVolatile().getName()); } @Override void generateArrayElementEquals( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { String get = arrayFieldModel.getOrGetVolatile().getName(); methodBuilder.addCode("if ($N($N(index)) != $N(other.$N(index))) return false;\n", toBits(), get, toBits(), get); } @Override String generateHashCode(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { return String.format("%s.hashCode(%s())", boxed(type).getName(), getOrGetVolatile().getName()); } @Override String generateArrayElementHashCode( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { return String.format("%s.hashCode(%s(index))", boxed(type).getName(), arrayFieldModel.getOrGetVolatile().getName()); } }; @Override MemberGenerator nativeGenerator() { return nativeGenerator; } @Override MemberGenerator createHeapGenerator() { return new NumberHeapMemberGenerator(this) { @Override public void generateAddAtomic( ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.beginControlFlow("while (true)"); methodBuilder.addStatement( "$T $N = " + wrap(valueBuilder, methodBuilder, "$N.$N(this, $N)"), type, oldName(), valueBuilder.unsafe(), getVolatile(), fieldOffset(valueBuilder)); methodBuilder.addStatement("$T $N = $N + addition", type, newName(), oldName()); methodBuilder.beginControlFlow( format("if ($N.$N(this, $N, %s, %s))", unwrap(methodBuilder, "$N"), unwrap(methodBuilder, "$N")), valueBuilder.unsafe(), compareAndSwap(), fieldOffset(valueBuilder), oldName(), newName()); methodBuilder.addStatement("return $N", newName()); methodBuilder.endControlFlow(); methodBuilder.endControlFlow(); } @Override public void generateArrayElementAddAtomic( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { arrayFieldModel.checkBounds(methodBuilder); methodBuilder.beginControlFlow("while (true)"); methodBuilder.addStatement( "$T $N = " + wrap(valueBuilder, methodBuilder, "$N.$N($N, (long) $T.$N + (index * (long) $T.$N))"), type, oldName(), valueBuilder.unsafe(), getVolatile(), field, Unsafe.class, arrayBase(), Unsafe.class, arrayScale()); methodBuilder.addStatement("$T $N = $N + addition", type, newName(), oldName()); methodBuilder.beginControlFlow( format("if ($N.$N($N, (long) $T.$N + (index * (long) $T.$N), %s, %s))", unwrap(methodBuilder, "$N"), unwrap(methodBuilder, "$N")), valueBuilder.unsafe(), compareAndSwap(), field, Unsafe.class, arrayBase(), Unsafe.class, arrayScale(), oldName(), newName()); methodBuilder.addStatement("return $N", newName()); methodBuilder.endControlFlow(); methodBuilder.endControlFlow(); } @Override void generateEquals(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addCode( format("if ($N(%s) != $N(other.$N())) return false;\n", wrap(valueBuilder, methodBuilder, "$N")), toBits(), field, toBits(), getOrGetVolatile().getName()); } @Override void generateArrayElementEquals( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addCode( format("if ($N(%s) != $N(other.$N(index))) return false;\n", wrap(valueBuilder, methodBuilder, "$N[index]")), toBits(), field, toBits(), getOrGetVolatile().getName()); } }; } private String toBits() { if (type == float.class) { return "java.lang.Float.floatToIntBits"; } else { return "java.lang.Double.doubleToLongBits"; } } }