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}