| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.v23.vom; |
| |
| import io.v.v23.vdl.Kind; |
| import io.v.v23.vdl.NativeTypes; |
| import io.v.v23.vdl.Types; |
| import io.v.v23.vdl.VdlAny; |
| import io.v.v23.vdl.VdlArray; |
| import io.v.v23.vdl.VdlBool; |
| import io.v.v23.vdl.VdlByte; |
| import io.v.v23.vdl.VdlEnum; |
| import io.v.v23.vdl.VdlField; |
| import io.v.v23.vdl.VdlFloat32; |
| import io.v.v23.vdl.VdlFloat64; |
| import io.v.v23.vdl.VdlInt16; |
| import io.v.v23.vdl.VdlInt32; |
| import io.v.v23.vdl.VdlInt64; |
| import io.v.v23.vdl.VdlInt8; |
| import io.v.v23.vdl.VdlList; |
| import io.v.v23.vdl.VdlOptional; |
| import io.v.v23.vdl.VdlString; |
| import io.v.v23.vdl.VdlStruct; |
| import io.v.v23.vdl.VdlType; |
| import io.v.v23.vdl.VdlTypeObject; |
| import io.v.v23.vdl.VdlUint16; |
| import io.v.v23.vdl.VdlUint32; |
| import io.v.v23.vdl.VdlUint64; |
| import io.v.v23.vdl.VdlUnion; |
| import io.v.v23.vdl.VdlValue; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * BinaryEncoder writes VDL values to {@code OutputStream} in binary VOM format. |
| */ |
| public class BinaryEncoder { |
| private final EncodingStream valueBuffer; |
| private final EncodingStream typeBuffer; |
| private final OutputStream out; |
| private final Map<VdlType, TypeId> visitedTypes; |
| private TypeId nextTypeId; |
| private boolean binaryMagicByteWritten; |
| private Version version; |
| private List<Long> typeIds; |
| private List<Long> anyLens; |
| |
| public BinaryEncoder(OutputStream out) { |
| this(out, Constants.DEFAULT_VERSION); |
| } |
| |
| public BinaryEncoder(OutputStream out, Version version) { |
| this.valueBuffer = new EncodingStream(); |
| this.typeBuffer = new EncodingStream(); |
| this.out = out; |
| this.visitedTypes = new HashMap<VdlType, TypeId>(); |
| this.nextTypeId = Constants.WIRE_ID_FIRST_USER_TYPE; |
| this.binaryMagicByteWritten = false; |
| this.version = version; |
| } |
| |
| /** |
| * Encodes a value into binary VOM format. |
| * |
| * @param type runtime VDL type of the value |
| * @param value the value to encode |
| * @throws IOException |
| */ |
| public void encodeValue(VdlType type, Object value) throws IOException { |
| if (!binaryMagicByteWritten) { |
| binaryMagicByteWritten = true; |
| out.write(version.getValue()); |
| } |
| valueBuffer.reset(); |
| typeIds = new ArrayList<Long>(); |
| anyLens = new ArrayList<Long>(); |
| TypeId typeId = getType(type); |
| writeValue(valueBuffer, value, type); |
| writeMessage(valueBuffer, BinaryUtil.hasAny(type), BinaryUtil.hasTypeObject(type), |
| false, typeId.getValue(), BinaryUtil.hasBinaryMsgLen(type)); |
| } |
| |
| /** |
| * Encodes a value into binary VOM format. |
| * |
| * @param type runtime {@code java.lang.reflectType} of the value |
| * @param value the value to encode |
| * @throws IOException |
| */ |
| public void encodeValue(Type type, Object value) throws IOException { |
| encodeValue(Types.getVdlTypeFromReflect(type), value); |
| } |
| |
| /** |
| * Encodes a VDL value into binary VOM format. |
| * |
| * @param value the value to encode |
| * @throws IOException |
| */ |
| public void encodeValue(VdlValue value) throws IOException { |
| encodeValue(value.vdlType(), value); |
| } |
| |
| private void writeMessage(ByteArrayOutputStream buffer, boolean hasAny, boolean hasTypeObject, |
| boolean typeIncomplete, long messageId, boolean encodeLength) |
| throws IOException { |
| if (version != Constants.VERSION_80 && typeIncomplete) { |
| out.write(Constants.WIRE_CTRL_TYPE_INCOMPLETE); |
| } |
| BinaryUtil.encodeInt(out, messageId); |
| if (version != Constants.VERSION_80 && (hasAny || hasTypeObject) && messageId > 0) { |
| BinaryUtil.encodeUint(out, typeIds.size()); |
| for (Long id : typeIds) { |
| BinaryUtil.encodeUint(out, id); |
| } |
| typeIds = null; |
| } |
| if (version != Constants.VERSION_80 && hasAny && messageId > 0) { |
| BinaryUtil.encodeUint(out, anyLens.size()); |
| for (Long len : anyLens) { |
| BinaryUtil.encodeUint(out, len); |
| } |
| anyLens = null; |
| } |
| if (encodeLength) { |
| BinaryUtil.encodeUint(out, buffer.size()); |
| } |
| buffer.writeTo(out); |
| } |
| |
| private TypeId getType(VdlType type) throws IOException { |
| return getTypeInternal(type, new HashSet<VdlType>()); |
| } |
| |
| private TypeId getTypeInternal(VdlType type, Set<VdlType> pending) throws IOException { |
| TypeId typeId = BootstrapType.getBootstrapTypeId(type); |
| if (typeId != null) { |
| return typeId; |
| } else if (visitedTypes.containsKey(type)) { |
| return visitedTypes.get(type); |
| } else { |
| return encodeType(type, pending); |
| } |
| } |
| |
| private TypeId encodeType(VdlType type, Set<VdlType> pending) throws IOException { |
| pending.add(type); |
| TypeId typeId = nextTypeId; |
| nextTypeId = new TypeId(nextTypeId.getValue() + 1); |
| visitedTypes.put(type, typeId); |
| |
| WireType wireType = convertToWireType(type, pending); |
| pending.remove(type); |
| boolean incomplete = typeIncomplete(type, pending, new HashSet<VdlType>()); |
| typeBuffer.reset(); |
| writeValue(typeBuffer, wireType, wireType.vdlType()); |
| writeMessage(typeBuffer, BinaryUtil.hasAny(type), BinaryUtil.hasTypeObject(type), |
| incomplete, -typeId.getValue(), true); |
| return typeId; |
| } |
| |
| private WireType convertToWireType(VdlType type, Set<VdlType> pending) throws IOException { |
| switch (type.getKind()) { |
| case INT8: |
| if (version == Constants.VERSION_80) { |
| throw new RuntimeException("int8 not supported in VOM version 80"); |
| } |
| // fallthrough |
| case BOOL: |
| case BYTE: |
| case UINT16: |
| case UINT32: |
| case UINT64: |
| case INT16: |
| case INT32: |
| case INT64: |
| case FLOAT32: |
| case FLOAT64: |
| case STRING: |
| return new WireType.NamedT(new WireNamed( |
| type.getName(), getTypeInternal(Types.primitiveTypeFromKind(type.getKind()), pending))); |
| case ARRAY: |
| return new WireType.ArrayT(new WireArray( |
| type.getName(), getTypeInternal(type.getElem(), pending), new VdlUint64(type.getLength()))); |
| case ENUM: |
| return new WireType.EnumT(new WireEnum(type.getName(), type.getLabels())); |
| case LIST: |
| return new WireType.ListT(new WireList(type.getName(), getTypeInternal(type.getElem(), pending))); |
| case MAP: |
| return new WireType.MapT(new WireMap( |
| type.getName(), getTypeInternal(type.getKey(),pending), |
| getTypeInternal(type.getElem(), pending))); |
| case STRUCT: |
| case UNION: |
| List<WireField> wireFields = new ArrayList<WireField>(); |
| for (VdlField field : type.getFields()) { |
| wireFields.add(new WireField(field.getName(), getTypeInternal(field.getType(), pending))); |
| } |
| if (type.getKind() == Kind.UNION) { |
| return new WireType.UnionT(new WireUnion(type.getName(), wireFields)); |
| } else { |
| return new WireType.StructT(new WireStruct(type.getName(), wireFields)); |
| } |
| case SET: |
| return new WireType.SetT(new WireSet(type.getName(), getTypeInternal(type.getKey(), pending))); |
| case OPTIONAL: |
| return new WireType.OptionalT(new WireOptional( |
| type.getName(), getTypeInternal(type.getElem(), pending))); |
| default: |
| throw new RuntimeException("Unknown wiretype for kind: " + type.getKind()); |
| } |
| } |
| |
| private boolean typeIncomplete(VdlType type, Set<VdlType> pending, Set<VdlType> seen) { |
| if (seen.contains(type)) { |
| return false; |
| } |
| seen.add(type); |
| if (pending.contains(type)) { |
| return true; |
| } |
| switch (type.getKind()) { |
| case OPTIONAL: |
| case ARRAY: |
| case LIST: |
| return typeIncomplete(type.getElem(), pending, seen); |
| case SET: |
| return typeIncomplete(type.getKey(), pending, seen); |
| case MAP: |
| return typeIncomplete(type.getKey(), pending, seen) || |
| typeIncomplete(type.getElem(), pending, seen); |
| case STRUCT: |
| case UNION: |
| for (VdlField field : type.getFields()) { |
| if (typeIncomplete(field.getType(), pending, seen)) { |
| return true; |
| } |
| } |
| return false; |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * Writes a value to output stream and returns true iff the value is non-zero. |
| * The returned value can be used skip encoding of zero fields in structs. |
| */ |
| private boolean writeValue(EncodingStream out, Object value, VdlType type) throws IOException { |
| if (value == null) { |
| value = VdlValue.zeroValue(type); |
| } |
| |
| // Convert native value. |
| NativeTypes.Converter converter = Types.getNativeTypeConverter(value.getClass()); |
| if (converter != null) { |
| VdlValue vdlValue = converter.vdlValueFromNative(value); |
| return writeValue(out, vdlValue, type); |
| } |
| switch (type.getKind()) { |
| case ANY: |
| return writeVdlAny(out, value); |
| case ARRAY: |
| if (type.getElem().getKind() == Kind.BYTE) { |
| return writeVdlBytes(out, value, type); |
| } else { |
| return writeVdlArray(out, value); |
| } |
| case BOOL: |
| return writeVdlBool(out, value); |
| case ENUM: |
| return writeVdlEnum(out, value); |
| case FLOAT32: |
| case FLOAT64: |
| return writeVdlFloat(out, value); |
| case INT8: |
| case INT16: |
| case INT32: |
| case INT64: |
| if (type.getKind() == Kind.INT8 && version == Constants.VERSION_80) { |
| throw new RuntimeException("int8 not supported in VOM version 80"); |
| } |
| return writeVdlInt(out, value); |
| case LIST: |
| if (type.getElem().getKind() == Kind.BYTE) { |
| return writeVdlBytes(out, value, type); |
| } else { |
| return writeVdlList(out, value, type); |
| } |
| case MAP: |
| return writeVdlMap(out, value, type); |
| case UNION: |
| return writeVdlUnion(out, value); |
| case OPTIONAL: |
| return writeVdlOptional(out, value); |
| case SET: |
| return writeVdlSet(out, value, type); |
| case STRING: |
| return writeVdlString(out, value); |
| case STRUCT: |
| return writeVdlStruct(out, value, type); |
| case BYTE: |
| case UINT16: |
| case UINT32: |
| case UINT64: |
| return writeVdlUint(out, value); |
| case TYPEOBJECT: |
| return writeVdlTypeObject(out, value); |
| default: |
| throw new RuntimeException("Unknown kind: " + type.getKind()); |
| } |
| } |
| |
| /** |
| * Writes a VDL any to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlAny(EncodingStream out, Object value) throws IOException { |
| expectClass(Kind.ANY, value, VdlAny.class); |
| VdlAny anyValue = (VdlAny) value; |
| Object elem = anyValue.getElem(); |
| |
| if (elem != null) { |
| long id = getType(anyValue.getElemType()).getValue(); |
| writeTypeId(out, id); |
| int anyLenIndex = -1; |
| long startPos = -1; |
| if (version != Constants.VERSION_80) { |
| anyLenIndex = anyLens.size(); |
| BinaryUtil.encodeUint(out, anyLenIndex); |
| anyLens.add(0L); |
| startPos = out.getCount(); |
| } |
| writeValue(out, elem, anyValue.getElemType()); |
| if (version != Constants.VERSION_80) { |
| long endPos = out.getCount(); |
| anyLens.set(anyLenIndex, endPos - startPos); |
| } |
| return true; |
| } else { |
| writeVdlControlByte(out, Constants.WIRE_CTRL_NIL); |
| return false; |
| } |
| } |
| |
| /** |
| * Writes the type id in a format suitable for the current VOM version. |
| */ |
| public void writeTypeId(OutputStream out, long id) throws IOException { |
| if (version == Constants.VERSION_80) { |
| BinaryUtil.encodeUint(out, id); |
| } else { |
| int index = typeIds.indexOf(id); |
| if (index == -1) { |
| index = typeIds.size(); |
| typeIds.add(id); |
| } |
| BinaryUtil.encodeUint(out, index); |
| } |
| } |
| |
| /** |
| * Writes a VDL array to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlArray(EncodingStream out, Object value) throws IOException { |
| expectClass(Kind.ARRAY, value, VdlArray.class); |
| VdlArray<?> arrayValue = (VdlArray<?>) value; |
| BinaryUtil.encodeUint(out, 0); |
| boolean isNonzero = false; |
| for (Object elem : arrayValue) { |
| isNonzero = writeValue(out, elem, arrayValue.vdlType().getElem()) || isNonzero; |
| } |
| return isNonzero; |
| } |
| |
| /** |
| * Writes a VDL bool to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlBool(OutputStream out, Object value) throws IOException { |
| boolean val; |
| if (value instanceof VdlBool) { |
| val = ((VdlBool) value).getValue(); |
| } else if (value instanceof Boolean) { |
| val = (Boolean) value; |
| } else { |
| throw new IOException("Unsupported VDL bool value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| int boolAsInt = 0; |
| if (val) { |
| boolAsInt = 1; |
| } |
| if (version == Constants.VERSION_80) { |
| out.write(boolAsInt); |
| } else { |
| BinaryUtil.encodeUint(out, boolAsInt); |
| } |
| return val; |
| } |
| |
| /** |
| * Writes a VDL byte to output stream and returns true iff the value is non-zero. |
| * In version 0x80, bytes are written directly as bytes to the stream. |
| * In later versions, they are written as vdl uints. |
| */ |
| private boolean writeVdlByte(EncodingStream out, Object value) throws IOException { |
| byte byteValue; |
| if (value instanceof VdlByte) { |
| byteValue = ((VdlByte) value).getValue(); |
| } else if (value instanceof Byte) { |
| byteValue = (Byte) value; |
| } else { |
| throw new IOException("Unsupported VDL byte value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| if (version == Constants.VERSION_80) { |
| out.write(byteValue); |
| } else { |
| int fullValue = byteValue; |
| if (fullValue < 0) { |
| fullValue += 0x100; |
| } |
| BinaryUtil.encodeUint(out, fullValue); |
| } |
| return byteValue != 0; |
| } |
| |
| /** |
| * Writes a VDL byte to output stream and returns true iff the value is non-zero. |
| * Control bytes are always written to the wire directly as bytes. |
| */ |
| private boolean writeVdlControlByte(EncodingStream out, Object value) throws IOException { |
| byte byteValue; |
| if (value instanceof VdlByte) { |
| byteValue = ((VdlByte) value).getValue(); |
| out.write(byteValue); |
| } else if (value instanceof Byte) { |
| byteValue = (Byte) value; |
| out.write(byteValue); |
| } else { |
| throw new IOException("Unsupported VDL control byte value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| return byteValue != 0; |
| } |
| |
| /** |
| * Writes a VDL enum to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlEnum(EncodingStream out, Object value) throws IOException { |
| expectClass(Kind.ENUM, value, VdlEnum.class); |
| int ordinal = ((VdlEnum) value).ordinal(); |
| BinaryUtil.encodeUint(out, ordinal); |
| return ordinal != 0; |
| } |
| |
| /** |
| * Writes a VDL float to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlFloat(EncodingStream out, Object value) throws IOException { |
| if (value instanceof VdlFloat32) { |
| return BinaryUtil.encodeDouble(out, ((VdlFloat32) value).getValue()); |
| } else if (value instanceof VdlFloat64) { |
| return BinaryUtil.encodeDouble(out, ((VdlFloat64) value).getValue()); |
| } else if (value instanceof Float) { |
| return BinaryUtil.encodeDouble(out, (Float) value); |
| } else if (value instanceof Double){ |
| return BinaryUtil.encodeDouble(out, (Double) value); |
| } else { |
| throw new IOException("Unsupported VDL float value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| } |
| |
| /** |
| * Writes a VDL int to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlInt(EncodingStream out, Object value) throws IOException { |
| if (value instanceof VdlInt8) { |
| return BinaryUtil.encodeInt(out, ((VdlInt8) value).getValue()); |
| } else if (value instanceof VdlInt16) { |
| return BinaryUtil.encodeInt(out, ((VdlInt16) value).getValue()); |
| } else if (value instanceof VdlInt32) { |
| return BinaryUtil.encodeInt(out, ((VdlInt32) value).getValue()); |
| } else if (value instanceof VdlInt64) { |
| return BinaryUtil.encodeInt(out, ((VdlInt64) value).getValue()); |
| } else if (value instanceof Short){ |
| return BinaryUtil.encodeInt(out, (Short) value); |
| } else if (value instanceof Integer) { |
| return BinaryUtil.encodeInt(out, (Integer) value); |
| } else if (value instanceof Long) { |
| return BinaryUtil.encodeInt(out, (Long) value); |
| } else { |
| throw new IOException("Unsupported VDL int value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| } |
| |
| /** |
| * Write a byte as a byte (as opposed to as a uint, as is done in version >= 0x81). |
| * Returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlRawByte(EncodingStream out, Object value) { |
| byte b; |
| if (value instanceof VdlByte) { |
| b = ((VdlByte)value).getValue(); |
| } else if (value instanceof Byte) { |
| b = (Byte)value; |
| } else { |
| throw new RuntimeException("unknown raw byte value " + value); |
| } |
| out.write(b); |
| return b != 0; |
| } |
| |
| /** |
| * Writes a VDL byte array to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlBytes(EncodingStream out, Object value, VdlType type) |
| throws IOException { |
| if (value.getClass().isArray()) { |
| Object arrayValue = value; |
| int len = Array.getLength(arrayValue); |
| BinaryUtil.encodeUint(out, len); |
| boolean nonZero = false; |
| for (int i = 0; i < len; i++) { |
| nonZero = writeVdlRawByte(out, Array.getByte(arrayValue, i)) || nonZero; |
| } |
| return nonZero; |
| } |
| |
| Collection<?> collection; |
| int size; |
| if (value instanceof VdlArray) { |
| collection = (VdlArray<?>) value; |
| size = 0; |
| } else if (value instanceof VdlList) { |
| VdlList<?> listValue = (VdlList<?>) value; |
| collection = listValue; |
| size = listValue.size(); |
| } else { |
| throw new IOException("Unsupported VDL list value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| |
| BinaryUtil.encodeUint(out, size); |
| boolean nonZero = false; |
| for (Object elem : collection) { |
| nonZero = writeVdlRawByte(out, elem) || nonZero; |
| } |
| return nonZero; |
| } |
| |
| /** |
| * Writes a VDL list to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlList(EncodingStream out, Object value, VdlType type) |
| throws IOException { |
| if (value instanceof List) { |
| List<?> listValue = (List<?>) value; |
| BinaryUtil.encodeUint(out, listValue.size()); |
| for (Object elem : listValue) { |
| writeValue(out, elem, type.getElem()); |
| } |
| return listValue.size() != 0; |
| } else if (value.getClass().isArray()) { |
| Object arrayValue = value; |
| int len = Array.getLength(arrayValue); |
| BinaryUtil.encodeUint(out, len); |
| for (int i = 0; i < len; i++) { |
| writeValue(out, Array.get(arrayValue, i), type.getElem()); |
| } |
| return len != 0; |
| } else { |
| throw new IOException("Unsupported VDL list value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| } |
| |
| /** |
| * Writes a VDL map to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlMap(EncodingStream out, Object value, VdlType type) throws IOException { |
| expectClass(Kind.MAP, value, Map.class); |
| Map<?, ?> mapValue = (Map<?, ?>) value; |
| BinaryUtil.encodeUint(out, mapValue.size()); |
| for (Map.Entry<?, ?> entry : mapValue.entrySet()) { |
| writeValue(out, entry.getKey(), type.getKey()); |
| writeValue(out, entry.getValue(), type.getElem()); |
| } |
| return mapValue.size() != 0; |
| } |
| |
| /** |
| * Writes a VDL union to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlUnion(EncodingStream out, Object value) throws IOException { |
| expectClass(Kind.UNION, value, VdlUnion.class); |
| VdlUnion unionValue = (VdlUnion) value; |
| Object elem = unionValue.getElem(); |
| int index = unionValue.getIndex(); |
| VdlType elemType = unionValue.vdlType().getFields().get(index).getType(); |
| BinaryUtil.encodeUint(out, index); |
| boolean isNonZero = index != 0; |
| isNonZero |= writeValue(out, elem, elemType); |
| return isNonZero; |
| } |
| |
| /** |
| * Writes a VDL optional to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlOptional(EncodingStream out, Object value) throws IOException { |
| expectClass(Kind.OPTIONAL, value, VdlOptional.class); |
| VdlOptional<?> optionalValue = (VdlOptional<?>) value; |
| if (optionalValue.isNull()) { |
| writeVdlControlByte(out, Constants.WIRE_CTRL_NIL); |
| return false; |
| } else { |
| writeValue(out, optionalValue.getElem(), optionalValue.vdlType().getElem()); |
| return true; |
| } |
| } |
| |
| /** |
| * Writes a VDL set to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlSet(EncodingStream out, Object value, VdlType type) throws IOException { |
| expectClass(Kind.SET, value, Set.class); |
| Set<?> setValue = (Set<?>) value; |
| BinaryUtil.encodeUint(out, setValue.size()); |
| for (Object key : setValue) { |
| writeValue(out, key, type.getKey()); |
| } |
| return setValue.size() != 0; |
| } |
| |
| /** |
| * Writes a VDL string to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlString(EncodingStream out, Object value) throws IOException { |
| String stringValue; |
| if (value instanceof VdlString) { |
| stringValue = ((VdlString) value).getValue(); |
| } else if (value instanceof String ){ |
| stringValue = (String) value; |
| } else { |
| throw new IOException("Unsupported VDL string value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| BinaryUtil.encodeBytes(out, BinaryUtil.getBytes(stringValue)); |
| return stringValue.length() != 0; |
| } |
| |
| /** |
| * Writes a VDL struct to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlStruct(EncodingStream out, Object value, VdlType type) throws IOException { |
| List<VdlField> fields = type.getFields(); |
| boolean hasNonZeroField = false; |
| for (int i = 0; i < fields.size(); i++) { |
| VdlField field = fields.get(i); |
| Object fieldValue = null; |
| if (value instanceof VdlStruct) { |
| fieldValue = ((VdlStruct) value).getField(field.getName()); |
| } else { |
| try { |
| Field f = value.getClass().getDeclaredField( |
| BinaryUtil.firstCharToLower(field.getName())); |
| f.setAccessible(true); |
| fieldValue = f.get(value); |
| } catch (Exception e) { |
| throw new IOException("Unsupported VDL struct value (type " + value.getClass() |
| + ", value " + value + ")", e); |
| } |
| } |
| int prevCount = out.getCount(); |
| int prevTypeIdCount = typeIds.size(); |
| BinaryUtil.encodeUint(out, i); |
| if (writeValue(out, fieldValue, field.getType())) { |
| hasNonZeroField = true; |
| } else { |
| // Roll back writing of a zero value. |
| out.setCount(prevCount); |
| for (;typeIds.size() > prevTypeIdCount;) { |
| typeIds.remove(typeIds.size() - 1); |
| } |
| } |
| } |
| writeVdlControlByte(out, Constants.WIRE_CTRL_END); |
| return hasNonZeroField; |
| } |
| |
| /** |
| * Writes a VDL uint to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlUint(EncodingStream out, Object value) throws IOException { |
| if (value instanceof VdlByte || value instanceof Byte) { |
| return writeVdlByte(out, value); |
| } else if (value instanceof VdlUint16) { |
| return BinaryUtil.encodeUint(out, ((VdlUint16) value).getValue()); |
| } else if (value instanceof VdlUint32) { |
| return BinaryUtil.encodeUint(out, ((VdlUint32) value).getValue()); |
| } else if (value instanceof VdlUint64) { |
| return BinaryUtil.encodeUint(out, ((VdlUint64) value).getValue()); |
| } else { |
| throw new IOException("Unsupported VDL uint value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| } |
| |
| /** |
| * Writes a VDL typeObject to output stream and returns true iff the value is non-zero. |
| */ |
| private boolean writeVdlTypeObject(EncodingStream out, Object object) throws IOException { |
| expectClass(Kind.TYPEOBJECT, object, VdlTypeObject.class); |
| VdlTypeObject value = (VdlTypeObject) object; |
| long id = getType(value.getTypeObject()).getValue(); |
| writeTypeId(out, id); |
| return value.getTypeObject() != Types.ANY; |
| } |
| |
| private void expectClass(Kind kind, Object value, Class<?> klass) throws IOException { |
| if (!klass.isAssignableFrom(value.getClass())) { |
| throw new IOException("Unsupported VDL " + kind + " value (type " + value.getClass() |
| + ", value " + value + ")"); |
| } |
| } |
| } |