use std::sync::{Mutex, RwLock};
use crate::error::BackhandError;
use crate::kinds::Kind;
use crate::v4::compressor::{CompressionOptions, Compressor};
use crate::v4::data::DataSize;
use crate::v4::filesystem::node::Nodes;
use crate::v4::fragment::Fragment;
use crate::v4::id::Id;
use crate::v4::reader::BufReadSeek;
use crate::v4::squashfs::Cache;
use crate::{Node, Squashfs, SquashfsFileReader};
#[cfg(not(feature = "parallel"))]
use crate::v4::filesystem::reader_no_parallel::{SquashfsRawData, SquashfsReadFile};
#[cfg(feature = "parallel")]
use crate::v4::filesystem::reader_parallel::{SquashfsRawData, SquashfsReadFile};
pub struct FilesystemReader<'b> {
pub kind: Kind,
pub block_size: u32,
pub block_log: u16,
pub compressor: Compressor,
pub compression_options: Option<CompressionOptions>,
pub mod_time: u32,
pub id_table: Vec<Id>,
pub fragments: Option<Vec<Fragment>>,
pub root: Nodes<SquashfsFileReader>,
pub(crate) reader: Mutex<Box<dyn BufReadSeek + 'b>>,
pub(crate) cache: RwLock<Cache>,
pub(crate) no_duplicate_files: bool,
}
impl<'b> FilesystemReader<'b> {
pub fn from_reader<R>(reader: R) -> Result<Self, BackhandError>
where
R: BufReadSeek + 'b,
{
let squashfs = Squashfs::from_reader_with_offset(reader, 0)?;
squashfs.into_filesystem_reader()
}
pub fn from_reader_with_offset<R>(reader: R, offset: u64) -> Result<Self, BackhandError>
where
R: BufReadSeek + 'b,
{
let squashfs = Squashfs::from_reader_with_offset(reader, offset)?;
squashfs.into_filesystem_reader()
}
pub fn from_reader_with_offset_and_kind<R>(
reader: R,
offset: u64,
kind: Kind,
) -> Result<Self, BackhandError>
where
R: BufReadSeek + 'b,
{
let squashfs = Squashfs::from_reader_with_offset_and_kind(reader, offset, kind)?;
squashfs.into_filesystem_reader()
}
pub fn file<'a>(&'a self, file: &'a SquashfsFileReader) -> FilesystemReaderFile<'a, 'b> {
FilesystemReaderFile::new(self, file)
}
pub fn files(&self) -> impl Iterator<Item = &Node<SquashfsFileReader>> {
self.root.nodes.iter()
}
}
#[derive(Copy, Clone)]
pub struct FilesystemReaderFile<'a, 'b> {
pub(crate) system: &'a FilesystemReader<'b>,
pub(crate) file: &'a SquashfsFileReader,
}
impl<'a, 'b> FilesystemReaderFile<'a, 'b> {
pub fn new(system: &'a FilesystemReader<'b>, file: &'a SquashfsFileReader) -> Self {
Self { system, file }
}
pub fn reader(&self) -> SquashfsReadFile<'a, 'b> {
self.raw_data_reader().into_reader()
}
pub fn fragment(&self) -> Option<&'a Fragment> {
if self.file.frag_index() == 0xffffffff {
None
} else {
self.system.fragments.as_ref().map(|fragments| &fragments[self.file.frag_index()])
}
}
pub(crate) fn raw_data_reader(&self) -> SquashfsRawData<'a, 'b> {
SquashfsRawData::new(Self { system: self.system, file: self.file })
}
}
impl<'a> IntoIterator for FilesystemReaderFile<'a, '_> {
type IntoIter = BlockIterator<'a>;
type Item = <BlockIterator<'a> as Iterator>::Item;
fn into_iter(self) -> Self::IntoIter {
BlockIterator { blocks: self.file.block_sizes(), fragment: self.fragment() }
}
}
pub enum BlockFragment<'a> {
Block(&'a DataSize),
Fragment(&'a Fragment),
}
pub struct BlockIterator<'a> {
pub blocks: &'a [DataSize],
pub fragment: Option<&'a Fragment>,
}
impl<'a> Iterator for BlockIterator<'a> {
type Item = BlockFragment<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.blocks
.split_first()
.map(|(first, rest)| {
self.blocks = rest;
BlockFragment::Block(first)
})
.or_else(|| self.fragment.take().map(BlockFragment::Fragment))
}
}