use crate::{Error, ObjectDigest, ObjectFormat, ObjectId, ObjectInfo, Repository, Result};
pub const DEFAULT_READ_BUFFER_SIZE: usize = 1024;
pub struct ObjectReader<'repo, R: Repository + ?Sized> {
repository: &'repo R,
index: usize,
info: ObjectInfo,
position: u64,
}
impl<'repo, R: Repository + ?Sized> ObjectReader<'repo, R> {
pub fn new(repository: &'repo R, index: usize) -> Result<Self> {
Ok(Self {
repository,
index,
info: repository.object_info(index)?,
position: 0,
})
}
pub fn info(&self) -> ObjectInfo {
self.info
}
pub fn object_format(&self) -> ObjectFormat {
self.repository.object_format()
}
pub fn position(&self) -> u64 {
self.position
}
pub fn remaining(&self) -> u64 {
self.info.size - self.position
}
pub fn seek(&mut self, position: u64) -> Result<()> {
if position > self.info.size {
return Err(Error::InvalidObject);
}
self.position = position;
Ok(())
}
pub fn read(&mut self, storage: &mut [u8]) -> Result<usize> {
if self.remaining() == 0 {
return Ok(0);
}
if storage.is_empty() {
return Err(Error::BufferTooSmall);
}
let capacity = self.remaining().min(storage.len() as u64) as usize;
let length =
self.repository
.read_object(self.index, self.position, &mut storage[..capacity])?;
if length == 0 || length > capacity {
return Err(Error::InvalidObject);
}
self.position += length as u64;
Ok(length)
}
}
pub struct ObjectHasher<'repo, R: Repository + ?Sized> {
reader: ObjectReader<'repo, R>,
hash: ObjectDigest,
}
impl<'repo, R: Repository + ?Sized> ObjectHasher<'repo, R> {
pub fn new(repository: &'repo R, index: usize) -> Result<Self> {
let reader = ObjectReader::new(repository, index)?;
let hash = ObjectDigest::new(reader.info(), repository.object_format());
Ok(Self { reader, hash })
}
pub fn position(&self) -> u64 {
self.reader.position()
}
pub fn step(&mut self, scratch: &mut [u8]) -> Result<Option<ObjectId>> {
if self.reader.remaining() != 0 {
let length = self.reader.read(scratch)?;
self.hash.update(&scratch[..length])?;
}
if self.reader.remaining() == 0 {
Ok(Some(self.hash.finish()?))
} else {
Ok(None)
}
}
pub fn finish(mut self, scratch: &mut [u8]) -> Result<ObjectId> {
loop {
if let Some(id) = self.step(scratch)? {
return Ok(id);
}
}
}
}