use super::{DecodeStatus, EntryDecoder};
use crate::{
Error, ObjectFormat, Result,
zlib::{InflateLimits, WINDOW_SIZE},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ByteRange {
pub offset: u64,
pub length: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EntryRangeStatus {
Continue,
NeedInput,
NeedOutput,
Finished,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EntryRangeProgress {
pub consumed: usize,
pub written: usize,
pub status: EntryRangeStatus,
}
pub struct EntryRangeDecoder<'window, 'scratch> {
decoder: EntryDecoder<'window>,
scratch: &'scratch mut [u8],
range: ByteRange,
end: u64,
inflated: u64,
emitted: u64,
pending: usize,
pending_end: usize,
header_seen: bool,
finished: bool,
failure: Option<Error>,
}
impl<'window, 'scratch> EntryRangeDecoder<'window, 'scratch> {
pub fn new(
format: ObjectFormat,
entry_offset: u64,
window: &'window mut [u8; WINDOW_SIZE],
max_entry_bytes: u64,
inflate: InflateLimits,
range: ByteRange,
scratch: &'scratch mut [u8],
) -> Result<Self> {
let end = range
.offset
.checked_add(range.length)
.ok_or(Error::InvalidObject)?;
if scratch.is_empty() && range.length != 0 {
return Err(Error::BufferTooSmall);
}
Ok(Self {
decoder: EntryDecoder::new(format, entry_offset, window, max_entry_bytes, inflate)?,
scratch,
range,
end,
inflated: 0,
emitted: 0,
pending: 0,
pending_end: 0,
header_seen: false,
finished: false,
failure: None,
})
}
pub fn wire_position(&self) -> u64 {
self.decoder.position()
}
pub fn inflated_position(&self) -> u64 {
self.inflated
}
pub fn position(&self) -> u64 {
self.emitted
}
pub fn update(
&mut self,
input: &[u8],
output: &mut [u8],
end_of_input: bool,
) -> Result<EntryRangeProgress> {
let result = self.advance(input, output, end_of_input);
if let Err(error) = result {
self.failure = Some(error);
}
result
}
pub fn finish(&self) -> Result<()> {
if let Some(error) = self.failure {
return Err(error);
}
if self.finished {
Ok(())
} else {
Err(Error::CorruptPack)
}
}
fn advance(
&mut self,
input: &[u8],
output: &mut [u8],
eof: bool,
) -> Result<EntryRangeProgress> {
if let Some(error) = self.failure {
return Err(error);
}
if self.finished {
return Ok(EntryRangeProgress {
consumed: 0,
written: 0,
status: EntryRangeStatus::Finished,
});
}
if self.pending < self.pending_end {
return Ok(self.drain(0, output));
}
if output.is_empty() && self.range.length != 0 {
return Ok(EntryRangeProgress {
consumed: 0,
written: 0,
status: EntryRangeStatus::NeedOutput,
});
}
let progress = self.decoder.update(input, self.scratch, eof)?;
if let DecodeStatus::EntryStart(header) = progress.status {
if self.end > header.size {
return Err(Error::InvalidObject);
}
self.header_seen = true;
if self.range.length == 0 {
self.finished = true;
}
}
let before = self.inflated;
self.inflated += progress.written as u64;
let first = before.max(self.range.offset);
let last = self.inflated.min(self.end);
if first < last {
self.pending = (first - before) as usize;
self.pending_end = (last - before) as usize;
return Ok(self.drain(progress.consumed, output));
}
let status = if self.finished {
EntryRangeStatus::Finished
} else {
match progress.status {
DecodeStatus::NeedInput => EntryRangeStatus::NeedInput,
DecodeStatus::EntryEnd { .. } => return Err(Error::CorruptPack),
_ => EntryRangeStatus::Continue,
}
};
Ok(EntryRangeProgress {
consumed: progress.consumed,
written: 0,
status,
})
}
fn drain(&mut self, consumed: usize, output: &mut [u8]) -> EntryRangeProgress {
let written = (self.pending_end - self.pending).min(output.len());
output[..written].copy_from_slice(&self.scratch[self.pending..self.pending + written]);
self.pending += written;
self.emitted += written as u64;
self.finished = self.header_seen && self.emitted == self.range.length;
let status = if self.finished {
EntryRangeStatus::Finished
} else if self.pending < self.pending_end {
EntryRangeStatus::NeedOutput
} else {
EntryRangeStatus::Continue
};
EntryRangeProgress {
consumed,
written,
status,
}
}
}