/**
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.flink.python.api.streaming.util;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.python.api.types.CustomTypeWrapper;
import java.nio.ByteBuffer;
import java.util.ArrayList;
public class SerializationUtils {
public static final byte TYPE_BOOLEAN = 34;
public static final byte TYPE_BYTE = 33;
public static final byte TYPE_INTEGER = 32;
public static final byte TYPE_LONG = 31;
public static final byte TYPE_DOUBLE = 30;
public static final byte TYPE_FLOAT = 29;
public static final byte TYPE_STRING = 28;
public static final byte TYPE_BYTES = 27;
public static final byte TYPE_NULL = 26;
private enum SupportedTypes {
TUPLE, BOOLEAN, BYTE, BYTES, INTEGER, LONG, FLOAT, DOUBLE, STRING, NULL, CUSTOMTYPEWRAPPER
}
@SuppressWarnings("unchecked")
public static <IN> Serializer<IN> getSerializer(IN value) {
String className = value.getClass().getSimpleName().toUpperCase();
if (className.startsWith("TUPLE")) {
className = "TUPLE";
}
if (className.startsWith("BYTE[]")) {
className = "BYTES";
}
SupportedTypes type = SupportedTypes.valueOf(className);
Serializer<?> serializer;
switch (type) {
case TUPLE:
serializer = new TupleSerializer((Tuple) value);
break;
case BOOLEAN:
serializer = new BooleanSerializer();
break;
case BYTE:
serializer = new ByteSerializer();
break;
case BYTES:
serializer = new BytesSerializer();
break;
case INTEGER:
serializer = new IntSerializer();
break;
case LONG:
serializer = new LongSerializer();
break;
case STRING:
serializer = new StringSerializer();
break;
case FLOAT:
serializer = new FloatSerializer();
break;
case DOUBLE:
serializer = new DoubleSerializer();
break;
case NULL:
serializer = new NullSerializer();
break;
case CUSTOMTYPEWRAPPER:
serializer = new CustomTypeWrapperSerializer((CustomTypeWrapper) value);
break;
default:
throw new IllegalArgumentException("Unsupported Type encountered: " + type);
}
return (Serializer<IN>) serializer;
}
public abstract static class Serializer<IN> {
private byte[] typeInfo = null;
public byte[] serialize(IN value) {
if (typeInfo == null) {
typeInfo = new byte[getTypeInfoSize()];
ByteBuffer typeBuffer = ByteBuffer.wrap(typeInfo);
putTypeInfo(typeBuffer);
}
byte[] bytes = serializeWithoutTypeInfo(value);
byte[] total = new byte[typeInfo.length + bytes.length];
ByteBuffer.wrap(total).put(typeInfo).put(bytes);
return total;
}
public abstract byte[] serializeWithoutTypeInfo(IN value);
protected abstract void putTypeInfo(ByteBuffer buffer);
protected int getTypeInfoSize() {
return 1;
}
}
public static class CustomTypeWrapperSerializer extends Serializer<CustomTypeWrapper> {
private final byte type;
public CustomTypeWrapperSerializer(CustomTypeWrapper value) {
this.type = value.getType();
}
@Override
public byte[] serializeWithoutTypeInfo(CustomTypeWrapper value) {
byte[] result = new byte[4 + value.getData().length];
ByteBuffer.wrap(result).putInt(value.getData().length).put(value.getData());
return result;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(type);
}
}
public static class ByteSerializer extends Serializer<Byte> {
@Override
public byte[] serializeWithoutTypeInfo(Byte value) {
return new byte[]{value};
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_BYTE);
}
}
public static class BooleanSerializer extends Serializer<Boolean> {
@Override
public byte[] serializeWithoutTypeInfo(Boolean value) {
return new byte[]{(byte) (value ? 1 : 0)};
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_BOOLEAN);
}
}
public static class IntSerializer extends Serializer<Integer> {
@Override
public byte[] serializeWithoutTypeInfo(Integer value) {
byte[] data = new byte[4];
ByteBuffer.wrap(data).putInt(value);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_INTEGER);
}
}
public static class LongSerializer extends Serializer<Long> {
@Override
public byte[] serializeWithoutTypeInfo(Long value) {
byte[] data = new byte[8];
ByteBuffer.wrap(data).putLong(value);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_LONG);
}
}
public static class StringSerializer extends Serializer<String> {
@Override
public byte[] serializeWithoutTypeInfo(String value) {
byte[] string = value.getBytes(ConfigConstants.DEFAULT_CHARSET);
byte[] data = new byte[4 + string.length];
ByteBuffer.wrap(data).putInt(string.length).put(string);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_STRING);
}
}
public static class FloatSerializer extends Serializer<Float> {
@Override
public byte[] serializeWithoutTypeInfo(Float value) {
byte[] data = new byte[4];
ByteBuffer.wrap(data).putFloat(value);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_FLOAT);
}
}
public static class DoubleSerializer extends Serializer<Double> {
@Override
public byte[] serializeWithoutTypeInfo(Double value) {
byte[] data = new byte[8];
ByteBuffer.wrap(data).putDouble(value);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_DOUBLE);
}
}
public static class NullSerializer extends Serializer<Object> {
@Override
public byte[] serializeWithoutTypeInfo(Object value) {
return new byte[0];
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_NULL);
}
}
public static class BytesSerializer extends Serializer<byte[]> {
@Override
public byte[] serializeWithoutTypeInfo(byte[] value) {
byte[] data = new byte[4 + value.length];
ByteBuffer.wrap(data).putInt(value.length).put(value);
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put(TYPE_BYTES);
}
}
public static class TupleSerializer extends Serializer<Tuple> {
private final Serializer<Object>[] serializer;
public TupleSerializer(Tuple value) {
serializer = new Serializer[value.getArity()];
for (int x = 0; x < serializer.length; x++) {
serializer[x] = getSerializer(value.getField(x));
}
}
@Override
public byte[] serializeWithoutTypeInfo(Tuple value) {
ArrayList<byte[]> bits = new ArrayList<>();
int totalSize = 0;
for (int x = 0; x < serializer.length; x++) {
byte[] bit = serializer[x].serializeWithoutTypeInfo(value.getField(x));
bits.add(bit);
totalSize += bit.length;
}
int pointer = 0;
byte[] data = new byte[totalSize];
for (byte[] bit : bits) {
System.arraycopy(bit, 0, data, pointer, bit.length);
pointer += bit.length;
}
return data;
}
@Override
public void putTypeInfo(ByteBuffer buffer) {
buffer.put((byte) serializer.length);
for (Serializer<Object> s : serializer) {
s.putTypeInfo(buffer);
}
}
@Override
public int getTypeInfoSize() {
int size = 1;
for (Serializer<Object> s : serializer) {
size += s.getTypeInfoSize();
}
return size;
}
}
}