Skip to main content

Crate sefer_region

Crate sefer_region 

Source
Expand description

§sefer-region — typed handle-addressed store

A thin typed membrane over slotmap: values live in slotmap::SlotMap — a contiguous slot array resolved by a single indirection (the lookup/churn axis it was benchmarked to win; see https://github.com/PHPCraftdream/sefer-alloc/blob/main/docs/BENCHMARKS.md). SlotMap keeps tombstone holes after removals, so it is NOT always-compact; DenseSlotMap is the dense-iteration alternative. Every operation exposes only typed Handle<T> values — raw DefaultKeys never escape as usable values through the API (Debug output renders the underlying key for diagnostics only — it cannot be turned back into a functioning handle through this crate’s public surface).

Runtime relationship to sefer-alloc: This crate exists as a public surface for the sefer-alloc workspace root crate (which re-exports Region, Handle, and SyncRegion), but it is not used by the sefer-alloc allocator runtime itself. Empirically verified by searching the allocator source code (src/) in the main workspace: no direct calls into Region/Handle/SyncRegion on any hot path. Further API evolution is intentionally deferred until a confirmed external consumer requests it.

§What makes this different from using slotmap directly?

Slotmap’s DefaultKey is untyped: a DefaultKey from one map can be passed to another of a different value type without a compile error. sefer-region wraps it in Handle<T> — a PhantomData<fn() -> T>-branded key plus a region_id — so the compiler rejects cross-type handle confusion at the type level (a Handle<Foo> cannot be used where a Handle<Bar> is expected), and the runtime region_id check rejects cross-instance handle confusion at the value level: a Handle<T> minted by one Region<T> is rejected (treated exactly like a stale handle — None/ false, no panic) by every other Region<T> of the same type, even one whose slotmap key happens to collide with the handle’s own key.

§Invariants upheld (I1–I7)

  • I1 — resolution: a fresh handle resolves via Region::get to the inserted value until it is Region::removed.
  • I2 — tombstone: after remove(h), get(h) returns None for roughly 2^31 reuse cycles of that slot (a stale handle that has survived that many insert/remove cycles may wrap and spuriously resolve to a later value). A second remove(h) is a no-op None.
  • I3 — no ABA: a stale handle — one whose slot has since been reused — does not resolve to a live value for roughly 2^31 reuse cycles of that slot. slotmap’s DefaultKey carries a 32-bit generation (odd = occupied, even = vacant): insert sets the low bit, remove increments via wrapping_add(1), so a full occupy/free cycle advances the generation by 2 — after 2^31 such cycles it wraps and a very old handle may alias a later value. Memory safety is never affected — slotmap guarantees this even after wrap.
  • I4 — accounting: Region::len equals the number of live entries and Region::is_empty agrees.
  • I5 — drop-once: every live value is dropped exactly once. Successful remove transfers ownership to the caller without calling Drop; values still owned when a normally-destroyed Region drops are dropped. The crate never duplicates or internally forgets values.
  • I6 — slot reuse and bounded growth: freed slots are reused by insert; capacity grows to a historical high-water mark of live entries and does not increase further under steady-state churn (slotmap does not physically compact — tombstone slots remain in the backing store; I6 guarantees only reuse and bounded growth, not physical density).
  • I7 — instance isolation: a Handle<T> resolves only through the Region<T> instance that minted it. Every accessor stamps its region_id at construction and checks it before touching the backing slotmap; a mismatch is treated exactly like a stale handle. Two Region<T>s can never alias each other’s values through a shared DefaultKey, even when that key collides (as it commonly does — the first insert into any fresh Region tends to produce the same key).

§Pure Rust / zero own unsafe

#![forbid(unsafe_code)] at the top of this crate. The internal unsafe lives upstream, in the mature, widely-used slotmap dependency, not in this crate. This crate adds no C / C++ libraries and contributes zero unsafe blocks of its own.

§no_std support

With default-features = false (disabling std) the crate compiles under no_std + alloc, providing Region<T> and Handle<T>. The std feature (on by default) additionally enables SyncRegion<T>, which wraps Region<T> in std::sync::RwLock.

Structs§

Handle
An opaque, copyable reference to a value stored in a Region.
Iter
Iterator over the live values in a Region<T>, returned by Region::iter and IntoIterator for &Region<T>.
IterMut
Mutable iterator over the live values in a Region<T>, returned by Region::iter_mut and IntoIterator for &mut Region<T>.
Region
A handle-addressed store of T.
RegionIdExhaustedError
Error type returned when the process-wide region_id counter is exhausted.
SyncRegionstd
A thread-safe wrapper around Region<T> — the trusted concurrent baseline.

Enums§

TryReserveError
Error returned by fallible Region<T> constructors and capacity operations.