Skip to main content

plugmem_arena/
slot.rs

1//! The [`Slot`] trait: type erasure of fixed-size records to raw bytes.
2
3/// A fixed-size record that can be stored in an [`Arena`](crate::Arena).
4///
5/// The container itself only ever sees bytes — this trait is the *entire*
6/// typed surface. Type erasure is deliberate: the generic container code is
7/// instantiated once per slot **size**, not once per rich type, which keeps
8/// monomorphization (and therefore wasm binary size) small, and lets binary
9/// search and shifting operate on raw byte ranges.
10///
11/// # Layout contract
12///
13/// A slot is `SIZE` bytes: the first `KEY_LEN` bytes are the **key prefix**,
14/// the rest is payload. Rules:
15///
16/// - `1 <= KEY_LEN <= SIZE <= PAGE_BYTES` (4096) — checked at
17///   [`Arena::new`](crate::Arena::new), violations return
18///   [`Error::BadSlot`](crate::Error::BadSlot).
19/// - The key prefix must be **order-preserving**: byte-wise comparison of
20///   two encoded keys must equal the intended ordering of the records.
21///   Encode integers big-endian — use the [`key`](crate::key) helpers.
22/// - `write` must fill all `SIZE` bytes (the container never zeroes slots
23///   for you — see the crate-level notes on the uninitialized-page
24///   optimization); `read` must be its inverse: `read(write(x)) == x`.
25///
26/// # Example
27///
28/// ```
29/// use plugmem_arena::{key, Slot};
30///
31/// /// An edge record: `[src | dst]` key, 4-byte payload.
32/// struct Edge { src: u32, dst: u32, weight: u32 }
33///
34/// impl Slot for Edge {
35///     const SIZE: usize = 12;
36///     const KEY_LEN: usize = 8;
37///     fn write(&self, out: &mut [u8]) {
38///         key::write_u32(out, self.src);
39///         key::write_u32(&mut out[4..], self.dst);
40///         key::write_u32(&mut out[8..], self.weight);
41///     }
42///     fn read(bytes: &[u8]) -> Self {
43///         Edge {
44///             src: key::read_u32(bytes),
45///             dst: key::read_u32(&bytes[4..]),
46///             weight: key::read_u32(&bytes[8..]),
47///         }
48///     }
49/// }
50/// ```
51pub trait Slot {
52    /// Total size of the record in bytes.
53    const SIZE: usize;
54
55    /// Length of the key prefix; sorting and lookups compare only these
56    /// leading bytes. Must not exceed [`SIZE`](Self::SIZE).
57    const KEY_LEN: usize;
58
59    /// Serializes the record into `out` (`out.len() == SIZE`), filling every
60    /// byte.
61    fn write(&self, out: &mut [u8]);
62
63    /// Reconstructs the record from `bytes` (`bytes.len() == SIZE`).
64    fn read(bytes: &[u8]) -> Self;
65}
66
67/// Byte arrays are slots whose key is the whole value (set semantics) —
68/// handy for id/hash sets, mirroring the first test version of this
69/// structure which stored 20/32-byte identifiers.
70impl<const N: usize> Slot for [u8; N] {
71    const SIZE: usize = N;
72    const KEY_LEN: usize = N;
73
74    #[inline]
75    fn write(&self, out: &mut [u8]) {
76        out.copy_from_slice(self);
77    }
78
79    #[inline]
80    fn read(bytes: &[u8]) -> Self {
81        let mut arr = [0u8; N];
82        arr.copy_from_slice(bytes);
83        arr
84    }
85}