/*
* 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 net.openhft.chronicle.values.Primitives.boxed;
import static net.openhft.chronicle.values.Utils.capitalize;
class PrimitiveBackedHeapMemberGenerator extends HeapMemberGenerator {
final String capType;
private final String upperType;
private final Class fieldType;
PrimitiveBackedHeapMemberGenerator(FieldModel fieldModel) {
super(fieldModel);
fieldType = determineFieldType();
assert fieldType.isPrimitive();
capType = capitalize(fieldType.getName());
upperType = fieldType.getName().toUpperCase();
}
PrimitiveBackedHeapMemberGenerator(FieldModel fieldModel, Class fieldType) {
super(fieldModel);
this.fieldType = fieldType;
assert fieldType.isPrimitive();
capType = capitalize(fieldType.getName());
upperType = fieldType.getName().toUpperCase();
}
@Override
Class fieldType() {
return fieldType;
}
private Class determineFieldType() {
Class modelType = super.fieldType();
if (modelType == long.class || modelType == int.class)
return modelType;
PrimitiveFieldModel fieldModel = (PrimitiveFieldModel) this.fieldModel;
if (fieldModel.setOrdered != null || fieldModel.compareAndSwap != null ||
fieldModel.addAtomic != null) {
if (modelType == double.class)
return long.class;
return int.class;
}
return modelType;
}
String getVolatile() {
return "get" + capType + "Volatile";
}
@Override
String putVolatile() {
return "put" + capType + "Volatile";
}
@Override
String putOrdered() {
return "putOrdered" + capType;
}
@Override
String compareAndSwap() {
return "compareAndSwap" + capType;
}
@Override
String arrayBase() {
return "ARRAY_" + upperType + "_BASE_OFFSET";
}
@Override
String arrayScale() {
return "ARRAY_" + upperType + "_INDEX_SCALE";
}
@Override
String wrap(
ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String rawStoredValue) {
if (fieldType() == fieldModel.type)
return rawStoredValue;
if (fieldModel.type == float.class)
return "java.lang.Float.intBitsToFloat(" + rawStoredValue + ")";
if (fieldModel.type == double.class)
return "java.lang.Double.longBitsToDouble(" + rawStoredValue + ")";
if (fieldModel.type == boolean.class)
return "(" + rawStoredValue + " != 0)";
assert fieldModel.type == byte.class || fieldModel.type == short.class ||
fieldModel.type == char.class;
return "((" + fieldModel.type.getSimpleName() + ") " + rawStoredValue + ")";
}
@Override
String unwrap(MethodSpec.Builder methodBuilder, String inputValue) {
if (fieldType() == fieldModel.type)
return inputValue;
if (fieldModel.type == float.class)
return "java.lang.Float.floatToRawIntBits(" + inputValue + ")";
if (fieldModel.type == double.class)
return "java.lang.Double.doubleToRawLongBits(" + inputValue + ")";
if (fieldModel.type == boolean.class)
return "(" + inputValue + " ? 1 : 0)";
assert fieldModel.type == byte.class || fieldModel.type == short.class ||
fieldModel.type == char.class;
return inputValue; // byte, short, char -- auto widening, no explicit conversion needed
}
@Override
public void generateGetVolatile(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N.$N(this, $N)",
fieldType(), rawValue,
valueBuilder.unsafe(), getVolatile(), fieldOffset(valueBuilder));
methodBuilder.addStatement("return $N", wrap(valueBuilder, methodBuilder, rawValue));
}
@Override
public void generateArrayElementGetVolatile(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
arrayFieldModel.checkBounds(methodBuilder);
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N.$N($N, (long) $T.$N + (index * (long) $T.$N))",
fieldType(), rawValue,
valueBuilder.unsafe(), getVolatile(), field, Unsafe.class, arrayBase(),
Unsafe.class, arrayScale());
methodBuilder.addStatement("return $N", wrap(valueBuilder, methodBuilder, rawValue));
}
@Override
void generateReadMarshallable(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
if (fieldModel.type != char.class || fieldType() != fieldModel.type) {
methodBuilder.addStatement("$N = " + unwrap(methodBuilder, "bytes.$N()"),
field, fieldModel.readMethod());
} else {
methodBuilder.addStatement("$N = (char) bytes.readUnsignedShort()", field);
}
}
@Override
void generateArrayElementReadMarshallable(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
if (fieldModel.type != char.class || fieldType() != fieldModel.type) {
methodBuilder.addStatement("$N[index] = " + unwrap(methodBuilder, "bytes.$N()"),
field, fieldModel.readMethod());
} else {
methodBuilder.addStatement("$N[index] = (char) bytes.readUnsignedShort()", field);
}
}
@Override
void generateEquals(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
methodBuilder.addCode("if ($N() != other.$N()) return false;\n",
fieldModel.getOrGetVolatile().getName(), fieldModel.getOrGetVolatile().getName());
}
@Override
void generateArrayElementEquals(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
String get = arrayFieldModel.getOrGetVolatile().getName();
methodBuilder.addCode("if ($N(index) != other.$N(index)) return false;\n", get, get);
}
@Override
String generateHashCode(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
return String.format("%s.hashCode(%s())",
boxed(fieldModel.type).getName(), fieldModel.getOrGetVolatile().getName());
}
@Override
String generateArrayElementHashCode(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
return String.format("%s.hashCode(%s(index))",
boxed(fieldModel.type).getName(), arrayFieldModel.getOrGetVolatile().getName());
}
}