Binstructor

Binstructor makes it easy to deal with simple but large binary formats by generating the boilerplate Elixir code for you, including a Struct definition, encode and decode functions and helpers for pattern matching. Writing a binary pattern match for 37 different fields is tedious, so let Binstructor do it for you from a simple declaritive description that’s easy to compare to the spec!

Usage

To define a new packet structure, create a module and add the Binstructure.Packet module with use Binstructor.Packet. This will add the defpacket macro which is where you define your fields.

Each field in the packet is defined by a function call. Basic data types are identified with the same name as in a binary pattern match.

Example

defmodule Foo do
  defstruct a: 0, b: 10, c: <<1,2,3,4>>, d:3

  def decode(<<a :: integer-size(8), b :: integer-size(8), c :: binary-size(4), d :: integer-size(8)>>) do
    %Foo{a: a, b: b, c: c, d: d}
  end

  def encode(%Foo{a: a, b: b, c: c, d: c}) do
    <<a :: integer-size(8), b :: integer-size(8), c :: binary-size(4), d::integer-size(8)>>
  end
end

becomes

defmodule Foo do
  use Binstructor.Packet

  @c_default <<1,2,3,4>>

  defpacket do
    integer :a, 0, 8
    integer :b, 10, 8
    binary :c, @c_default, 3
    integer :d, 3, 8
  end
end

Metaprogramming

The body of defpacket supports metaprogramming like normal Elixir code. The fields in the binary are defined to be in the order that the functions declaring them are called. This can be used to automatically build packet structures from machine readable specifications.

For Example:

  defpacket do
    integer :first, 0, 8

    names = [{:a1, {:a2, <<1,2,3>>}}, {:b1, {:b2, <<2,3,4>>}}, {:c1, {:c2, <<3,4,5>>}}]

    Enum.map(names, fn({v1, {v2, default}}) ->
      integer v1, 0, 8
      binary v2, default, 3
    end)

    integer :last, 0, 8
  end

is equivalent to

    defpacket do
      integer :first, 0, 8

      integer :a1, 0, 8
      binary :a2, <<1,2,3>>, 3

      integer :b1, 0, 8
      binary :b2, <<2,3,4>>, 3

      integer :c1, 0, 8
      binary :c2, <<3,4,5>>, 3

      integer :last, 0, 8
    end
  end