pearl/blob/
header.rs

1use crate::prelude::*;
2use anyhow::{Context, Result};
3use bincode::{deserialize, serialized_size};
4
5use crate::{error::ValidationErrorKind, Error};
6
7pub(crate) const BLOB_VERSION: u32 = 1;
8pub(crate) const BLOB_MAGIC_BYTE: u64 = 0xdeaf_abcd;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct Header {
12    pub(crate) magic_byte: u64,
13    pub(crate) version: u32,
14    pub(crate) flags: u64,
15}
16
17impl Header {
18    pub(crate) const fn new() -> Self {
19        Self {
20            magic_byte: BLOB_MAGIC_BYTE,
21            version: BLOB_VERSION,
22            flags: 0,
23        }
24    }
25
26    pub(crate) async fn from_file(file: &File, file_name: &Path) -> Result<Self> {
27        let size = serialized_size(&Header::new()).expect("failed to serialize default header");
28        let buf = file
29            .read_exact_at_allocate(size as usize, 0)
30            .await
31            .map_err(|err| err.into_bincode_if_unexpected_eof())
32            .with_context(|| format!("failed to read from file: {:?}", file_name))?;
33        let header: Self = deserialize(&buf)
34            .map_err(|err| Error::from(err))
35            .with_context(|| format!("failed to deserialize header from file: {:?}", file_name))?;
36        header.validate().context("header validation failed")?;
37        Ok(header)
38    }
39
40    fn validate(&self) -> Result<()> {
41        self.validate_without_version()?;
42        if self.version != BLOB_VERSION {
43            let cause = format!(
44                "old blob version: {}, expected: {}",
45                self.version, BLOB_VERSION
46            );
47            return Err(Error::validation(ValidationErrorKind::BlobVersion, cause).into());
48        }
49
50        Ok(())
51    }
52
53    pub fn validate_without_version(&self) -> Result<()> {
54        if self.magic_byte != BLOB_MAGIC_BYTE {
55            let param = ValidationErrorKind::BlobMagicByte;
56            return Err(Error::validation(param, "blob header magic byte mismatch").into());
57        }
58        Ok(())
59    }
60
61    #[inline]
62    pub(crate) fn serialized_size(&self) -> u64 {
63        serialized_size(&self).expect("blob header size")
64    }
65}