TlvDecoder { public static Tlv[] decode(ByteBuffer input) throws TlvException { try { List<Tlv> tlvs = new ArrayList<>(); while (input.remaining() > 0) { input.order(ByteOrder.BIG_ENDIAN); int typeByte = input.get() & 0xFF; TlvType type; switch (typeByte & 0b1100_0000) { case 0b0000_0000: type = TlvType.OBJECT_INSTANCE; break; case 0b0100_0000: type = TlvType.RESOURCE_INSTANCE; break; case 0b1000_0000: type = TlvType.MULTIPLE_RESOURCE; break; case 0b1100_0000: type = TlvType.RESOURCE_VALUE; break; default: throw new TlvException("unknown type: " + (typeByte & 0b1100_0000)); } int identifier; try { if ((typeByte & 0b0010_0000) == 0) { identifier = input.get() & 0xFF; } else { identifier = input.getShort() & 0xFFFF; } } catch (BufferUnderflowException e) { throw new TlvException("Invalid 'identifier' length", e); } LOG.trace("decoding {} {}", type, identifier); int length; int lengthType = typeByte & 0b0001_1000; try { switch (lengthType) { case 0b0000_0000: length = typeByte & 0b0000_0111; break; case 0b0000_1000: length = input.get() & 0xFF; break; case 0b0001_0000: length = input.getShort() & 0xFFFF; break; case 0b0001_1000: int b = input.get() & 0x000000FF; int s = input.getShort() & 0x0000FFFF; length = (b << 16) | s; break; default: throw new TlvException("unknown length type: " + (typeByte & 0b0001_1000)); } } catch (BufferUnderflowException e) { throw new TlvException("Invalid 'length' length", e); } LOG.trace("length: {} (length type: {})", length, lengthType); if (type == TlvType.RESOURCE_VALUE || type == TlvType.RESOURCE_INSTANCE) { try { byte[] payload = new byte[length]; input.get(payload); tlvs.add(new Tlv(type, null, payload, identifier)); if (LOG.isTraceEnabled()) { LOG.trace("payload value: {}", Hex.encodeHexString(payload)); } } catch (BufferOverflowException e) { throw new TlvException("Invalid 'value' length", e); } } else { try { ByteBuffer slice = input.slice(); ((Buffer) slice).limit(length); Tlv[] children = decode(slice); ((Buffer) input).position(((Buffer) input).position() + length); Tlv tlv = new Tlv(type, children, null, identifier); tlvs.add(tlv); } catch (IllegalArgumentException e) { throw new TlvException("Invalid 'value' length", e); } } } return tlvs.toArray(new Tlv[] {}); } catch (TlvException ex) { String printHexBinary = Hex.encodeHexString(input.array()); throw new TlvException("Impossible to parse TLV: \n" + printHexBinary, ex); } catch (RuntimeException ex) { String printHexBinary = Hex.encodeHexString(input.array()); throw new TlvException("Unexpected TLV parse error: \n" + printHexBinary, ex); } }  static Tlv[] decode(ByteBuffer input); static String decodeString(byte[] value); static boolean decodeBoolean(byte[] value); static Date decodeDate(byte[] value); static ObjectLink decodeObjlnk(byte[] value); static Number decodeInteger(byte[] value); static Number decodeFloat(byte[] value);  }
@Test public void decode_device_object() throws TlvException { String dataStr = "C800144F70656E204D6F62696C6520416C6C69616E6365C801164C69676874776569676874204D324D20436C69656E74C80209333435303030313233C303312E30860641000141010588070842000ED842011388870841007D42010384C10964C10A0F830B410000C40D5182428FC60E2B30323A3030C10F55"; byte[] bytes = Hex.decodeHex(dataStr.toCharArray()); ByteBuffer b = ByteBuffer.wrap(bytes); Tlv[] tlv = TlvDecoder.decode(b); LOG.debug(Arrays.toString(tlv)); ByteBuffer buff = TlvEncoder.encode(tlv); assertTrue(Arrays.equals(bytes, buff.array())); }
@Test public void decode_broken_tlv() throws TlvException { String dataStr = "0011223344556677889900"; byte[] bytes = Hex.decodeHex(dataStr.toCharArray()); ByteBuffer b = ByteBuffer.wrap(bytes); try { TlvDecoder.decode(b); fail(); } catch (TlvException ex) { assertEquals("Impossible to parse TLV: \n0011223344556677889900", ex.getMessage()); } }