Skip to main content

lzma_rust2/lzip/
reader.rs

1use alloc::vec::Vec;
2
3use super::{HEADER_SIZE, LzipHeader, LzipTrailer, TRAILER_SIZE};
4use crate::{
5    CountingReader, LzmaReader, Read, Result, crc::Crc32, error_invalid_data, error_invalid_input,
6};
7
8/// A single-threaded LZIP decompressor.
9pub struct LzipReader<R> {
10    inner: Option<R>,
11    lzma_reader: Option<LzmaReader<CountingReader<R>>>,
12    current_header: Option<LzipHeader>,
13    finished: bool,
14    trailer_buf: Vec<u8>,
15    crc_digest: Option<Crc32>,
16    data_size: u64,
17}
18
19impl<R> LzipReader<R> {
20    /// Consume the LzipReader and return the inner reader.
21    pub fn into_inner(mut self) -> R {
22        if let Some(lzma_reader) = self.lzma_reader.take() {
23            return lzma_reader.into_inner().inner;
24        }
25
26        self.inner.take().expect("inner reader not set")
27    }
28
29    /// Returns a reference to the inner reader.
30    pub fn inner(&self) -> &R {
31        self.lzma_reader
32            .as_ref()
33            .map(|reader| reader.inner().inner())
34            .unwrap_or_else(|| self.inner.as_ref().expect("inner reader not set"))
35    }
36
37    /// Returns a mutable reference to the inner reader.
38    pub fn inner_mut(&mut self) -> &mut R {
39        self.lzma_reader
40            .as_mut()
41            .map(|reader| reader.inner_mut().inner_mut())
42            .unwrap_or_else(|| self.inner.as_mut().expect("inner reader not set"))
43    }
44}
45
46impl<R: Read> LzipReader<R> {
47    /// Create a new LZIP reader.
48    pub fn new(inner: R) -> Self {
49        Self {
50            inner: Some(inner),
51            lzma_reader: None,
52            current_header: None,
53            finished: false,
54            trailer_buf: Vec::with_capacity(TRAILER_SIZE),
55            crc_digest: None,
56            data_size: 0,
57        }
58    }
59
60    /// Start processing the next LZIP member.
61    /// Returns Ok(true) if a new member was started, Ok(false) if EOF was reached.
62    fn start_next_member(&mut self) -> Result<bool> {
63        let mut reader = self.inner.take().expect("inner reader not set");
64
65        let header = match LzipHeader::parse(&mut reader) {
66            Ok(header) => header,
67            Err(_) => {
68                // If header parsing fails, we've probably reached EOF:
69                // Put the reader back and indicate we're done:
70                self.inner = Some(reader);
71                return Ok(false);
72            }
73        };
74
75        if header.version != 1 {
76            return Err(error_invalid_input("unsupported LZIP version"));
77        }
78
79        let counting_reader = CountingReader::new(reader);
80
81        // Create LZMA reader with LZMA-302eos properties:
82        // - lc=3 (literal context bits)
83        // - lp=0 (literal position bits)
84        // - pb=2 (position bits)
85        // - Unlimited uncompressed size (we'll use trailer to verify)
86        let lzma_reader =
87            LzmaReader::new(counting_reader, u64::MAX, 3, 0, 2, header.dict_size, None)?;
88
89        self.current_header = Some(header);
90        self.lzma_reader = Some(lzma_reader);
91        self.trailer_buf.clear();
92        self.crc_digest = Some(Crc32::new());
93        self.data_size = 0;
94
95        Ok(true)
96    }
97
98    fn finish_current_member(&mut self) -> Result<()> {
99        let lzma_reader = self.lzma_reader.take().expect("lzma reader not set");
100
101        let counting_reader = lzma_reader.into_inner();
102        let compressed_bytes = counting_reader.bytes_read();
103
104        let mut inner_reader = counting_reader.inner;
105        let trailer = LzipTrailer::parse(&mut inner_reader)?;
106
107        let computed_crc = self.crc_digest.take().expect("no CRC digest").finalize();
108
109        if computed_crc != trailer.crc32 {
110            self.inner = Some(inner_reader);
111            return Err(error_invalid_data("LZIP CRC32 mismatch"));
112        }
113
114        if self.data_size != trailer.data_size {
115            self.inner = Some(inner_reader);
116            return Err(error_invalid_data("LZIP data size mismatch"));
117        }
118
119        let actual_member_size = HEADER_SIZE as u64 + compressed_bytes + TRAILER_SIZE as u64;
120        if actual_member_size != trailer.member_size {
121            self.inner = Some(inner_reader);
122            return Err(error_invalid_data("LZIP member size mismatch"));
123        }
124
125        // Store the reader for potential next member.
126        self.inner = Some(inner_reader);
127
128        Ok(())
129    }
130}
131
132impl<R: Read> Read for LzipReader<R> {
133    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
134        if buf.is_empty() {
135            return Ok(0);
136        }
137
138        loop {
139            // If we have an active LZMA reader, try to read from it.
140            if let Some(ref mut lzma_reader) = self.lzma_reader {
141                match lzma_reader.read(buf) {
142                    Ok(0) => {
143                        // Current member is finished, verify trailer.
144                        self.finish_current_member()?;
145
146                        if !self.start_next_member()? {
147                            // No more members, we're done.
148                            self.finished = true;
149                            return Ok(0);
150                        }
151
152                        // Continue to read from the new member.
153                        continue;
154                    }
155                    Ok(bytes_read) => {
156                        // Update CRC with the decompressed data
157                        if let Some(ref mut crc_digest) = self.crc_digest {
158                            crc_digest.update(&buf[..bytes_read]);
159                            self.data_size += bytes_read as u64;
160                        }
161                        return Ok(bytes_read);
162                    }
163                    Err(e) => {
164                        return Err(e);
165                    }
166                }
167            } else if self.finished {
168                // Already finished, return EOF.
169                return Ok(0);
170            } else {
171                // No active LZMA reader, start the first/next member.
172                if !self.start_next_member()? {
173                    // No members found, we're done.
174                    self.finished = true;
175                    return Ok(0);
176                }
177            }
178        }
179    }
180}