use std::cmp;
use crate::block::{BlockData, BlockId};
#[derive(Debug, Default)]
struct HoleCache {
null_bytes: Vec<u8>,
}
impl HoleCache {
pub fn new() -> Self {
Self {
null_bytes: Vec::new(),
}
}
pub fn fill(&mut self, buf: &mut [u8]) {
if self.null_bytes.len() < buf.len() {
self.null_bytes.resize(buf.len(), 0);
}
buf.copy_from_slice(match &self.null_bytes.get(..buf.len()) {
Some(slice) => slice,
None => unreachable!("By this point, the null bytes cache should have been resized to accommodate the requested buffer."),
});
}
}
#[derive(Debug)]
pub struct CachedBlock {
id: BlockId,
data: BlockData,
hole_cache: HoleCache,
}
impl CachedBlock {
pub fn new(id: BlockId, data: BlockData) -> Self {
Self {
id,
data,
hole_cache: HoleCache::new(),
}
}
pub fn id(&self) -> BlockId {
self.id
}
pub fn replace<F, R>(&mut self, id: BlockId, f: F) -> R
where
F: FnOnce(&mut BlockData) -> R,
{
self.id = id;
f(&mut self.data)
}
pub fn fill(&mut self, buf: &mut [u8], off: usize) -> usize {
let block_size = match &self.data {
BlockData::Data { bytes } => bytes.len(),
BlockData::Hole { len } => *len as usize,
};
let bytes_read = cmp::min(buf.len(), block_size - off);
let buf_slice_to_fill = match buf.get_mut(..bytes_read) {
Some(slice) => slice,
None => unreachable!(
"We should have already constrained the number of bytes we're trying to copy to the size of the buffer."
),
};
match &self.data {
BlockData::Data { bytes } => {
buf_slice_to_fill.copy_from_slice(match bytes.get(off..off + bytes_read) {
Some(slice) => slice,
None => panic!(
"Attempted to read at an offset which is beyond the end of the data block."
),
})
}
BlockData::Hole { .. } => self.hole_cache.fill(buf_slice_to_fill),
}
bytes_read
}
}