use super::{core::seek as core, *};
use std::io::{Read, Seek};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FramedSeekError {
#[error("framed source failed: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Decode(#[from] FramedDecodeError),
#[error("central directory is required for random access")]
CentralDirectoryRequired,
#[error("resource index is out of range")]
ResourceNotFound,
#[error("central directory entry does not match the original chunk")]
DirectoryMismatch,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ResourceInfo {
pub(super) index: ResourceIndex,
pub(super) hidden: bool,
pub(super) checksum: Option<crate::framing::DictionaryId>,
pub(super) decoded_size: Option<u64>,
}
impl ResourceInfo {
pub const fn index(&self) -> ResourceIndex {
self.index
}
pub const fn hidden(&self) -> bool {
self.hidden
}
pub const fn checksum(&self) -> Option<crate::framing::DictionaryId> {
self.checksum
}
pub const fn decoded_size(&self) -> Option<u64> {
self.decoded_size
}
}
#[derive(Debug)]
pub struct FramedSeekReader<'d, 'dict, R> {
source: R,
owner: core::Lease<'d>,
resolver: Option<DictionaryResolverRef<'dict>>,
index: core::Index,
}
impl FramedDecompressor {
pub fn framed_seek_reader<R: Read + Seek>(
&mut self,
source: R,
) -> Result<FramedSeekReader<'_, 'static, R>, FramedSeekError> {
FramedSeekReader::open(self, None, source)
}
pub fn framed_seek_reader_with_dictionaries<'d, 'dict, R: Read + Seek>(
&'d mut self,
dictionaries: impl Into<DictionaryResolverRef<'dict>>,
source: R,
) -> Result<FramedSeekReader<'d, 'dict, R>, FramedSeekError> {
FramedSeekReader::open(self, Some(dictionaries.into()), source)
}
}
impl<'d, 'dict, R: Read + Seek> FramedSeekReader<'d, 'dict, R> {
fn open(
owner: &'d mut FramedDecompressor,
resolver: Option<DictionaryResolverRef<'dict>>,
mut source: R,
) -> Result<Self, FramedSeekError> {
drop(owner.start(Default::default())?);
let index = core::Index::open(&mut source, &owner.engine)?;
owner.active = true;
Ok(Self {
source,
owner: core::Lease { owner },
resolver,
index,
})
}
pub const fn header(&self) -> ContainerHeader {
self.index.header
}
pub const fn footer(&self) -> ContainerFooter {
self.index.footer
}
pub fn resources(&self) -> &[ResourceInfo] {
&self.index.infos
}
pub fn resource_info(&self, index: ResourceIndex) -> Option<&ResourceInfo> {
usize::try_from(index.0)
.ok()
.and_then(|i| self.index.infos.get(i))
}
pub fn resource_metadata(
&mut self,
index: ResourceIndex,
) -> Result<Option<&Metadata>, FramedSeekError> {
self.metadata(index, false)
}
pub fn resource_footer_metadata(
&mut self,
index: ResourceIndex,
) -> Result<Option<&Metadata>, FramedSeekError> {
self.metadata(index, true)
}
fn metadata(
&mut self,
index: ResourceIndex,
footer: bool,
) -> Result<Option<&Metadata>, FramedSeekError> {
let slot = self.index.metadata_chunk(index, footer)?;
let Some(slot) = slot else { return Ok(None) };
if self.index.metadata[slot].is_none() {
let mut operation =
core::Operation::new(self.owner.owner, &self.index, slot..slot + 1)?;
let result = operation.metadata(&mut self.source, &self.index, self.resolver, slot);
drop(operation);
self.index.cache_metadata(slot, result?)?;
}
Ok(self.index.metadata[slot].as_ref())
}
pub fn resource(
&mut self,
index: ResourceIndex,
) -> Result<ResourceReader<'_, 'dict, R>, FramedSeekError> {
let range = self.index.resource_range(index)?;
let info = &self.index.infos
[usize::try_from(index.0).map_err(|_| FramedSeekError::ResourceNotFound)?];
let operation = core::Operation::new(self.owner.owner, &self.index, range)?;
Ok(ResourceReader {
source: &mut self.source,
index: &self.index,
info,
resolver: self.resolver,
operation,
total_out: 0,
failed: false,
})
}
pub const fn get_ref(&self) -> &R {
&self.source
}
pub const fn get_mut(&mut self) -> &mut R {
&mut self.source
}
pub fn into_inner(self) -> R {
self.source
}
}
#[derive(Debug)]
pub struct ResourceReader<'a, 'dict, R> {
source: &'a mut R,
index: &'a core::Index,
info: &'a ResourceInfo,
resolver: Option<DictionaryResolverRef<'dict>>,
operation: core::Operation<'a>,
total_out: u64,
failed: bool,
}
impl<R: Read + Seek> ResourceReader<'_, '_, R> {
pub const fn index(&self) -> ResourceIndex {
self.info.index
}
pub const fn info(&self) -> &ResourceInfo {
self.info
}
pub const fn total_out(&self) -> u64 {
self.total_out
}
}
impl<R: Read + Seek> Read for ResourceReader<'_, '_, R> {
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
if output.is_empty() {
return Ok(0);
}
if self.failed {
return Err(std::io::Error::other(FramedSeekError::Decode(
FramedDecodeError::InvalidState,
)));
}
match self
.operation
.read(self.source, self.index, self.resolver, output)
{
Ok(n) => {
self.total_out += n as u64;
Ok(n)
}
Err(error) => {
self.failed = true;
Err(std::io::Error::other(error))
}
}
}
}