pub struct ZeroTree<K: Key, const V: usize, D: Durability = Bitcask, T: Copy = [u8; V], H: TypedWriteHook<K, T> = NoHook> { /* private fields */ }Expand description
A tree with fixed-size keys and zerocopy-compatible values.
Thin wrapper around ConstTree<K, V, D> that provides a typed API for values
implementing [FromBytes] + [IntoBytes] + [Immutable]. Conversions are
zero-cost — values are transmuted without serialization.
Generic over D: Durability (default: Bitcask). Use Fixed backend for
frequent updates without compaction: ZeroTree::<K, V, Fixed, T>::open(...).
Requires size_of::<T>() == V (compile-time assertion in constructor).
§Write hooks
Uses TypedWriteHook<K, T> — the hook receives &T directly (not raw bytes).
on_write fires on put/insert/delete/cas/update.
Does not fire inside atomic(). Old value is always provided (it lives in
memory) — NEEDS_OLD_VALUE is ignored.
on_init fires once per live entry during migrate() or replay_init()
(enable via NEEDS_INIT = true).
§When to use
For T with trivial representation (no heap allocations, fixed layout):
structs with #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)].
All values live in memory — reads never touch disk.
For complex types (String, Vec, nested structs) use TypedTree instead.
§Usage
use zerocopy::{FromBytes, IntoBytes, Immutable, KnownLayout};
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Clone, Copy)]
#[repr(C)]
struct Counters {
views: u64,
likes: u64,
}
let tree = ZeroTree::<[u8; 16], { size_of::<Counters>() }, Counters>::open(
"data/counters",
Config::default(),
)?;
tree.put(&key, &Counters { views: 1, likes: 0 })?;
if let Some(c) = tree.get(&key) {
println!("views: {}", c.views);
}§Iteration
iter(), range(), and prefix_iter() return ZeroIter which
implements Iterator + DoubleEndedIterator with Item = (K, T).
Implementations§
Source§impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, Bitcask, T, H>
impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, Bitcask, T, H>
Sourcepub fn open_hooked(
path: impl AsRef<Path>,
config: Config,
hook: H,
) -> DbResult<Self>
pub fn open_hooked( path: impl AsRef<Path>, config: Config, hook: H, ) -> DbResult<Self>
Open or create a ZeroTree with a write hook for secondary index maintenance.
Sourcepub fn close(self) -> DbResult<()>
pub fn close(self) -> DbResult<()>
Graceful shutdown: write hint files, flush write buffers + fsync.
Sourcepub fn flush_buffers(&self) -> DbResult<()>
pub fn flush_buffers(&self) -> DbResult<()>
Flush all shard write buffers to disk (without fsync).
Sourcepub fn sync_hints(&self) -> DbResult<()>
pub fn sync_hints(&self) -> DbResult<()>
Write hint files for all active shard files. Call during graceful shutdown.
Sourcepub fn migrate(&self, f: impl Fn(&K, &T) -> MigrateAction<T>) -> DbResult<usize>
pub fn migrate(&self, f: impl Fn(&K, &T) -> MigrateAction<T>) -> DbResult<usize>
Iterate all entries and optionally mutate them. Call once at startup.
The callback receives each (key, &T) and returns MigrateAction:
Keep— no change (fireson_initifNEEDS_INIT)Update(new_value)— replace value (hook-free write, fireson_init)Delete— remove entry (hook-free tombstone, noon_init)
on_write is never fired during migration.
Returns the number of mutated entries.
Source§impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, Fixed, T, H>
impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, Fixed, T, H>
Sourcepub fn open_with_hook(
path: impl AsRef<Path>,
config: FixedConfig,
hook: H,
) -> DbResult<Self>
pub fn open_with_hook( path: impl AsRef<Path>, config: FixedConfig, hook: H, ) -> DbResult<Self>
Open or create a ZeroTree with a write hook, using Fixed (fixed-slot) backend.
Source§impl<K: Key, const V: usize, D: Durability, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, D, T, H>
impl<K: Key, const V: usize, D: Durability, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, D, T, H>
Sourcepub fn get(&self, key: &K) -> Option<T>
pub fn get(&self, key: &K) -> Option<T>
Get a value by key. Lock-free, zero disk I/O. Returns a copy of T.
Sourcepub fn get_or_err(&self, key: &K) -> DbResult<T>
pub fn get_or_err(&self, key: &K) -> DbResult<T>
Get a value by key, returning Err(KeyNotFound) if absent.
Sourcepub fn first(&self) -> Option<(K, T)>
pub fn first(&self) -> Option<(K, T)>
Return the first entry in index order, or None if empty.
With reversed=true (default): the entry with the largest key.
O(1) — follows head’s level-0 pointer.
Sourcepub fn last(&self) -> Option<(K, T)>
pub fn last(&self) -> Option<(K, T)>
Return the last entry in index order, or None if empty.
With reversed=true (default): the entry with the smallest key.
Sourcepub fn put(&self, key: &K, value: &T) -> DbResult<Option<T>>
pub fn put(&self, key: &K, value: &T) -> DbResult<Option<T>>
Insert or update a key-value pair. Returns the old value if the key existed.
Sourcepub fn insert(&self, key: &K, value: &T) -> DbResult<()>
pub fn insert(&self, key: &K, value: &T) -> DbResult<()>
Insert a key-value pair only if the key does not exist.
Returns Err(KeyExists) if the key is already present.
Sourcepub fn delete(&self, key: &K) -> DbResult<Option<T>>
pub fn delete(&self, key: &K) -> DbResult<Option<T>>
Delete a key. Returns the old value if the key existed.
Sourcepub fn cas(&self, key: &K, expected: &T, new_value: &T) -> DbResult<()>
pub fn cas(&self, key: &K, expected: &T, new_value: &T) -> DbResult<()>
Compare-and-swap: if current value == expected, replace with new_value.
Returns Ok(()) on success, Err(CasMismatch) if current != expected,
Err(KeyNotFound) if key doesn’t exist.
Sourcepub fn update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>>
pub fn update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>>
Atomically read-modify-write. Returns Some(T) (the new value)
if key existed, None otherwise.
The closure must not be heavy (shard lock is held).
Sourcepub fn fetch_update(
&self,
key: &K,
f: impl FnOnce(&T) -> T,
) -> DbResult<Option<T>>
pub fn fetch_update( &self, key: &K, f: impl FnOnce(&T) -> T, ) -> DbResult<Option<T>>
Like update(), but returns Some(T) with the old value.
Sourcepub fn atomic<R>(
&self,
shard_key: &K,
f: impl FnOnce(&mut ZeroShard<'_, K, V, D, T>) -> DbResult<R>,
) -> DbResult<R>
pub fn atomic<R>( &self, shard_key: &K, f: impl FnOnce(&mut ZeroShard<'_, K, V, D, T>) -> DbResult<R>, ) -> DbResult<R>
Atomically execute multiple operations on a single shard.
All keys must route to the same shard as shard_key.
The closure must be short — shard lock is held for its duration.
Sourcepub fn prefix_iter(&self, prefix: &[u8]) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
pub fn prefix_iter(&self, prefix: &[u8]) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
Iterate entries whose keys start with prefix.
reversed=true (default): yields matching keys in DESC order.
next() is O(1), next_back() is O(log n).
Sourcepub fn iter(&self) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
pub fn iter(&self) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
Iterate all entries in index order.
reversed=true (default): DESC. reversed=false: ASC.
next() is O(1), next_back() is O(log n).
Sourcepub fn range(&self, start: &K, end: &K) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
pub fn range(&self, start: &K, end: &K) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
Iterate entries in [start, end) — start inclusive, end exclusive.
reversed=true (default): DESC within range. reversed=false: ASC.
next() is O(1), next_back() is O(log n).
Sourcepub fn range_bounds(
&self,
start: Bound<&K>,
end: Bound<&K>,
) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
pub fn range_bounds( &self, start: Bound<&K>, end: Bound<&K>, ) -> ZeroIter<'_, K, V, D::Loc, T> ⓘ
Iterate entries in range defined by start and end bounds.
Unlike range(), allows Included, Excluded, or Unbounded
for each bound independently.
reversed=true (default): DESC within range. reversed=false: ASC.
next() is O(1), next_back() is O(log n).
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool
pub fn shard_for(&self, key: &K) -> usize
Trait Implementations§
Source§impl<T, const V: usize, H> Collection for ZeroTree<T::SelfId, V, Bitcask, T, H>where
T: CollectionMeta + Copy + Send + Sync,
H: TypedWriteHook<T::SelfId, T>,
T::SelfId: Key + Ord,
Available on crate feature armour only.
impl<T, const V: usize, H> Collection for ZeroTree<T::SelfId, V, Bitcask, T, H>where
T: CollectionMeta + Copy + Send + Sync,
H: TypedWriteHook<T::SelfId, T>,
T::SelfId: Key + Ord,
armour only.Source§impl<K: Key, const V: usize, T: Copy + Send + Sync, H: TypedWriteHook<K, T>> CompactionIndex<K> for ZeroTree<K, V, Bitcask, T, H>
impl<K: Key, const V: usize, T: Copy + Send + Sync, H: TypedWriteHook<K, T>> CompactionIndex<K> for ZeroTree<K, V, Bitcask, T, H>
Source§fn update_if_match(&self, key: &K, old_loc: DiskLoc, new_loc: DiskLoc) -> bool
fn update_if_match(&self, key: &K, old_loc: DiskLoc, new_loc: DiskLoc) -> bool
old_loc, it is updated to new_loc and returns true.Source§fn contains_key(&self, key: &K) -> bool
fn contains_key(&self, key: &K) -> bool
Auto Trait Implementations§
impl<K, const V: usize, D = Bitcask, T = [u8; V], H = NoHook> !Freeze for ZeroTree<K, V, D, T, H>
impl<K, const V: usize, D = Bitcask, T = [u8; V], H = NoHook> !RefUnwindSafe for ZeroTree<K, V, D, T, H>
impl<K, const V: usize, D, T, H> Send for ZeroTree<K, V, D, T, H>where
T: Send,
impl<K, const V: usize, D, T, H> Sync for ZeroTree<K, V, D, T, H>where
T: Sync,
impl<K, const V: usize, D, T, H> Unpin for ZeroTree<K, V, D, T, H>
impl<K, const V: usize, D, T, H> UnsafeUnpin for ZeroTree<K, V, D, T, H>where
D: UnsafeUnpin,
H: UnsafeUnpin,
impl<K, const V: usize, D = Bitcask, T = [u8; V], H = NoHook> !UnwindSafe for ZeroTree<K, V, D, T, H>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more