urna_format/layout/section_entry.rs
1//! section table entry (32 bytes per entry, fixed layout).
2//!
3//! `offset` is 64-byte aligned (see `SECTION_ALIGNMENT`). `size` is the
4//! length of the actual payload, NOT including the trailing padding.
5//! `encoding` declares the on-disk payload format; v1 supports raw,
6//! zstd, float16 and int8.
7
8use crate::error::UrnaError;
9use sha2::{Digest, Sha256};
10
11use super::SECTION_ENCODING_RAW;
12
13// `Pod` + `Zeroable` are derived, not asserted: the derive fails to
14// compile if the struct ever gains padding or a field that is not valid
15// for every bit pattern, which is exactly the invariant the on-disk
16// byte view below relies on. No `unsafe` in this file.
17#[repr(C)]
18#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
19pub struct SectionEntry {
20 pub section_id: u32,
21 pub encoding: u32,
22 pub offset: u64,
23 pub size: u64,
24 pub checksum: [u8; 8],
25}
26
27impl SectionEntry {
28 pub fn new(section_id: u32, offset: u64, size: u64) -> Self {
29 Self {
30 section_id,
31 encoding: SECTION_ENCODING_RAW,
32 offset,
33 size,
34 checksum: [0; 8],
35 }
36 }
37
38 /// The exact on-disk bytes of this record (little-endian host only,
39 /// which is every supported target; see `layout::tests`).
40 pub fn as_bytes(&self) -> &[u8] {
41 bytemuck::bytes_of(self)
42 }
43
44 /// Mutable view over the on-disk bytes; `from_bytes`-style readers copy
45 /// a slice in here. Sound for any content because every field accepts
46 /// every bit pattern (`Pod`).
47 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
48 bytemuck::bytes_of_mut(self)
49 }
50
51 pub fn compute_checksum(&mut self, data: &[u8]) {
52 let hash = Sha256::digest(data);
53 self.checksum.copy_from_slice(&hash[..8]);
54 }
55
56 pub fn validate_checksum(&self, data: &[u8]) -> crate::Result<()> {
57 let hash = Sha256::digest(data);
58 if hash[..8] != self.checksum[..] {
59 return Err(UrnaError::SectionChecksumMismatch(self.section_id));
60 }
61 Ok(())
62 }
63}