Codex (bin_codex v0.1.1)

Provides functions to create composable and bidirectional serializers.

Introduction

The Codex module provides a number of predefined codecs and combinators that you can use to build new codes.

iex> import Codex
...> first_codec = [byte(), byte(), byte()] |> sequence()
...> <<0x10, 0xFF, 0xAB>> |> decode(first_codec)
{:ok, [16, 255, 171], <<>>}

Link to this section Summary

Functions

Combines two codecs into a codec that produces a 2-element tuple of each value.

A codec that always encodes to and decodes from the same value.

Creates a new codec.

Decodes a value from its binary form using the supplied codec.

Encodes a value to its binary form using the supplied codec.

Creates a codec that always fails with the supplied error message.

Creates a codec that always fails with the supplied error messages.

Uses the first codec, falling back to the second if it fails.

A codec that always decodes to a certain value.

Link to this section Types

Link to this type

boolean_codec()

Specs

boolean_codec() :: codec(boolean())

Specs

codec(a) :: %Codex{decode: decoder(a), encode: encoder(a)}
Link to this type

decode_result(a)

Specs

decode_result(a) ::
  {:ok, a, remaining_bits()} | {:error, String.t(), remaining_bits()}

Specs

decoder(a) :: (bitstring() -> decode_result(a))
Link to this type

encode_result()

Specs

encode_result() :: {:ok, bitstring()} | {:error, String.t()}

Specs

encoder(a) :: (a -> encode_result())
Link to this type

list_codec(a)

Specs

list_codec(a) :: codec([a])
Link to this type

remaining_bits()

Specs

remaining_bits() :: bitstring()

Specs

type() :: any()

Specs

type2() :: any()

Link to this section Functions

Link to this function

append(list_codec, codec)

Specs

append(list_codec(type()), codec(type())) :: list_codec(type())

Specs

bit() :: codec(0 | 1)

Specs

Link to this function

bits_remaining()

Specs

bits_remaining() :: boolean_codec()

Specs

bool_bit() :: boolean_codec()

Specs

byte() :: codec(non_neg_integer())

Specs

Specs

choice([codec(type())]) :: list_codec(type())
Link to this function

combine(codec1, codec2)

Specs

combine(codec(type()), codec(type2())) :: codec({type(), type2()})

Combines two codecs into a codec that produces a 2-element tuple of each value.

Examples

iex> import Codex
...> {198, 2} |> encode(combine(byte(), byte()))
{:ok, <<198, 2>>}

...> <<198, 2>> |> decode(combine(byte(), byte()))
{:ok, {198, 2}, <<>>}
Link to this function

cons(codec, list_codec)

Specs

cons(codec(type()), list_codec(type())) :: list_codec(type())
Link to this function

constant(value, bits)

Specs

constant(type(), bitstring()) :: codec(type())

A codec that always encodes to and decodes from the same value.

It fails if it doesn't see the expected value that it always encodes/decodes.

Examples

iex> import Codex
...> codec = constant(10, <<200>>)
...> <<200>> |> decode(codec)
{:ok, 10, <<>>}

...> <<234>> |> decode(codec)
{:error, "<<234>> did not equal <<200>> in constant", <<234>>}

...> 10 |> encode(codec)
{:ok, <<200>>}

...> 22 |> encode(codec)
{:error, "22 did not equal 10 in constant"}
Link to this function

convert(codec, convert_to, convert_from)

Specs

convert(codec(type()), (type() -> type2()), (type2() -> type())) ::
  codec(type2())
Link to this function

create(encode, decode)

Specs

create(encoder(type()), decoder(type())) :: %Codex{
  decode: term(),
  encode: term()
}

Creates a new codec.

It is recommended to use the other, well tested, functions in the module to build a codec instead of creating your own unless you absolutely have to.

When creating your own codec it MUST be idempotent.

Examples

iex> Codex.create(fn _ -> {:ok, <<>>} end, fn
...>   <<>> -> {:ok, false, <<>>}
...>   bits -> {:ok, true, bits}
...> end)
#Codex<...>
Link to this function

decode(bits, codex)

Specs

decode(bitstring(), codec(type())) :: decode_result(type())

Decodes a value from its binary form using the supplied codec.

