1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Slab source trait — abstraction over how slab bytes are accessed.
//!
//! Today only [`MmapSlabSource`] is wired (sync, mmap-backed). The
//! trait exists so a future Linux `IoUringSlabSource` (batched
//! submission queues) can slot in behind the same interface without
//! touching callers.
//!
//! The trait is intentionally NOT `async` to keep the dependency
//! graph clean (no `tokio` / `async-trait`). When the io_uring impl
//! lands it can present a sync surface backed by `io_uring_submit`
//! + `io_uring_wait` — that's still synchronous from the caller's
//! view, just batched internally.
//!
//! See `TODO.impl/03-core-reader/03-async-slab-source.md`.
use crate::error::CoreError;
use crate::slab_store::SlabStore;
/// Behaviour every slab source implements.
///
/// `Send + Sync` so the source can be shared across rayon workers
/// (parallel extract, parallel `cat-multi`).
pub trait SlabSource: Send + Sync {
/// Fetch the plaintext of `drop_id` into an owned `Vec<u8>`.
///
/// Returns:
/// - `None` if no slab contains this drop.
/// - `Some(Err(..))` if the slab is corrupt or the codec is unsupported.
/// - `Some(Ok(bytes))` on success.
#[must_use]
fn plaintext_for(&self, drop_id: &[u8; 32]) -> Option<Result<Vec<u8>, CoreError>>;
/// Number of slabs in the source.
#[must_use]
fn slab_count(&self) -> usize;
/// Number of unique drops indexed across all slabs.
#[must_use]
fn drop_count(&self) -> usize;
}
/// Synchronous mmap-backed slab source. Wraps a [`SlabStore`] and
/// delegates every method. Today this is the only production impl;
/// future Linux builds can add `IoUringSlabSource` behind a feature
/// flag and callers don't change.
pub struct MmapSlabSource {
inner: SlabStore,
}
impl MmapSlabSource {
/// Wrap an existing SlabStore. The store typically comes from
/// `SlabStore::load_mmap(manifest_path, slab_index)`.
#[must_use]
pub fn new(inner: SlabStore) -> Self {
Self { inner }
}
/// Borrow the underlying SlabStore for callers that need its
/// richer API (e.g. `set_dictionaries`).
#[must_use]
pub fn inner(&self) -> &SlabStore {
&self.inner
}
/// Mutably borrow the underlying SlabStore (e.g. to call
/// `set_dictionaries`).
pub fn inner_mut(&mut self) -> &mut SlabStore {
&mut self.inner
}
}
impl SlabSource for MmapSlabSource {
fn plaintext_for(&self, drop_id: &[u8; 32]) -> Option<Result<Vec<u8>, CoreError>> {
self.inner.plaintext_for(drop_id)
}
fn slab_count(&self) -> usize {
self.inner.slab_count()
}
fn drop_count(&self) -> usize {
self.inner.drop_count()
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Smoke test: MmapSlabSource delegates correctly to a fresh
/// SlabStore. The full SlabStore round-trip is exercised in
/// slab_store.rs and slab_cache.rs.
#[test]
fn mmap_source_delegates_to_inner() {
let store = SlabStore::default();
let source = MmapSlabSource::new(store);
assert_eq!(source.slab_count(), 0);
assert_eq!(source.drop_count(), 0);
// No slabs → no drops resolvable.
let drop_id = [0u8; 32];
assert!(source.plaintext_for(&drop_id).is_none());
}
}