rc_zip_sync/
entry_reader.rs

1use rc_zip::{
2    fsm::{EntryFsm, FsmResult},
3    parse::Entry,
4};
5use std::io;
6use tracing::trace;
7
8/// Reader for an entry inside an archive
9pub struct EntryReader<R>
10where
11    R: io::Read,
12{
13    rd: R,
14    fsm: Option<EntryFsm>,
15}
16
17impl<R> EntryReader<R>
18where
19    R: io::Read,
20{
21    pub(crate) fn new(entry: &Entry, rd: R) -> Self {
22        Self {
23            rd,
24            fsm: Some(EntryFsm::new(Some(entry.clone()), None)),
25        }
26    }
27}
28
29impl<R> io::Read for EntryReader<R>
30where
31    R: io::Read,
32{
33    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
34        loop {
35            let mut fsm = match self.fsm.take() {
36                Some(fsm) => fsm,
37                None => return Ok(0),
38            };
39
40            #[allow(clippy::needless_late_init)] // don't tell me what to do
41            let filled_bytes;
42            if fsm.wants_read() {
43                tracing::trace!(space_avail = fsm.space().len(), "fsm wants read");
44                let n = self.rd.read(fsm.space())?;
45                fsm.fill(n);
46                filled_bytes = n;
47            } else {
48                trace!("fsm does not want read");
49                filled_bytes = 0;
50            }
51
52            match fsm.process(buf)? {
53                FsmResult::Continue((fsm, outcome)) => {
54                    self.fsm = Some(fsm);
55
56                    if outcome.bytes_written > 0 {
57                        tracing::trace!("wrote {} bytes", outcome.bytes_written);
58                        return Ok(outcome.bytes_written);
59                    } else if filled_bytes > 0 || outcome.bytes_read > 0 {
60                        // progress was made, keep reading
61                        continue;
62                    } else {
63                        return Err(io::Error::new(
64                            io::ErrorKind::Other,
65                            "entry reader: no progress",
66                        ));
67                    }
68                }
69                FsmResult::Done(_) => {
70                    // neat!
71                    return Ok(0);
72                }
73            }
74        }
75    }
76}