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}