use std::ops::RangeBounds;
use crate::hash::MerkleHash;
use super::{
store::BlockStore,
types::{
BlockData, BlockLen, BlockList, BlockOffset, BlockSignature, FileId, FileOffset, HoleLen,
},
};
pub type MerkleNodeIndex = usize;
pub type MerkleNodeLevel = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MerkleNodePosition {
pub file: FileId,
pub level: MerkleNodeLevel,
pub index: MerkleNodeIndex,
}
#[derive(Debug, Clone)]
pub struct MerkleNode {
pub pos: MerkleNodePosition,
pub hash: MerkleHash,
pub dirty: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DirtyFilter {
Dirty,
#[allow(dead_code)]
NotDirty,
All,
}
pub trait MerkleNodeStore {
fn get_root(&mut self, file: FileId) -> crate::Result<Option<MerkleNode>>;
fn node_indices(
&mut self,
file: FileId,
level: u32,
filter: DirtyFilter,
) -> crate::Result<Vec<usize>>;
fn list_nodes<R>(
&mut self,
file: FileId,
level: u32,
indices: R,
) -> crate::Result<Vec<MerkleNode>>
where
R: RangeBounds<usize>;
fn put_node(&mut self, pos: MerkleNodePosition, hash: MerkleHash) -> crate::Result<()>;
fn delete_node(&mut self, file: FileId, index: usize) -> crate::Result<()>;
fn mark_dirty<R>(&mut self, file: FileId, indices: R) -> crate::Result<()>
where
R: RangeBounds<usize>;
}
pub type LogicalBlockIndex = usize;
#[derive(Debug)]
pub struct LogicalBlockData(BlockData);
impl LogicalBlockData {
pub fn new(data: BlockData) -> Self {
Self(data)
}
}
impl AsRef<BlockData> for LogicalBlockData {
fn as_ref(&self) -> &BlockData {
&self.0
}
}
impl From<LogicalBlockData> for BlockData {
fn from(value: LogicalBlockData) -> Self {
value.0
}
}
impl From<BlockData> for LogicalBlockData {
fn from(value: BlockData) -> Self {
Self(value)
}
}
pub trait LogicalBlockStore {
fn get_block_at_index(
&mut self,
index: LogicalBlockIndex,
block_list: &BlockList,
) -> crate::Result<Option<LogicalBlockData>>;
}
#[derive(Debug)]
pub struct LogicalBlockStoreAdapter<Store> {
store: Store,
logical_block_size: BlockLen,
}
impl<Store> LogicalBlockStoreAdapter<Store> {
pub fn new(store: Store, logical_block_size: BlockLen) -> Self {
Self {
store,
logical_block_size,
}
}
}
impl<Store> LogicalBlockStore for LogicalBlockStoreAdapter<Store>
where
Store: BlockStore,
{
fn get_block_at_index(
&mut self,
index: LogicalBlockIndex,
block_list: &BlockList,
) -> crate::Result<Option<LogicalBlockData>> {
let logical_start = (index * self.logical_block_size) as FileOffset;
let logical_end = logical_start + self.logical_block_size as FileOffset;
let mut current_offset: FileOffset = 0;
let mut found_any = false;
let mut contains_data = false;
let mut result = Vec::with_capacity(self.logical_block_size);
let mut block_buf = Vec::with_capacity(self.logical_block_size);
for block in block_list.iter() {
let physical_start = current_offset;
let physical_end = current_offset + block.len() as FileOffset;
if physical_end <= logical_start {
current_offset = physical_end;
continue;
}
if physical_start >= logical_end {
break;
}
found_any = true;
let overlap_start = physical_start.max(logical_start);
let overlap_end = physical_end.min(logical_end);
let offset_in_block = (overlap_start - physical_start) as BlockOffset;
let overlap_len = (overlap_end - overlap_start) as BlockLen;
match &block.signature {
BlockSignature::Data { .. } => {
contains_data = true;
block_buf.clear();
self.store.read_block(block.id, &mut block_buf).map(drop)?;
let slice = block_buf
.get(offset_in_block..offset_in_block + overlap_len)
.expect("Block slice out of bounds.");
result.extend_from_slice(slice);
}
BlockSignature::Hole { .. } => {
result.resize(result.len() + overlap_len, 0);
}
}
current_offset = physical_end;
}
if !found_any {
Ok(None)
} else if !contains_data {
Ok(Some(LogicalBlockData::new(BlockData::Hole {
len: self.logical_block_size as HoleLen,
})))
} else {
if result.len() < self.logical_block_size {
result.resize(self.logical_block_size, 0);
}
Ok(Some(LogicalBlockData::new(BlockData::Data {
bytes: result,
})))
}
}
}
impl<Store> MerkleNodeStore for LogicalBlockStoreAdapter<Store>
where
Store: MerkleNodeStore,
{
fn get_root(&mut self, file: FileId) -> crate::Result<Option<MerkleNode>> {
self.store.get_root(file)
}
fn node_indices(
&mut self,
file: FileId,
level: u32,
filter: DirtyFilter,
) -> crate::Result<Vec<usize>> {
self.store.node_indices(file, level, filter)
}
fn list_nodes<R>(
&mut self,
file: FileId,
level: u32,
indices: R,
) -> crate::Result<Vec<MerkleNode>>
where
R: RangeBounds<usize>,
{
self.store.list_nodes(file, level, indices)
}
fn put_node(&mut self, pos: MerkleNodePosition, hash: MerkleHash) -> crate::Result<()> {
self.store.put_node(pos, hash)
}
fn delete_node(&mut self, file: FileId, index: usize) -> crate::Result<()> {
self.store.delete_node(file, index)
}
fn mark_dirty<R>(&mut self, file: FileId, indices: R) -> crate::Result<()>
where
R: RangeBounds<usize>,
{
self.store.mark_dirty(file, indices)
}
}