package uk.ac.imperial.lsds.seep.api.data;
import java.nio.ByteBuffer;
import uk.ac.imperial.lsds.seep.errors.SchemaException;
public class OTuple {
private Schema schema;
private Object[] values;
private int fixedSchemaSize;
private byte[] data;
public OTuple() { }
public OTuple(Schema schema){
this.schema = schema;
this.values = new Object[schema.fields().length];
if(! schema.isVariableSize()){
// This only happens once
this.fixedSchemaSize = this.calculateSizeFromSchema();
}
else {
this.fixedSchemaSize = -1; // variable size, so it needs to be computed per tuple
}
}
public void setValues(Object[] values) {
this.values = values;
}
public Object[] getValues() {
return values;
}
public int getTupleSize() {
return fixedSchemaSize;
}
public byte[] getData(){
return data;
}
public static byte[] create(Schema schema, String[] fields, Object[] vs) {
OTuple o = new OTuple(schema);
if(fields.length != vs.length){
throw new SchemaException("Mismatch between fieldNames and values");
}
if(fields.length != schema.fields().length){
throw new SchemaException("Mismatch between input fields and schema fields");
}
Object[] values = new Object[vs.length];
for(int i = 0; i < fields.length; i++){
Object toTypeCheck = vs[i];
if(! schema.typeCheck(fields[i], toTypeCheck)) {
String error = "Field: " + fields[i].toString() + " does not type check";
throw new SchemaException(error);
}
else{
values[i] = toTypeCheck;
}
}
o.values = values;
return o.getBytes();
}
public static byte[] createUnsafe(Type[] types, Object[] values, int size){
byte[] data = new byte[size];
ByteBuffer wrapper = ByteBuffer.wrap(data);
for(int i = 0; i < values.length; i++) {
Type t = types[i];
t.write(wrapper, values[i]);
}
return data;
}
public void writeValues(ByteBuffer bb) {
Type[] types = schema.fields();
bb.putInt(this.fixedSchemaSize);
for(int i = 0; i < values.length; i++) {
Type t = types[i];
t.write(bb, values[i]);
}
}
public static byte[] createUnsafe(Type[] types, Object[] values){
int size = calculateSizeFromTypes(types, values);
return createUnsafe(types, values, size);
}
private byte[] getBytes(){
int requiredSize = this.fixedSchemaSize;
if(schema.isVariableSize()){
requiredSize = calculateSizeFromSchema();
}
data = new byte[requiredSize];
ByteBuffer wrapper = ByteBuffer.wrap(data);
for(int i = 0; i < values.length; i++){
Type t = schema.fields()[i];
t.write(wrapper, values[i]);
}
return data;
}
private int calculateSizeFromSchema(){
int size = 0;
for(int i = 0; i < schema.fields().length; i++){
Type t = schema.fields()[i];
size = size + t.sizeOf(values[i]);
}
return size;
}
public static int calculateSizeFromTypes(Type[] types, Object[] values){
int size = 0;
for(int i = 0; i < types.length; i++){
size = size + types[i].sizeOf(values[i]);
}
return size;
}
public static byte[] getWireBytes(Schema schema, String[] fields, Object[] vs) {
byte[] data = OTuple.create(schema, fields, vs);
int tuplesInBatch = 1;
int tupleSize = data.length;
int currentBatchSize = tupleSize + TupleInfo.TUPLE_SIZE_OVERHEAD;
ByteBuffer buf = ByteBuffer.allocate(tupleSize + TupleInfo.PER_BATCH_OVERHEAD_SIZE + TupleInfo.TUPLE_SIZE_OVERHEAD);
buf.put((byte) 0); // control
buf.putInt(tuplesInBatch);
buf.putInt(currentBatchSize);
buf.putInt(tupleSize);
buf.put(data);
return buf.array();
}
}