qualia_core_db/bundle/mod.rs
1//! `.hmc` — the **hyper-media-container**: a **transparent container-of-files**
2//! bundle for shipping a set of sealed Qualia assets (`.emf` spectral media,
3//! `.10d` meshes, `.q42` graph volumes, `.p64` weights, …)
4//! as one attestable unit (a release artefact, a downloadable pack, a signed
5//! asset set).
6//!
7//! # Why a bundle, and why *transparent*
8//! The Qualia container family (`.10d` / `.q42` / `.p64`) are all internally
9//! navigable — you can pull a *segment* (a mesh section, a subgraph, a weight
10//! shard) without reading the whole file, ideally `mmap` zero-copy. A bundle
11//! must not destroy that. So a `.hmc` bundle only **concatenates intact files,
12//! page-aligned, with an index of absolute offsets** — it never compresses,
13//! re-chunks, or reframes an entry. Consequences:
14//!
15//! - `reader.get(key)` returns a zero-copy slice that **is** a byte-identical
16//! standalone file — feed it straight to the existing `.q42`/`.10d`/`.p64`
17//! reader; its interior offsets resolve unchanged.
18//! - `reader.segment(key, off, len)` reaches an interior segment of an entry
19//! directly — the bundle does not interfere with segment-level access.
20//! - HTTP range-fetching one interior segment works: `entry.offset + seg.off`.
21//!
22//! It lives in `qualia-core-db` so it is **one reader for both channels** —
23//! native (`BundleMmap`, zero-copy) and WASM (parse fetched/ranged bytes).
24//!
25//! See [`format`] for the on-disk layout.
26
27mod format;
28mod reader;
29mod writer;
30
31pub use format::{
32 BundleEntry, BundleError, BUNDLE_ENTRY_ALIGN, BUNDLE_HEADER_SIZE, BUNDLE_MAGIC, BUNDLE_VERSION,
33 SHA256_LEN,
34};
35pub use reader::BundleReader;
36pub use writer::BundleWriter;
37
38#[cfg(not(target_arch = "wasm32"))]
39pub use reader::BundleMmap;
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn heterogeneous_pack_of_three_kinds_roundtrips() {
47 // A bundle can carry different sealed formats side by side.
48 let ten_d: Vec<u8> = (0..300u32).map(|i| (i * 7) as u8).collect();
49 let q42 = b"q42 graph volume bytes".to_vec();
50 let p64: Vec<u8> = vec![0x40; 129];
51
52 let mut w = BundleWriter::new();
53 w.add_file("body.10d", "10d", ten_d.clone(), None).unwrap();
54 w.add_file("wordnet.q42", "q42", q42.clone(), None).unwrap();
55 w.add_file("model.p64", "p64", p64.clone(), None).unwrap();
56 let bytes = w.build().unwrap();
57
58 let r = BundleReader::parse(&bytes).unwrap();
59 assert_eq!(r.entries().len(), 3);
60 assert_eq!(r.get("body.10d").unwrap(), ten_d.as_slice());
61 assert_eq!(r.get("wordnet.q42").unwrap(), q42.as_slice());
62 assert_eq!(r.get("model.p64").unwrap(), p64.as_slice());
63 assert_eq!(r.entry("model.p64").unwrap().kind, "p64");
64 // Every entry is page-aligned so its interior alignment is preserved.
65 for e in r.entries() {
66 assert_eq!(e.offset % BUNDLE_ENTRY_ALIGN as u64, 0);
67 }
68 // Every entry verifies against its recorded hash.
69 assert!(r.entries().iter().all(|e| r.verify_entry(&e.key)));
70 }
71
72 #[test]
73 fn empty_bundle_is_valid() {
74 let bytes = BundleWriter::new().build().unwrap();
75 let r = BundleReader::parse(&bytes).unwrap();
76 assert!(r.entries().is_empty());
77 }
78}