Skip to main content

Crate nearest

Crate nearest 

Source
Expand description

§nearest

Self-relative pointers and region-based allocation for Rust.

nearest stores entire data graphs in a single contiguous byte buffer where all internal pointers are 4-byte i32 offsets relative to their own address. This makes Clone a plain memcpy — no pointer fixup needed.

Mutations use a branded Session API (ghost-cell pattern) where Ref tokens carry a compile-time brand preventing cross-session use or escape — all at zero runtime cost.

§Core types

TypeSizeRole
Region<T>heapOwning contiguous buffer; root T at byte 0
Near<T>4 BSelf-relative pointer (NonZero<i32> offset)
NearList<T>8 BSegmented linked list (offset + length)
SessionBranded mutable session for safe mutation
Ref4 BBranded position token (Copy, no borrow)
FlatMarker trait: no Drop, no heap pointers
EmitBuilder trait for declarative construction

§Defining types

#[derive(Flat)] generates both the Flat safety marker and the Emit builder, including a make() factory for each struct or enum variant.

§Structs

use nearest::{Flat, Near, NearList};

#[derive(Flat, Debug)]
struct Func {
  name: u32,
  entry: Near<Block>,
}

#[derive(Flat, Debug)]
struct Block {
  id: u32,
  insts: NearList<u32>,
}

Fields may be primitives (u8u64, i32i64, bool), Near<T>, NearList<T>, Option<Near<T>>, or any other Flat type.

§Enums

Enums require #[repr(C, u8)] (or #[repr(u8)] for unit-only enums):

use nearest::Flat;

#[derive(Flat, Copy, Clone, Debug, PartialEq)]
#[repr(C, u8)]
enum Value {
  Int(u32),
  Bool(bool),
}

Each data variant gets a make_<variant>() factory. Unit variants need no factory.

§Constructing a region

Region::new takes any Emit<T> builder — typically the make() factory generated by #[derive(Flat)]. Builder arguments correspond to fields in declaration order. Near<T> fields accept any impl Emit<T> (usually a nested make()), and NearList<T> fields accept any IntoIterator whose items implement Emit:

use nearest::{Flat, Near, NearList, Region, empty};

let region = Region::new(Func::make(
  1,                              // name: u32
  Block::make(0, [10u32, 20, 30]),  // entry: Near<Block>
));

assert_eq!(region.name, 1);
assert_eq!(region.entry.insts.len(), 3);

Use empty() for empty NearList fields:

let region = Region::new(Block::make(0, empty()));
assert!(region.insts.is_empty());

§Reading data

Region<T> implements Deref<Target = T>, giving direct access to the root value. Near<T> also dereferences to &T:

// Region<T>: Deref<Target = T>
assert_eq!(region.name, 1);

// Near<T>: Deref<Target = T>
let block: &Block = &region.entry;
assert_eq!(block.id, 0);

// NearList<T>: indexing and iteration
assert_eq!(block.insts[0], 10);
let sum: u32 = block.insts.iter().sum();
assert_eq!(sum, 60);

§Mutating with sessions

Region::session opens a branded closure where mutations are performed through Ref position tokens. The ghost-cell pattern guarantees refs cannot escape the closure or be used across sessions — enforced at compile time with zero runtime cost.

#[derive(Flat, Debug)]
struct Block {
  id: u32,
  items: NearList<u32>,
}

let mut region = Region::new(Block::make(1, [10u32, 20, 30]));

region.session(|s| {
  // Navigate to a field, then overwrite it
  let id = s.nav(s.root(), |b| &b.id);
  s.set(id, 42);

  // Replace an entire list
  let items = s.nav(s.root(), |b| &b.items);
  s.splice_list(items, [100u32, 200]);
});

assert_eq!(region.id, 42);
assert_eq!(region.items.len(), 2);
assert_eq!(region.items[0], 100);
MethodPurpose
Session::rootRef to the root value
Session::navNavigate to a sub-field
Session::followDereference a Near<T> ref
Session::atRead the value at a ref

§Scalar mutation

MethodPurpose
Session::setOverwrite a Copy field in place
Session::writeOverwrite with an Emit builder

§Pointer and list replacement

MethodPurpose
Session::spliceReplace the target of a Near<T>
Session::splice_listReplace the contents of a NearList<T>

§List operations

MethodPurpose
Session::push_frontPrepend one element (O(1))
Session::push_backAppend with cached ListTail (O(1))
Session::extend_listAppend multiple elements
Session::filter_listKeep elements matching a predicate
Session::map_listTransform Copy elements in place
Session::reverse_listReverse element order
Session::sort_listSort elements by comparator
Session::dedup_listRemove consecutive duplicates

§Cursor API

For deeply nested navigation, Session::cursor provides a chainable interface:

region.session(|s| {
  s.cursor()
    .at(|f| &f.entry)
    .follow()
    .at(|b| &b.id)
    .set(99);
});

assert_eq!(region.entry.id, 99);

§Serialization

Region::as_bytes returns the raw buffer contents as a &[u8], suitable for writing to a file or sending over the network. Region::from_bytes copies the bytes into an aligned buffer, then runs Flat::validate to check bounds, alignment, pointer validity, and type-specific invariants (e.g. enum discriminants, bool values) before reconstructing the region.

#[derive(Flat, Debug)]
struct Node {
  id: u32,
  children: NearList<u32>,
}

let original = Region::new(Node::make(1, [10u32, 20, 30]));
let bytes = original.as_bytes();

// bytes can be persisted to disk, sent over the network, etc.
let restored: Region<Node> = Region::from_bytes(bytes).unwrap();
assert_eq!(restored.id, 1);
assert_eq!(restored.children.len(), 3);

