Skip to main content

lzma_rust2/xz/
reader.rs

1use alloc::{boxed::Box, vec::Vec};
2
3use super::{
4    BlockHeader, CheckType, ChecksumCalculator, FilterType, Index, IndexRecord, StreamFooter,
5    StreamHeader, XZ_FOOTER_MAGIC, XZ_MAGIC, count_multibyte_integer_size, parse_multibyte_integer,
6};
7use crate::{
8    CountingReader, Lzma2Reader, Read, Result,
9    crc::Crc32,
10    error_invalid_data,
11    filter::{
12        bcj::{BcjFilter, BcjReader},
13        delta::{Delta, DeltaReader},
14    },
15    lzma2_reader::{Action, Lzma2Stream, Status, StreamResult},
16};
17
18#[allow(clippy::large_enum_variant)]
19enum FilterReader<R: Read> {
20    Counting(CountingReader<R>),
21    Lzma2(Lzma2Reader<Box<FilterReader<R>>>),
22    Delta(DeltaReader<Box<FilterReader<R>>>),
23    Bcj(BcjReader<Box<FilterReader<R>>>),
24    Dummy,
25}
26
27impl<R: Read> Read for FilterReader<R> {
28    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
29        match self {
30            FilterReader::Counting(reader) => reader.read(buf),
31            FilterReader::Lzma2(reader) => reader.read(buf),
32            FilterReader::Delta(reader) => reader.read(buf),
33            FilterReader::Bcj(reader) => reader.read(buf),
34            FilterReader::Dummy => unimplemented!(),
35        }
36    }
37}
38
39impl<R: Read> FilterReader<R> {
40    fn create_filter_chain(inner: R, filters: &[Option<FilterType>], properties: &[u32]) -> Self {
41        let mut chain_reader = FilterReader::Counting(CountingReader::new(inner));
42
43        for (filter, property) in filters
44            .iter()
45            .copied()
46            .zip(properties)
47            .filter_map(|(filter, property)| filter.map(|filter| (filter, *property)))
48            .rev()
49        {
50            chain_reader = match filter {
51                FilterType::Delta => {
52                    let distance = property as usize;
53                    FilterReader::Delta(DeltaReader::new(Box::new(chain_reader), distance))
54                }
55                FilterType::BcjX86 => {
56                    let start_offset = property as usize;
57                    FilterReader::Bcj(BcjReader::new_x86(Box::new(chain_reader), start_offset))
58                }
59                FilterType::BcjPpc => {
60                    let start_offset = property as usize;
61                    FilterReader::Bcj(BcjReader::new_ppc(Box::new(chain_reader), start_offset))
62                }
63                FilterType::BcjIa64 => {
64                    let start_offset = property as usize;
65                    FilterReader::Bcj(BcjReader::new_ia64(Box::new(chain_reader), start_offset))
66                }
67                FilterType::BcjArm => {
68                    let start_offset = property as usize;
69                    FilterReader::Bcj(BcjReader::new_arm(Box::new(chain_reader), start_offset))
70                }
71                FilterType::BcjArmThumb => {
72                    let start_offset = property as usize;
73                    FilterReader::Bcj(BcjReader::new_arm_thumb(
74                        Box::new(chain_reader),
75                        start_offset,
76                    ))
77                }
78                FilterType::BcjSparc => {
79                    let start_offset = property as usize;
80                    FilterReader::Bcj(BcjReader::new_sparc(Box::new(chain_reader), start_offset))
81                }
82                FilterType::BcjArm64 => {
83                    let start_offset = property as usize;
84                    FilterReader::Bcj(BcjReader::new_arm64(Box::new(chain_reader), start_offset))
85                }
86                FilterType::BcjRiscv => {
87                    let start_offset = property as usize;
88                    FilterReader::Bcj(BcjReader::new_riscv(Box::new(chain_reader), start_offset))
89                }
90                FilterType::Lzma2 => {
91                    let dict_size = property;
92                    FilterReader::Lzma2(Lzma2Reader::new(Box::new(chain_reader), dict_size, None))
93                }
94            };
95        }
96
97        chain_reader
98    }
99
100    fn bytes_read(&self) -> u64 {
101        match self {
102            FilterReader::Counting(reader) => reader.bytes_read(),
103            FilterReader::Lzma2(reader) => reader.inner().bytes_read(),
104            FilterReader::Delta(reader) => reader.inner().bytes_read(),
105            FilterReader::Bcj(reader) => reader.inner().bytes_read(),
106            FilterReader::Dummy => unimplemented!(),
107        }
108    }
109
110    fn into_inner(self) -> R {
111        match self {
112            FilterReader::Counting(reader) => reader.inner,
113            FilterReader::Lzma2(reader) => {
114                let filter_reader = reader.into_inner();
115                filter_reader.into_inner()
116            }
117            FilterReader::Delta(reader) => {
118                let filter_reader = reader.into_inner();
119                filter_reader.into_inner()
120            }
121            FilterReader::Bcj(reader) => {
122                let filter_reader = reader.into_inner();
123                filter_reader.into_inner()
124            }
125            FilterReader::Dummy => unimplemented!(),
126        }
127    }
128
129    fn inner(&self) -> &R {
130        match self {
131            FilterReader::Counting(reader) => &reader.inner,
132            FilterReader::Lzma2(reader) => {
133                let filter_reader = reader.inner();
134
135                filter_reader.inner()
136            }
137            FilterReader::Delta(reader) => {
138                let filter_reader = reader.inner();
139                filter_reader.inner()
140            }
141            FilterReader::Bcj(reader) => {
142                let filter_reader = reader.inner();
143                filter_reader.inner()
144            }
145            FilterReader::Dummy => unimplemented!(),
146        }
147    }
148
149    fn inner_mut(&mut self) -> &mut R {
150        match self {
151            FilterReader::Counting(reader) => &mut reader.inner,
152            FilterReader::Lzma2(reader) => {
153                let filter_reader = reader.inner_mut();
154                filter_reader.inner_mut()
155            }
156            FilterReader::Delta(reader) => {
157                let filter_reader = reader.inner_mut();
158                filter_reader.inner_mut()
159            }
160            FilterReader::Bcj(reader) => {
161                let filter_reader = reader.inner_mut();
162                filter_reader.inner_mut()
163            }
164            FilterReader::Dummy => unimplemented!(),
165        }
166    }
167}
168
169/// A single-threaded XZ decompressor.
170pub struct XzReader<R: Read> {
171    reader: FilterReader<R>,
172    stream_header: Option<StreamHeader>,
173    checksum_calculator: Option<ChecksumCalculator>,
174    finished: bool,
175    allow_multiple_streams: bool,
176    blocks_processed: u64,
177}
178
179impl<R: Read> XzReader<R> {
180    /// Create a new [`XzReader`].
181    pub fn new(inner: R, allow_multiple_streams: bool) -> Self {
182        let reader = FilterReader::Counting(CountingReader::new(inner));
183
184        Self {
185            reader,
186            stream_header: None,
187            checksum_calculator: None,
188            finished: false,
189            allow_multiple_streams,
190            blocks_processed: 0,
191        }
192    }
193
194    /// Consume the XzReader and return the inner reader.
195    pub fn into_inner(self) -> R {
196        self.reader.into_inner()
197    }
198
199    /// Returns a reference to the inner reader.
200    pub fn inner(&self) -> &R {
201        self.reader.inner()
202    }
203
204    /// Returns a mutable reference to the inner reader.
205    pub fn inner_mut(&mut self) -> &mut R {
206        self.reader.inner_mut()
207    }
208}
209
210impl<R: Read> XzReader<R> {
211    fn ensure_stream_header(&mut self) -> Result<()> {
212        if self.stream_header.is_none() {
213            let header = StreamHeader::parse(&mut self.reader)?;
214            self.stream_header = Some(header);
215        }
216        Ok(())
217    }
218
219    fn prepare_next_block(&mut self) -> Result<bool> {
220        match BlockHeader::parse(&mut self.reader)? {
221            Some(block_header) => {
222                let base_reader: FilterReader<R> =
223                    core::mem::replace(&mut self.reader, FilterReader::Dummy);
224
225                self.reader = FilterReader::create_filter_chain(
226                    base_reader.into_inner(),
227                    &block_header.filters,
228                    &block_header.properties,
229                );
230
231                match self.stream_header.as_ref() {
232                    Some(header) => {
233                        self.checksum_calculator = Some(ChecksumCalculator::new(header.check_type));
234                    }
235                    None => {
236                        panic!("stream_header not set");
237                    }
238                }
239
240                self.blocks_processed += 1;
241
242                Ok(true)
243            }
244            None => {
245                // End of blocks reached, index follows.
246                self.parse_index_and_footer()?;
247
248                if self.allow_multiple_streams && self.try_start_next_stream()? {
249                    return self.prepare_next_block();
250                }
251
252                self.finished = true;
253                Ok(false)
254            }
255        }
256    }
257
258    fn consume_padding(&mut self, compressed_bytes: u64) -> Result<()> {
259        let padding_needed = match (4 - (compressed_bytes % 4)) % 4 {
260            0 => return Ok(()),
261            n => n as usize,
262        };
263
264        let mut padding_buf = [0u8; 3];
265
266        let bytes_read = self.reader.read(&mut padding_buf[..padding_needed])?;
267
268        if bytes_read != padding_needed {
269            return Err(error_invalid_data("incomplete XZ block padding"));
270        }
271
272        if !padding_buf[..bytes_read].iter().all(|&byte| byte == 0) {
273            return Err(error_invalid_data("invalid XZ block padding"));
274        }
275
276        Ok(())
277    }
278
279    fn verify_block_checksum(&mut self) -> Result<()> {
280        let checksum_calculator = self
281            .checksum_calculator
282            .take()
283            .expect("checksum_calculator not set");
284
285        match checksum_calculator {
286            ChecksumCalculator::None => { /* Nothing to check */ }
287            ChecksumCalculator::Crc32(_) => {
288                let mut checksum = [0u8; 4];
289                self.reader.read_exact(&mut checksum)?;
290
291                if !checksum_calculator.verify(&checksum) {
292                    return Err(error_invalid_data("invalid block checksum"));
293                }
294            }
295            ChecksumCalculator::Crc64(_) => {
296                let mut checksum = [0u8; 8];
297                self.reader.read_exact(&mut checksum)?;
298
299                if !checksum_calculator.verify(&checksum) {
300                    return Err(error_invalid_data("invalid block checksum"));
301                }
302            }
303            ChecksumCalculator::Sha256(_) => {
304                let mut checksum = [0u8; 32];
305                self.reader.read_exact(&mut checksum)?;
306
307                if !checksum_calculator.verify(&checksum) {
308                    return Err(error_invalid_data("invalid block checksum"));
309                }
310            }
311        }
312
313        Ok(())
314    }
315
316    /// Look for the start of the next stream by reading bytes one at a time
317    /// and checking for the XZ magic sequence, allowing for stream padding.
318    fn try_start_next_stream(&mut self) -> Result<bool> {
319        let mut padding_bytes = 0;
320        let mut buffer = [0u8; 6];
321
322        loop {
323            let mut byte_buffer = [0u8; 1];
324            let read = self.reader.read(&mut byte_buffer)?;
325            if read == 0 {
326                // EOF reached, no more streams.
327                return Ok(false);
328            }
329
330            let byte = byte_buffer[0];
331
332            if byte == 0 {
333                // Potential stream padding.
334                padding_bytes += 1;
335                continue;
336            }
337
338            // Non-zero byte found - check if it starts XZ magic.
339            if byte != XZ_MAGIC[0] {
340                return Err(error_invalid_data("invalid data after stream"));
341            }
342
343            buffer[0] = byte;
344            let mut buffer_pos = 1;
345
346            // Read the rest of the magic bytes.
347            while buffer_pos < 6 {
348                match self.reader.read(&mut byte_buffer)? {
349                    0 => {
350                        return Err(error_invalid_data("incomplete XZ magic bytes"));
351                    }
352                    1 => {
353                        buffer[buffer_pos] = byte_buffer[0];
354                        buffer_pos += 1;
355                    }
356                    _ => unreachable!(),
357                }
358            }
359
360            if buffer != XZ_MAGIC {
361                return Err(error_invalid_data("invalid data after stream padding"));
362            }
363
364            if padding_bytes % 4 != 0 {
365                return Err(error_invalid_data("stream padding size not multiple of 4"));
366            }
367
368            let stream_header = StreamHeader::parse_stream_header_flags_and_crc(&mut self.reader)?;
369
370            // Reset state for new stream.
371            self.stream_header = Some(stream_header);
372            self.blocks_processed = 0;
373
374            return Ok(true);
375        }
376    }
377
378    fn parse_index_and_footer(&mut self) -> Result<()> {
379        let index = Index::parse(&mut self.reader)?;
380
381        if index.number_of_records != self.blocks_processed {
382            return Err(error_invalid_data(
383                "number of blocks processed doesn't match index records",
384            ));
385        }
386
387        let stream_footer = StreamFooter::parse(&mut self.reader)?;
388
389        let header = self.stream_header.as_ref().expect("stream_header not set");
390
391        let header_flags = [0, header.check_type as u8];
392        if stream_footer.stream_flags != header_flags {
393            return Err(error_invalid_data(
394                "stream header and footer flags mismatch",
395            ));
396        }
397
398        Ok(())
399    }
400}
401
402impl<R: Read> Read for XzReader<R> {
403    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
404        if self.finished {
405            return Ok(0);
406        }
407
408        self.ensure_stream_header()?;
409
410        loop {
411            if self.checksum_calculator.is_some() {
412                let bytes_read = self.reader.read(buf)?;
413
414                if bytes_read > 0 {
415                    if let Some(ref mut calc) = self.checksum_calculator {
416                        calc.update(&buf[..bytes_read]);
417                    }
418
419                    return Ok(bytes_read);
420                } else {
421                    let reader = core::mem::replace(&mut self.reader, FilterReader::Dummy);
422                    let compressed_bytes = reader.bytes_read();
423                    self.reader = FilterReader::Counting(CountingReader::with_count(
424                        reader.into_inner(),
425                        compressed_bytes,
426                    ));
427
428                    self.consume_padding(compressed_bytes)?;
429                    self.verify_block_checksum()?;
430                }
431            } else {
432                // No current block, prepare the next one.
433                if !self.prepare_next_block()? {
434                    // No more blocks, we're done.
435                    return Ok(0);
436                }
437            }
438        }
439    }
440}
441
442enum StreamFilter {
443    Delta(Box<Delta>),
444    Bcj(BcjFilter),
445}
446
447impl StreamFilter {
448    fn from_filter_type(ft: FilterType, property: u32) -> Option<Self> {
449        match ft {
450            FilterType::Delta => Some(StreamFilter::Delta(Box::new(Delta::new(property as usize)))),
451            FilterType::BcjX86 => Some(StreamFilter::Bcj(BcjFilter::new_x86(
452                property as usize,
453                false,
454            ))),
455            FilterType::BcjArm => Some(StreamFilter::Bcj(BcjFilter::new_arm(
456                property as usize,
457                false,
458            ))),
459            FilterType::BcjArm64 => Some(StreamFilter::Bcj(BcjFilter::new_arm64(
460                property as usize,
461                false,
462            ))),
463            FilterType::BcjArmThumb => Some(StreamFilter::Bcj(BcjFilter::new_arm_thumb(
464                property as usize,
465                false,
466            ))),
467            FilterType::BcjPpc => Some(StreamFilter::Bcj(BcjFilter::new_power_pc(
468                property as usize,
469                false,
470            ))),
471            FilterType::BcjSparc => Some(StreamFilter::Bcj(BcjFilter::new_sparc(
472                property as usize,
473                false,
474            ))),
475            FilterType::BcjIa64 => Some(StreamFilter::Bcj(BcjFilter::new_ia64(
476                property as usize,
477                false,
478            ))),
479            FilterType::BcjRiscv => Some(StreamFilter::Bcj(BcjFilter::new_riscv(
480                property as usize,
481                false,
482            ))),
483            FilterType::Lzma2 => None,
484        }
485    }
486
487    fn apply_decode(&mut self, buf: &mut [u8]) -> usize {
488        match self {
489            StreamFilter::Delta(d) => {
490                d.decode(buf);
491                buf.len()
492            }
493            StreamFilter::Bcj(b) => b.code(buf),
494        }
495    }
496}
497
498#[derive(Clone, Copy)]
499enum XzStreamState {
500    StreamHeader,
501    BlockHeaderSize,
502    BlockHeaderBody { header_size: usize },
503    Lzma2Data,
504    BlockPadding,
505    BlockChecksum { remaining: usize },
506    IndexCount,
507    IndexRecordUnpadded { remaining: u64 },
508    IndexRecordUncompressed { remaining: u64 },
509    IndexPaddingCrc,
510    StreamFooter,
511    InterStreamPadding,
512    Finished,
513}
514
515/// Sans-I/O XZ stream decoder.
516///
517/// Implements a buffer-pair API: call `process()` repeatedly with input/output
518/// buffers until `Status::StreamEnd` is returned.
519///
520/// # Limitations
521///
522/// A block may contain at most one non-LZMA2 filter (a single BCJ or Delta
523/// filter) preceding the terminating LZMA2 filter. Streams that chain multiple
524/// non-LZMA2 filters in a block are rejected with an error. Use the blocking
525/// [`XzReader`] to decode those.
526pub struct XzStream {
527    state: XzStreamState,
528    accum: Vec<u8>,
529    accum_needed: usize,
530    lzma2: Option<Lzma2Stream>,
531    checksum: Option<ChecksumCalculator>,
532    check_type: Option<CheckType>,
533    block_count: usize,
534    block_header_size: u64,
535    block_compressed_size: u64,
536    block_uncompressed_size: u64,
537    index_records: Vec<IndexRecord>,
538    index_crc: Crc32,
539    index_size: usize,
540    allow_multiple_streams: bool,
541    total_in: u64,
542    total_out: u64,
543    filter: Option<StreamFilter>,
544    filter_buf: Vec<u8>,
545    filter_pos: usize,
546    filter_unfiltered: usize,
547}
548
549impl XzStream {
550    /// Create a new XZ stream decoder.
551    ///
552    /// If `allow_multiple_streams` is true, concatenated XZ streams are decoded
553    /// sequentially until EOF.
554    pub fn new(allow_multiple_streams: bool) -> Self {
555        Self {
556            state: XzStreamState::StreamHeader,
557            accum: Vec::with_capacity(1024),
558            accum_needed: 12,
559            lzma2: None,
560            checksum: None,
561            check_type: None,
562            block_count: 0,
563            block_header_size: 0,
564            block_compressed_size: 0,
565            block_uncompressed_size: 0,
566            index_records: Vec::new(),
567            index_crc: Crc32::new(),
568            index_size: 0,
569            allow_multiple_streams,
570            total_in: 0,
571            total_out: 0,
572            filter: None,
573            filter_buf: Vec::new(),
574            filter_pos: 0,
575            filter_unfiltered: 0,
576        }
577    }
578
579    /// Total bytes consumed from input across all `process()` calls.
580    pub fn total_in(&self) -> u64 {
581        self.total_in
582    }
583
584    /// Total bytes produced to output across all `process()` calls.
585    pub fn total_out(&self) -> u64 {
586        self.total_out
587    }
588
589    /// The integrity check type used by the current stream.
590    ///
591    /// This is read from the stream header during decoding.
592    /// Before the header has been parsed, returns [`CheckType::None`].
593    pub fn check_type(&self) -> Option<CheckType> {
594        self.check_type
595    }
596
597    /// Process available data from `input` into `output`.
598    ///
599    /// Returns how many bytes were consumed/produced and the stream status.
600    /// Call repeatedly until `Status::StreamEnd` is returned.
601    pub fn process(
602        &mut self,
603        input: &[u8],
604        output: &mut [u8],
605        action: Action,
606    ) -> Result<StreamResult> {
607        let mut in_pos = 0;
608        let mut out_pos = 0;
609
610        loop {
611            match &self.state {
612                XzStreamState::Finished => {
613                    return Ok(StreamResult {
614                        bytes_consumed: in_pos,
615                        bytes_produced: out_pos,
616                        status: Status::StreamEnd,
617                    });
618                }
619
620                XzStreamState::Lzma2Data => {
621                    if self.filter.is_some() {
622                        if self.process_lzma2_filtered(
623                            input,
624                            output,
625                            action,
626                            &mut in_pos,
627                            &mut out_pos,
628                        )? == 0
629                        {
630                            return Ok(StreamResult {
631                                bytes_consumed: in_pos,
632                                bytes_produced: out_pos,
633                                status: Status::Ok,
634                            });
635                        }
636                    } else if let Some(result) = self.process_lzma2_unfiltered(
637                        input,
638                        output,
639                        action,
640                        &mut in_pos,
641                        &mut out_pos,
642                    )? {
643                        return Ok(result);
644                    }
645                }
646
647                _ => {
648                    if self.accum.len() < self.accum_needed {
649                        if in_pos >= input.len() {
650                            if action == Action::Finish {
651                                if matches!(self.state, XzStreamState::InterStreamPadding) {
652                                    if !self.accum.is_empty() {
653                                        return Err(error_invalid_data(
654                                            "inter-stream padding not a multiple of 4 bytes",
655                                        ));
656                                    }
657                                    self.state = XzStreamState::Finished;
658                                    continue;
659                                }
660                                return Err(error_invalid_data("unexpected end of XZ stream"));
661                            }
662                            return Ok(StreamResult {
663                                bytes_consumed: in_pos,
664                                bytes_produced: out_pos,
665                                status: Status::Ok,
666                            });
667                        }
668                        let available = &input[in_pos..];
669                        let need = self.accum_needed - self.accum.len();
670                        let to_copy = need.min(available.len());
671                        self.accum.extend_from_slice(&available[..to_copy]);
672                        in_pos += to_copy;
673                        self.total_in += to_copy as u64;
674                        if self.accum.len() < self.accum_needed {
675                            return Ok(StreamResult {
676                                bytes_consumed: in_pos,
677                                bytes_produced: out_pos,
678                                status: Status::Ok,
679                            });
680                        }
681                    }
682
683                    self.process_accumulated()?;
684                }
685            }
686        }
687    }
688
689    fn process_lzma2_unfiltered(
690        &mut self,
691        input: &[u8],
692        output: &mut [u8],
693        action: Action,
694        in_pos: &mut usize,
695        out_pos: &mut usize,
696    ) -> Result<Option<StreamResult>> {
697        let lzma2 = self.lzma2.as_mut().unwrap();
698
699        if lzma2.is_draining() {
700            if *out_pos >= output.len() {
701                return Ok(Some(StreamResult {
702                    bytes_consumed: *in_pos,
703                    bytes_produced: *out_pos,
704                    status: Status::Ok,
705                }));
706            }
707            let prev_out = *out_pos;
708            lzma2.drain_with_filter(output, out_pos);
709            let drained = *out_pos - prev_out;
710            if drained > 0 {
711                self.total_out += drained as u64;
712                if let Some(cs) = self.checksum.as_mut() {
713                    cs.update(&output[prev_out..*out_pos]);
714                }
715            }
716            if lzma2.has_output() {
717                return Ok(Some(StreamResult {
718                    bytes_consumed: *in_pos,
719                    bytes_produced: *out_pos,
720                    status: Status::Ok,
721                }));
722            }
723            if lzma2.is_finished() {
724                self.finish_lzma2_block()?;
725            }
726            return Ok(None);
727        }
728
729        let result = lzma2.process(&input[*in_pos..], &mut output[*out_pos..], action)?;
730        *in_pos += result.bytes_consumed;
731        self.total_in += result.bytes_consumed as u64;
732
733        if result.bytes_produced > 0 {
734            if let Some(cs) = self.checksum.as_mut() {
735                cs.update(&output[*out_pos..*out_pos + result.bytes_produced]);
736            }
737            *out_pos += result.bytes_produced;
738            self.total_out += result.bytes_produced as u64;
739        }
740
741        if result.status == Status::StreamEnd {
742            self.finish_lzma2_block()?;
743        } else if *in_pos >= input.len() || *out_pos >= output.len() {
744            return Ok(Some(StreamResult {
745                bytes_consumed: *in_pos,
746                bytes_produced: *out_pos,
747                status: Status::Ok,
748            }));
749        }
750        Ok(None)
751    }
752
753    fn process_lzma2_filtered(
754        &mut self,
755        input: &[u8],
756        output: &mut [u8],
757        action: Action,
758        in_pos: &mut usize,
759        out_pos: &mut usize,
760    ) -> Result<usize> {
761        if *out_pos >= output.len() {
762            return Ok(0);
763        }
764
765        if self.filter_pos < self.filter_buf.len() - self.filter_unfiltered {
766            return self.emit_filtered_output(output, out_pos);
767        }
768
769        if self.lzma2.as_ref().unwrap().is_draining() {
770            self.drain_and_filter_lzma2();
771            let lzma2 = self.lzma2.as_ref().unwrap();
772            if !lzma2.has_output() && lzma2.is_finished() {
773                self.flush_filter_pending();
774            }
775            return Ok(1);
776        }
777
778        let result = self
779            .lzma2
780            .as_mut()
781            .unwrap()
782            .process(&input[*in_pos..], &mut [], action)?;
783        *in_pos += result.bytes_consumed;
784        self.total_in += result.bytes_consumed as u64;
785
786        if result.status == Status::StreamEnd {
787            return self.try_complete_filtered_block();
788        }
789
790        if *in_pos >= input.len() && !self.lzma2.as_ref().unwrap().is_draining() {
791            return Ok(0);
792        }
793        Ok(1)
794    }
795
796    fn emit_filtered_output(&mut self, output: &mut [u8], out_pos: &mut usize) -> Result<usize> {
797        let ready_end = self.filter_buf.len() - self.filter_unfiltered;
798        let available = ready_end - self.filter_pos;
799        let space = output.len() - *out_pos;
800        let n = available.min(space);
801        output[*out_pos..*out_pos + n]
802            .copy_from_slice(&self.filter_buf[self.filter_pos..self.filter_pos + n]);
803        if let Some(cs) = self.checksum.as_mut() {
804            cs.update(&output[*out_pos..*out_pos + n]);
805        }
806        *out_pos += n;
807        self.total_out += n as u64;
808        self.filter_pos += n;
809
810        if self.filter_pos < ready_end {
811            return Ok(0);
812        }
813
814        self.compact_filter_buf();
815
816        let is_finished = {
817            let lzma2 = self.lzma2.as_ref().unwrap();
818            !lzma2.has_output() && lzma2.is_finished()
819        };
820        if is_finished {
821            return self.try_complete_filtered_block();
822        }
823        Ok(1)
824    }
825
826    fn drain_and_filter_lzma2(&mut self) -> usize {
827        let lzma2 = self.lzma2.as_mut().unwrap();
828        let prev_len = self.filter_buf.len();
829        lzma2.drain_to_buf(&mut self.filter_buf, 4096);
830        let new_bytes = self.filter_buf.len() - prev_len;
831        if new_bytes > 0 {
832            let filter_start = prev_len - self.filter_unfiltered;
833            let filter_slice = &mut self.filter_buf[filter_start..];
834            let filtered = self.filter.as_mut().unwrap().apply_decode(filter_slice);
835            self.filter_unfiltered = filter_slice.len() - filtered;
836        }
837        new_bytes
838    }
839
840    fn compact_filter_buf(&mut self) {
841        if self.filter_unfiltered > 0 {
842            let tail_start = self.filter_buf.len() - self.filter_unfiltered;
843            let pending: Vec<u8> = self.filter_buf[tail_start..].to_vec();
844            self.filter_buf.clear();
845            self.filter_buf.extend_from_slice(&pending);
846        } else {
847            self.filter_buf.clear();
848        }
849        self.filter_pos = 0;
850        self.filter_unfiltered = self.filter_buf.len();
851    }
852
853    fn try_complete_filtered_block(&mut self) -> Result<usize> {
854        self.flush_filter_pending();
855        if self.filter_pos < self.filter_buf.len() - self.filter_unfiltered {
856            return Ok(1);
857        }
858        self.filter.take();
859        self.finish_lzma2_block()?;
860        Ok(1)
861    }
862
863    fn flush_filter_pending(&mut self) {
864        self.filter_unfiltered = 0;
865    }
866
867    fn finish_lzma2_block(&mut self) -> Result<()> {
868        let lzma2 = self.lzma2.as_ref().unwrap();
869        self.block_compressed_size = lzma2.total_in();
870        self.block_uncompressed_size = lzma2.total_out();
871
872        let pad_needed = ((4 - (self.block_compressed_size % 4)) % 4) as usize;
873        if pad_needed > 0 {
874            self.state = XzStreamState::BlockPadding;
875            self.accum.clear();
876            self.accum_needed = pad_needed;
877        } else {
878            let check_size = self.check_type.map(|c| c.checksum_size()).unwrap_or(0) as usize;
879            if check_size > 0 {
880                self.state = XzStreamState::BlockChecksum {
881                    remaining: check_size,
882                };
883                self.accum.clear();
884                self.accum_needed = check_size;
885            } else {
886                self.push_index_record();
887                self.state = XzStreamState::BlockHeaderSize;
888                self.accum.clear();
889                self.accum_needed = 1;
890            }
891        }
892        Ok(())
893    }
894
895    fn push_index_record(&mut self) {
896        let check_size = self.check_type.map(|c| c.checksum_size()).unwrap_or(0);
897        self.checksum.take();
898        self.index_records.push(IndexRecord {
899            unpadded_size: self.block_header_size + self.block_compressed_size + check_size,
900            uncompressed_size: self.block_uncompressed_size,
901        });
902    }
903
904    fn process_accumulated(&mut self) -> Result<()> {
905        match self.state {
906            XzStreamState::StreamHeader => self.process_stream_header(),
907            XzStreamState::BlockHeaderSize => self.process_block_header_size(),
908            XzStreamState::BlockHeaderBody { header_size } => {
909                self.process_block_header_body(header_size)
910            }
911            XzStreamState::BlockPadding => self.process_block_padding(),
912            XzStreamState::BlockChecksum { remaining } => self.process_block_checksum(remaining),
913            XzStreamState::IndexCount => self.process_index_count(),
914            XzStreamState::IndexRecordUnpadded { remaining } => {
915                self.process_index_record_unpadded(remaining)
916            }
917            XzStreamState::IndexRecordUncompressed { remaining } => {
918                self.process_index_record_uncompressed(remaining)
919            }
920            XzStreamState::IndexPaddingCrc => self.process_index_padding_crc(),
921            XzStreamState::StreamFooter => self.process_stream_footer(),
922            XzStreamState::InterStreamPadding => self.process_inter_stream_padding(),
923            _ => Ok(()),
924        }
925    }
926
927    fn process_stream_header(&mut self) -> Result<()> {
928        let data = &self.accum;
929        if data[..6] != XZ_MAGIC {
930            return Err(error_invalid_data("invalid XZ magic bytes"));
931        }
932        if data[6] != 0 {
933            return Err(error_invalid_data("invalid XZ stream flags"));
934        }
935        let check_type = CheckType::from_byte(data[7])?;
936        let expected_crc = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
937        if expected_crc != Crc32::checksum(&data[6..8]) {
938            return Err(error_invalid_data("XZ stream header CRC32 mismatch"));
939        }
940        self.check_type = Some(check_type);
941        self.index_records.clear();
942        self.block_count = 0;
943        self.state = XzStreamState::BlockHeaderSize;
944        self.accum.clear();
945        self.accum_needed = 1;
946        Ok(())
947    }
948
949    fn process_block_header_size(&mut self) -> Result<()> {
950        let byte = self.accum[0];
951        if byte == 0x00 {
952            self.state = XzStreamState::IndexCount;
953            self.index_crc = Crc32::new();
954            self.index_crc.update(&[0x00]);
955            self.index_size = 0;
956            self.accum.clear();
957            self.accum_needed = 1;
958        } else {
959            let header_size = (byte as usize + 1) * 4;
960            self.state = XzStreamState::BlockHeaderBody { header_size };
961            self.accum_needed = header_size;
962        }
963        Ok(())
964    }
965
966    fn process_block_header_body(&mut self, header_size: usize) -> Result<()> {
967        let data = &self.accum[..header_size];
968
969        let crc_offset = header_size - 4;
970        let expected_crc = u32::from_le_bytes([
971            data[crc_offset],
972            data[crc_offset + 1],
973            data[crc_offset + 2],
974            data[crc_offset + 3],
975        ]);
976        let actual_crc = Crc32::checksum(&data[..crc_offset]);
977        if expected_crc != actual_crc {
978            return Err(error_invalid_data("block header CRC32 mismatch"));
979        }
980
981        let (filters, properties, _) = BlockHeader::parse_from_slice(data)?;
982
983        let mut lzma2_dict_size = 0u32;
984        let mut found_lzma2 = false;
985        let mut pre_filter: Option<StreamFilter> = None;
986        for i in 0..4 {
987            if let Some(ft) = filters[i] {
988                if ft == FilterType::Lzma2 {
989                    lzma2_dict_size = properties[i];
990                    found_lzma2 = true;
991                } else if let Some(f) = StreamFilter::from_filter_type(ft, properties[i]) {
992                    // TODO: Support multiple filters for sans-I/O API.
993                    if pre_filter.is_some() {
994                        return Err(error_invalid_data(
995                            "multiple non-LZMA2 filters not supported yet for stream API",
996                        ));
997                    }
998                    pre_filter = Some(f);
999                }
1000            }
1001        }
1002        if !found_lzma2 {
1003            return Err(error_invalid_data("no LZMA2 filter in block"));
1004        }
1005
1006        self.lzma2 = Some(Lzma2Stream::new(lzma2_dict_size));
1007        if let Some(ct) = self.check_type {
1008            self.checksum = Some(ChecksumCalculator::new(ct));
1009        }
1010        self.filter = pre_filter;
1011        self.filter_buf.clear();
1012        self.filter_pos = 0;
1013        self.filter_unfiltered = 0;
1014        self.block_count += 1;
1015        self.block_header_size = header_size as u64;
1016        self.block_compressed_size = 0;
1017        self.block_uncompressed_size = 0;
1018
1019        self.state = XzStreamState::Lzma2Data;
1020        self.accum.clear();
1021        Ok(())
1022    }
1023
1024    fn process_block_padding(&mut self) -> Result<()> {
1025        for &b in self.accum.iter() {
1026            if b != 0 {
1027                return Err(error_invalid_data("non-zero block padding"));
1028            }
1029        }
1030
1031        let check_size = self.check_type.map(|c| c.checksum_size()).unwrap_or(0) as usize;
1032        if check_size > 0 {
1033            self.state = XzStreamState::BlockChecksum {
1034                remaining: check_size,
1035            };
1036            self.accum.clear();
1037            self.accum_needed = check_size;
1038        } else {
1039            self.push_index_record();
1040            self.state = XzStreamState::BlockHeaderSize;
1041            self.accum.clear();
1042            self.accum_needed = 1;
1043        }
1044        Ok(())
1045    }
1046
1047    fn process_block_checksum(&mut self, remaining: usize) -> Result<()> {
1048        if self.accum.len() < remaining {
1049            self.accum_needed = remaining;
1050            return Ok(());
1051        }
1052        if let Some(checksum) = self.checksum.take() {
1053            if !checksum.verify(&self.accum[..remaining]) {
1054                return Err(error_invalid_data("block checksum mismatch"));
1055            }
1056        }
1057        self.push_index_record();
1058        self.state = XzStreamState::BlockHeaderSize;
1059        self.accum.clear();
1060        self.accum_needed = 1;
1061        Ok(())
1062    }
1063
1064    fn process_index_count(&mut self) -> Result<()> {
1065        if !has_complete_vli(&self.accum)? {
1066            self.accum_needed = self.accum.len() + 1;
1067            return Ok(());
1068        }
1069        let num_records = parse_multibyte_integer(&self.accum)?;
1070        let vli_size = count_multibyte_integer_size(&self.accum);
1071        self.index_crc.update(&self.accum[..vli_size]);
1072        self.index_size += vli_size;
1073
1074        if num_records != self.block_count as u64 {
1075            return Err(error_invalid_data(
1076                "index record count does not match number of blocks",
1077            ));
1078        }
1079
1080        self.accum.clear();
1081        if num_records > 0 {
1082            self.state = XzStreamState::IndexRecordUnpadded {
1083                remaining: num_records,
1084            };
1085            self.accum_needed = 1;
1086        } else {
1087            self.state = XzStreamState::IndexPaddingCrc;
1088            let pad_needed = (4 - ((1 + self.index_size) % 4)) % 4;
1089            self.accum_needed = pad_needed + 4;
1090        }
1091        Ok(())
1092    }
1093
1094    fn process_index_record_unpadded(&mut self, remaining: u64) -> Result<()> {
1095        if !has_complete_vli(&self.accum)? {
1096            self.accum_needed = self.accum.len() + 1;
1097            return Ok(());
1098        }
1099        let unpadded = parse_multibyte_integer(&self.accum)?;
1100        let vli_size = count_multibyte_integer_size(&self.accum);
1101        self.index_crc.update(&self.accum[..vli_size]);
1102        self.index_size += vli_size;
1103
1104        let idx = self.block_count - remaining as usize;
1105        if self.index_records[idx].unpadded_size != unpadded {
1106            return Err(error_invalid_data("index unpadded size mismatch"));
1107        }
1108
1109        self.accum.clear();
1110        self.state = XzStreamState::IndexRecordUncompressed { remaining };
1111        self.accum_needed = 1;
1112        Ok(())
1113    }
1114
1115    fn process_index_record_uncompressed(&mut self, remaining: u64) -> Result<()> {
1116        if !has_complete_vli(&self.accum)? {
1117            self.accum_needed = self.accum.len() + 1;
1118            return Ok(());
1119        }
1120        let uncompressed = parse_multibyte_integer(&self.accum)?;
1121        let vli_size = count_multibyte_integer_size(&self.accum);
1122        self.index_crc.update(&self.accum[..vli_size]);
1123        self.index_size += vli_size;
1124
1125        let idx = self.block_count - remaining as usize;
1126        if self.index_records[idx].uncompressed_size != uncompressed {
1127            return Err(error_invalid_data("index uncompressed size mismatch"));
1128        }
1129
1130        self.accum.clear();
1131        let remaining = remaining - 1;
1132        if remaining > 0 {
1133            self.state = XzStreamState::IndexRecordUnpadded { remaining };
1134            self.accum_needed = 1;
1135        } else {
1136            self.state = XzStreamState::IndexPaddingCrc;
1137            let pad_needed = (4 - ((1 + self.index_size) % 4)) % 4;
1138            self.accum_needed = pad_needed + 4;
1139        }
1140        Ok(())
1141    }
1142
1143    fn process_index_padding_crc(&mut self) -> Result<()> {
1144        let pad_needed = (4 - ((1 + self.index_size) % 4)) % 4;
1145        let total_needed = pad_needed + 4;
1146        if self.accum.len() < total_needed {
1147            self.accum_needed = total_needed;
1148            return Ok(());
1149        }
1150
1151        for &b in &self.accum[..pad_needed] {
1152            if b != 0 {
1153                return Err(error_invalid_data("non-zero index padding"));
1154            }
1155        }
1156        self.index_crc.update(&self.accum[..pad_needed]);
1157
1158        let expected_crc = u32::from_le_bytes([
1159            self.accum[pad_needed],
1160            self.accum[pad_needed + 1],
1161            self.accum[pad_needed + 2],
1162            self.accum[pad_needed + 3],
1163        ]);
1164
1165        let actual_crc = core::mem::replace(&mut self.index_crc, Crc32::new()).finalize();
1166        if actual_crc != expected_crc {
1167            return Err(error_invalid_data("index CRC32 mismatch"));
1168        }
1169
1170        self.accum.clear();
1171        self.accum_needed = 12;
1172        self.state = XzStreamState::StreamFooter;
1173        Ok(())
1174    }
1175
1176    fn process_stream_footer(&mut self) -> Result<()> {
1177        let data = &self.accum;
1178        if data.len() < 12 {
1179            self.accum_needed = 12;
1180            return Ok(());
1181        }
1182
1183        let expected_crc = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1184        let actual_crc = Crc32::checksum(&data[4..10]);
1185        if expected_crc != actual_crc {
1186            return Err(error_invalid_data("stream footer CRC32 mismatch"));
1187        }
1188        if data[10..12] != XZ_FOOTER_MAGIC {
1189            return Err(error_invalid_data("invalid XZ footer magic"));
1190        }
1191        if data[8] != 0 {
1192            return Err(error_invalid_data(
1193                "reserved stream footer flags byte is non-zero",
1194            ));
1195        }
1196        let footer_check_type = CheckType::from_byte(data[9])?;
1197        if Some(footer_check_type) != self.check_type {
1198            return Err(error_invalid_data("stream footer flags don't match header"));
1199        }
1200
1201        if self.allow_multiple_streams {
1202            self.state = XzStreamState::InterStreamPadding;
1203            self.accum.clear();
1204            self.accum_needed = 4;
1205        } else {
1206            self.state = XzStreamState::Finished;
1207        }
1208        Ok(())
1209    }
1210
1211    fn process_inter_stream_padding(&mut self) -> Result<()> {
1212        if self.accum.len() < 4 {
1213            self.accum_needed = 4;
1214            return Ok(());
1215        }
1216        if self.accum[..4] == [0, 0, 0, 0] {
1217            self.accum.clear();
1218            self.accum_needed = 4;
1219        } else if self.accum[..6.min(self.accum.len())] == XZ_MAGIC[..self.accum.len().min(6)] {
1220            if self.accum.len() >= 6 && self.accum[..6] == XZ_MAGIC {
1221                self.state = XzStreamState::StreamHeader;
1222                self.accum_needed = 12;
1223            } else {
1224                self.accum_needed = 12;
1225            }
1226        } else {
1227            return Err(error_invalid_data("invalid inter-stream padding"));
1228        }
1229        Ok(())
1230    }
1231}
1232
1233fn has_complete_vli(data: &[u8]) -> Result<bool> {
1234    if data.len() > 9 {
1235        return Err(error_invalid_data("XZ multibyte integer too long"));
1236    }
1237    for &byte in data {
1238        if (byte & 0x80) == 0 {
1239            return Ok(true);
1240        }
1241    }
1242    Ok(false)
1243}