You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Built for no_std from day one — runs in zkVMs, WASM, and embedded targets. Up to 17x faster than ethrex-rlp and up to 1.4x faster than alloy-rlp on struct encode/decode. Fuzz-tested against both reference implementations.
Single-pass RlpBuf — records list start positions during encoding, computes headers after all payload data is written, and assembles final output in one linear pass. No intermediate allocations for nested lists
Compile-time ENCODED_LEN — the derive macro const-folds field lengths at compile time. Only dynamic-length fields are measured at runtime
encode_unchecked — unsafe direct-write path that skips bounds checks after a single upfront length calculation. Used automatically by to_rlp() and encode_into()
get_unchecked in decode — after bounds validation, inner loops use unchecked indexing to eliminate redundant bounds checks in hot paths
Zero-copy RlpRef — parses only RLP headers without copying payload data. Field access skips over preceding items at header-scan speed
Aggressive inlining — #[inline(always)] on all trait impls that cross crate boundaries
no_std Support
librlp is no_std + alloc by default. The std feature (enabled by default) only adds std::error::Error impls. Disable it for embedded, zkVM, or WASM targets:
RlpRef parses only RLP headers, deferring field decoding until accessed:
use librlp::{RlpEncode,RlpRef};let encoded = (42u64,100u64,vec![0u8;1000]).to_rlp();let r = RlpRef::new(&encoded).unwrap();// Access only the first field — skips parsing the restlet nonce:u64 = r.get(0).unwrap().decode().unwrap();
Buffer Reuse
encode_into reuses an existing Vec allocation across multiple encodes:
use librlp::RlpEncode;letmut buf = Vec::new();for tx in&transactions {
tx.encode_into(&mut buf);send(&buf);}
List Encoding
use librlp::encode::{encode_list_to_rlp, encode_list};use librlp::decode::decode_list;use librlp::RlpDecode;let items = vec![1u64,2,3,4,5];// Encode a listlet encoded = encode_list_to_rlp(&items);// Decode a listletmut buf:&[u8] = &encoded;let decoded:Vec<u64> = decode_list(&mut buf).unwrap();
Derive Attributes
Attribute
Target
Effect
#[rlp(trailing)]
struct
Last Option<T> fields are omitted when None
#[rlp(skip)]
field
Field excluded from encoding; uses Default on decode
cargo test --workspace # all tests, default features
cargo test --workspace --all-features # all tests, all features
cargo test --workspace --no-default-features # no_std tests
Miri (undefined behavior detection)
cargo +nightly miri test --workspace
Fuzzing
Five fuzz targets, differential-tested against alloy-rlp and ethrex-rlp:
Target
Strategy
roundtrip
encode -> decode -> equality for all primitive types
decode_arbitrary
decode random bytes, assert no panics
differential
compare encoding output against alloy-rlp and ethrex-rlp
canonical
validate canonical encoding invariants
rlpref
fuzz lazy RlpRef header parsing and field access
# Run all fuzz targets (30s each)cd crates/librlp
fortargetin roundtrip decode_arbitrary differential canonical rlpref;do
cargo +nightly fuzz run "$target" -- -max_total_time=30
done
Benchmarks
cargo bench --bench comparison # vs alloy-rlp, ethrex-rlp, parity-rlp