Pbwire (pbwire_elixir v0.1.14)

Copy Markdown

Fast Protobuf encoder/decoder for Elixir: elixir-protobuf structs in, elixir-protobuf structs out, wire bytes identical to the official runtime.

The structs stay exactly as protoc-gen-elixir generates them; only the codec is swapped. Load the schema once, then encode and decode:

ok = Pbwire.load_schema([MyApp.Person])
bin = Pbwire.encode(%MyApp.Person{name: "alice", id: 1})
%MyApp.Person{} = Pbwire.decode(bin, MyApp.Person)

Underneath, Pbwire.Schema translates the generated __message_props__/0 into the defs the :pbwire Erlang NIF consumes (the same NIF and C core as bindings/erlang), and this module translates structs to the record tuples the NIF encodes and back. No field numbers, wire types or presence rules are re-derived here: they all come from the generated modules.

The schema cache is global to the BEAM node (it lives in the NIF), so one VM holds one schema: two applications with different .proto schemas need separate nodes, exactly like the Erlang binding.

Summary

Functions

Decodes wire bytes into a struct, equal to Protobuf.Decoder.decode(bin, module, []).

Encodes a struct to protobuf wire format, byte-identical to Protobuf.encode/1.

Loads the schema for modules (plus every message and enum they reference), replacing any schema loaded before.

Forgets the loaded schema. Later encode/1 / decode/2 calls raise until load_schema/2 runs again.

Functions

decode(binary, module)

@spec decode(binary(), module()) :: struct()

Decodes wire bytes into a struct, equal to Protobuf.Decoder.decode(bin, module, []).

Unknown fields are dropped (the tuple form the NIF decodes to has no storage for them, same as the Erlang binding); the decoded struct carries __unknown_fields__: [].

decode_msg(binary, module, modules)

@spec decode_msg(binary(), module(), [module()]) :: struct()

load_schema/2 followed by decode/2.

encode(struct)

@spec encode(struct()) :: binary()

Encodes a struct to protobuf wire format, byte-identical to Protobuf.encode/1.

Raises when no schema is loaded for the struct's module, or when a value does not fit its field (for example a missing required field).

encode_msg(struct, modules)

@spec encode_msg(struct(), [module()]) :: binary()

load_schema/2 followed by encode/1.

load_schema(modules, opts \\ [])

@spec load_schema([module()], keyword()) :: :ok

Loads the schema for modules (plus every message and enum they reference), replacing any schema loaded before.

Options are passed to :pbwire.set_opts/1 before loading:

  • :with_utf8 (default true) — accept non-Latin-1 UTF-8 in string fields when encoding. Elixir strings are UTF-8, so this stays on unless you need the Erlang default (false).
  • :string_as_list — when true, decode string fields as charlists instead of binaries. Leave it off: Elixir strings are binaries.
  • :retain_cap — resident encode-buffer ceiling per scheduler, like the C core's retain cap (1 MiB default when unset, 0 keeps nothing).

Raises ArgumentError when a module is not an elixir-protobuf generated module or carries a field this binding does not cover.

purge_schema()

@spec purge_schema() :: :ok

Forgets the loaded schema. Later encode/1 / decode/2 calls raise until load_schema/2 runs again.