# `Pbwire`

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.

# `decode`

```elixir
@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`

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

`load_schema/2` followed by `decode/2`.

# `encode`

```elixir
@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`

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

`load_schema/2` followed by `encode/1`.

# `load_schema`

```elixir
@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`

```elixir
@spec purge_schema() :: :ok
```

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
