use crate::Result;
use crate::tree_store::TransactionalMemory;
use alloc::sync::Arc;
use alloc::vec::Vec;
pub struct BlobReader {
mem: Arc<TransactionalMemory>,
file_offset: u64,
blob_length: u64,
position: u64,
}
impl BlobReader {
pub(crate) fn new(mem: Arc<TransactionalMemory>, file_offset: u64, blob_length: u64) -> Self {
Self {
mem,
file_offset,
blob_length,
position: 0,
}
}
pub fn len(&self) -> u64 {
self.blob_length
}
pub fn is_empty(&self) -> bool {
self.blob_length == 0
}
pub fn position(&self) -> u64 {
self.position
}
pub fn remaining(&self) -> u64 {
self.blob_length.saturating_sub(self.position)
}
pub fn read_range(&mut self, offset: u64, length: usize) -> Result<Vec<u8>> {
if length == 0 {
return Ok(Vec::new());
}
let end =
offset
.checked_add(length as u64)
.ok_or(crate::StorageError::BlobRangeOutOfBounds {
blob_length: self.blob_length,
requested_offset: offset,
requested_length: length as u64,
})?;
if end > self.blob_length {
return Err(crate::StorageError::BlobRangeOutOfBounds {
blob_length: self.blob_length,
requested_offset: offset,
requested_length: length as u64,
});
}
let data = self.mem.blob_read(self.file_offset + offset, length)?;
self.position = end;
Ok(data)
}
}
#[cfg(feature = "std")]
impl std::io::Read for BlobReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.position >= self.blob_length {
return Ok(0);
}
#[allow(clippy::cast_possible_truncation)]
let remaining = (self.blob_length - self.position) as usize;
let to_read = buf.len().min(remaining);
if to_read == 0 {
return Ok(0);
}
let data = self
.mem
.blob_read(self.file_offset + self.position, to_read)
.map_err(|e| std::io::Error::other(e.to_string()))?;
buf[..to_read].copy_from_slice(&data);
self.position += to_read as u64;
Ok(to_read)
}
}
#[cfg(feature = "std")]
impl std::io::Seek for BlobReader {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let new_pos = match pos {
std::io::SeekFrom::Start(offset) => i64::try_from(offset).ok(),
std::io::SeekFrom::End(offset) => i64::try_from(self.blob_length)
.ok()
.and_then(|len| len.checked_add(offset)),
std::io::SeekFrom::Current(offset) => i64::try_from(self.position)
.ok()
.and_then(|pos| pos.checked_add(offset)),
};
match new_pos {
Some(pos) if pos >= 0 => {
#[allow(clippy::cast_sign_loss)]
let unsigned = pos as u64;
self.position = unsigned;
Ok(self.position)
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"invalid seek to a negative or overflowing position",
)),
}
}
}
#[allow(clippy::missing_fields_in_debug)]
impl core::fmt::Debug for BlobReader {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BlobReader")
.field("blob_length", &self.blob_length)
.field("position", &self.position)
.finish()
}
}