defmodule TLV8 do @moduledoc """ A small library that decodes and encodes TLV8 according to Apple's specifications. It is intended to be compatible with TLV8 data structures used by HomeKit. * Encodes a list of type & value pairs into binary * Decodes binary into a list of type & value pairs * Automatically handles values longer than 255 bytes by splitting them into fragments This TLV8 library is written by Charles Gutjahr, but was inspired by [elixir_tlv](https://github.com/bitgamma/elixir_tlv). That library implements the ASN.1 BER-TLV format, a format that is somewhat similar to the Apple TLV8 format but is sufficiently different for that library to be unsuitable for working with Apple TLV8. Licensed under the MIT license, see the LICENSE file for details. """ @typedoc """ Represents a type and value pair, a single item in an Apple TLV8 structure. TLV8 binary data may contain one or more of these items. The `type` must be an integer between 0 and 255. Specifying a value outside this range will cause encoding to fail. Note that the 'L' in TLV8, the length, is not included because it is calculated automatically during encoding and so does not need to be manually specified. """ @type t :: %TLV8{type: integer, value: binary} defstruct [:type, :value] @doc """ Decodes the given binary TLV8 data, returning a list of all the items found (as TLV8 type & value pairs). The decoder will recursively extract all the items in the binary data until the data is exhausted. If invalid data is found, the decoder will return a list containing as many valid TLV8 type & value pairs as possible, then return the atom `:invalid_tlv8` and stop. If the binary data cannot be parsed at all a list containing a single `:invalid_tlv8` will be returned. ## Examples # Decoding binary data containing a single TLV8 iex> TLV8.decode(<<0xFF, 0x02, 0x03, 0x04>>) [%TLV8{type: 255, value: <<3, 4>>}] # Decoding binary data containing multiple TLV8s iex> TLV8.decode(<<0x06, 0x01, 0x03, 0x01, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f>>) [%TLV8{type: 6, value: <<3>>}, %TLV8{type: 1, value: "hello"}] # Invalid TLV8 iex> TLV8.decode(<<0x80, 0x02, 0x00>>) [:invalid_tlv8] """ @spec decode(binary) :: [ TLV8.t | :invalid_tlv8 ] defdelegate decode(tlv8), to: TLV8Decoder @doc """ Encodes the given single TLV8 item or list of TLV8 items, and returns it in Apple TLV8 binary format. If any of the items given for encoding contain an invalid type, the encoder will return the atom `:invalid_tlv8`. ## Examples # Encoding a single TLV8 iex> TLV8.encode(%TLV8{type: 0xFF, value: <<0x03, 0x04>>}) <<0xFF, 0x02, 0x03, 0x04>> # Encoding multiple TLV8s iex> TLV8.encode([%TLV8{type: 0x06, value: <<0x03>>}, %TLV8{type: 1, value: "hello"}]) <<0x06, 0x01, 0x03, 0x01, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f>> # Encoding an invalid TLV8 type iex> TLV8.encode([%TLV8{type: 0x00, value: <<>>}, %TLV8{type: 0x100, value: <<0x00>>}]) :invalid_tlv8 """ @spec encode(TLV8.t | [ TLV8.t ]) :: binary | :invalid_tlv8 defdelegate encode(tlv8), to: TLV8Encoder end