# `ToonEx.Btoon.Dictionary`
[🔗](https://github.com/ohhi-vn/toon_ex/blob/v1.6.0/lib/toon_ex/btoon/dictionary.ex#L1)

A session string dictionary.

A session dictionary is negotiated once per connection (see the BTOON
handshake in the specification) and shared between encoder and decoder.
Strings present in the dictionary are encoded as `StringRef` (tag `0x0B`)
referencing their zero-based index, avoiding repeated UTF-8 transmission.

The per-message string table carried in the envelope is layered on top of
the session dictionary: entry `i` of the table gets ref id
`length(dictionary) + i`.

## Persistent Term Storage

For sharing a session dictionary across many processes without deep-copying
on each message send, use `:persistent_term`:

    iex> dict = Btoon.Dictionary.new(["player", "position", "velocity"])
    iex> Btoon.Dictionary.put_persistent(:session_dict, dict)
    iex> dict = Btoon.Dictionary.get_persistent(:session_dict)
    iex> Btoon.Dictionary.ref(dict, "velocity")
    2

## Examples

    iex> dict = ToonEx.Btoon.Dictionary.new(["player", "position", "velocity"])
    iex> ToonEx.Btoon.Dictionary.entries(dict)
    ["player", "position", "velocity"]
    iex> ToonEx.Btoon.Dictionary.ref(dict, "velocity")
    2

# `t`

```elixir
@type t() :: %ToonEx.Btoon.Dictionary{
  entries: [String.t()],
  entries_tuple: tuple(),
  index: map(),
  size: non_neg_integer()
}
```

# `delete_persistent`

```elixir
@spec delete_persistent(term()) :: :ok
```

Deletes a dictionary from `:persistent_term`.

Warning: this triggers a full garbage collection sweep across all
processes on the node. Use sparingly.

# `entries`

```elixir
@spec entries(t()) :: [String.t()]
```

Returns the dictionary entries in ref-id order.

# `entries_tuple`

```elixir
@spec entries_tuple(t()) :: tuple()
```

Returns the dictionary entries as a tuple for fast indexed access.

# `get_persistent`

```elixir
@spec get_persistent(term()) :: {:ok, t()} | :error
```

Retrieves a dictionary from `:persistent_term`.

Returns `{:ok, dict}` or `:error` if the key doesn't exist.

# `lookup`

```elixir
@spec lookup(t()) :: %{optional(String.t()) =&gt; non_neg_integer()}
```

Returns the precomputed string → ref-id lookup map for the encoder's hot path.

# `member?`

```elixir
@spec member?(t(), String.t()) :: boolean()
```

Returns `true` when the dictionary contains the string.

# `new`

```elixir
@spec new([String.t()]) :: t()
```

Builds a dictionary from a list of strings.

# `put_persistent`

```elixir
@spec put_persistent(term(), t()) :: :ok
```

Stores a dictionary in `:persistent_term` under the given key.

Data in `:persistent_term` lives outside process heaps and is shared
across all processes on the node without copying on read. Updates are
expensive (global GC), so use only for write-once data like session
dictionaries negotiated at connection time.

## Example

    iex> dict = Btoon.Dictionary.new(["player", "position"])
    iex> Btoon.Dictionary.put_persistent(:my_session_dict, dict)
    :ok

# `ref`

```elixir
@spec ref(t(), String.t()) :: non_neg_integer() | nil
```

Returns the ref id for a string, or `nil` when absent.

# `size`

```elixir
@spec size(t()) :: non_neg_integer()
```

Number of entries in the dictionary.

---

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