use nom::{multi::count, number::complete::*, IResult}; use thiserror::Error; use crate::checksum; use crate::io_engine::BLOCK_SIZE; use crate::pdata::btree; use crate::pdata::unpack::Unpack; //------------------------------------------ const ARRAY_BLOCK_HEADER_SIZE: u32 = 24; pub struct ArrayBlockHeader { pub csum: u32, pub max_entries: u32, pub nr_entries: u32, pub value_size: u32, pub blocknr: u64, } impl Unpack for ArrayBlockHeader { fn disk_size() -> u32 { ARRAY_BLOCK_HEADER_SIZE } fn unpack(data: &[u8]) -> IResult<&[u8], ArrayBlockHeader> { let (i, csum) = le_u32(data)?; let (i, max_entries) = le_u32(i)?; let (i, nr_entries) = le_u32(i)?; let (i, value_size) = le_u32(i)?; let (i, blocknr) = le_u64(i)?; Ok(( i, ArrayBlockHeader { csum, max_entries, nr_entries, value_size, blocknr, }, )) } } pub struct ArrayBlock { pub header: ArrayBlockHeader, pub values: Vec, } //------------------------------------------ #[derive(Error, Clone, Debug)] pub enum ArrayError { #[error("io_error")] IoError, #[error("block error: {0}")] BlockError(String), #[error("value error: {0}")] ValueError(String), #[error("aggregate: {0:?}")] Aggregate(Vec), #[error("{0:?}, {1}")] Path(Vec, Box), #[error(transparent)] BTreeError(#[from] btree::BTreeError), } pub fn array_block_err(path: &[u64], msg: &str) -> ArrayError { ArrayError::Path( path.to_vec(), Box::new(ArrayError::BlockError(msg.to_string())), ) } pub fn value_err(msg: String) -> ArrayError { ArrayError::ValueError(msg) } pub fn aggregate_error(errs: Vec) -> ArrayError { ArrayError::Aggregate(errs) } pub type Result = std::result::Result; //------------------------------------------ fn convert_result<'a, V>(path: &[u64], r: IResult<&'a [u8], V>) -> Result<(&'a [u8], V)> { r.map_err(|_| array_block_err(path, "parse error")) } pub fn unpack_array_block(path: &[u64], data: &[u8]) -> Result> { let bt = checksum::metadata_block_type(data); if bt != checksum::BT::ARRAY { return Err(array_block_err( path, &format!("checksum failed for array block {}, {:?}", path.last().unwrap(), bt) )); } let (i, header) = ArrayBlockHeader::unpack(data).map_err(|_| array_block_err( path, "Couldn't parse array block header" ))?; // check value_size if header.value_size != V::disk_size() { return Err(array_block_err( path, &format!( "value_size mismatch: expected {}, was {}", V::disk_size(), header.value_size ) )); } // check max_entries if header.value_size * header.max_entries + ARRAY_BLOCK_HEADER_SIZE > BLOCK_SIZE as u32 { return Err(array_block_err( path, &format!("max_entries is too large ({})", header.max_entries) )); } // TODO: check nr_entries < max_entries // TODO: check block_nr let (_i, values) = convert_result(path, count(V::unpack, header.nr_entries as usize)(i))?; Ok(ArrayBlock { header, values }) } //------------------------------------------