use crate::foundation::{AlgoError, Result};
use crate::store::schema::StoreSchema;
pub(crate) const MAGIC: [u8; 4] = *b"PTS\x01";
pub(crate) const SEAL: [u8; 4] = *b"PTSZ";
pub(crate) const ALIGN: u64 = 64;
pub(crate) const SEAL_LEN: u64 = 4 + 8 + 8;
fn align_up(x: u64, a: u64) -> u64 {
x.div_ceil(a) * a
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct LaneLayout {
pub offset: u64,
pub byte_len: u64,
}
#[derive(Debug, Clone)]
pub(crate) struct Layout {
pub lanes: Vec<LaneLayout>,
pub data_end: u64,
pub seal_offset: u64,
pub total_len: u64,
}
impl Layout {
pub fn compute(schema: &StoreSchema, header_len: u64) -> Layout {
let data_base = align_up(MAGIC.len() as u64 + 4 + header_len, ALIGN);
let mut off = data_base;
let mut lanes = Vec::with_capacity(schema.lanes.len());
for lane in &schema.lanes {
off = align_up(off, ALIGN);
let byte_len = lane.byte_len(schema.nslabs);
lanes.push(LaneLayout {
offset: off,
byte_len,
});
off += byte_len;
}
let data_end = off;
let seal_offset = align_up(data_end, 8);
Layout {
lanes,
data_end,
seal_offset,
total_len: seal_offset + SEAL_LEN,
}
}
pub fn write_seal(&self, buf: &mut [u8], nslabs: u64) {
let s = self.seal_offset as usize;
buf[s..s + 4].copy_from_slice(&SEAL);
buf[s + 4..s + 12].copy_from_slice(&nslabs.to_le_bytes());
buf[s + 12..s + 20].copy_from_slice(&self.data_end.to_le_bytes());
}
pub fn verify_seal(&self, buf: &[u8], nslabs: u64) -> Result<()> {
if (buf.len() as u64) < self.total_len {
return Err(AlgoError::Parse(
"store not finalized or partially written (file shorter than its schema)".into(),
));
}
let s = self.seal_offset as usize;
if buf[s..s + 4] != SEAL {
return Err(AlgoError::Parse(
"store not finalized (missing end-of-store seal)".into(),
));
}
let seal_nslabs = u64::from_le_bytes(buf[s + 4..s + 12].try_into().unwrap());
let seal_end = u64::from_le_bytes(buf[s + 12..s + 20].try_into().unwrap());
if seal_nslabs != nslabs || seal_end != self.data_end {
return Err(AlgoError::Parse(
"store seal disagrees with its header (corrupt or truncated)".into(),
));
}
Ok(())
}
}