use crate::error::{Error, Result};
pub const PAGE_SIZE: usize = 4096;
const TRAILER_OFF: usize = 0xFF0;
const CRC_OFF: usize = 0xFFC;
#[derive(Debug, Clone, Copy)]
pub struct Page<'a> {
pub(crate) index: u64,
pub(crate) bytes: &'a [u8],
}
impl<'a> Page<'a> {
pub fn from_bytes(index: u64, bytes: &'a [u8]) -> Self {
assert!(
bytes.len() >= PAGE_SIZE,
"Page::from_bytes requires at least {PAGE_SIZE} bytes (got {})",
bytes.len()
);
Page {
index,
bytes: &bytes[..PAGE_SIZE],
}
}
#[inline]
pub fn index(&self) -> u64 {
self.index
}
#[inline]
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
#[inline]
pub fn trailer(&self) -> PageTrailer {
let t = &self.bytes[TRAILER_OFF..CRC_OFF];
PageTrailer {
flag_ff0: t[0],
flag_ff1: t[1],
page_type_raw: t[2],
zero_ff3: t[3],
meta_ff4: t[4],
meta_ff5: t[5],
zero_ff6: [t[6], t[7], t[8], t[9], t[10], t[11]],
}
}
#[inline]
pub fn stored_crc(&self) -> u32 {
u32::from_le_bytes([
self.bytes[CRC_OFF],
self.bytes[CRC_OFF + 1],
self.bytes[CRC_OFF + 2],
self.bytes[CRC_OFF + 3],
])
}
#[inline]
pub fn computed_crc(&self) -> u32 {
crc32fast::hash(&self.bytes[..CRC_OFF])
}
pub fn verify_crc(&self) -> Result<()> {
let stored = self.stored_crc();
let computed = self.computed_crc();
if stored == computed {
Ok(())
} else {
Err(Error::BadCrc {
page: self.index,
stored,
computed,
})
}
}
pub fn verify_trailer(&self) -> Result<()> {
let t = self.trailer();
if t.zero_ff3 == 0 && t.zero_ff6 == [0; 6] {
Ok(())
} else {
Err(Error::BadTrailer { page: self.index })
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct PageTrailer {
pub flag_ff0: u8,
pub flag_ff1: u8,
pub page_type_raw: u8,
pub zero_ff3: u8,
pub meta_ff4: u8,
pub meta_ff5: u8,
pub zero_ff6: [u8; 6],
}
impl PageTrailer {
#[inline]
pub fn page_type(&self) -> PageType {
PageType::from_byte(self.page_type_raw)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum PageType {
Extent,
Alloc,
Map,
Header,
Catalog,
Bootstrap,
Index,
UnknownG,
Other(u8),
}
impl PageType {
#[inline]
pub fn from_byte(b: u8) -> Self {
match b {
b'E' => PageType::Extent,
b'A' => PageType::Alloc,
b'M' => PageType::Map,
b'H' => PageType::Header,
b'C' => PageType::Catalog,
b'@' => PageType::Bootstrap,
b'I' => PageType::Index,
b'G' => PageType::UnknownG,
other => PageType::Other(other),
}
}
pub fn name(&self) -> &'static str {
match self {
PageType::Extent => "extent",
PageType::Alloc => "alloc",
PageType::Map => "map",
PageType::Header => "header",
PageType::Catalog => "catalog",
PageType::Bootstrap => "bootstrap",
PageType::Index => "index",
PageType::UnknownG => "unknown_G",
PageType::Other(_) => "other",
}
}
pub fn as_byte(&self) -> u8 {
match self {
PageType::Extent => b'E',
PageType::Alloc => b'A',
PageType::Map => b'M',
PageType::Header => b'H',
PageType::Catalog => b'C',
PageType::Bootstrap => b'@',
PageType::Index => b'I',
PageType::UnknownG => b'G',
PageType::Other(b) => *b,
}
}
}