1use crate::{Error, ObjectDigest, ObjectFormat, ObjectId, ObjectInfo, Repository, Result};
4
5pub const DEFAULT_READ_BUFFER_SIZE: usize = 1024;
8
9pub struct ObjectReader<'repo, R: Repository + ?Sized> {
13 repository: &'repo R,
14 index: usize,
15 info: ObjectInfo,
16 position: u64,
17}
18
19impl<'repo, R: Repository + ?Sized> ObjectReader<'repo, R> {
20 pub fn new(repository: &'repo R, index: usize) -> Result<Self> {
21 Ok(Self {
22 repository,
23 index,
24 info: repository.object_info(index)?,
25 position: 0,
26 })
27 }
28
29 pub fn info(&self) -> ObjectInfo {
30 self.info
31 }
32
33 pub fn object_format(&self) -> ObjectFormat {
34 self.repository.object_format()
35 }
36
37 pub fn position(&self) -> u64 {
38 self.position
39 }
40
41 pub fn remaining(&self) -> u64 {
42 self.info.size - self.position
43 }
44
45 pub fn seek(&mut self, position: u64) -> Result<()> {
47 if position > self.info.size {
48 return Err(Error::InvalidObject);
49 }
50 self.position = position;
51 Ok(())
52 }
53
54 pub fn read(&mut self, storage: &mut [u8]) -> Result<usize> {
58 if self.remaining() == 0 {
59 return Ok(0);
60 }
61 if storage.is_empty() {
62 return Err(Error::BufferTooSmall);
63 }
64 let capacity = self.remaining().min(storage.len() as u64) as usize;
65 let length =
66 self.repository
67 .read_object(self.index, self.position, &mut storage[..capacity])?;
68 if length == 0 || length > capacity {
69 return Err(Error::InvalidObject);
70 }
71 self.position += length as u64;
72 Ok(length)
73 }
74}
75
76pub struct ObjectHasher<'repo, R: Repository + ?Sized> {
80 reader: ObjectReader<'repo, R>,
81 hash: ObjectDigest,
82}
83
84impl<'repo, R: Repository + ?Sized> ObjectHasher<'repo, R> {
85 pub fn new(repository: &'repo R, index: usize) -> Result<Self> {
86 let reader = ObjectReader::new(repository, index)?;
87 let hash = ObjectDigest::new(reader.info(), repository.object_format());
88 Ok(Self { reader, hash })
89 }
90
91 pub fn position(&self) -> u64 {
92 self.reader.position()
93 }
94
95 pub fn step(&mut self, scratch: &mut [u8]) -> Result<Option<ObjectId>> {
99 if self.reader.remaining() != 0 {
100 let length = self.reader.read(scratch)?;
101 self.hash.update(&scratch[..length])?;
102 }
103 if self.reader.remaining() == 0 {
104 Ok(Some(self.hash.finish()?))
105 } else {
106 Ok(None)
107 }
108 }
109
110 pub fn finish(mut self, scratch: &mut [u8]) -> Result<ObjectId> {
112 loop {
113 if let Some(id) = self.step(scratch)? {
114 return Ok(id);
115 }
116 }
117 }
118}