📦 lencode
Compact, fast binary encoding with varints, optional deduplication, and opportunistic zstd compression for bytes and strings. no_std by default, with an opt‑in std feature.
Highlights
- Fast varints: efficient for small and large integers
- Optional deduplication: replace repeats with compact IDs for supported types
- Bytes/strings compression: flagged header + zstd when smaller; high‑entropy data is detected and skipped automatically
- Bulk encoding:
Vec<T>of fixed‑size types (e.g.[u8; 32]) are encoded/decoded via bulkmemcpy, not per‑element - no_std + alloc: works without
std(useszstd-safe) - Derive macros:
#[derive(Encode, Decode)]for your types,#[derive(Pack)]for dedupe/bulk types - Solana support: feature
solanaadds v2/v3 SDK types - Big-endian ready: CI runs tests on s390x
Install
[]
= "0.1"
# With standard library types (e.g., Cow)
= { = "0.1", = ["std"] }
# With Solana type support (implies std)
= { = "0.1", = ["solana"] }
Quick start
Derive and round‑trip
use *;
let p = Point ;
let mut buf = Vecnew;
encode?;
let q: Point = decode?;
assert_eq!;
Collections and primitives
use *;
let values: = .collect;
let mut buf = Vecnew;
encode?;
let rt: = decode?;
assert_eq!;
Deduplication (optional)
To benefit from deduplication for your own types, implement Pack and the marker traits and pass the encoder/decoder via encode_ext/decode_ext.
use *;
;
let vals = vec!;
// Encode with deduplication enabled
let mut enc_ctx = with_dedupe;
let mut buf = Vecnew;
encode_ext?;
// Decode with deduplication enabled
let mut dec_ctx = with_dedupe;
let roundtrip: = decode_ext?;
assert_eq!;
Compact bytes and strings
&[u8], Vec<u8], VecDeque<u8], &str, and String use a compact flagged header: varint((payload_len << 1) | flag) + payload.
flag = 0→ raw bytes/UTF‑8flag = 1→ zstd frame (original size stored inside the frame)
The encoder picks whichever is smaller per value. High‑entropy data (random bytes, encrypted content) is detected via a fast entropy check and skips compression entirely.
Bulk encoding for fixed‑size types
Vec<T> where T has a fixed‑size wire representation (e.g. [u8; 32], or #[repr(transparent)] newtypes over byte arrays) is encoded and decoded via bulk memcpy rather than per‑element iteration. This is handled automatically through Encode::encode_slice / Decode::decode_vec and their Pack counterparts Pack::pack_slice / Pack::unpack_vec.
Custom Pack types can opt in by overriding pack_slice and unpack_vec, or by using #[derive(Pack)] on a #[repr(transparent)] single‑field struct, which generates the bulk overrides automatically:
use *;
;
Incremental diff encoding
DiffEncoder/DiffDecoder provide stateful delta encoding for keyed byte blobs. When the same key is re‑encoded, only the diff is emitted. Two strategies are tried automatically and the smaller output is picked:
- RLE patches — run‑length‑encoded list of changed regions (fast, best for sparse changes)
- XOR + zstd — XOR old and new blobs, then zstd‑compress the result (best for scattered changes)
Supported byte types: Vec<u8>, &[u8], [u8; N], VecDeque<u8>. Set a key via set_key() on the diff encoder/decoder before each encode_ext/decode_ext call to opt in.
use *;
use ;
use ;
let key = 1u64;
let mut enc_ctx = EncoderContext ;
let mut dec_ctx = DecoderContext ;
// First encode (full blob)
let data1: = vec!;
let mut buf = Vecnew;
enc_ctx.diff.as_mut.unwrap.set_key;
dec_ctx.diff.as_mut.unwrap.set_key;
data1.encode_ext.unwrap;
// Second encode (only the diff is written)
let mut data2 = data1.clone;
data2 = 0xFF;
buf.clear;
enc_ctx.diff.as_mut.unwrap.set_key;
dec_ctx.diff.as_mut.unwrap.set_key;
data2.encode_ext.unwrap;
assert!; // diff is much smaller
let mut cursor = new;
let result: = Vecdecode_ext.unwrap;
assert_eq!;
Writer pre‑allocation
The Write trait provides a reserve(additional) hint. Growable writers like VecWriter use this to pre‑allocate capacity before encoding large collections, reducing intermediate reallocations.
Supported types
- Primitives: all ints,
bool,f32,f64 - Arrays:
[T; N] - Option:
Option<T> - Bytes/strings:
&[u8],Vec<u8],VecDeque<u8],&str,String - Collections (alloc):
Vec<T>,BTreeMap<K,V>,BTreeSet<V>,VecDeque<T>,LinkedList<T>,BinaryHeap<T> - Tuples:
(T1,)… up to 11 elements stdfeature: adds support forstd::borrow::Cow<'_, T>solanafeature:Pubkey,Signature,Hash, messages (legacy/v0), and related v2/v3 types
Note: HashMap/HashSet are not implemented.
Cargo features
default: core +no_std(usesalloc)std: enablesstdadapters andCowsolana: Solana SDK v2 + Agave v3 types (impliesstd)
Big‑endian and portability
- Varints are decoded efficiently on little‑endian and portably on big‑endian
- CI runs tests on
s390x-unknown-linux-gnuusingcross Packalways uses a stable little‑endian layout
Benchmarks
# Full suite
# Compare against borsh/bincode
# Diff encoder (RLE vs XOR+zstd strategies)
# Solana‑specific
Errors
Errors use lencode::io::Error and map to std::io::Error under std.
use *;
use Error;
let mut buf = Vecnew;
match encode
Examples
examples/size_comparison.rs: space savings on repeated Solana pubkeysexamples/versioned_tx_compression.rs: end‑to‑end on Solana versioned transactions
Run with --features solana.
License
MIT