The third element of the tuple is any remaining unparsed bits.

Examples

iex> import Codex
iex> <<198>> |> decode(byte())
{:ok, 198, <<>>}

Specs

done(codec(type())) :: codec(type())

Specs

empty() :: codec(any())
Link to this function

encode(value, codex)

Specs

encode(type(), codec(type())) :: encode_result()

Encodes a value to its binary form using the supplied codec.

Examples

iex> import Codex
...> 198 |> encode(byte())
{:ok, <<198>>}
Link to this function

ensure(codec, boolean_codec, error)

Specs

ensure(codec(type()), boolean_codec(), String.t()) :: codec(type())

Specs

fail(String.t()) :: codec(any())

Creates a codec that always fails with the supplied error message.

This is not useful on its own but can be useful when building other codecs.

Examples

iex> defmodule Example do
...>   import Codex
...>   def choose([]), do: fail("None of the choices worked")
...>   def choose([codec | rest]), do: fallback(codec, choice(rest))
...> end
...> <<1>> |> Codex.decode(Example.choose([Codex.byte(), Codex.bits(4)]))
{:ok, 1, <<>>}

...> <<1::2>> |> Codex.decode(Example.choose([Codex.bits(4), Codex.byte()]))
{:error, "None of the choices worked", <<1::2>>}
Link to this function

fail(encode_error, decode_error)

Specs

fail(String.t(), String.t()) :: codec(any())

Creates a codec that always fails with the supplied error messages.

Same as fail/1 but you can supply a different error message for encoding and decoding.

Link to this function

fallback(codec1, codec2)

Specs

fallback(codec(type()), codec(type2())) :: codec(type() | type2())

Uses the first codec, falling back to the second if it fails.

Examples

iex> import Codex
...> optional_byte = byte() |> fallback(nothing())
...> <<8>> |> Codex.decode(optional_byte)
{:ok, 8, <<>>}

...> <<8::4>> |> Codex.decode(optional_byte)
{:ok, nil, <<8::4>>}
Link to this function

join(codec, group_size \\ 1)

Specs

Link to this function

length_prefixed(length_codec, codec)

Specs

length_prefixed(codec(non_neg_integer()), codec(type())) :: list_codec(type())

Specs

list(codec(type())) :: list_codec(type())
Link to this function

list_of(count, codec)

Specs

list_of(non_neg_integer(), codec(type())) :: list_codec(type())
Link to this function

lookahead(codec)

Specs

lookahead(codec(any())) :: boolean_codec()
Link to this function

non_empty_list(codec)

Specs

non_empty_list(codec(type())) :: codec([type(), ...])
Link to this function

not_(boolean_codec)

Specs

Specs

nothing() :: codec(any())
Link to this function

optional(codec)

Specs

optional(codec(type())) :: codec(type() | nil)
Link to this function

pad(codec, bits)

Specs

peek(codec(type())) :: codec(type())

Specs

recover(codec(any())) :: boolean_codec()
Link to this function

refute(codec, boolean_codec, error)

Specs

refute(codec(type()), boolean_codec(), String.t()) :: codec(type())
Link to this function

reverse(list_codec)

Specs

reverse(list_codec(type())) :: list_codec(type())

Specs

sequence([codec(type())]) :: list_codec(type())
Link to this function

take_until(codec, boolean_codec)

Specs

take_until(codec(type()), boolean_codec()) :: list_codec(type())
Link to this function

take_while(boolean_codec, codec)

Specs

take_while(boolean_codec(), codec(type())) :: list_codec(type())
Link to this function

then(codec, f, g)

Specs

then(codec(type()), (type() -> codec(type2())), (type2() -> type())) ::
  codec(type2())

Specs

value(type()) :: codec(type())

A codec that always decodes to a certain value.

It never consumes bits while decoding so it can't fail. It can fail on encoding if the value to encode doesn't match.

Examples

iex> import Codex
...> codec = value(10)
...> <<>> |> decode(codec)
{:ok, 10, <<>>}

...> <<200>> |> decode(codec)
{:ok, 10, <<200>>}

...> 10 |> encode(codec)
{:ok, <<>>}

...> 22 |> encode(codec)
{:error, "22 did not equal 10 in constant"}