Skip to main content

applesauce_core/
reader.rs

1use crate::decmpfs::{BlockInfo, Storage};
2use crate::{compressor, decmpfs};
3use std::io::{self, BufReader, Cursor, Read, Seek};
4
5pub trait Open {
6    type ResourceFork: Read + Seek;
7
8    fn open_resource_fork(self) -> io::Result<Self::ResourceFork>;
9}
10
11impl<R: Read + Seek, F: FnOnce() -> R> Open for F {
12    type ResourceFork = R;
13
14    #[inline]
15    fn open_resource_fork(self) -> io::Result<Self::ResourceFork> {
16        Ok(self())
17    }
18}
19
20#[derive(Debug)]
21enum State<R> {
22    Xattr(Cursor<Vec<u8>>),
23    ResourceFork {
24        // Stored in reverse order, so that we can pop() them off
25        block_infos: Vec<BlockInfo>,
26        last_offset: u32,
27        reader: BufReader<R>,
28    },
29}
30
31#[derive(Debug)]
32pub struct Reader<R> {
33    kind: compressor::Kind,
34    state: State<R>,
35}
36
37impl<R: Read + Seek> Reader<R> {
38    pub fn new<O>(decmpfs_data: &[u8], open: O) -> io::Result<Self>
39    where
40        O: Open<ResourceFork = R>,
41    {
42        let decmpfs_value = decmpfs::Value::from_data(decmpfs_data)?;
43        let (kind, storage) = decmpfs_value
44            .compression_type
45            .compression_storage()
46            .filter(|(kind, _)| kind.supported())
47            .ok_or_else(|| io::Error::other("unsupported compression kind or storage"))?;
48        let state = match storage {
49            Storage::Xattr => State::Xattr(Cursor::new(decmpfs_value.extra_data.to_vec())),
50            Storage::ResourceFork => {
51                let mut rfork = BufReader::new(open.open_resource_fork()?);
52                let mut blocks_info =
53                    kind.read_block_info(&mut rfork, decmpfs_value.uncompressed_size)?;
54
55                // Seek back to the beginning of the resource fork
56                rfork.rewind()?;
57                // Reverse the block infos so that we can pop() them off
58                blocks_info.reverse();
59
60                State::ResourceFork {
61                    block_infos: blocks_info,
62                    last_offset: 0,
63                    reader: rfork,
64                }
65            }
66        };
67        Ok(Self { kind, state })
68    }
69
70    pub fn read_block_into(&mut self, dst: &mut Vec<u8>) -> io::Result<bool> {
71        match &mut self.state {
72            State::Xattr(cursor) => cursor.read_to_end(dst).map(|bytes_read| bytes_read > 0),
73            State::ResourceFork {
74                block_infos,
75                last_offset,
76                reader,
77            } => {
78                let block = match block_infos.pop() {
79                    Some(block) => block,
80                    None => return Ok(false),
81                };
82                let diff = i64::from(block.offset) - i64::from(*last_offset);
83                reader.seek_relative(diff)?;
84
85                let bytes_read = reader
86                    .by_ref()
87                    .take(block.compressed_size.into())
88                    .read_to_end(dst)?;
89                if bytes_read < block.compressed_size as usize {
90                    return Err(io::ErrorKind::UnexpectedEof.into());
91                }
92                *last_offset = block
93                    .offset
94                    .checked_add(block.compressed_size)
95                    .ok_or(io::ErrorKind::InvalidData)?;
96                Ok(true)
97            }
98        }
99    }
100
101    #[inline]
102    pub fn compression_kind(&self) -> compressor::Kind {
103        self.kind
104    }
105
106    #[inline]
107    pub fn remaining_blocks(&self) -> usize {
108        match &self.state {
109            State::Xattr(cursor) => {
110                let remaining = cursor.get_ref().len() as u64 - cursor.position();
111                usize::from(remaining > 0)
112            }
113            State::ResourceFork { block_infos, .. } => block_infos.len(),
114        }
115    }
116}