package com.xiaomi.infra.galaxy.sds.thrift; import libthrift091.TBase; import libthrift091.TDeserializer; import libthrift091.TException; import libthrift091.TSerializer; import libthrift091.protocol.TCompactProtocol; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import static com.xiaomi.infra.galaxy.sds.thrift.Value.binarySetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.binaryValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.boolSetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.boolValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.doubleSetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.doubleValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int16SetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int16Value; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int32SetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int32Value; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int64SetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int64Value; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int8SetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.int8Value; import static com.xiaomi.infra.galaxy.sds.thrift.Value.stringSetValue; import static com.xiaomi.infra.galaxy.sds.thrift.Value.stringValue; public class DatumUtil { public static <T> Map<String, Datum> toDatum(Map<String, T> keyValues) { return toDatum(keyValues, (Map<String, DataType>) null); } public static <T> Map<String, Datum> toDatum(Map<String, T> keyValues, Map<String, DataType> attributes) { if (keyValues != null) { try { Map<String, Datum> kvs = new HashMap<String, Datum>(); for (Map.Entry<String, T> kv : keyValues.entrySet()) { if (attributes != null && kv.getValue() instanceof Set) { kvs.put(kv.getKey(), fromSetToDatum((Set) (kv.getValue()), attributes.get(kv.getKey()))); } else { kvs.put(kv.getKey(), DatumUtil.toDatum(kv.getValue())); } } return kvs; } catch (IllegalArgumentException iae) { throw new IllegalArgumentException("Failed to convert key values [" + keyValues + "]: " + iae.getMessage(), iae); } } return null; } public static <T> Datum fromSetToDatum(Set<T> set, DataType dataType) { List<T> list = new ArrayList<T>(); for (T e : set) { list.add(e); } switch (dataType) { case BOOL_SET: return toDatum(list, DataType.BOOL); case INT8_SET: return toDatum(list, DataType.INT8); case INT16_SET: return toDatum(list, DataType.INT16); case INT32_SET: return toDatum(list, DataType.INT32); case INT64_SET: return toDatum(list, DataType.INT64); case FLOAT_SET: return toDatum(list, DataType.FLOAT); case DOUBLE_SET: return toDatum(list, DataType.DOUBLE); case STRING_SET: return toDatum(list, DataType.STRING); case BINARY_SET: return toDatum(list, DataType.BINARY); default: throw new RuntimeException("Unsupported repeated type " + dataType); } } public static Datum toDatum(Object value) { return toDatum(value, null); } public static Datum toDatum(Object value, DataType repeatedType) { if (value == null) { throw new IllegalArgumentException("Datum must not be null"); } if (repeatedType == null) { if (value instanceof Boolean) { return newDatum(DataType.BOOL, boolValue((Boolean) value)); } else if (value instanceof Byte) { return newDatum(DataType.INT8, int8Value((Byte) value)); } else if (value instanceof Short) { return newDatum(DataType.INT16, int16Value((Short) value)); } else if (value instanceof Integer) { return newDatum(DataType.INT32, int32Value((Integer) value)); } else if (value instanceof Long) { return newDatum(DataType.INT64, int64Value((Long) value)); } else if (value instanceof Float) { return newDatum(DataType.FLOAT, doubleValue((Float) value)); } else if (value instanceof Double) { return newDatum(DataType.DOUBLE, doubleValue((Double) value)); } else if (value instanceof String) { return newDatum(DataType.STRING, stringValue((String) value)); } else if (value instanceof byte[]) { return newDatum(DataType.BINARY, binaryValue((byte[]) value)); } else if (value instanceof ByteBuffer) { return newDatum(DataType.BINARY, binaryValue((ByteBuffer) value)); } else { throw new RuntimeException("Unsupported datum type: " + value.getClass().getSimpleName() + ", value: " + value); } } else { assert value instanceof List; switch (repeatedType) { case BOOL: return newDatum(DataType.BOOL_SET, boolSetValue((List<Boolean>) value)); case INT8: return newDatum(DataType.INT8_SET, int8SetValue((List<Byte>) value)); case INT16: return newDatum(DataType.INT16_SET, int16SetValue((List<Short>) value)); case INT32: return newDatum(DataType.INT32_SET, int32SetValue((List<Integer>) value)); case INT64: return newDatum(DataType.INT64_SET, int64SetValue((List<Long>) value)); case FLOAT: return newDatum(DataType.FLOAT_SET, doubleSetValue(fromFloatListToDoubleList((List<Float>) value))); case DOUBLE: return newDatum(DataType.DOUBLE_SET, doubleSetValue((List<Double>) value)); case STRING: return newDatum(DataType.STRING_SET, stringSetValue((List<String>) value)); case BINARY: if (((List) value).isEmpty()) { return newDatum(DataType.BINARY_SET, binarySetValue(new ArrayList<ByteBuffer>())); } if (((List) value).get(0) instanceof byte[]) { return newDatum(DataType.BINARY_SET, binarySetValue(fromByteArrayListToByteBufferList((List<byte[]>) value))); } else if (((List) value).get(0) instanceof ByteBuffer) { return newDatum(DataType.BINARY_SET, binarySetValue((List<ByteBuffer>) value)); } default: throw new RuntimeException("Unsupported datum type: " + repeatedType); } } } private static Datum newDatum(DataType type, Value value) { return new Datum().setType(type).setValue(value); } private static List<Double> fromFloatListToDoubleList(List<Float> floatList) { List<Double> doubleList = new ArrayList<Double>(); for (float e : floatList) { doubleList.add((double) e); } return doubleList; } private static List<ByteBuffer> fromByteArrayListToByteBufferList(List<byte[]> byteArrayList) { List<ByteBuffer> byteBufferList = new ArrayList<ByteBuffer>(); for (byte[] e : byteArrayList) { byteBufferList.add(ByteBuffer.wrap(e)); } return byteBufferList; } public static List<Map<String, Object>> fromDatum(List<Map<String, Datum>> keyValuesList) { if (keyValuesList != null) { List<Map<String, Object>> kvsList = new ArrayList<Map<String, Object>>(); for (Map<String, Datum> kvs : keyValuesList) { kvsList.add(fromDatum(kvs)); } return kvsList; } return null; } public static Map<String, Object> fromDatum(Map<String, Datum> keyValues) { if (keyValues != null) { Map<String, Object> kvs = new HashMap<String, Object>(); for (Map.Entry<String, Datum> kv : keyValues.entrySet()) { kvs.put(kv.getKey(), fromDatum(kv.getValue())); } return kvs; } return null; } public static Number fromNumericDatum(Datum datum) throws IllegalArgumentException { if (datum != null) { Object value = fromDatum(datum); if (value == null || value instanceof Number) { return (Number) value; } else { throw new IllegalArgumentException( "Input datum is expected to be numeric value, but actual type is: " + value.getClass().getSimpleName() ); } } return null; } public static Map<String, Number> fromNumericDatum(Map<String, Datum> keyValues) throws IllegalArgumentException { if (keyValues != null) { Map<String, Number> kvsBo = new HashMap<String, Number>(); for (Map.Entry<String, Datum> kv : keyValues.entrySet()) { Object value = fromDatum(kv.getValue()); if (value == null || value instanceof Number) { kvsBo.put(kv.getKey(), (Number) value); } else { throw new IllegalArgumentException( "Input datum is expected to be numeric value, but actual type is: " + value.getClass().getSimpleName() ); } } return kvsBo; } return null; } public static Object fromDatum(Datum datum) { if (datum == null) { // datum should not be null, keep silent and let service layer do further validation return null; } if (datum.getType() == null || datum.getValue() == null) { throw new IllegalArgumentException("Datum must has value and type"); } Value value = datum.getValue(); switch (datum.getType()) { case BOOL: return value.getBoolValue(); case INT8: return value.getInt8Value(); case INT16: return value.getInt16Value(); case INT32: return value.getInt32Value(); case INT64: return value.getInt64Value(); case FLOAT: return (float) value.getDoubleValue(); case DOUBLE: return value.getDoubleValue(); case STRING: return value.getStringValue(); case BINARY: case RAWBINARY: return value.getBinaryValue(); case BOOL_SET: return fromListToSet(value.getBoolSetValue()); case INT8_SET: return fromListToSet(value.getInt8SetValue()); case INT16_SET: return fromListToSet(value.getInt16SetValue()); case INT32_SET: return fromListToSet(value.getInt32SetValue()); case INT64_SET: return fromListToSet(value.getInt64SetValue()); case FLOAT_SET: return fromListToSetFloat(value.getDoubleSetValue()); case DOUBLE_SET: return fromListToSet(value.getDoubleSetValue()); case STRING_SET: return fromListToSet(value.getStringSetValue()); case BINARY_SET: return fromListToSetByteArray(value.getBinarySetValue()); default: throw new IllegalArgumentException("Unsupported datum type: " + datum.getType() + ", value: " + value); } } private static Set<Float> fromListToSetFloat(List<Double> list) { Set<Float> set = new HashSet<Float>(); for (double e : list) { set.add((float) e); } return set; } private static Set<byte[]> fromListToSetByteArray(List<ByteBuffer> list) { Set<byte[]> set = new TreeSet<byte[]>(new Comparator<byte[]>() { @Override public int compare(byte[] left, byte[] right) { return compareTo(left, 0, left.length, right, 0, right.length); } public int compareTo(byte[] buffer1, int offset1, int length1, byte[] buffer2, int offset2, int length2) { if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { return 0; } //WritableComparator code int end1 = offset1 + length1; int end2 = offset2 + length2; for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { int a = (buffer1[i] & 0xff); int b = (buffer2[j] & 0xff); if (a != b) { return a - b; } } return length1 - length2; } }); for (ByteBuffer e : list) { set.add(e.array()); } return set; } private static <T> Set<T> fromListToSet(List<T> list) { Set<T> set = new HashSet<T>(); set.addAll(list); return set; } public static byte[] serialize(Datum datum) { try { TSerializer serializer = new TSerializer(new TCompactProtocol.Factory()); return serializer.serialize(datum); } catch (TException te) { throw new RuntimeException("Failed to serialize thrift object: " + datum, te); } } public static Datum deserialize(byte[] bytes) { try { TDeserializer deserializer = new TDeserializer(new TCompactProtocol.Factory()); Datum datum = new Datum(); deserializer.deserialize(datum, bytes); return datum; } catch (TException te) { throw new RuntimeException("Failed to deserialize thrift object", te); } } public static byte[] serializeDatumMap(Map<String, Datum> record) { try { TSerializer serializer = new TSerializer(new TCompactProtocol.Factory()); return serializer.serialize(new DatumMap().setData(record)); } catch (TException te) { throw new RuntimeException("Failed to serialize thrift object: " + record, te); } } public static Map<String, Datum> deserializeDatumMap(byte[] bytes) { try { TDeserializer deserializer = new TDeserializer(new TCompactProtocol.Factory()); DatumMap datumMap = new DatumMap(); deserializer.deserialize(datumMap, bytes); return datumMap.getData(); } catch (TException te) { throw new RuntimeException("Failed to deserialize thrift object", te); } } public static <T extends TBase> byte[] serialize(T t) { try { TSerializer serializer = new TSerializer(new TCompactProtocol.Factory()); return serializer.serialize(t); } catch (TException te) { throw new RuntimeException("Failed to serialize thrift object: " + t, te); } } public static <T extends TBase> T deserialize(byte[] bytes, Class<T> clazz) throws InstantiationException, IllegalAccessException { try { TDeserializer deserializer = new TDeserializer(new TCompactProtocol.Factory()); T instance = clazz.newInstance(); deserializer.deserialize(instance, bytes); return instance; } catch (TException te) { throw new RuntimeException("Failed to deserialize thrift object", te); } } }