Skip to main content

rvf_types/
manifest.rs

1//! Level 0 root manifest and hotset pointer types.
2//!
3//! The root manifest is always the last 4096 bytes of the most recent
4//! MANIFEST_SEG. Its fixed size enables instant location via `seek(EOF - 4096)`.
5
6use crate::constants::ROOT_MANIFEST_MAGIC;
7
8/// Inline hotset pointer for HNSW entry points.
9///
10/// Offset 0x038 in Level0Root.
11#[derive(Clone, Copy, Debug)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[repr(C)]
14pub struct EntrypointPtr {
15    /// Byte offset to the segment containing HNSW entry points.
16    pub seg_offset: u64,
17    /// Block offset within that segment.
18    pub block_offset: u32,
19    /// Number of entry points.
20    pub count: u32,
21}
22
23/// Inline hotset pointer for top-layer adjacency.
24///
25/// Offset 0x048 in Level0Root.
26#[derive(Clone, Copy, Debug)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[repr(C)]
29pub struct TopLayerPtr {
30    /// Byte offset to the segment with top-layer adjacency.
31    pub seg_offset: u64,
32    /// Block offset within the segment.
33    pub block_offset: u32,
34    /// Number of nodes in the top layer.
35    pub node_count: u32,
36}
37
38/// Inline hotset pointer for cluster centroids / pivots.
39///
40/// Offset 0x058 in Level0Root.
41#[derive(Clone, Copy, Debug)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43#[repr(C)]
44pub struct CentroidPtr {
45    /// Byte offset to the segment with cluster centroids.
46    pub seg_offset: u64,
47    /// Block offset within the segment.
48    pub block_offset: u32,
49    /// Number of centroids.
50    pub count: u32,
51}
52
53/// Inline hotset pointer for quantization dictionary.
54///
55/// Offset 0x068 in Level0Root.
56#[derive(Clone, Copy, Debug)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[repr(C)]
59pub struct QuantDictPtr {
60    /// Byte offset to the quantization dictionary segment.
61    pub seg_offset: u64,
62    /// Block offset within the segment.
63    pub block_offset: u32,
64    /// Dictionary size in bytes.
65    pub size: u32,
66}
67
68/// Inline hotset pointer for the hot vector cache (HOT_SEG).
69///
70/// Offset 0x078 in Level0Root.
71#[derive(Clone, Copy, Debug)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[repr(C)]
74pub struct HotCachePtr {
75    /// Byte offset to the HOT_SEG with interleaved hot vectors.
76    pub seg_offset: u64,
77    /// Block offset within the segment.
78    pub block_offset: u32,
79    /// Number of vectors in the hot cache.
80    pub vector_count: u32,
81}
82
83/// Inline hotset pointer for prefetch hint table.
84///
85/// Offset 0x088 in Level0Root.
86#[derive(Clone, Copy, Debug)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[repr(C)]
89pub struct PrefetchMapPtr {
90    /// Byte offset to the prefetch hint table.
91    pub offset: u64,
92    /// Number of prefetch entries.
93    pub entries: u32,
94    /// Padding to align to 16 bytes (matches other hotset pointers).
95    pub _pad: u32,
96}
97
98/// The Level 0 root manifest (exactly 4096 bytes).
99///
100/// Always located at the last 4096 bytes of the most recent MANIFEST_SEG.
101/// Its fixed size enables instant boot: `seek(EOF - 4096)`.
102///
103/// ## Binary layout
104///
105/// | Offset | Size | Field |
106/// |--------|------|-------|
107/// | 0x000  | 4    | magic (0x52564D30 "RVM0") |
108/// | 0x004  | 2    | version |
109/// | 0x006  | 2    | flags |
110/// | 0x008  | 8    | l1_manifest_offset |
111/// | 0x010  | 8    | l1_manifest_length |
112/// | 0x018  | 8    | total_vector_count |
113/// | 0x020  | 2    | dimension |
114/// | 0x022  | 1    | base_dtype |
115/// | 0x023  | 1    | profile_id |
116/// | 0x024  | 4    | epoch |
117/// | 0x028  | 8    | created_ns |
118/// | 0x030  | 8    | modified_ns |
119/// | 0x038  | 16   | entrypoint_ptr |
120/// | 0x048  | 16   | toplayer_ptr |
121/// | 0x058  | 16   | centroid_ptr |
122/// | 0x068  | 16   | quantdict_ptr |
123/// | 0x078  | 16   | hot_cache_ptr |
124/// | 0x088  | 16   | prefetch_map_ptr (includes 4B padding) |
125/// | 0x098  | 2    | sig_algo |
126/// | 0x09A  | 2    | sig_length |
127/// | 0x09C  | 3684 | signature_buf |
128/// | 0xF00  | 252  | reserved |
129/// | 0xFFC  | 4    | root_checksum |
130#[derive(Clone, Copy, Debug)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132#[repr(C)]
133pub struct Level0Root {
134    // ---- Basic header (0x000 - 0x037) ----
135    /// Magic number: must be `0x52564D30` ("RVM0").
136    pub magic: u32,
137    /// Root manifest version.
138    pub version: u16,
139    /// Root manifest flags.
140    pub flags: u16,
141    /// Byte offset to the Level 1 manifest segment.
142    pub l1_manifest_offset: u64,
143    /// Byte length of the Level 1 manifest segment.
144    pub l1_manifest_length: u64,
145    /// Total vectors across all segments.
146    pub total_vector_count: u64,
147    /// Vector dimensionality.
148    pub dimension: u16,
149    /// Base data type enum (see `DataType`).
150    pub base_dtype: u8,
151    /// Domain profile id.
152    pub profile_id: u8,
153    /// Current overlay epoch number.
154    pub epoch: u32,
155    /// File creation timestamp (nanoseconds).
156    pub created_ns: u64,
157    /// Last modification timestamp (nanoseconds).
158    pub modified_ns: u64,
159
160    // ---- Hotset pointers (0x038 - 0x093) ----
161    /// HNSW entry points.
162    pub entrypoint: EntrypointPtr,
163    /// Top-layer adjacency.
164    pub toplayer: TopLayerPtr,
165    /// Cluster centroids / pivots.
166    pub centroid: CentroidPtr,
167    /// Quantization dictionary.
168    pub quantdict: QuantDictPtr,
169    /// Hot vector cache (HOT_SEG).
170    pub hot_cache: HotCachePtr,
171    /// Prefetch hint table.
172    pub prefetch_map: PrefetchMapPtr,
173
174    // ---- Crypto (0x094 - 0x097 + signature) ----
175    /// Manifest signature algorithm.
176    pub sig_algo: u16,
177    /// Signature byte length.
178    pub sig_length: u16,
179    /// Signature bytes (up to 3688 bytes; only first `sig_length` are meaningful).
180    pub signature_buf: [u8; Self::SIG_BUF_SIZE],
181
182    // ---- Reserved + checksum (0xF00 - 0xFFF) ----
183    /// Reserved / zero-padded area.
184    pub reserved: [u8; 252],
185    /// CRC32C of bytes 0x000 through 0xFFB.
186    pub root_checksum: u32,
187}
188
189// Compile-time assertion: Level0Root must be exactly 4096 bytes.
190const _: () = assert!(core::mem::size_of::<Level0Root>() == 4096);
191
192impl Level0Root {
193    /// Size of the signature buffer within the root manifest.
194    /// From offset 0x09C to 0xEFF inclusive = 3684 bytes.
195    pub const SIG_BUF_SIZE: usize = 3684;
196
197    /// Create a zeroed root manifest with only the magic set.
198    pub const fn zeroed() -> Self {
199        Self {
200            magic: ROOT_MANIFEST_MAGIC,
201            version: 0,
202            flags: 0,
203            l1_manifest_offset: 0,
204            l1_manifest_length: 0,
205            total_vector_count: 0,
206            dimension: 0,
207            base_dtype: 0,
208            profile_id: 0,
209            epoch: 0,
210            created_ns: 0,
211            modified_ns: 0,
212            entrypoint: EntrypointPtr {
213                seg_offset: 0,
214                block_offset: 0,
215                count: 0,
216            },
217            toplayer: TopLayerPtr {
218                seg_offset: 0,
219                block_offset: 0,
220                node_count: 0,
221            },
222            centroid: CentroidPtr {
223                seg_offset: 0,
224                block_offset: 0,
225                count: 0,
226            },
227            quantdict: QuantDictPtr {
228                seg_offset: 0,
229                block_offset: 0,
230                size: 0,
231            },
232            hot_cache: HotCachePtr {
233                seg_offset: 0,
234                block_offset: 0,
235                vector_count: 0,
236            },
237            prefetch_map: PrefetchMapPtr {
238                offset: 0,
239                entries: 0,
240                _pad: 0,
241            },
242            sig_algo: 0,
243            sig_length: 0,
244            signature_buf: [0u8; Self::SIG_BUF_SIZE],
245            reserved: [0u8; 252],
246            root_checksum: 0,
247        }
248    }
249
250    /// Check whether the magic field matches the expected value.
251    #[inline]
252    pub const fn is_valid_magic(&self) -> bool {
253        self.magic == ROOT_MANIFEST_MAGIC
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260
261    #[test]
262    fn level0_root_size_is_4096() {
263        assert_eq!(core::mem::size_of::<Level0Root>(), 4096);
264    }
265
266    #[test]
267    fn zeroed_has_valid_magic() {
268        let root = Level0Root::zeroed();
269        assert!(root.is_valid_magic());
270    }
271
272    #[test]
273    fn field_offsets() {
274        let root = Level0Root::zeroed();
275        let base = core::ptr::addr_of!(root) as usize;
276
277        // Use addr_of! for packed struct fields to avoid UB.
278        let magic_off = core::ptr::addr_of!(root.magic) as usize - base;
279        let version_off = core::ptr::addr_of!(root.version) as usize - base;
280        let flags_off = core::ptr::addr_of!(root.flags) as usize - base;
281        let l1_offset_off = core::ptr::addr_of!(root.l1_manifest_offset) as usize - base;
282        let l1_length_off = core::ptr::addr_of!(root.l1_manifest_length) as usize - base;
283        let total_vec_off = core::ptr::addr_of!(root.total_vector_count) as usize - base;
284        let dim_off = core::ptr::addr_of!(root.dimension) as usize - base;
285        let dtype_off = core::ptr::addr_of!(root.base_dtype) as usize - base;
286        let profile_off = core::ptr::addr_of!(root.profile_id) as usize - base;
287        let epoch_off = core::ptr::addr_of!(root.epoch) as usize - base;
288        let created_off = core::ptr::addr_of!(root.created_ns) as usize - base;
289        let modified_off = core::ptr::addr_of!(root.modified_ns) as usize - base;
290        let entry_off = core::ptr::addr_of!(root.entrypoint) as usize - base;
291        let toplayer_off = core::ptr::addr_of!(root.toplayer) as usize - base;
292        let centroid_off = core::ptr::addr_of!(root.centroid) as usize - base;
293        let quantdict_off = core::ptr::addr_of!(root.quantdict) as usize - base;
294        let hot_cache_off = core::ptr::addr_of!(root.hot_cache) as usize - base;
295        let prefetch_off = core::ptr::addr_of!(root.prefetch_map) as usize - base;
296        let sig_algo_off = core::ptr::addr_of!(root.sig_algo) as usize - base;
297        let sig_len_off = core::ptr::addr_of!(root.sig_length) as usize - base;
298        let sig_buf_off = core::ptr::addr_of!(root.signature_buf) as usize - base;
299        let reserved_off = core::ptr::addr_of!(root.reserved) as usize - base;
300        let checksum_off = core::ptr::addr_of!(root.root_checksum) as usize - base;
301
302        assert_eq!(magic_off, 0x000);
303        assert_eq!(version_off, 0x004);
304        assert_eq!(flags_off, 0x006);
305        assert_eq!(l1_offset_off, 0x008);
306        assert_eq!(l1_length_off, 0x010);
307        assert_eq!(total_vec_off, 0x018);
308        assert_eq!(dim_off, 0x020);
309        assert_eq!(dtype_off, 0x022);
310        assert_eq!(profile_off, 0x023);
311        assert_eq!(epoch_off, 0x024);
312        assert_eq!(created_off, 0x028);
313        assert_eq!(modified_off, 0x030);
314        assert_eq!(entry_off, 0x038);
315        assert_eq!(toplayer_off, 0x048);
316        assert_eq!(centroid_off, 0x058);
317        assert_eq!(quantdict_off, 0x068);
318        assert_eq!(hot_cache_off, 0x078);
319        assert_eq!(prefetch_off, 0x088);
320        assert_eq!(sig_algo_off, 0x098);
321        assert_eq!(sig_len_off, 0x09A);
322        assert_eq!(sig_buf_off, 0x09C);
323        assert_eq!(reserved_off, 0xF00);
324        assert_eq!(checksum_off, 0xFFC);
325    }
326
327    #[test]
328    fn hotset_pointer_sizes() {
329        assert_eq!(core::mem::size_of::<EntrypointPtr>(), 16);
330        assert_eq!(core::mem::size_of::<TopLayerPtr>(), 16);
331        assert_eq!(core::mem::size_of::<CentroidPtr>(), 16);
332        assert_eq!(core::mem::size_of::<QuantDictPtr>(), 16);
333        assert_eq!(core::mem::size_of::<HotCachePtr>(), 16);
334        assert_eq!(core::mem::size_of::<PrefetchMapPtr>(), 16);
335    }
336}