Invalid or corrupted data is rejected with a ValidateError:

use nearest::{Flat, Near, Region, ValidateError};

#[derive(Flat, Debug)]
struct Flags {
  active: bool,
  label: Near<u32>,
}

let region = Region::new(Flags::make(true, 42u32));
let mut bytes = region.as_bytes().to_vec();
bytes[core::mem::offset_of!(Flags, active)] = 2; // corrupt the bool
assert!(Region::<Flags>::from_bytes(&bytes).is_err());

§Compaction

Mutations are append-only — replaced data becomes dead bytes in the buffer. Region::trim deep-copies only reachable data into a fresh buffer, reclaiming the waste:

let mut region = Region::new(Block::make(1, [10u32, 20, 30]));
let before = region.byte_len();

region.session(|s| {
  let items = s.nav(s.root(), |b| &b.items);
  s.splice_list(items, [100u32]);
});
assert!(region.byte_len() > before);

region.trim();
assert!(region.byte_len() <= before);
assert_eq!(region.items[0], 100);

After trimming, every NearList is compacted to a single contiguous segment, making indexing O(1).

§Cloning

Self-relative offsets are position-independent, so Clone for Region<T> is a byte-for-byte buffer copy — no pointer fixup or graph traversal:

let region = Region::new(Block::make(1, [10u32, 20, 30]));
let cloned = region.clone();

assert_eq!(cloned.id, region.id);
assert_eq!(cloned.items[0], region.items[0]);

§Features

FeatureDefaultEnables
allocyesHeap-backed AlignedBuf, Region::new, Region::trim, growable sessions
stdnoImplies alloc; reserved for future std-only APIs
serdenoSerialize / Deserialize for Region<T>

§no_std support

nearest is #![no_std] by default. Disabling the alloc feature removes all heap allocation — use FixedBuf<N> for stack-backed regions with a compile-time capacity:

[dependencies]
nearest = { version = "0.2", default-features = false }
use nearest::{Flat, NearList, Region, FixedBuf, empty};

#[derive(Flat, Debug)]
struct Msg {
  id: u32,
  tags: NearList<u32>,
}

// 128-byte inline buffer, no heap allocation.
let region: Region<Msg, FixedBuf<128>> =
  Region::new_in(Msg::make(1, [10u32, 20]));
assert_eq!(region.id, 1);
assert_eq!(region.tags.len(), 2);

§serde

With the serde feature, Region<T> implements Serialize and Deserialize. The region is serialized as its raw byte buffer (via as_bytes), and deserialization runs from_bytes validation automatically.

[dependencies]
nearest = { version = "0.2", features = ["serde"] }

§Safety model

nearest is an unsafe-heavy crate. This section documents the invariants that make the safe public API sound.

§Flat invariants

Every type stored in a region must implement Flat, which guarantees:

  • No Drop: A const assertion rejects types with destructors at compile time.
  • No heap pointers: All indirection uses Near<T> or NearList<T> (self-relative offsets), never Box, Vec, or raw pointers.
  • Correct deep_copy: The derived deep_copy copies fields and patches self-relative offsets to their new positions.

§Provenance

Self-relative pointers cannot use strict Rust provenance: &self.offset has provenance over only 4 bytes, but the target T may be larger. Instead, every buffer allocation exposes its provenance via expose_provenance, and reads recover it via with_exposed_provenance. This is the canonical pattern for self-relative pointers in Rust, validated by Miri.

§Aliasing

Mutation through Session uses pre-reservation to prevent buffer reallocation, then recovers provenance for reads so the &T is not derived from the &mut Region — avoiding Stacked Borrows violations.

§Branding

Region::session uses the ghost-cell pattern (for<'id> FnOnce(&mut Session<'id, …>)) to make Ref tokens invariant in their brand lifetime 'id. This prevents refs from escaping the session closure or being mixed across sessions — enforced by compile-fail tests.

§Alignment

The buffer base is aligned to max(align_of::<Root>(), 8). Every allocation within the buffer is aligned to align_of::<T>() via padding, with a compile-time assertion that no type exceeds the buffer alignment.

§No-drop

The derive macro emits const { assert!(!needs_drop::<T>()) } for every derived type, rejecting Drop impls at compile time. This guarantees regions can be memcpy’d without running destructors.

§Minimum Supported Rust Version

This crate requires nightly Rust (pinned to nightly-2026-02-10) due to offset_of_enum.

Re-exports§

pub use session::ListTail;
pub use session::Ref;
pub use session::Session;

Modules§

session
Branded session API for safe region mutation.

Structs§

AlignedBuf
Growable byte buffer with base pointer aligned to max(align_of::<T>(), 8).
FixedBuf
Stack-backed fixed-capacity buffer with 8-byte alignment.
Near
A self-relative pointer stored as a 4-byte NonZero<i32> offset.
NearList
A linked list of T values stored in a Region.
NearListIter
Iterator over the elements of a NearList.
Region
An owning, contiguous byte buffer whose root value T starts at byte 0.

Enums§

ValidateError
Error returned by Flat::validate and Region::from_bytes.

Traits§

Buf
Backing storage for a Region.
Emit
Builder trait for constructing values in a Region.
Flat
Marker trait for types that can be stored in a Region.

Functions§

empty
Returns an empty iterator suitable for any NearList<T> emitter parameter.

Derive Macros§

Emit
Derive Emit<T> for a proxy enum that dispatches to inner builders.
Flat
Derive Flat and Emit for region-based storage.