package org.corfudb.util.serializer;
import com.google.common.collect.ImmutableMap;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.corfudb.runtime.CorfuRuntime;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Created by mwei on 2/18/16.
*/
@Slf4j
public class PrimitiveSerializer implements ISerializer {
final private byte type;
public static final Map<Byte, DeserializerFunction> DeserializerMap =
Arrays.stream(Primitives.values())
.collect(Collectors.toMap(Primitives::getTypeNum, Primitives::getDeserializer));
public static final Map<Class, SerializerFunction> SerializerMap = getSerializerMap();
public PrimitiveSerializer(byte type) {
this.type = type;
}
@Override
public byte getType() {
return type;
}
@SuppressWarnings("unchecked")
private static Map<Class, SerializerFunction> getSerializerMap() {
ImmutableMap.Builder b = ImmutableMap.<Class, SerializerFunction>builder();
Arrays.stream(Primitives.values())
.forEach(e -> {
b.put(e.getClassType(), e.getSerializer());
if (e.getPrimitiveClass() != null) {
b.put(e.getPrimitiveClass(), e.getSerializer());
}
}
);
return b.build();
}
@SuppressWarnings("unchecked")
static <T, R> void writeArray(T[] o, ByteBuf b, BiFunction<ByteBuf, R, ByteBuf> applyFunc) {
int length = Array.getLength(o);
b.writeInt(length);
Arrays.stream(o)
.forEach(i -> applyFunc.apply(b, (R) i));
}
@SuppressWarnings("unchecked")
static <T, R> T[] readArray(ByteBuf b, Function<ByteBuf, T> applyFunc, Function<Integer, T[]> arrayGen) {
int length = b.readInt();
T[] r = arrayGen.apply(length);
for (int i = 0; i < length; i++) {
r[i] = applyFunc.apply(b);
}
return r;
}
static void writeBytes(Object o, ByteBuf b) {
int length = Array.getLength(o);
b.writeInt(length);
b.writeBytes((byte[]) o);
}
static byte[] readBytes(ByteBuf b, CorfuRuntime rt) {
int length = b.readInt();
byte[] bytes = new byte[length];
b.readBytes(bytes, 0, length);
return bytes;
}
/**
* Deserialize an object from a given byte buffer.
*
* @param b The bytebuf to deserialize.
* @return The deserialized object.
*/
@Override
@SuppressWarnings("unchecked")
public Object deserialize(ByteBuf b, CorfuRuntime rt) {
byte type = b.readByte();
DeserializerFunction d = DeserializerMap.get(type);
return d.deserialize(b, rt);
}
/**
* Serialize an object into a given byte buffer.
*
* @param o The object to serialize.
* @param b The bytebuf to serialize it into.
*/
@Override
@SuppressWarnings("unchecked")
public void serialize(Object o, ByteBuf b) {
if (o.getClass().getName().contains("$ByteBuddy$")) {
((SerializerFunction<Object>) Primitives.CORFU_SMR.getSerializer()).serialize(o, b);
} else {
SerializerFunction f = SerializerMap.get(o.getClass());
if (f == null) {
throw new RuntimeException("Unsupported class for serialization: " + o.getClass());
}
f.serialize(o, b);
}
}
enum Primitives {
BYTE(0, Byte.class, byte.class, (o, b) -> b.writeByte(o), (b, r) -> b.readByte()),
SHORT(1, Short.class, short.class, (o, b) -> b.writeShort(o), (b, r) -> b.readShort()),
INTEGER(2, Integer.class, int.class, (o, b) -> b.writeInt(o), (b, r) -> b.readInt()),
LONG(3, Long.class, long.class, (o, b) -> b.writeLong(o), (b, r) -> b.readLong()),
BOOLEAN(4, Boolean.class, boolean.class, (o, b) -> b.writeBoolean(o), (b, r) -> b.readBoolean()),
DOUBLE(5, Double.class, double.class, (o, b) -> b.writeDouble(o), (b, r) -> b.readDouble()),
FLOAT(6, Float.class, float.class, (o, b) -> b.writeFloat(o), (b, r) -> b.readFloat()),
BYTE_ARRAY(7, Byte[].class, byte[].class, PrimitiveSerializer::writeBytes,
(DeserializerFunction)PrimitiveSerializer::readBytes),
SHORT_ARRAY(8, Short[].class, short[].class, (o, b) -> writeArray(o, b, ByteBuf::writeShort),
(b, r) -> readArray(b, ByteBuf::readShort, Short[]::new)),
INTEGER_ARRAY(9, Integer[].class, int[].class, (o, b) -> writeArray(o, b, ByteBuf::writeInt),
(b, r) -> readArray(b, ByteBuf::readInt, Integer[]::new)),
LONG_ARRAY(10, Long[].class, long[].class, (o, b) -> writeArray(o, b, ByteBuf::writeLong),
(b, r) -> readArray(b, ByteBuf::readLong, Long[]::new)),
BOOLEAN_ARRAY(11, Boolean[].class, boolean[].class, (o, b) -> writeArray(o, b, ByteBuf::writeBoolean),
(b, r) -> readArray(b, ByteBuf::readBoolean, Boolean[]::new)),
DOUBLE_ARRAY(12, Double[].class, double[].class, (o, b) -> writeArray(o, b, ByteBuf::writeDouble),
(b, r) -> readArray(b, ByteBuf::readDouble, Double[]::new)),
FLOAT_ARRAY(13, Float[].class, float[].class, (o, b) -> writeArray(o, b, ByteBuf::writeFloat),
(b, r) -> readArray(b, ByteBuf::readFloat, Float[]::new)),
STRING(14, String.class, null, (o, b) -> {
b.writeInt(o.length());
b.writeBytes(o.getBytes());
},
(b, r) -> {
int length = b.readInt();
byte[] bs = new byte[length];
b.readBytes(bs, 0, length);
return new String(bs);
}),
CORFU_SMR(15, Object.class, null, (o, b) -> {
String className = o.getClass().toString();
String SMRClass = className.split("\\$")[0];
className = "CorfuObject";
byte[] classNameBytes = className.getBytes();
b.writeShort(classNameBytes.length);
b.writeBytes(classNameBytes);
byte[] SMRClassNameBytes = SMRClass.getBytes();
b.writeShort(SMRClassNameBytes.length);
b.writeBytes(SMRClassNameBytes);
try {
Field f = o.getClass().getDeclaredField("_corfuStreamID");
f.setAccessible(true);
UUID id = (UUID) f.get(o);
log.trace("Serializing a CorfuObject of type {} as a stream pointer to {}", SMRClass, id);
b.writeLong(id.getMostSignificantBits());
b.writeLong(id.getLeastSignificantBits());
} catch (NoSuchFieldException | IllegalAccessException nsfe) {
log.error("Error serializing fields");
throw new RuntimeException(nsfe);
}
},
(b, r) -> {
int SMRClassNameLength = b.readShort();
byte[] SMRClassNameBytes = new byte[SMRClassNameLength];
b.readBytes(SMRClassNameBytes, 0, SMRClassNameLength);
String SMRClassName = new String(SMRClassNameBytes);
try {
return r.getObjectsView().build()
.setStreamID(new UUID(b.readLong(), b.readLong()))
.setType(Class.forName(SMRClassName))
.open();
} catch (ClassNotFoundException cnfe) {
log.error("Exception during deserialization!", cnfe);
throw new RuntimeException(cnfe);
}
});
@Getter
public final byte typeNum;
@Getter
public final Class<?> classType;
@Getter
public final Class<?> primitiveClass;
@Getter
public final SerializerFunction<?> serializer;
@Getter
public final DeserializerFunction deserializer;
<T> Primitives(int typeNum, Class<T> classType, Class<?> primitiveClass,
SerializerFunction<T> serializer, DeserializerFunction<T> deserializer) {
this.typeNum = (byte) typeNum;
this.classType = classType;
this.primitiveClass = primitiveClass;
this.serializer = serializer;
this.deserializer = deserializer;
}
}
@FunctionalInterface
interface DeserializerFunction<T> {
T deserialize(ByteBuf b, CorfuRuntime r);
}
@FunctionalInterface
interface SerializerFunction<T> {
void serialize(T o, ByteBuf b);
}
}