1mod reader;
4#[cfg(feature = "std")]
5mod reader_mt;
6#[cfg(feature = "encoder")]
7mod writer;
8#[cfg(all(feature = "encoder", feature = "std"))]
9mod writer_mt;
10
11use alloc::{boxed::Box, vec, vec::Vec};
12#[cfg(feature = "std")]
13use std::io::{self, Seek, SeekFrom};
14
15pub use reader::{XzReader, XzStream};
16#[cfg(feature = "std")]
17pub use reader_mt::XzReaderMt;
18use sha2::Digest;
19#[cfg(feature = "encoder")]
20pub use writer::{XzOptions, XzWriter};
21#[cfg(all(feature = "encoder", feature = "std"))]
22pub use writer_mt::XzWriterMt;
23
24use crate::{
25 ByteReader, Read,
26 crc::{Crc32, Crc64},
27 error_invalid_data, error_invalid_input,
28};
29#[cfg(feature = "encoder")]
30use crate::{ByteWriter, Write};
31#[cfg(feature = "std")]
32use crate::{
33 Lzma2Reader,
34 filter::{bcj::BcjReader, delta::DeltaReader},
35};
36
37const XZ_MAGIC: [u8; 6] = [0xFD, b'7', b'z', b'X', b'Z', 0x00];
38
39const XZ_FOOTER_MAGIC: [u8; 2] = *b"YZ";
40
41#[derive(Debug, Clone)]
42struct IndexRecord {
43 unpadded_size: u64,
44 uncompressed_size: u64,
45}
46
47#[derive(Debug)]
48struct Index {
49 pub number_of_records: u64,
50 pub records: Vec<IndexRecord>,
51}
52
53#[derive(Debug)]
54struct StreamHeader {
55 pub check_type: CheckType,
56}
57
58#[derive(Debug)]
59struct StreamFooter {
60 pub backward_size: u32,
61 pub stream_flags: [u8; 2],
62}
63
64#[derive(Debug)]
65struct BlockHeader {
66 header_size: usize,
67 compressed_size: Option<u64>,
68 uncompressed_size: Option<u64>,
69 filters: [Option<FilterType>; 4],
70 properties: [u32; 4],
71}
72
73#[derive(Debug, Clone)]
74struct Block {
75 start_pos: u64,
76 unpadded_size: u64,
77 uncompressed_size: u64,
78}
79
80#[derive(Debug, Clone)]
82pub struct FilterConfig {
83 pub filter_type: FilterType,
85 pub property: u32,
87}
88
89impl FilterConfig {
90 pub fn new_delta(distance: u32) -> Self {
92 Self {
93 filter_type: FilterType::Delta,
94 property: distance,
95 }
96 }
97
98 pub fn new_bcj_x86(start_pos: u32) -> Self {
100 Self {
101 filter_type: FilterType::BcjX86,
102 property: start_pos,
103 }
104 }
105
106 pub fn new_bcj_arm(start_pos: u32) -> Self {
108 Self {
109 filter_type: FilterType::BcjArm,
110 property: start_pos,
111 }
112 }
113
114 pub fn new_bcj_arm_thumb(start_pos: u32) -> Self {
116 Self {
117 filter_type: FilterType::BcjArmThumb,
118 property: start_pos,
119 }
120 }
121
122 pub fn new_bcj_arm64(start_pos: u32) -> Self {
124 Self {
125 filter_type: FilterType::BcjArm64,
126 property: start_pos,
127 }
128 }
129
130 pub fn new_bcj_ia64(start_pos: u32) -> Self {
132 Self {
133 filter_type: FilterType::BcjIa64,
134 property: start_pos,
135 }
136 }
137
138 pub fn new_bcj_ppc(start_pos: u32) -> Self {
140 Self {
141 filter_type: FilterType::BcjPpc,
142 property: start_pos,
143 }
144 }
145
146 pub fn new_bcj_sparc(start_pos: u32) -> Self {
148 Self {
149 filter_type: FilterType::BcjSparc,
150 property: start_pos,
151 }
152 }
153
154 pub fn new_bcj_risc_v(start_pos: u32) -> Self {
156 Self {
157 filter_type: FilterType::BcjRiscv,
158 property: start_pos,
159 }
160 }
161}
162
163#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
165pub enum CheckType {
166 None = 0x00,
168 Crc32 = 0x01,
170 #[default]
172 Crc64 = 0x04,
173 Sha256 = 0x0A,
175}
176
177impl CheckType {
178 fn from_byte(byte: u8) -> crate::Result<Self> {
179 match byte {
180 0x00 => Ok(CheckType::None),
181 0x01 => Ok(CheckType::Crc32),
182 0x04 => Ok(CheckType::Crc64),
183 0x0A => Ok(CheckType::Sha256),
184 _ => Err(error_invalid_data("unsupported XZ check type")),
185 }
186 }
187
188 #[cfg(any(feature = "encoder", feature = "xz"))]
189 fn checksum_size(self) -> u64 {
190 match self {
191 CheckType::None => 0,
192 CheckType::Crc32 => 4,
193 CheckType::Crc64 => 8,
194 CheckType::Sha256 => 32,
195 }
196 }
197}
198
199#[derive(Debug, Copy, Clone, Eq, PartialEq)]
201pub enum FilterType {
202 Delta,
204 BcjX86,
206 BcjPpc,
208 BcjIa64,
210 BcjArm,
212 BcjArmThumb,
214 BcjSparc,
216 BcjArm64,
218 BcjRiscv,
220 Lzma2,
222}
223
224impl TryFrom<u64> for FilterType {
225 type Error = ();
226
227 fn try_from(value: u64) -> Result<Self, Self::Error> {
228 match value {
229 0x03 => Ok(FilterType::Delta),
230 0x04 => Ok(FilterType::BcjX86),
231 0x05 => Ok(FilterType::BcjPpc),
232 0x06 => Ok(FilterType::BcjIa64),
233 0x07 => Ok(FilterType::BcjArm),
234 0x08 => Ok(FilterType::BcjArmThumb),
235 0x09 => Ok(FilterType::BcjSparc),
236 0x0A => Ok(FilterType::BcjArm64),
237 0x0B => Ok(FilterType::BcjRiscv),
238 0x21 => Ok(FilterType::Lzma2),
239 _ => Err(()),
240 }
241 }
242}
243
244fn parse_multibyte_integer(data: &[u8]) -> crate::Result<u64> {
246 let mut result = 0u64;
247 let mut shift = 0;
248
249 for &byte in data {
250 if shift >= 63 {
251 return Err(error_invalid_data("XZ multibyte integer too large"));
252 }
253
254 result |= ((byte & 0x7F) as u64) << shift;
255 shift += 7;
256
257 if (byte & 0x80) == 0 {
258 return Ok(result);
259 }
260 }
261
262 Err(error_invalid_data("incomplete XZ multibyte integer"))
263}
264
265fn count_multibyte_integer_size(data: &[u8]) -> usize {
267 for (i, &byte) in data.iter().enumerate() {
268 if (byte & 0x80) == 0 {
269 return i + 1;
270 }
271 }
272 data.len()
273}
274
275fn parse_multibyte_integer_from_reader<R: Read>(reader: &mut R) -> crate::Result<u64> {
276 let mut result = 0u64;
277 let mut shift = 0;
278
279 for _ in 0..9 {
280 let byte = reader.read_u8()?;
282
283 if shift >= 63 {
284 return Err(error_invalid_data("XZ multibyte integer too large"));
285 }
286
287 result |= ((byte & 0x7F) as u64) << shift;
288 shift += 7;
289
290 if (byte & 0x80) == 0 {
291 return Ok(result);
292 }
293 }
294
295 Err(error_invalid_data("XZ multibyte integer too long"))
296}
297
298fn count_multibyte_integer_size_for_value(mut value: u64) -> usize {
299 if value == 0 {
300 return 1;
301 }
302
303 let mut count = 0;
304 while value > 0 {
305 count += 1;
306 value >>= 7;
307 }
308 count
309}
310
311fn encode_multibyte_integer(mut value: u64, buf: &mut [u8]) -> crate::Result<usize> {
312 if value > (u64::MAX / 2) {
313 return Err(error_invalid_data("value too big to encode"));
314 }
315
316 let mut i = 0;
317 while value >= 0x80 && i < buf.len() {
318 buf[i] = (value as u8) | 0x80;
319 value >>= 7;
320 i += 1;
321 }
322
323 if i < buf.len() {
324 buf[i] = value as u8;
325 i += 1;
326 }
327
328 Ok(i)
329}
330
331impl BlockHeader {
332 fn parse<R: Read>(reader: &mut R) -> crate::Result<Option<Self>> {
333 let header_size_encoded = reader.read_u8()?;
334
335 if header_size_encoded == 0 {
336 return Ok(None);
338 }
339
340 let header_size = (header_size_encoded as usize + 1) * 4;
341 if !(8..=1024).contains(&header_size) {
342 return Err(error_invalid_data("invalid XZ block header size"));
343 }
344
345 let mut header_data = vec![0u8; header_size - 1];
347 reader.read_exact(&mut header_data)?;
348
349 let block_flags = header_data[0];
350 let num_filters = ((block_flags & 0x03) + 1) as usize;
351 let has_compressed_size = (block_flags & 0x40) != 0;
352 let has_uncompressed_size = (block_flags & 0x80) != 0;
353
354 let mut offset = 1;
355 let mut compressed_size = None;
356 let mut uncompressed_size = None;
357
358 if has_compressed_size {
360 if offset + 8 > header_data.len() {
361 return Err(error_invalid_data(
362 "XZ block header too short for compressed size",
363 ));
364 }
365 compressed_size = Some(parse_multibyte_integer(&header_data[offset..])?);
366 offset += count_multibyte_integer_size(&header_data[offset..]);
367 }
368
369 if has_uncompressed_size {
370 if offset >= header_data.len() {
371 return Err(error_invalid_data(
372 "XZ block header too short for uncompressed size",
373 ));
374 }
375 uncompressed_size = Some(parse_multibyte_integer(&header_data[offset..])?);
376 offset += count_multibyte_integer_size(&header_data[offset..]);
377 }
378
379 let mut filters = [None; 4];
380 let mut properties = [0; 4];
381
382 for i in 0..num_filters {
383 if offset >= header_data.len() {
384 return Err(error_invalid_data("XZ block header too short for filters"));
385 }
386
387 let filter_type =
388 FilterType::try_from(parse_multibyte_integer(&header_data[offset..])?)
389 .map_err(|_| error_invalid_input("unsupported filter type found"))?;
390
391 offset += count_multibyte_integer_size(&header_data[offset..]);
392
393 let property = match filter_type {
394 FilterType::Delta => {
395 if offset >= header_data.len() {
396 return Err(error_invalid_data(
397 "XZ block header too short for Delta properties",
398 ));
399 }
400
401 let props_size = parse_multibyte_integer(&header_data[offset..])?;
402 offset += count_multibyte_integer_size(&header_data[offset..]);
403
404 if props_size != 1 {
405 return Err(error_invalid_data("invalid Delta properties size"));
406 }
407
408 if offset >= header_data.len() {
409 return Err(error_invalid_data(
410 "XZ block header too short for Delta properties",
411 ));
412 }
413
414 let distance_prop = header_data[offset];
415 offset += 1;
416
417 (distance_prop as u32) + 1
419 }
420 FilterType::BcjX86
421 | FilterType::BcjPpc
422 | FilterType::BcjIa64
423 | FilterType::BcjArm
424 | FilterType::BcjArmThumb
425 | FilterType::BcjSparc
426 | FilterType::BcjArm64
427 | FilterType::BcjRiscv => {
428 if offset >= header_data.len() {
429 return Err(error_invalid_data(
430 "XZ block header too short for BCJ properties",
431 ));
432 }
433
434 let props_size = parse_multibyte_integer(&header_data[offset..])?;
435 offset += count_multibyte_integer_size(&header_data[offset..]);
436
437 match props_size {
438 0 => {
439 0
441 }
442 4 => {
443 if offset + 4 > header_data.len() {
445 return Err(error_invalid_data(
446 "XZ block header too short for BCJ start offset",
447 ));
448 }
449
450 let start_offset_value = u32::from_le_bytes([
451 header_data[offset],
452 header_data[offset + 1],
453 header_data[offset + 2],
454 header_data[offset + 3],
455 ]);
456 offset += 4;
457
458 let bcj_alignment = match filter_type {
460 FilterType::BcjX86 => 1,
461 FilterType::BcjPpc => 4,
462 FilterType::BcjIa64 => 16,
463 FilterType::BcjArm => 4,
464 FilterType::BcjArmThumb => 2,
465 FilterType::BcjSparc => 4,
466 FilterType::BcjArm64 => 4,
467 FilterType::BcjRiscv => 2,
468 _ => unreachable!(),
469 };
470
471 if start_offset_value % bcj_alignment != 0 {
472 return Err(error_invalid_data(
473 "BCJ start offset not aligned to filter requirements",
474 ));
475 }
476
477 start_offset_value
478 }
479 _ => {
480 return Err(error_invalid_data("invalid BCJ properties size"));
481 }
482 }
483 }
484 FilterType::Lzma2 => {
485 if offset >= header_data.len() {
486 return Err(error_invalid_data(
487 "XZ block header too short for LZMA2 properties",
488 ));
489 }
490
491 let props_size = parse_multibyte_integer(&header_data[offset..])?;
492 offset += count_multibyte_integer_size(&header_data[offset..]);
493
494 if props_size != 1 {
495 return Err(error_invalid_data("invalid LZMA2 properties size"));
496 }
497
498 if offset >= header_data.len() {
499 return Err(error_invalid_data(
500 "XZ block header too short for LZMA2 properties",
501 ));
502 }
503
504 let dict_size_prop = header_data[offset];
505 offset += 1;
506
507 if dict_size_prop > 40 {
508 return Err(error_invalid_data("invalid LZMA2 dictionary size"));
509 }
510
511 if dict_size_prop == 40 {
512 0xFFFFFFFF
513 } else {
514 let base = 2 | ((dict_size_prop & 1) as u32);
515 base << (dict_size_prop / 2 + 11)
516 }
517 }
518 };
519
520 filters[i] = Some(filter_type);
521 properties[i] = property;
522 }
523
524 if filters.iter().filter_map(|x| *x).next_back() != Some(FilterType::Lzma2) {
525 return Err(error_invalid_input(
526 "XZ block's last filter must be a LZMA2 filter",
527 ));
528 }
529
530 let expected_offset = header_size - 1 - 4; while offset < expected_offset {
534 if offset >= header_data.len() || header_data[offset] != 0 {
535 return Err(error_invalid_data("invalid XZ block header padding"));
536 }
537 offset += 1;
538 }
539
540 if offset + 4 != header_data.len() {
542 return Err(error_invalid_data("invalid XZ block header CRC32 position"));
543 }
544
545 let expected_crc = u32::from_le_bytes([
546 header_data[offset],
547 header_data[offset + 1],
548 header_data[offset + 2],
549 header_data[offset + 3],
550 ]);
551
552 let mut crc = Crc32::new();
554 crc.update(&[header_size_encoded]);
555 crc.update(&header_data[..offset]);
556
557 if expected_crc != crc.finalize() {
558 return Err(error_invalid_data("XZ block header CRC32 mismatch"));
559 }
560
561 Ok(Some(BlockHeader {
562 header_size,
563 compressed_size,
564 uncompressed_size,
565 filters,
566 properties,
567 }))
568 }
569
570 pub fn parse_from_slice(
571 block_data: &[u8],
572 ) -> crate::Result<([Option<FilterType>; 4], [u32; 4], usize)> {
573 if block_data.is_empty() {
574 return Err(error_invalid_data("Empty block data"));
575 }
576
577 let header_size_encoded = block_data[0];
578 if header_size_encoded == 0 {
579 return Err(error_invalid_data("Invalid block header size"));
580 }
581
582 let header_size = (header_size_encoded as usize + 1) * 4;
583 if header_size > block_data.len() {
584 return Err(error_invalid_data("Block data too short for header"));
585 }
586
587 let header_data = &block_data[1..header_size];
588 let block_flags = header_data[0];
589 let num_filters = ((block_flags & 0x03) + 1) as usize;
590 let has_compressed_size = (block_flags & 0x40) != 0;
591 let has_uncompressed_size = (block_flags & 0x80) != 0;
592
593 let mut offset = 1;
594
595 if has_compressed_size {
597 if offset >= header_data.len() {
598 return Err(error_invalid_data(
599 "Block header too short for compressed size",
600 ));
601 }
602 offset += count_multibyte_integer_size(&header_data[offset..]);
603 }
604
605 if has_uncompressed_size {
607 if offset >= header_data.len() {
608 return Err(error_invalid_data(
609 "Block header too short for uncompressed size",
610 ));
611 }
612 offset += count_multibyte_integer_size(&header_data[offset..]);
613 }
614
615 let mut filters = [None; 4];
616 let mut properties = [0; 4];
617
618 for i in 0..num_filters {
620 if offset >= header_data.len() {
621 return Err(error_invalid_data("Block header too short for filters"));
622 }
623
624 let filter_id = parse_multibyte_integer(&header_data[offset..])?;
625 let filter_type = FilterType::try_from(filter_id)
626 .map_err(|_| error_invalid_data("Unsupported filter type"))?;
627
628 offset += count_multibyte_integer_size(&header_data[offset..]);
629
630 let property = match filter_type {
631 FilterType::Delta => {
632 if offset >= header_data.len() {
633 return Err(error_invalid_data(
634 "Block header too short for Delta properties",
635 ));
636 }
637
638 let props_size = parse_multibyte_integer(&header_data[offset..])?;
639 offset += count_multibyte_integer_size(&header_data[offset..]);
640
641 if props_size != 1 {
642 return Err(error_invalid_data("Invalid Delta properties size"));
643 }
644
645 if offset >= header_data.len() {
646 return Err(error_invalid_data(
647 "Block header too short for Delta properties",
648 ));
649 }
650
651 let distance_prop = header_data[offset];
652 offset += 1;
653 (distance_prop as u32) + 1
654 }
655 FilterType::BcjX86
656 | FilterType::BcjPpc
657 | FilterType::BcjIa64
658 | FilterType::BcjArm
659 | FilterType::BcjArmThumb
660 | FilterType::BcjSparc
661 | FilterType::BcjArm64
662 | FilterType::BcjRiscv => {
663 if offset >= header_data.len() {
664 return Err(error_invalid_data(
665 "Block header too short for BCJ properties",
666 ));
667 }
668
669 let props_size = parse_multibyte_integer(&header_data[offset..])?;
670 offset += count_multibyte_integer_size(&header_data[offset..]);
671
672 match props_size {
673 0 => 0,
674 4 => {
675 if offset + 4 > header_data.len() {
676 return Err(error_invalid_data(
677 "Block header too short for BCJ start offset",
678 ));
679 }
680
681 let start_offset = u32::from_le_bytes([
682 header_data[offset],
683 header_data[offset + 1],
684 header_data[offset + 2],
685 header_data[offset + 3],
686 ]);
687 offset += 4;
688 start_offset
689 }
690 _ => return Err(error_invalid_data("Invalid BCJ properties size")),
691 }
692 }
693 FilterType::Lzma2 => {
694 if offset >= header_data.len() {
695 return Err(error_invalid_data(
696 "Block header too short for LZMA2 properties",
697 ));
698 }
699
700 let props_size = parse_multibyte_integer(&header_data[offset..])?;
701 offset += count_multibyte_integer_size(&header_data[offset..]);
702
703 if props_size != 1 {
704 return Err(error_invalid_data("Invalid LZMA2 properties size"));
705 }
706
707 if offset >= header_data.len() {
708 return Err(error_invalid_data(
709 "Block header too short for LZMA2 properties",
710 ));
711 }
712
713 let dict_size_prop = header_data[offset];
714 offset += 1;
715
716 if dict_size_prop > 40 {
717 return Err(error_invalid_data("Invalid LZMA2 dictionary size"));
718 }
719
720 if dict_size_prop == 40 {
721 0xFFFFFFFF
722 } else {
723 let base = 2 | ((dict_size_prop & 1) as u32);
724 base << (dict_size_prop / 2 + 11)
725 }
726 }
727 };
728
729 filters[i] = Some(filter_type);
730 properties[i] = property;
731 }
732
733 if filters.iter().filter_map(|x| *x).next_back() != Some(FilterType::Lzma2) {
734 return Err(error_invalid_data(
735 "XZ block's last filter must be a LZMA2 filter",
736 ));
737 }
738
739 Ok((filters, properties, header_size))
740 }
741}
742
743enum ChecksumCalculator {
745 None,
746 Crc32(Crc32),
747 Crc64(Crc64),
748 Sha256(sha2::Sha256),
749}
750
751impl ChecksumCalculator {
752 fn new(check_type: CheckType) -> Self {
753 match check_type {
754 CheckType::None => Self::None,
755 CheckType::Crc32 => Self::Crc32(Crc32::new()),
756 CheckType::Crc64 => Self::Crc64(Crc64::new()),
757 CheckType::Sha256 => Self::Sha256(sha2::Sha256::new()),
758 }
759 }
760
761 fn update(&mut self, data: &[u8]) {
762 match self {
763 ChecksumCalculator::None => {}
764 ChecksumCalculator::Crc32(crc) => {
765 crc.update(data);
766 }
767 ChecksumCalculator::Crc64(crc) => {
768 crc.update(data);
769 }
770 ChecksumCalculator::Sha256(sha) => {
771 sha.update(data);
772 }
773 }
774 }
775
776 fn verify(self, expected: &[u8]) -> bool {
777 match self {
778 ChecksumCalculator::None => true,
779 ChecksumCalculator::Crc32(crc) => {
780 if expected.len() != 4 {
781 return false;
782 }
783
784 let expected_crc =
785 u32::from_le_bytes([expected[0], expected[1], expected[2], expected[3]]);
786
787 let final_crc = crc.finalize();
788
789 final_crc == expected_crc
790 }
791 ChecksumCalculator::Crc64(crc) => {
792 if expected.len() != 8 {
793 return false;
794 }
795
796 let expected_crc = u64::from_le_bytes([
797 expected[0],
798 expected[1],
799 expected[2],
800 expected[3],
801 expected[4],
802 expected[5],
803 expected[6],
804 expected[7],
805 ]);
806
807 let final_crc = crc.finalize();
808
809 final_crc == expected_crc
810 }
811 ChecksumCalculator::Sha256(sha) => {
812 if expected.len() != 32 {
813 return false;
814 }
815
816 let final_sha = sha.finalize();
817
818 &final_sha[..32] == expected
819 }
820 }
821 }
822
823 #[cfg(feature = "encoder")]
824 fn finalize_to_bytes(self) -> Vec<u8> {
825 match self {
826 ChecksumCalculator::None => Vec::new(),
827 ChecksumCalculator::Crc32(crc) => crc.finalize().to_le_bytes().to_vec(),
828 ChecksumCalculator::Crc64(crc) => crc.finalize().to_le_bytes().to_vec(),
829 ChecksumCalculator::Sha256(sha) => sha.finalize().to_vec(),
830 }
831 }
832}
833
834impl StreamHeader {
835 fn parse<R: Read>(reader: &mut R) -> crate::Result<Self> {
836 let mut magic = [0u8; 6];
837 reader.read_exact(&mut magic)?;
838 if magic != XZ_MAGIC {
839 return Err(error_invalid_data("invalid XZ magic bytes"));
840 }
841
842 Self::parse_stream_header_flags_and_crc(reader)
843 }
844
845 pub(crate) fn parse_stream_header_flags_and_crc<R: Read>(
846 reader: &mut R,
847 ) -> crate::Result<Self> {
848 let mut flags = [0u8; 2];
849 reader.read_exact(&mut flags)?;
850
851 if flags[0] != 0 {
852 return Err(error_invalid_data("invalid XZ stream flags"));
853 }
854
855 let check_type = CheckType::from_byte(flags[1])?;
856
857 let expected_crc = reader.read_u32()?;
858
859 if expected_crc != Crc32::checksum(&flags) {
860 return Err(error_invalid_data("XZ stream header CRC32 mismatch"));
861 }
862
863 Ok(StreamHeader { check_type })
864 }
865}
866
867impl StreamFooter {
868 pub(crate) fn parse<R: Read>(reader: &mut R) -> crate::Result<Self> {
869 let expected_crc = reader.read_u32()?;
870
871 let backward_size = reader.read_u32()?;
872
873 let mut stream_flags = [0u8; 2];
874 reader.read_exact(&mut stream_flags)?;
875
876 let mut crc = Crc32::new();
878 crc.update(&backward_size.to_le_bytes());
879 crc.update(&stream_flags);
880
881 if expected_crc != crc.finalize() {
882 return Err(error_invalid_data("stream footer CRC32 mismatch"));
883 }
884
885 let mut footer_magic = [0u8; 2];
886 reader.read_exact(&mut footer_magic)?;
887 if footer_magic != XZ_FOOTER_MAGIC {
888 return Err(error_invalid_data("invalid XZ footer magic bytes"));
889 }
890
891 Ok(StreamFooter {
892 backward_size,
893 stream_flags,
894 })
895 }
896}
897
898impl Index {
899 pub(crate) fn parse<R: Read>(reader: &mut R) -> crate::Result<Index> {
900 let number_of_records = parse_multibyte_integer_from_reader(reader)?;
902 let mut records = Vec::new();
903 records.try_reserve_exact(number_of_records as usize)?;
904
905 for _ in 0..number_of_records {
906 let unpadded_size = parse_multibyte_integer_from_reader(reader)?;
907 let uncompressed_size = parse_multibyte_integer_from_reader(reader)?;
908
909 if unpadded_size == 0 {
910 return Err(error_invalid_data("invalid index record unpadded size"));
911 }
912
913 records.push(IndexRecord {
914 unpadded_size,
915 uncompressed_size,
916 });
917 }
918
919 let mut bytes_read = 1;
921 bytes_read += count_multibyte_integer_size_for_value(number_of_records);
922 for record in &records {
923 bytes_read += count_multibyte_integer_size_for_value(record.unpadded_size);
924 bytes_read += count_multibyte_integer_size_for_value(record.uncompressed_size);
925 }
926
927 let padding_needed = (4 - (bytes_read % 4)) % 4;
928
929 if padding_needed > 0 {
930 let mut padding_buf = [0u8; 3];
931 reader.read_exact(&mut padding_buf[..padding_needed])?;
932
933 if !padding_buf[..padding_needed].iter().all(|&b| b == 0) {
934 return Err(error_invalid_data("invalid index padding"));
935 }
936 }
937
938 let expected_crc = reader.read_u32()?;
939
940 let mut crc = Crc32::new();
942 crc.update(&[0]);
943
944 let mut temp_buf = [0u8; 10];
946 let size = encode_multibyte_integer(number_of_records, &mut temp_buf)?;
947 crc.update(&temp_buf[..size]);
948
949 for record in &records {
951 let size = encode_multibyte_integer(record.unpadded_size, &mut temp_buf)?;
952 crc.update(&temp_buf[..size]);
953 let size = encode_multibyte_integer(record.uncompressed_size, &mut temp_buf)?;
954 crc.update(&temp_buf[..size]);
955 }
956
957 update_crc_with_padding(&mut crc, padding_needed);
958
959 if expected_crc != crc.finalize() {
960 return Err(error_invalid_data("index CRC32 mismatch"));
961 }
962
963 Ok(Index {
964 number_of_records,
965 records,
966 })
967 }
968}
969
970#[cfg(feature = "encoder")]
971fn write_xz_stream_header<W: Write>(writer: &mut W, check_type: CheckType) -> crate::Result<()> {
972 writer.write_all(&XZ_MAGIC)?;
973
974 let stream_flags = [0u8, check_type as u8];
975 writer.write_all(&stream_flags)?;
976
977 let crc = Crc32::checksum(&stream_flags);
978 writer.write_u32(crc)?;
979
980 Ok(())
981}
982
983#[cfg(feature = "encoder")]
984fn encode_lzma2_dict_size(dict_size: u32) -> crate::Result<u8> {
985 if dict_size < 4096 {
986 return Err(error_invalid_input("LZMA2 dictionary size too small"));
987 }
988
989 if dict_size == 0xFFFFFFFF {
990 return Ok(40);
991 }
992
993 for prop in 0u8..40 {
995 let base = 2 | ((prop & 1) as u32);
996 let size = base << (prop / 2 + 11);
997
998 if size >= dict_size {
999 return Ok(prop);
1000 }
1001 }
1002
1003 Err(error_invalid_input("LZMA2 dictionary size too large"))
1004}
1005
1006fn update_crc_with_padding(crc: &mut Crc32, padding_needed: usize) {
1007 match padding_needed {
1008 1 => crc.update(&[0]),
1009 2 => crc.update(&[0, 0]),
1010 3 => crc.update(&[0, 0, 0]),
1011 _ => {}
1012 }
1013}
1014
1015#[cfg(feature = "std")]
1018fn scan_blocks<R: Read + Seek>(mut reader: R) -> io::Result<(R, Vec<Block>, CheckType)> {
1019 let stream_header = StreamHeader::parse(&mut reader)?;
1020 let check_type = stream_header.check_type;
1021
1022 let header_end_pos = reader.stream_position()?;
1023
1024 let file_size = reader.seek(SeekFrom::End(0))?;
1025
1026 if file_size < 32 {
1028 return Err(error_invalid_data(
1029 "File too small to contain a valid XZ stream",
1030 ));
1031 }
1032
1033 reader.seek(SeekFrom::End(-12))?;
1034
1035 let stream_footer = StreamFooter::parse(&mut reader)?;
1036
1037 let header_flags = [0, check_type as u8];
1038
1039 if stream_footer.stream_flags != header_flags {
1040 return Err(error_invalid_data(
1041 "stream header and footer flags mismatch",
1042 ));
1043 }
1044
1045 let index_size = (stream_footer.backward_size + 1) * 4;
1047 let index_start_pos = file_size - 12 - index_size as u64;
1048
1049 reader.seek(SeekFrom::Start(index_start_pos))?;
1050
1051 let index_indicator = reader.read_u8()?;
1053
1054 if index_indicator != 0 {
1055 return Err(error_invalid_data("invalid XZ index indicator"));
1056 }
1057
1058 let index = Index::parse(&mut reader)?;
1059
1060 let mut blocks = Vec::new();
1061 let mut block_start_pos = header_end_pos;
1062
1063 for record in &index.records {
1064 blocks.push(Block {
1065 start_pos: block_start_pos,
1066 unpadded_size: record.unpadded_size,
1067 uncompressed_size: record.uncompressed_size,
1068 });
1069
1070 let padding_needed = (4 - (record.unpadded_size % 4)) % 4;
1071 let actual_block_size = record.unpadded_size + padding_needed;
1072
1073 block_start_pos += actual_block_size;
1074 }
1075
1076 if blocks.is_empty() {
1077 return Err(io::Error::new(
1078 io::ErrorKind::InvalidData,
1079 "No valid XZ blocks found",
1080 ));
1081 }
1082
1083 reader.seek(SeekFrom::Start(0))?;
1084
1085 Ok((reader, blocks, check_type))
1086}
1087
1088#[cfg(feature = "std")]
1089fn create_filter_chain<'reader>(
1090 mut chain_reader: Box<dyn Read + 'reader>,
1091 filters: &[Option<FilterType>],
1092 properties: &[u32],
1093) -> Box<dyn Read + 'reader> {
1094 for (filter, property) in filters
1095 .iter()
1096 .copied()
1097 .zip(properties)
1098 .filter_map(|(filter, property)| filter.map(|filter| (filter, *property)))
1099 .rev()
1100 {
1101 chain_reader = match filter {
1102 FilterType::Delta => {
1103 let distance = property as usize;
1104 Box::new(DeltaReader::new(chain_reader, distance))
1105 }
1106 FilterType::BcjX86 => {
1107 let start_offset = property as usize;
1108 Box::new(BcjReader::new_x86(chain_reader, start_offset))
1109 }
1110 FilterType::BcjPpc => {
1111 let start_offset = property as usize;
1112 Box::new(BcjReader::new_ppc(chain_reader, start_offset))
1113 }
1114 FilterType::BcjIa64 => {
1115 let start_offset = property as usize;
1116 Box::new(BcjReader::new_ia64(chain_reader, start_offset))
1117 }
1118 FilterType::BcjArm => {
1119 let start_offset = property as usize;
1120 Box::new(BcjReader::new_arm(chain_reader, start_offset))
1121 }
1122 FilterType::BcjArmThumb => {
1123 let start_offset = property as usize;
1124 Box::new(BcjReader::new_arm_thumb(chain_reader, start_offset))
1125 }
1126 FilterType::BcjSparc => {
1127 let start_offset = property as usize;
1128 Box::new(BcjReader::new_sparc(chain_reader, start_offset))
1129 }
1130 FilterType::BcjArm64 => {
1131 let start_offset = property as usize;
1132 Box::new(BcjReader::new_arm64(chain_reader, start_offset))
1133 }
1134 FilterType::BcjRiscv => {
1135 let start_offset = property as usize;
1136 Box::new(BcjReader::new_riscv(chain_reader, start_offset))
1137 }
1138 FilterType::Lzma2 => {
1139 let dict_size = property;
1140 Box::new(Lzma2Reader::new(chain_reader, dict_size, None))
1141 }
1142 };
1143 }
1144
1145 chain_reader
1146}
1147
1148#[cfg(feature = "encoder")]
1149fn add_padding<W: Write + ?Sized>(writer: &mut W, padding_needed: usize) -> crate::Result<()> {
1150 match padding_needed {
1151 1 => writer.write_all(&[0]),
1152 2 => writer.write_all(&[0, 0]),
1153 3 => writer.write_all(&[0, 0, 0]),
1154 _ => Ok(()),
1155 }
1156}
1157
1158#[cfg(feature = "encoder")]
1159fn generate_block_header_data(
1160 filters: &[FilterConfig],
1161 lzma_dict_size: u32,
1162) -> crate::Result<Vec<u8>> {
1163 let mut header_data = Vec::new();
1164 let num_filters = filters.len();
1165
1166 if num_filters > 4 {
1167 return Err(error_invalid_input("too many filters in chain (maximum 4)"));
1168 }
1169
1170 let block_flags = (num_filters - 1) as u8; header_data.push(block_flags);
1173
1174 let mut temp_buf = [0u8; 10];
1175
1176 for filter_config in filters {
1177 let filter_id = match filter_config.filter_type {
1179 FilterType::Delta => 0x03,
1180 FilterType::BcjX86 => 0x04,
1181 FilterType::BcjPpc => 0x05,
1182 FilterType::BcjIa64 => 0x06,
1183 FilterType::BcjArm => 0x07,
1184 FilterType::BcjArmThumb => 0x08,
1185 FilterType::BcjSparc => 0x09,
1186 FilterType::BcjArm64 => 0x0A,
1187 FilterType::BcjRiscv => 0x0B,
1188 FilterType::Lzma2 => 0x21,
1189 };
1190 let size = encode_multibyte_integer(filter_id, &mut temp_buf)?;
1191 header_data.extend_from_slice(&temp_buf[..size]);
1192
1193 match filter_config.filter_type {
1195 FilterType::Delta => {
1196 let size = encode_multibyte_integer(1, &mut temp_buf)?;
1198 header_data.extend_from_slice(&temp_buf[..size]);
1199 let distance_prop = (filter_config.property - 1) as u8;
1201 header_data.push(distance_prop);
1202 }
1203 FilterType::BcjX86
1204 | FilterType::BcjPpc
1205 | FilterType::BcjIa64
1206 | FilterType::BcjArm
1207 | FilterType::BcjArmThumb
1208 | FilterType::BcjSparc
1209 | FilterType::BcjArm64
1210 | FilterType::BcjRiscv => {
1211 if filter_config.property == 0 {
1212 let size = encode_multibyte_integer(0, &mut temp_buf)?;
1214 header_data.extend_from_slice(&temp_buf[..size]);
1215 } else {
1216 let size = encode_multibyte_integer(4, &mut temp_buf)?;
1218 header_data.extend_from_slice(&temp_buf[..size]);
1219 header_data.extend_from_slice(&filter_config.property.to_le_bytes());
1220 }
1221 }
1222 FilterType::Lzma2 => {
1223 let size = encode_multibyte_integer(1, &mut temp_buf)?;
1224 header_data.extend_from_slice(&temp_buf[..size]);
1225
1226 let dict_size_prop = encode_lzma2_dict_size(lzma_dict_size)?;
1227 header_data.push(dict_size_prop);
1228 }
1229 }
1230 }
1231
1232 Ok(header_data)
1233}
1234
1235#[cfg(feature = "encoder")]
1236fn write_xz_block_header<W: Write>(
1237 writer: &mut W,
1238 filters: &[FilterConfig],
1239 lzma_dict_size: u32,
1240) -> crate::Result<u64> {
1241 let header_data = generate_block_header_data(filters, lzma_dict_size)?;
1242
1243 let total_size_needed: usize = 1 + header_data.len() + 4;
1245 let header_size = total_size_needed.div_ceil(4) * 4;
1246 let header_size_encoded = ((header_size / 4) - 1) as u8;
1247
1248 let padding_needed = header_size - 1 - header_data.len() - 4;
1249
1250 let mut crc = Crc32::new();
1252 crc.update(&[header_size_encoded]);
1253 crc.update(&header_data);
1254 update_crc_with_padding(&mut crc, padding_needed);
1255
1256 let crc_value = crc.finalize();
1257
1258 writer.write_u8(header_size_encoded)?;
1260 writer.write_all(&header_data)?;
1261 add_padding(writer, padding_needed)?;
1262 writer.write_u32(crc_value)?;
1263
1264 Ok(header_size as u64)
1265}
1266
1267#[cfg(feature = "encoder")]
1268fn write_xz_index<W: Write>(writer: &mut W, index_records: &[IndexRecord]) -> crate::Result<()> {
1269 let mut index_data = Vec::new();
1270
1271 let mut temp_buf = [0u8; 10];
1272 let size = encode_multibyte_integer(index_records.len() as u64, &mut temp_buf)?;
1273 index_data.extend_from_slice(&temp_buf[..size]);
1274
1275 for record in index_records {
1276 let size = encode_multibyte_integer(record.unpadded_size, &mut temp_buf)?;
1277 index_data.extend_from_slice(&temp_buf[..size]);
1278
1279 let size = encode_multibyte_integer(record.uncompressed_size, &mut temp_buf)?;
1280 index_data.extend_from_slice(&temp_buf[..size]);
1281 }
1282
1283 let bytes_written = 1 + index_data.len(); let padding_needed = (4 - (bytes_written % 4)) % 4;
1285
1286 let mut crc = Crc32::new();
1287 crc.update(&[0x00]);
1288 crc.update(&index_data);
1289 update_crc_with_padding(&mut crc, padding_needed);
1290
1291 let crc_value = crc.finalize();
1292
1293 writer.write_u8(0x00)?;
1295 writer.write_all(&index_data)?;
1296 add_padding(writer, padding_needed)?;
1297 writer.write_u32(crc_value)?;
1298
1299 Ok(())
1300}
1301
1302#[cfg(feature = "encoder")]
1303fn write_xz_stream_footer<W: Write>(
1304 writer: &mut W,
1305 index_records: &[IndexRecord],
1306 check_type: CheckType,
1307) -> crate::Result<()> {
1308 let mut index_size = 1; index_size += count_multibyte_integer_size_for_value(index_records.len() as u64);
1311
1312 for record in index_records {
1313 index_size += count_multibyte_integer_size_for_value(record.unpadded_size);
1314 index_size += count_multibyte_integer_size_for_value(record.uncompressed_size);
1315 }
1316
1317 let padding_needed = (4 - (index_size % 4)) % 4;
1318 index_size += padding_needed;
1319 index_size += 4; let backward_size = ((index_size / 4) - 1) as u32;
1322
1323 let stream_flags = [0u8, check_type as u8];
1325
1326 let mut crc = Crc32::new();
1328 crc.update(&backward_size.to_le_bytes());
1329 crc.update(&stream_flags);
1330
1331 writer.write_u32(crc.finalize())?;
1332 writer.write_u32(backward_size)?;
1333 writer.write_all(&stream_flags)?;
1334 writer.write_all(&XZ_FOOTER_MAGIC)?;
1335
1336 Ok(())
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342
1343 #[test]
1344 fn test_encode_decode_multibyte_integer() {
1345 let values = [0, 127, 128, 16383, 16384, 2097151, 2097152];
1346
1347 for &value in &values {
1348 let mut buf = [0u8; 9];
1349 let encoded_size = encode_multibyte_integer(value, &mut buf).unwrap();
1350
1351 let decoded = parse_multibyte_integer(&buf[..encoded_size]).unwrap();
1352 assert_eq!(decoded, value);
1353
1354 let size_for_value = count_multibyte_integer_size_for_value(value);
1355 assert_eq!(size_for_value, encoded_size);
1356 }
1357 }
1358
1359 #[test]
1360 fn test_multibyte_integer_limits() {
1361 let max_value = u64::MAX / 2;
1363 let mut buf = [0u8; 9];
1364 let encoded_size = encode_multibyte_integer(max_value, &mut buf).unwrap();
1365
1366 let decoded = parse_multibyte_integer(&buf[..encoded_size]).unwrap();
1367 assert_eq!(decoded, max_value);
1368
1369 let too_large = u64::MAX;
1371 let encoded_size = encode_multibyte_integer(too_large, &mut buf);
1372 assert!(encoded_size.is_err());
1373 }
1374
1375 #[test]
1376 fn test_index_record_creation() {
1377 let record = IndexRecord {
1378 unpadded_size: 1024,
1379 uncompressed_size: 2048,
1380 };
1381
1382 assert_eq!(record.unpadded_size, 1024);
1383 assert_eq!(record.uncompressed_size, 2048);
1384 }
1385
1386 #[test]
1387 fn test_checksum_calculator_crc32() {
1388 let mut calc = ChecksumCalculator::new(CheckType::Crc32);
1389 calc.update(b"123456789");
1390
1391 let expected = [0x26, 0x39, 0xF4, 0xCB];
1393 assert!(calc.verify(&expected));
1394 }
1395
1396 #[test]
1397 fn test_checksum_calculator_crc64() {
1398 let mut calc = ChecksumCalculator::new(CheckType::Crc64);
1399 calc.update(b"123456789");
1400
1401 let expected = [250, 57, 25, 223, 187, 201, 93, 153];
1403 assert!(calc.verify(&expected));
1404 }
1405
1406 #[test]
1407 fn test_checksum_calculator_sha256() {
1408 let mut calc = ChecksumCalculator::new(CheckType::Sha256);
1409 calc.update(b"123456789");
1410
1411 let expected = [
1413 21, 226, 176, 211, 195, 56, 145, 235, 176, 241, 239, 96, 158, 196, 25, 66, 12, 32, 227,
1414 32, 206, 148, 198, 95, 188, 140, 51, 18, 68, 142, 178, 37,
1415 ];
1416 assert!(calc.verify(&expected));
1417 }
1418}