zero-alloc core — no_std, fixed-size, embedded-ready
the core cyber-hemera crate is #![no_std] with zero heap allocation in the hashing path. the Hasher, permutation, field arithmetic, and sponge all operate on fixed-size stack-resident buffers. suitable for embedded systems, WASM targets, and environments without an allocator.
what allocates and what does not
zero-alloc (stack only)
| component | buffer | size |
|---|---|---|
Hasher state |
[Goldilocks; 16] |
128 bytes |
Hasher input buffer |
[u8; RATE_BYTES] |
56 bytes |
| permutation | in-place mutation of state array | 0 extra |
| field arithmetic | u64 registers | 0 extra |
hash() |
Hasher on stack | ~200 bytes total |
keyed_hash() |
Hasher on stack | ~200 bytes total |
derive_key() |
two Hashers on stack | ~400 bytes total |
OutputReader (XOF) |
state array | 128 bytes |
heap-using (behind features or in tree/stream modules)
| component | allocation | reason |
|---|---|---|
stream::encode() |
Vec<u8> |
output size is data-dependent |
stream::decode() |
Vec<u8> |
recovered data buffer |
SparseTree |
BTreeMap |
sparse structure requires dynamic storage |
BatchInclusionProof |
Vec<Hash> |
proof size varies |
sentinel_table() |
Vec<Hash> |
one-time per tree depth |
StreamDecoder (async) |
Vec<(...)> stack |
bounded by tree depth |
heap-using modules import from alloc — they work on no_std targets that provide a global allocator.
feature gates
[features]
default = ["std"]
std = []
serde = ["dep:serde"]
async = ["dep:tokio", "std"]
- default (
std): enables standard library. the core hash API works identically without it. serde: adds Serialize/Deserialize for Hash, InclusionProof, Sibling, NodeIndex.async: enablesstream_asyncmodule. requiresstd(tokio dependency).
a bare no_std build: cyber-hemera = { version = "0.3", default-features = false }
rs-edition compliance
the crate passes rsc --rs-edition restrictions (RS501-RS507) for the core hashing path:
- no
Box::new()(RS501) — all values on stack - no
Vec<T>in core path (RS502) — fixed-size arrays - no
String(RS503) —&stronly - no
dyn Trait(RS504) — generics throughout - no
Arc/Rc(RS505) — no shared ownership - no
panic!()in library (RS506) —Resultfor fallible ops - no
HashMap(RS507) —BTreeMapwhere maps are needed
tree and stream modules use #[allow(rs_no_vec)] annotations where Vec is unavoidable (output buffers, proof data).
why this matters
- embedded: hemera can run on microcontrollers and bare-metal targets that lack a heap allocator
- WASM: no-std + fixed-size buffers produce small WASM binaries with predictable memory
- determinism: fixed-size buffers eliminate allocation-dependent behavior
- proof circuits: the zero-alloc structure maps directly to circuit constraints — each field operation is a bounded, traceable step
- rs-edition: the crate serves as a reference for rs-lang compliant library design
implementation
rs/src/lib.rs—#![no_std],extern crate allocrs/src/sponge.rs—Hasherwith fixed-size state and bufferrs/src/field.rs— inline Goldilocks arithmeticrs/src/permutation.rs— in-place permutation on&mut [Goldilocks; WIDTH]
see inversion-sbox for the permutation design, compact-output for 32-byte output