1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Render the "Available on crate feature X" labels on docs.rs (and any
// `RUSTDOCFLAGS="--cfg docsrs"` nightly doc build). The `feature(doc_cfg)` gate is
// emitted only when `--cfg docsrs` is set, so ordinary stable builds never see it.
// `auto_cfg = false` suppresses rustdoc's automatic pills for *every* `#[cfg]`
// (which would otherwise leak std-internal cfgs like `no_global_oom_handling` and
// platform gates onto our pages); only the explicit `#[doc(cfg(...))]` annotations
// on the feature-gated surface show.
//! ## Overview
//!
//! **Write fast and efficient Erlang NIFs in Rust**
//!
//! `otter` is built from the ground up to give Erlang and Elixir programmers an
//! easy, efficient way to write NIFs in Rust. The design is driven by two
//! foundational principles:
//! 1. **Functionality** — `otter`'s first priority is an API surface that
//! faithfully captures the capabilities of the underlying `erl_nif` C API — and
//! where the safe surface isn't enough, the raw API can be exposed by enabling
//! the `raw` feature.
//! 2. **Speed** — One of the prime motivations for writing NIFs is to achieve
//! speed and memory efficiency not possible in the Erlang VM. `otter`'s
//! datatypes are designed to "pay only for what you need" through controlled
//! layering of `enif_*` calls, with extensive work to employ compile-time
//! constraints rather than runtime ones.
//!
//! ## Quickstart
//!
//! ```no_run
//! use otter::types::CallEnv;
//!
//! // Declare the module: name, NIF table, and any atoms to pre-intern.
//! otter::init!("my_nif", [add], atoms = [ok, error]);
//!
//! // A NIF that adds two integers, decoded straight to `i64` and encoded back.
//! #[otter::nif]
//! fn add(_env: CallEnv<'_>, a: i64, b: i64) -> i64 {
//! a + b
//! }
//! ```
//!
//! ## Features
//!
//! A few highlights of the surface; the [guided tour](#a-guided-tour) below maps
//! the whole crate.
//!
//! ### Generative env/term lifetimes
//!
//! Each term is branded by the env that produced it. [`Env<'id>`](types::Env) is
//! a sealed trait whose lifetime `'id` is a *generative brand* — a fresh,
//! non-escaping marker minted at every entry point. Every term type carries that
//! brand (`Integer<'id>`, `Binary<'id>`, …), so a term from one env cannot be
//! used with another: the compiler rejects it, with no per-operation runtime
//! check. The BEAM treats a cross-env term as undefined behavior, and this
//! construction makes that mistake impossible to write. (This is the
//! [generativity pattern].)
//!
//! ### Layered term resolution
//!
//! Reading a term through the C API is a cascade: `enif_term_type` and the
//! `enif_is_*` family reveal a type, `enif_get_tuple` hands back elements that
//! are themselves opaque terms, and so on — a full decode can cost many calls
//! into the VM, most of them unnecessary. `otter` mirrors that progression in its
//! types, so you stop at exactly the depth you need: [`AnyTerm`](types::AnyTerm)
//! → [`TypedTerm`](types::TypedTerm) → a concrete term type → a native Rust
//! value.
//!
//! Resolution is lazy. Constructing a term is free — one machine word plus a
//! zero-sized brand marker — and nothing is read off the BEAM heap until an
//! env-passing accessor asks for it:
//!
//! ```no_run
//! # use otter::types::{CallEnv, Atom, Binary, Integer, Map};
//! # fn demo(env: CallEnv<'_>, key_atom: Atom) {
//! // Constructors are associated functions taking an env:
//! let i = Integer::from_i64(env, 42);
//! let b = Binary::from_bytes(env, b"hello");
//! let m = Map::new(env);
//!
//! // Accessors take an env:
//! let n = i.to_i64(env); // Option<i64>
//! let bytes = b.as_bytes(env); // &[u8], zero-copy into the BEAM heap
//!
//! // Term inputs are taken as `impl Term<'id>` — pass concrete types directly,
//! // no `.encode()` needed; a term of the wrong brand fails to compile:
//! let m = m.put(env, key_atom, i);
//! # let _ = (n, bytes, m);
//! # }
//! ```
//!
//! ### Honest codecs
//!
//! [`Encoder`](codec::Encoder)/[`Decoder`](codec::Decoder) move between terms and
//! native Rust types — integers, floats, `bool`, `str`/`String`, tuples,
//! `Vec<T>`, `HashMap<K, V>`, and (with the `bigint` feature) arbitrary-precision
//! integers. A decoder never lies: `300` into a `u8` is an `IntegerOverflow`
//! error, not a silently truncated `44`, and a float will not quietly swallow an
//! integer. A failed decode on a NIF argument becomes `badarg`; a failed encode
//! on a return becomes `badret`.
//!
//! ### Faithful Erlang types
//!
//! All eleven term types are present, and they mirror Erlang rather than a
//! convenient subset of it. [`List`](types::List) is a real cons cell
//! (`Nil | Cell(head, tail)`), so improper lists are first-class and a codec
//! rejects a bad tail with a clean error instead of papering over it.
//!
//! ### The full send matrix
//!
//! Message passing is a 2×2 of four free verbs: `send_copy`/`send_move` — copy a
//! live term, or steal an [`OwnedEnvArena`](types::OwnedEnvArena) heap in O(1) —
//! each available plain (NULL caller, off-thread) or `_from` (caller-attributed,
//! in-NIF). You can build a message on a worker thread with no env in hand and
//! move its whole heap into a process in a single send.
//!
//! ### Hot code upgrade without cross-build ABI assumptions
//!
//! Every `otter` module is hot-code-upgradeable. Erlang upgrades a running system
//! in place: a second build of a NIF library can load beside the first and
//! inherit its live state (resources, `priv_data`). The two builds may come from
//! different compilers and allocators and need not be byte-identical source, so
//! **the upgrade boundary is a foreign-ABI boundary.**
//!
//! Outside the `raw` feature, `otter` never assumes across that boundary that
//! allocators or drop glue are compatible, that std datatypes share a layout, or
//! that identical source compiles to a compatible layout; the safe path holds by
//! construction. Code that relies on cross-build ABI compatibility belongs only
//! behind `raw`, where the caller takes responsibility. The full treatment is in
//! the project's `docs/UPGRADE.md`.
//!
//! ### The raw escape hatch
//!
//! Where the safe surface isn't enough, the `raw` feature exposes the
//! all-`unsafe` [`enif_ffi`] crate directly, widens the brand-bridging term
//! constructors to `pub`, and accepts the `_raw` lifecycle callbacks in
//! [`init!`](macro@init) — a deliberate, opt-in boundary where you take
//! responsibility for the ABI. See [Cargo features](#cargo-features).
//!
//! ## A guided tour
//!
//! The crate is organized into a few logical areas:
//!
//! - **The env/term spine** — [`types`] defines the [`Env`](types::Env) trait and
//! its concrete kinds ([`CallEnv`](types::CallEnv), [`InitEnv`](types::InitEnv),
//! [`CallbackEnv`](types::CallbackEnv), [`DeinitEnv`](types::DeinitEnv),
//! [`OwnedEnv`](types::OwnedEnv)), the [`Term`](types::Term) trait and its
//! resolution levels, the [`OwnedEnvArena`](types::OwnedEnvArena), the four
//! message [send verbs](types#functions), and the [`Raised`](types::Raised)
//! exception witness.
//! - **Term types** — [`types::terms`] has one concrete type per Erlang type:
//! [`Atom`](types::Atom), [`Integer`](types::Integer), [`Float`](types::Float),
//! [`Binary`](types::Binary)/[`Bitstring`](types::Bitstring),
//! [`List`](types::List), [`Tuple`](types::Tuple), [`Map`](types::Map),
//! [`Pid`](types::Pid), [`Port`](types::Port), [`Reference`](types::Reference),
//! and [`Fun`](types::Fun).
//! - **Owned buffers** — [`BinaryBuf`](types::BinaryBuf), a growable byte buffer
//! you fill off-thread with no env in hand, then hand to the BEAM in one move.
//! - **Codecs** — [`codec`] holds the
//! [`Encoder`](codec::Encoder)/[`Decoder`](codec::Decoder) traits and their
//! impls for the native Rust types.
//! - **Resources and per-build state** — [`resource`] gives
//! [`ResourceArc<T>`](resource::ResourceArc), a refcounted handle to a Rust
//! value the BEAM can hold and hand back; [`priv_data`] is the per-build
//! private-data slot and the resource-type registry.
//! - **Runtime services** — [`time`] (BEAM monotonic clock), [`system`]
//! (thread-type introspection), [`select`] (I/O event multiplexing), and
//! [`alloc`] (route Rust's global allocator through the BEAM allocator).
//! - **Macros** — [`nif!`](macro@nif) marks a function as a NIF,
//! [`init!`](macro@init) declares the module entry point, and [`atom!`]
//! retrieves a pre-interned atom.
//!
//! ## Cargo features
//!
//! All features are off by default; `otter` always binds NIF 2.17 (OTP 26).
//!
//! | Feature | Effect |
//! |---|---|
//! | `nif_2_18` | Enable the NIF 2.18 (OTP 29) additions. |
//! | `bigint` | Pull in the optional `num-bigint` dependency (compiled only when this feature is enabled) and add arbitrary-precision integers: `types::BigInt` (a re-export of `num_bigint::BigInt`), its `Encoder`/`Decoder`, and `Integer::to_bigint`/`from_bigint`. |
//! | `raw` | Expose the raw, all-`unsafe` [`enif_ffi`] crate as the escape hatch, widen the brand-bridging term constructors to `pub`, and accept the `_raw` lifecycle callbacks in [`init!`](macro@init). |
//!
//! [generativity pattern]: https://arhan.sh/blog/the-generativity-pattern-in-rust/
// The raw, 1:1, all-unsafe `enif_ffi` crate — the escape hatch, available only
// under the `raw` feature. Generated code no longer names this path (it uses
// `__codegen::ffi::*`), and the enif types that appear in otter's own public API
// are re-exported through their modules (`select::{Event, SelectFlags, …}`,
// `types::{TermType, Hash, UniqueInteger}`, `time::*`, `system::SysInfo`,
// `resource::ResourceFlags`), so nothing in the safe surface needs this. (enhance-11)
pub use enif_ffi;
// enif-ffi's `nif_init!` builds the platform entry point and resolves the
// enif_* table at load. `#[macro_export]` macros aren't reachable through the
// re-exported crate path (`otter::enif_ffi::nif_init!`), so re-export it by name
// into otter's root; the `init!`-generated code invokes `::otter::nif_init!`.
pub use nif_init;
pub use nif;
pub use init;
pub use resource_impl;
// Internal: widen an item to `pub` under the `raw` feature without duplicating
// it. Used within otter to expose selected internals on the raw escape hatch;
// the emitted `cfg(feature = "raw")` resolves against otter's own `raw` feature.
pub use raw;
/// Retrieve an atom pre-declared in the `atoms = [...]` list of
/// [`init!`](crate::init).
///
/// Returns an [`Atom`] via a single atomic load — no hash lookup, no NIF call.
/// The atoms are interned once at NIF load (and re-interned on hot upgrade) by
/// the `init!`-generated scaffolding, so they are ready before any NIF runs.
///
/// ```no_run
/// # use otter::types::{Atom, CallEnv};
/// otter::init!("my_nif", [get_ok], atoms = [ok, error]);
///
/// #[otter::nif]
/// fn get_ok(_env: CallEnv<'_>) -> Atom {
/// otter::atom![ok]
/// }
/// ```
///
/// [`Atom`]: crate::types::Atom