urna_format/reader/mod.rs
1//! Zero-copy view over a `.urna` byte slice.
2//!
3//! The reader does no I/O - callers pass an `&[u8]` (e.g. backed by an
4//! `mmap`). Parsing validates magic, header checksum, file_size, all
5//! section checksums, footer hash, manifest schema, and the presence of
6//! every required section.
7//!
8//! Section payloads come in three encodings (`SECTION_ENCODING_*`):
9//! - `raw`: the section bytes ARE the canonical payload.
10//! - `zstd`: stored compressed; the reader decompresses on demand and
11//! returns an owned `Cow::Owned` buffer.
12//! - `float16` / `int8`: only valid for the embeddings section; the
13//! physical bytes are also the canonical bytes (the runtime
14//! dispatches on `manifest.dtype`).
15//!
16//! Section checksums hash the **physical** bytes as stored.
17//! `content_hash` hashes the **decoded** bytes so a zstd-compressed
18//! corpus and its raw equivalent share the same content_hash (and
19//! therefore the same citation URIs).
20
21mod decode;
22mod parse;
23mod validate;
24pub use validate::validate_slab_values;
25
26use crate::error::UrnaError;
27use crate::layout::{SectionEntry, UrnaFooter, UrnaHeader};
28use crate::manifest::Manifest;
29
30pub struct UrnaView<'a> {
31 pub(super) data: &'a [u8],
32 pub header: UrnaHeader,
33 pub section_table: Vec<SectionEntry>,
34 pub manifest: Manifest,
35 pub footer: UrnaFooter,
36}
37
38impl<'a> UrnaView<'a> {
39 pub fn len(&self) -> usize {
40 self.data.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
44 self.data.is_empty()
45 }
46
47 pub fn raw_bytes(&self) -> &[u8] {
48 self.data
49 }
50
51 /// Look up the section table entry for `section_id`.
52 pub fn entry(&self, section_id: u32) -> crate::Result<&SectionEntry> {
53 self.section_table
54 .iter()
55 .find(|e| e.section_id == section_id)
56 .ok_or(UrnaError::SectionNotFound(section_id))
57 }
58
59 /// Physical (on-disk, mmap-backed) bytes of a section's payload.
60 /// Use `decoded_section` if you want the logical bytes (e.g. zstd
61 /// decompressed) the chunk decoders consume.
62 pub fn get_section_data(&self, section_id: u32) -> crate::Result<&'a [u8]> {
63 let entry = self.entry(section_id)?;
64 let start = entry.offset as usize;
65 let end = start + entry.size as usize;
66 Ok(&self.data[start..end])
67 }
68}