Bincode v0.2.1 Bincode View Source

Module defining the functionalities of Bincode.

Bincode allows you to share data between Elixir and Rust using Rust's Bincode binary format.

You can implement your custom serialization manually, but for most use cases you can simply declare the Rust structs and enums using Bincode.declare_struct/3 and Bincode.declare_enum/3

Supported types

Most Rust types are supported, plus user defined structs and enums.

RustBincode notationElixir typespec
u8:u8non_neg_integer
.........
u128:u128non_neg_integer
i8:i8integer
.........
i128:i128integer
f32:f32float
f64:f64float
bool:boolboolean
String:stringbinary
(u32, String){:u32, :string}{non_neg_integer, binary}
Option<f32>{:option, :f32}float | nil
Vec<String>{:list, :string}[binary]
HashMap<i64, String>{:map, {:i64, :string}}%{required(integer) => binary}
HashSet<u8>{:set, :u8}MapSet.t(non_neg_integer)

User defined types such as structs and enums can be nested, in this case the type is the fully qualified module name. See Bincode.declare_struct/3.

The endianness is little since that's the default used by Bincode. Tuples are implemented for a max size of 12 by default. That should be enough for most practical cases but if you need to serialize tuples with more elements, you can set max_tuple_size in the mix config, like so:

config :bincode, max_tuple_size: 23

Examples

Consider the typical example where we want to send data structures across the network. Here with a Rust client and Elixir server:

#[derive(Serialize, Deserialize)]
pub struct PacketSendMessage {
  pub from: u64,
  pub to: u64,
  pub content: String,
}

pub fn send_message(sender_id: u64, receiver_id: u64) {
  let message = PacketSendMessage {
      from: sender_id,
      to: receiver_id,
      content: "hello!".to_owned()
  };
  let encoded: Vec<u8> = bincode::serialize(&message).unwrap();

  // now send "encoded" to Elixir app
}

On the Elixir side you can simply declare the same packet struct and deserialize the received bytes:

defmodule Packets do
  import Bincode

  declare_struct(PacketSendMessage,
    from: :u64,
    to: :u64,
    content: :string
  )
end

alias Packets.PacketSendMessage

# Receive "data" from the network
{:ok, {%PacketSendMessage{} = message, rest}} = PacketSendMessage.deserialize(data)
Logger.info("Received message packet #{inspect(message)}")

Link to this section Summary

Functions

Declares a new enum. This macro generates a module for the enum, plus a struct for each variant with serialization and deserialization methods according to the given fields.

Declares a new struct. This macro generates a struct with serialization and deserialization methods according to the given fields.

Deserializes the given binary data into an Elixir term according to the given type.

Same as deserialize/2 but raises an ArgumentError when the given value cannot be encoded according to type.

Serializes the given term in binary representation according to the given type.

Same as serialize/2 but raises an ArgumentError when the given value cannot be encoded according to type.

Link to this section Types

Specs

bincode_type() :: primitive() | collection() | user_defined()

Specs

collection() ::
  {:list, bincode_type()} | {:map, bincode_type()} | {:set, bincode_type()}

Specs

floating_point() :: :f32 | :f64

Specs

primitive() ::
  unsigned()
  | signed()
  | floating_point()
  | :bool
  | :string
  | tuple()
  | {:option, bincode_type()}

Specs

signed() :: :i8 | :i16 | :i32 | :i64 | :i128

Specs

unsigned() :: :u8 | :u16 | :u32 | :u64 | :u128

Specs

user_defined() :: module()

Link to this section Functions

Link to this macro

declare_enum(enum, variants, options \\ [])

View Source (macro)

Declares a new enum. This macro generates a module for the enum, plus a struct for each variant with serialization and deserialization methods according to the given fields.

Options

  • absolute - When set to true, the given struct name is interpreted as the absolute module name. When set to false, the given struct name is appended to the caller's module. Defaults to false.

Example

defmodule MyEnums do
  import Bincode

  declare_enum(IpAddr,
    V4: [tuple: {:u8, :u8, :u8, :u8}],
    V6: [addr: :string]
  )
end

alias MyEnums.IpAddr

ip_v4 = %IpAddr.V4{tuple: {127, 0, 0, 1}}
{:ok, <<0, 0, 0, 0, 127, 0, 0, 1>>} = Bincode.serialize(ip_v4, IpAddr)

ip_v6 = %IpAddr.V6{addr: "::1"}
{:ok, <<1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 58, 58, 49>>} = Bincode.serialize(ip_v6, IpAddr)

It's also possible to call serialize and deserialize from the struct module directly.

{:ok, {%IpAddr.V4{tuple: {127, 0, 0, 1}}, ""}} = IpAddr.deserialize(<<0, 0, 0, 0, 127, 0, 0, 1>>)
{:ok, {%IpAddr.V6{addr: "::1"}, ""}} = IpAddr.deserialize(<<1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 58, 58, 49>>)

