Judy arrays in pure Rust, rebuilt for modern hardware
Sparse, ordered maps and sets with adaptive density — memory follows the key ranges you populate, never pre-sized tables or fixed buckets.
Sorted iteration, range scans and rank over integers, strings and byte slices — with cache-line-aligned nodes, SIMD/SWAR search and optimistic reader concurrency. One engine from a 32-bit MCU to a server, reachable from nine languages over a stable C ABI.
Partitioning by key expanse, rather than population
Comparison trees (B-trees, red-black trees) divide nodes by key population count. Judy digital trees divide uniformly by key digit ranges — an architectural invariant where memory scales strictly with populated density rather than table sizing.
“Expanse, population, and density are not commonly used terms in tree search literature, so let’s define them here: Expanse is a range of possible keys. Population is the number of keys actually stored in that expanse. Density is the population divided by the expanse.”
Is this the right structure for you?
Two lists answer it in ten seconds, naming the alternatives by name.
Reach for Expanse when
- Your keys are clustered or sequential — timestamps, IDs, addresses, offsets.
- Memory is the binding constraint, not raw lookup throughput.
- You need ordered iteration, range scans or rank — not just point lookup.
- You are on a 32-bit MCU and a hash table's load factor costs too much SRAM.
- You want one engine across nine languages rather than a per-language reimplementation.
Use something else when
- Uniform-random point lookup is your hot path — hashbrown wins that, and random keys are a trie’s worst case (16.70 B/key against 0.67 for clustered).
- You never iterate in order — HashMap is simpler and faster, and ordering is most of what you are paying for here.
- You need a concurrent writer workload; on a 50/50 mix every single-writer arm loses throughput as threads are added (0.12×–0.55×). DashMap wins that regime.
- Your keys are long, high-entropy strings with no shared prefixes — the trie's structure buys you nothing.
Architectural Highlights
Engineered for cache-line density, hardware SIMD lanes, and optimistic multi-core read throughput.
Zero-Alloc Immediates
Up to 7 keys in sets and up to 3 key-value pairs in maps are packed directly inside tagged 64-bit edge words, bypassing heap allocation entirely for small collections.
Adaptive Compression Ladder
Trie branches dynamically morph between Linear leaves (sorted key arrays), Bitmap leaves (64-bit subexpanse bitboards), and full uncompressed 256-way digital branches.
Lock-Free OCC Concurrency
SyncExpanseMap and SyncExpanseSet employ epoch-based optimistic concurrency control (OCC). Readers perform optimistic validated traversals with zero reader-lock cache-line bouncing.
SIMD & SWAR Acceleration
Search kernels utilize vector instructions (AVX2, AVX-512, ARM NEON) with bitwise SWAR fallbacks, scanning linear leaves in single clock cycles.
glibc-hwcaps Multi-Arch
Debian and RPM packages provide optimized runtime libraries automatically selected by the dynamic loader for x86-64-v2, v3 (AVX2), and v4 (AVX-512).
Drop-in Judy C ABI Parity
Provides 100% C ABI compatibility with stock libjudy (Judy1, JudyL, JudySL, JudyHS) alongside modern, type-safe expanse_* C interfaces.
32-Bit Embedded (#![no_std])
Compact 8-byte Edge32 layout saving 50% structural SRAM on ARM Cortex-M and RISC-V RV32 microcontrollers, with 32-byte cache alignment and zero-alloc inlined payloads.
Database Engine Subsystems
MVCC visibility scans, string interning dictionaries, and an ExpanseBlobMap slab arena for variable-length values — plus a RocksDB MemTable plugin.
Explore the Interactive Data Visualizer
Inspect tagged pointer layouts, simulate dynamic compression transitions across the ladder, and step through branch bitboard operations in real-time.
Benchmarks
The same three charts already on the page — given titles, a one-line read, and provenance a reader can resolve.
commit 695b98d
CI runs 34881026495, 34882381735
commit 43b46f38
LEAF_CAP overflow cascade sets the tooth. The same structure spans 7.6–21 B/key under density alone; the memory-budget gate samples both sides of the cascade.deterministic byte accounting
Install
Native packages and zero-cost bindings. Pick your target.
Add core Expanse engine to your Cargo.toml:
cargo add expanse-trie
Usage example in Rust (Maps, Sets, Off-Heap Blobs, Lock-Free OCC):
use expanse_trie::{ExpanseMap, ExpanseSet, ExpanseBlobMap, SyncExpanseMap};
// 64-bit integer map with zero-allocation immediates
let mut map = ExpanseMap::new();
map.insert(42, 100);
assert_eq!(map.get(42), Some(100));
// Variable-length byte blob map with slab arena & hot metadata
let mut blobs = ExpanseBlobMap::new();
blobs.insert(1, b"hello expanse", 0x2A);
assert_eq!(blobs.get(1), Some(&b"hello expanse"[..]));
// Thread-safe optimistic OCC map (zero reader locks)
let sync_map = SyncExpanseMap::new();
sync_map.insert(99, 500);
let reader = sync_map.reader();
assert_eq!(reader.get(99), Some(500));
Canonical Documentation
Comprehensive architectural and algorithmic references.