/*
* Copyright 2014 Higher Frequency Trading
*
* http://www.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.io.serialization.direct;
import net.openhft.lang.Jvm;
import java.lang.reflect.Field;
import java.util.List;
import static net.openhft.lang.io.NativeBytes.UNSAFE;
public class DirectSerializationMetadata {
private static final int OBJECT_ALIGNMENT = 8;
private static final int OBJECT_ALIGNMENT_MASK = OBJECT_ALIGNMENT - 1;
static final long NATIVE_WORD_SIZE = Jvm.is64Bit() ? 8 : 4;
private static final long OOP_SIZE = UNSAFE.arrayIndexScale(Object[].class);
static final long OBJECT_HEADER_SIZE = NATIVE_WORD_SIZE + OOP_SIZE; // Object header has a native sized mark word + variable sized oop to klass meta object
private static final SerializationMetadata EmptyObjectMetadata = new SerializationMetadata(0, 0);
public static final class SerializationMetadata {
final long start;
final long length;
SerializationMetadata(long start, long length) {
this.start = start;
this.length = length;
}
@Override
public String toString() {
return String.format("SerializationMetadata: Start %s Length %s", start, length);
}
}
public static SerializationMetadata extractMetadata(List<Field> fields) {
if (fields.isEmpty()) return EmptyObjectMetadata;
Offsets offsets = minMaxOffsets(fields);
long totalSize = OBJECT_HEADER_SIZE + offsets.max - offsets.min + 1;
return new SerializationMetadata(offsets.min, padToObjectAlignment(totalSize) - OBJECT_HEADER_SIZE);
}
public static SerializationMetadata extractMetadataForPartialCopy(List<Field> fields) {
if (fields.isEmpty()) return EmptyObjectMetadata;
Offsets offsets = minMaxOffsets(fields);
Field lastField = fields.get(fields.size() - 1);
return new SerializationMetadata(offsets.min, offsets.max + sizeOf(lastField) - OBJECT_HEADER_SIZE);
}
private static Offsets minMaxOffsets(List<Field> fields) {
long minOffset = UNSAFE.objectFieldOffset(fields.get(0));
long maxOffset = UNSAFE.objectFieldOffset(fields.get(fields.size() - 1));
return new Offsets(minOffset, maxOffset);
}
private static long padToObjectAlignment(long length) {
if ((length & OBJECT_ALIGNMENT_MASK) != 0) {
long padding = OBJECT_ALIGNMENT - (length & OBJECT_ALIGNMENT_MASK);
length += padding;
}
return length;
}
private static long sizeOf(Field field) {
if (boolean.class.equals(field.getType())) return 1;
else if (byte.class.equals(field.getType())) return 1;
else if (short.class.equals(field.getType())) return 2;
else if (char.class.equals(field.getType())) return 2;
else if (int.class.equals(field.getType())) return 4;
else if (float.class.equals(field.getType())) return 4;
else return 8;
}
private static final class Offsets {
public final long min;
public final long max;
private Offsets(long min, long max) {
this.min = min;
this.max = max;
}
}
}