Enums can be nested and contain structs. See Bincode.declare_struct/3.

Link to this macro

declare_struct(struct, fields, options \\ [])

View Source (macro)

Declares a new struct. This macro generates a struct with serialization and deserialization methods according to the given fields.

Options

  • absolute - When set to true, the given struct name is interpreted as the absolute module name. When set to false, the given struct name is appended to the caller's module. Defaults to false.

Example

defmodule MyStructs do
  import Bincode

  declare_struct(Person,
    first_name: :string,
    last_name: :string,
    age: :u8
  )
end

alias MyStructs.Person

person = %Person{first_name: "John", last_name: "Doe", age: 44}
{:ok, <<4, 0, 0, 0, 0, 0, 0, 0, 74, 111, 104, 110, 3, 0, 0, 0, 0, 0, 0, 0, 68, 111, 101, 44>>} = Bincode.serialize(person, Person)

It's also possible to call serialize and deserialize from the struct module directly.

{:ok, {%Person{age: 44, first_name: "John", last_name: "Doe"}, ""}} = Person.deserialize(<<4, 0, 0, 0, 0, 0, 0, 0, 74, 111, 104, 110, 3, 0, 0, 0, 0, 0, 0, 0, 68, 111, 101, 44>>)

Structs and enums can be nested. In this case the type is the fully qualified module. For example:

defmodule MyStructs do
  import Bincode

  declare_struct(Person,
    first_name: :string,
    last_name: :string,
    age: :u8
  )

  declare_struct(Employee,
    employee_number: :u64,
    person: MyStructs.Person,
    job_title: :string,
  )
end
Link to this function

deserialize(value, type)

View Source

Specs

deserialize(binary(), bincode_type()) ::
  {:ok, {term(), binary()}} | {:error, String.t()}

Deserializes the given binary data into an Elixir term according to the given type.

Returns {:ok, {term, rest}} when successful or {:error, error_message} otherwise. The remaining binary data is returned.

Examples

iex> Bincode.deserialize(<<255>>, :u8)
{:ok, {255, ""}}

iex> Bincode.deserialize(<<7, 0, 0, 0, 0, 0, 0, 0, 66, 105, 110, 99, 111, 100, 101>>, :string)
{:ok, {"Bincode", ""}}

iex> Bincode.deserialize(<<144, 0, 0>>, {:u16, :bool})
{:ok, {{144, false}, ""}}

iex> Bincode.deserialize(<<4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4>>, {:list, :u8})
{:ok, {[1, 2, 3, 4], ""}}

iex> Bincode.deserialize(<<1, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 115, 111, 109, 101, 32, 115, 116, 114, 105, 110, 103, 32, 107, 101, 121, 4, 171, 161, 22, 100, 0, 0, 0>>, {:map, {:string, :u64}})
{:ok, {%{"some string key" => 429876423428}, ""}}

iex> Bincode.deserialize([], :bool)
{:error, "Cannot deserialize value [] into type :bool"}
Link to this function

deserialize!(value, type)

View Source

Same as deserialize/2 but raises an ArgumentError when the given value cannot be encoded according to type.

Examples

iex> Bincode.deserialize!(<<1, 54, 23>>, {:option, :u16})
{5942, ""}

iex> Bincode.deserialize!(<<>>, {:list, :string})
** (ArgumentError) Cannot deserialize value "" into type :list

Specs

serialize(term(), bincode_type()) :: {:ok, binary()} | {:error, String.t()}

Serializes the given term in binary representation according to the given type.

Returns {:ok, serialized_term} when successful or {:error, error_message} otherwise.

Examples

iex> Bincode.serialize(255, :u8)
{:ok, <<255>>}

iex> Bincode.serialize("Bincode", :string)
{:ok, <<7, 0, 0, 0, 0, 0, 0, 0, 66, 105, 110, 99, 111, 100, 101>>}

iex> Bincode.serialize({144, false}, {:u16, :bool})
{:ok, <<144, 0, 0>>}

iex> Bincode.serialize([1, 2, 3, 4], {:list, :u8})
{:ok, <<4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4>>}

iex> Bincode.serialize(%{"some string key" => 429876423428}, {:map, {:string, :u64}})
{:ok, <<1, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 115, 111, 109, 101, 32, 115, 116, 114, 105, 110, 103, 32, 107, 101, 121, 4, 171, 161, 22, 100, 0, 0, 0>>}

iex> Bincode.serialize(%{}, :bool)
{:error, "Cannot serialize value %{} into type :bool"}

Same as serialize/2 but raises an ArgumentError when the given value cannot be encoded according to type.

Examples

iex> Bincode.serialize!([111], {:list, :u16})
<<1, 0, 0, 0, 0, 0, 0, 0, 111, 0>>

iex> Bincode.serialize!(<<>>, {:option, :bool})
** (ArgumentError) Cannot serialize value "" into type :bool