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
169pub 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 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 pub fn into_inner(self) -> R {
196 self.reader.into_inner()
197 }
198
199 pub fn inner(&self) -> &R {
201 self.reader.inner()
202 }
203
204 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 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 => { }
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 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 return Ok(false);
328 }
329
330 let byte = byte_buffer[0];
331
332 if byte == 0 {
333 padding_bytes += 1;
335 continue;
336 }
337
338 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 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 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 if !self.prepare_next_block()? {
434 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
515pub 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 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 pub fn total_in(&self) -> u64 {
581 self.total_in
582 }
583
584 pub fn total_out(&self) -> u64 {
586 self.total_out
587 }
588
589 pub fn check_type(&self) -> Option<CheckType> {
594 self.check_type
595 }
596
597 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 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}