use crate::err::BlobError;
use crate::ChunkIndex;
use bit_array_rs::BitArray;
#[allow(unused)]
#[derive(Debug)]
pub struct BlobStreamIn {
pub(crate) bit_array: BitArray,
pub(crate) fixed_chunk_size: usize,
pub(crate) octet_count: usize,
blob: Vec<u8>,
}
impl BlobStreamIn {
#[allow(unused)]
#[must_use]
pub fn new(octet_count: usize, fixed_chunk_size: usize) -> Self {
assert!(
fixed_chunk_size > 0,
"fixed_chunk_size must be greater than zero"
);
let chunk_count = octet_count.div_ceil(fixed_chunk_size);
Self {
bit_array: BitArray::new(chunk_count),
fixed_chunk_size,
octet_count,
blob: vec![0u8; octet_count],
}
}
#[must_use]
pub const fn chunk_count(&self) -> usize {
self.bit_array.bit_count()
}
#[must_use]
pub const fn is_complete(&self) -> bool {
self.bit_array.all_set()
}
#[must_use]
pub fn blob(&self) -> Option<&[u8]> {
self.is_complete().then(|| &self.blob[..])
}
pub fn set_chunk(&mut self, chunk_index: ChunkIndex, payload: &[u8]) -> Result<(), BlobError> {
let chunk_count = self.bit_array.bit_count();
if chunk_index >= chunk_count {
return Err(BlobError::InvalidChunkIndex(chunk_index, chunk_count));
}
let expected_size = if chunk_index == chunk_count - 1 {
if self.octet_count % self.fixed_chunk_size == 0 {
self.fixed_chunk_size
} else {
self.octet_count % self.fixed_chunk_size
}
} else {
self.fixed_chunk_size
};
if payload.len() != expected_size {
return Err(BlobError::UnexpectedChunkSize(
expected_size,
payload.len(),
chunk_index,
));
}
let octet_offset = chunk_index * self.fixed_chunk_size;
if octet_offset + expected_size > self.blob.len() {
return Err(BlobError::OutOfBounds);
}
if self.bit_array.get(chunk_index) {
let is_same_contents =
&self.blob[octet_offset..octet_offset + expected_size] == payload;
let err = if is_same_contents {
return Ok(());
} else {
BlobError::RedundantContentDiffers(chunk_index)
};
return Err(err);
}
self.blob[octet_offset..octet_offset + expected_size].copy_from_slice(payload);
self.bit_array.set(chunk_index);
Ok(())
}
}