defmodule TLV8Decoder do @moduledoc false def decode(input) do # The decoder accumlates items in reverse order for speed, then reorders them at the end Enum.reverse(decode(input, [], nil)) end defp decode(input, acc, last_tlv8) do {type, value, remainder} = decode_type_value(input) {pending_tlv8, completed_tlv8} = generate_tlv8(type, value, last_tlv8) accumulate_tlv8(acc, pending_tlv8, completed_tlv8, remainder) end defp decode_type_value(<>) do if byte_size(valueremainder) >= length do <> = valueremainder {type, value, remainder} else { :invalid_tlv8, :invalid_tlv8, <<>> } end end defp decode_type_value(_input) do { :invalid_tlv8, :invalid_tlv8, <<>> } end defp generate_tlv8(type, value, last_tlv) when type == :invalid_tlv8 or value == :invalid_tlv8 do # The type or value is invalid so we cannot generate a TLV {:invalid_tlv8, last_tlv} end defp generate_tlv8(type, value, last_tlv) when not is_nil(last_tlv) do if (last_tlv.type == type) do # This TLV8 is a continuation of the last TLV8, so add its value to the last TLV8 {%TLV8{type: type, value: last_tlv.value <> value}, nil} else # This TLV8 is not a continuation of the last TLV8, so return the last one as complete {%TLV8{type: type, value: value}, last_tlv} end end defp generate_tlv8(type, value, last_tlv) when is_nil(last_tlv) do # There is no last TLV8 to consider, so just generate a new TLV8 {%TLV8{type: type, value: value}, nil} end defp accumulate_tlv8(acc, pending_tlv8, completed_tlv8, remainder) when byte_size(remainder) > 0 and not is_nil(completed_tlv8) do # There is a completed TLV so add it to the accumulator, then process the remaining bytes decode(remainder, [ completed_tlv8 | acc ], pending_tlv8) end defp accumulate_tlv8(acc, pending_tlv8, _completed_tlv8, remainder) when byte_size(remainder) > 0 do # There is no completed TLV so don't add anything to the accumulator, but there are remaining bytes to process decode(remainder, acc, pending_tlv8) end defp accumulate_tlv8(acc, pending_tlv8, completed_tlv8, _remainder) when not is_nil(completed_tlv8) do # There is a completed TLV so add it to the accumulator, and then add the current TLV too because there are no more bytes to process [ pending_tlv8 | [ completed_tlv8 | acc ]] end defp accumulate_tlv8(acc, pending_tlv8, _completed_tlv8, _remainder) do # There is no completed TLV, but there are no more bytes to process so add the current TLV [ pending_tlv8 | acc ] end end