use super::*;
#[cfg(feature = "compression")]
use alloc::boxed::Box;
#[cfg(feature = "compression")]
use std::io::Write;
#[cfg(feature = "decompression")]
use std::io::{Read, Seek, SeekFrom};
#[cfg(feature = "compression")]
pub struct FramedFinishError<T> {
pub error: FramedError,
pub writer: T,
}
#[cfg(feature = "compression")]
impl<T> std::fmt::Debug for FramedFinishError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FramedFinishError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
#[cfg(feature = "compression")]
impl<T> std::fmt::Display for FramedFinishError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "framed container could not be finished: {}", self.error)
}
}
#[cfg(feature = "compression")]
impl<T> std::error::Error for FramedFinishError<T> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
#[cfg(feature = "compression")]
#[derive(Debug)]
pub struct FramedWriter<'a, W> {
session: FramedEncoderSession<'a>,
sink: W,
buffer: [u8; 8192],
start: usize,
end: usize,
finished: bool,
}
#[cfg(feature = "compression")]
impl FramedCompressor {
pub fn framed_writer<W: Write>(&mut self, sink: W) -> Result<FramedWriter<'_, W>, FramedError> {
Ok(FramedWriter {
session: self.start()?,
sink,
buffer: [0; 8192],
start: 0,
end: 0,
finished: false,
})
}
}
#[cfg(feature = "compression")]
impl<W: Write> FramedWriter<'_, W> {
pub fn resource(&mut self, resource: FramedResource<'_>) -> Result<(), FramedError> {
if self.finished {
return Err(FramedError::InvalidState);
}
self.drain(FramedEncodeOperation::Process)?;
self.session.resource(resource)
}
pub fn flush(&mut self) -> Result<(), FramedError> {
self.drain(FramedEncodeOperation::Process)?;
self.sink.flush()?;
Ok(())
}
pub fn try_finish(&mut self) -> Result<(), FramedError> {
self.drain(FramedEncodeOperation::Finish)?;
self.sink.flush()?;
self.finished = true;
Ok(())
}
pub fn finish(mut self) -> Result<W, Box<FramedFinishError<Self>>> {
match self.try_finish() {
Ok(()) => Ok(self.sink),
Err(error) => Err(Box::new(FramedFinishError {
error,
writer: self,
})),
}
}
fn drain(&mut self, op: FramedEncodeOperation) -> Result<(), FramedError> {
loop {
while self.start < self.end {
match self.sink.write(&self.buffer[self.start..self.end]) {
Ok(0) => return Err(FramedError::Io(std::io::ErrorKind::WriteZero.into())),
Ok(n) if n <= self.end - self.start => self.start += n,
Ok(_) => {
return Err(FramedError::Invalid("writer returned an oversized count"));
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
}
}
let progress = self.session.process(&mut self.buffer, op)?;
self.start = 0;
self.end = progress.produced;
if self.end == 0 && progress.status != FramedEncoderStatus::NeedsOutput {
return Ok(());
}
}
}
}
#[cfg(feature = "decompression")]
#[derive(Debug)]
pub struct FramedReader<'a, R> {
session: FramedDecoderSession<'a>,
source: R,
buffer: [u8; 8192],
start: usize,
end: usize,
eof: bool,
failed: bool,
}
#[cfg(feature = "decompression")]
impl FramedDecompressor {
pub fn framed_reader<R: Read>(&mut self, source: R) -> FramedReader<'_, R> {
FramedReader {
session: self.start(),
source,
buffer: [0; 8192],
start: 0,
end: 0,
eof: false,
failed: false,
}
}
pub fn framed_seek_reader<R: Read + Seek>(
&mut self,
mut source: R,
) -> Result<FramedSeekReader<'_, R>, FramedError> {
use super::core::{FOOTER_LEN, MAGIC};
let base = source.stream_position()?;
let end = source.seek(SeekFrom::End(0))?;
let total = end
.checked_sub(base)
.ok_or(FramedError::Invalid("container range"))?;
core::limit(total, self.config.max_container_bytes, "container size")?;
if total < (MAGIC.len() + 12 + FOOTER_LEN) as u64 {
return Err(FramedError::UnexpectedEndOfInput);
}
source.seek(SeekFrom::Start(base))?;
let mut header = [0; 8];
source.read_exact(&mut header)?;
core::check_magic(&header)?;
source.seek(SeekFrom::Start(end - FOOTER_LEN as u64))?;
let mut footer = [0; FOOTER_LEN];
source.read_exact(&mut footer)?;
let offset = u64::from_le_bytes(core::number(&footer, 8)?);
let len = u64::from_le_bytes(core::number(&footer, 16)?);
core::footer(&footer, offset, len, total)?;
if offset < 8 || core::add(core::add(offset, len)?, FOOTER_LEN as u64)? != total {
return Err(FramedError::Invalid("directory range"));
}
core::limit(
len,
core::index_size(self.config.max_resources)? as u64,
"directory size",
)?;
let mut directory = Vec::new();
let host_len = core::host(len)?;
core::reserve(&mut directory, host_len)?;
directory.resize(host_len, 0);
source.seek(SeekFrom::Start(core::add(base, offset)?))?;
source.read_exact(&mut directory)?;
let entries = core::index(&directory, offset, self.raw.backend().0, self.config)?;
Ok(FramedSeekReader {
owner: self,
source,
base,
entries,
})
}
}
#[cfg(feature = "decompression")]
impl<R: Read> FramedReader<'_, R> {
pub fn read_resource(&mut self) -> Result<Option<DecodedResource>, FramedError> {
if self.failed {
return Err(FramedError::InvalidState);
}
loop {
if self.start == self.end && !self.eof {
match self.source.read(&mut self.buffer) {
Ok(n) if n <= self.buffer.len() => {
self.start = 0;
self.end = n;
self.eof = n == 0;
}
Ok(_) => {
self.failed = true;
return Err(FramedError::Invalid("reader returned an oversized count"));
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
}
}
let op = if self.eof {
crate::DecodeOperation::Finish
} else {
crate::DecodeOperation::Process
};
let progress = match self.session.process(&self.buffer[self.start..self.end], op) {
Ok(p) => p,
Err(failure) => {
self.failed = true;
return Err(failure.error);
}
};
self.start += progress.consumed;
if let Some(resource) = progress.resource {
return Ok(Some(resource));
}
if progress.status == FramedDecoderStatus::Finished {
return Ok(None);
}
}
}
}
#[cfg(feature = "decompression")]
#[derive(Debug)]
pub struct FramedSeekReader<'a, R> {
owner: &'a mut FramedDecompressor,
source: R,
base: u64,
entries: Vec<ResourceInfo>,
}
#[cfg(feature = "decompression")]
impl<R: Read + Seek> FramedSeekReader<'_, R> {
pub fn resources(&self) -> &[ResourceInfo] {
&self.entries
}
pub fn read_resource(&mut self, index: usize) -> Result<DecodedResource, FramedError> {
let info = *self
.entries
.get(index)
.ok_or(FramedError::Invalid("resource index"))?;
self.source
.seek(SeekFrom::Start(core::add(self.base, info.offset)?))?;
let mut header = [0; core::RECORD_HEADER];
self.source.read_exact(&mut header)?;
let len = core::decode::record_length(&header, self.owner.config)?;
if len as u64 != info.length {
return Err(FramedError::Invalid("resource directory mismatch"));
}
let mut record = Vec::new();
core::reserve(&mut record, len)?;
record.extend_from_slice(&header);
record.resize(len, 0);
self.source.read_exact(&mut record[header.len()..])?;
core::decode::resource(&mut self.owner.raw, &record, info, self.owner.config)
}
}