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::getto the inserted value until it isRegion::removed. - I2 — tombstone: after
remove(h),get(h)returnsNonefor roughly2^31reuse 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 secondremove(h)is a no-opNone. - I3 — no ABA: a stale handle — one whose slot has since been reused —
does not resolve to a live value for roughly
2^31reuse cycles of that slot.slotmap’sDefaultKeycarries a 32-bit generation (odd = occupied, even = vacant):insertsets the low bit,removeincrements viawrapping_add(1), so a full occupy/free cycle advances the generation by 2 — after2^31such cycles it wraps and a very old handle may alias a later value. Memory safety is never affected —slotmapguarantees this even after wrap. - I4 — accounting:
Region::lenequals the number of live entries andRegion::is_emptyagrees. - I5 — drop-once: every live value is dropped exactly once. Successful
removetransfers ownership to the caller without callingDrop; values still owned when a normally-destroyedRegiondrops 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 (slotmapdoes 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 theRegion<T>instance that minted it. Every accessor stamps itsregion_idat construction and checks it before touching the backing slotmap; a mismatch is treated exactly like a stale handle. TwoRegion<T>s can never alias each other’s values through a sharedDefaultKey, even when that key collides (as it commonly does — the first insert into any freshRegiontends 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 byRegion::iterandIntoIterator for &Region<T>. - IterMut
- Mutable iterator over the live values in a
Region<T>, returned byRegion::iter_mutandIntoIterator for &mut Region<T>. - Region
- A handle-addressed store of
T. - Region
IdExhausted Error - Error type returned when the process-wide
region_idcounter is exhausted. - Sync
Region std - A thread-safe wrapper around
Region<T>— the trusted concurrent baseline.
Enums§
- TryReserve
Error - Error returned by fallible
Region<T>constructors and capacity operations.