1use crate::backend::resolve_cursor_format;
14use crate::crc32::Crc32;
15use crate::gzip::{
16 DetailedMemberHeader, InputCursor, SourceCursor, StreamCursor, parse_member_header_detailed,
17};
18use crate::parallel::deflate::{
19 self, DISTANCE_BASE, DISTANCE_EXTRA, DeflateBits, END_OF_BLOCK, Huffman, LENGTH_BASE,
20 LENGTH_EXTRA, dynamic_trees_with_lengths, fixed_trees,
21};
22use crate::zlib::Adler32;
23use crate::{
24 AnalysisCounter, AnalysisErrorKind, AnalysisResource, DecodeError, DeflateErrorKind, Format,
25 GzipErrorKind, ReadAt, ZlibErrorKind,
26};
27use std::io::Read;
28
29const WINDOW_SIZE: usize = 32 * 1024;
30const CHECKSUM_BUFFER_SIZE: usize = 8 * 1024;
31const DEFAULT_MAXIMUM_STREAMS: usize = 100_000;
32const DEFAULT_MAXIMUM_BLOCKS: usize = 100_000;
33const DEFAULT_MAXIMUM_HEADER_BYTES: usize = 1024 * 1024;
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42#[non_exhaustive]
43pub struct AnalyzeOptions {
44 maximum_streams: usize,
45 maximum_blocks: usize,
46 maximum_header_bytes: usize,
47 maximum_retained_backreferences: usize,
48}
49
50impl Default for AnalyzeOptions {
51 fn default() -> Self {
52 Self {
53 maximum_streams: DEFAULT_MAXIMUM_STREAMS,
54 maximum_blocks: DEFAULT_MAXIMUM_BLOCKS,
55 maximum_header_bytes: DEFAULT_MAXIMUM_HEADER_BYTES,
56 maximum_retained_backreferences: 0,
57 }
58 }
59}
60
61impl AnalyzeOptions {
62 #[must_use]
64 pub const fn maximum_streams(mut self, maximum: usize) -> Self {
65 self.maximum_streams = maximum;
66 self
67 }
68
69 #[must_use]
71 pub const fn maximum_blocks(mut self, maximum: usize) -> Self {
72 self.maximum_blocks = maximum;
73 self
74 }
75
76 #[must_use]
82 pub const fn maximum_header_bytes(mut self, maximum: usize) -> Self {
83 self.maximum_header_bytes = maximum;
84 self
85 }
86
87 #[must_use]
92 pub const fn maximum_retained_backreferences(mut self, maximum: usize) -> Self {
93 self.maximum_retained_backreferences = maximum;
94 self
95 }
96
97 #[must_use]
99 pub const fn stream_limit(self) -> usize {
100 self.maximum_streams
101 }
102
103 #[must_use]
105 pub const fn block_limit(self) -> usize {
106 self.maximum_blocks
107 }
108
109 #[must_use]
111 pub const fn header_byte_limit(self) -> usize {
112 self.maximum_header_bytes
113 }
114
115 #[must_use]
117 pub const fn retained_backreference_limit(self) -> usize {
118 self.maximum_retained_backreferences
119 }
120}
121
122#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
124#[non_exhaustive]
125pub enum BlockType {
126 #[default]
128 Uncompressed,
129 FixedHuffman,
131 DynamicHuffman,
133}
134
135#[derive(Clone, Debug, Default, Eq, PartialEq)]
137#[non_exhaustive]
138pub struct AlphabetShape {
139 pub code_lengths: Vec<u8>,
141 pub declared_count: usize,
143}
144
145impl AlphabetShape {
146 #[must_use]
148 pub fn used_count(&self) -> usize {
149 self.code_lengths
150 .iter()
151 .filter(|&&length| length != 0)
152 .count()
153 }
154
155 #[must_use]
157 pub fn length_range(&self) -> Option<(u8, u8)> {
158 let mut lengths = self
159 .code_lengths
160 .iter()
161 .copied()
162 .filter(|&length| length != 0);
163 let first = lengths.next()?;
164 Some(lengths.fold((first, first), |(minimum, maximum), length| {
165 (minimum.min(length), maximum.max(length))
166 }))
167 }
168
169 #[must_use]
171 pub fn counts_by_length(&self) -> Vec<(u8, usize)> {
172 let mut counts = [0_usize; 16];
173 for &length in &self.code_lengths {
174 counts[usize::from(length).min(15)] += 1;
175 }
176 counts
177 .into_iter()
178 .enumerate()
179 .filter(|&(_, count)| count != 0)
180 .map(|(length, count)| (length as u8, count))
181 .collect()
182 }
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
187#[non_exhaustive]
188pub struct Backreference {
189 pub distance: u16,
191 pub length: u16,
193}
194
195#[derive(Clone, Debug, Default, Eq, PartialEq)]
197#[non_exhaustive]
198pub struct BlockAnalysis {
199 pub stream_index: u64,
201 pub index_in_stream: u64,
203 pub is_final: bool,
205 pub block_type: BlockType,
207 pub compressed_offset_in_bits: u64,
209 pub compressed_data_offset_in_bits: u64,
211 pub uncompressed_offset_in_bytes: u64,
213 pub compressed_size_in_bits: u64,
215 pub uncompressed_size_in_bytes: u64,
217 pub precode: Option<AlphabetShape>,
219 pub distance: Option<AlphabetShape>,
221 pub literal: Option<AlphabetShape>,
223 pub literal_symbols: u64,
225 pub backreference_symbols: u64,
227 pub copied_bytes: u64,
229 pub farthest_backreference: u64,
231 pub window_backreference_count: u64,
233 pub merged_window_backreference_count: u64,
235 pub used_window_symbols: Option<u64>,
237 pub retained_backreferences: Vec<Backreference>,
239 pub omitted_backreference_count: u64,
241}
242
243#[derive(Clone, Debug, Default, Eq, PartialEq)]
245#[non_exhaustive]
246pub struct GzipHeaderFields {
247 pub flags: u8,
249 pub modification_time: u32,
251 pub extra_flags: u8,
253 pub operating_system: u8,
255 pub file_name: Option<Vec<u8>>,
257 pub comment: Option<Vec<u8>>,
259 pub extra: Option<Vec<u8>>,
261 pub header_crc16: Option<u16>,
263 pub bgzf_block_size: Option<u16>,
265}
266
267#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
269#[non_exhaustive]
270pub struct ZlibHeaderFields {
271 pub window_size: u32,
273 pub compression_level: u8,
275 pub dictionary_id: Option<u32>,
278}
279
280#[derive(Clone, Debug, Eq, PartialEq)]
282#[non_exhaustive]
283pub enum StreamHeader {
284 Gzip(GzipHeaderFields),
286 Zlib(ZlibHeaderFields),
288 RawDeflate,
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
294#[non_exhaustive]
295pub enum StreamFooter {
296 Gzip {
298 crc32: u32,
300 uncompressed_size: u32,
302 },
303 Zlib {
305 adler32: u32,
307 },
308 None,
310}
311
312#[derive(Clone, Debug, Eq, PartialEq)]
314#[non_exhaustive]
315pub struct StreamAnalysis {
316 pub index: u64,
318 pub header: StreamHeader,
320 pub header_offset_in_bits: u64,
322 pub deflate_offset_in_bits: u64,
324 pub footer_offset_in_bits: u64,
326 pub uncompressed_offset_in_bytes: u64,
328 pub footer: StreamFooter,
330 pub compressed_size_in_bits: u64,
332 pub uncompressed_size_in_bytes: u64,
334 pub first_block_index: usize,
336 pub block_count: usize,
338}
339
340#[derive(Clone, Debug, Eq, PartialEq)]
342#[non_exhaustive]
343pub struct Analysis {
344 pub format: Format,
346 pub streams: Vec<StreamAnalysis>,
348 pub blocks: Vec<BlockAnalysis>,
350 pub uncompressed_size_in_bytes: u64,
352 pub compressed_size_in_bytes: u64,
354 pub backreference_length_counts: [u64; 259],
356}
357
358impl Analysis {
359 #[must_use]
361 pub fn block_type_counts(&self) -> Vec<(BlockType, u64)> {
362 let mut counts = [0_u64; 3];
363 for block in &self.blocks {
364 let index = match block.block_type {
365 BlockType::Uncompressed => 0,
366 BlockType::FixedHuffman => 1,
367 BlockType::DynamicHuffman => 2,
368 };
369 counts[index] += 1;
370 }
371 [
372 BlockType::Uncompressed,
373 BlockType::FixedHuffman,
374 BlockType::DynamicHuffman,
375 ]
376 .into_iter()
377 .zip(counts)
378 .filter(|&(_, count)| count != 0)
379 .collect()
380 }
381
382 #[must_use]
384 pub fn has_complete_backreference_details(&self) -> bool {
385 self.blocks
386 .iter()
387 .all(|block| block.omitted_backreference_count == 0)
388 }
389}
390
391struct AnalysisCursor<C> {
392 inner: C,
393 bits: u64,
394 buffered_bits: u8,
395 bit_position: u64,
396 scratch: [u8; 8],
397 serving_buffer: bool,
398 position_overflowed: bool,
399}
400
401impl<C: InputCursor> AnalysisCursor<C> {
402 fn new(inner: C) -> Result<Self, DecodeError> {
403 let bit_position = inner
404 .position()
405 .checked_mul(8)
406 .ok_or(DecodeError::Analysis {
407 reason: AnalysisErrorKind::CounterOverflow {
408 counter: AnalysisCounter::CompressedBits,
409 },
410 })?;
411 Ok(Self {
412 inner,
413 bits: 0,
414 buffered_bits: 0,
415 bit_position,
416 scratch: [0; 8],
417 serving_buffer: false,
418 position_overflowed: false,
419 })
420 }
421
422 const fn bit_position(&self) -> u64 {
423 self.bit_position
424 }
425
426 #[cold]
427 #[inline(never)]
428 fn refill_bits(&mut self, wanted: u8) -> Result<(), DecodeError> {
429 self.check_position()?;
430 while self.buffered_bits < wanted {
431 let available = self.inner.available()?;
432 let capacity = usize::from((56 - self.buffered_bits) / 8);
433 let count = capacity.min(available.len());
434 if count == 0 {
435 break;
436 }
437 let word = if available.len() >= std::mem::size_of::<u64>() {
438 unsafe { available.as_ptr().cast::<u64>().read_unaligned() }.to_le()
444 } else {
445 let mut tail = [0_u8; 8];
446 tail[..count].copy_from_slice(&available[..count]);
447 u64::from_le_bytes(tail)
448 };
449 let refill_bits = count * 8;
450 let mask = (1_u64 << refill_bits) - 1;
451 self.bits |= (word & mask) << self.buffered_bits;
452 self.buffered_bits += u8::try_from(count * 8).expect("at most seven bytes fit");
453 self.inner.advance(count);
454 }
455 Ok(())
456 }
457
458 #[inline(always)]
459 fn fill_to(&mut self, wanted: u8) -> Result<(), DecodeError> {
460 if self.buffered_bits >= wanted && !self.position_overflowed {
461 Ok(())
462 } else {
463 self.refill_bits(wanted)
464 }
465 }
466
467 fn check_position(&self) -> Result<(), DecodeError> {
468 if self.position_overflowed {
469 Err(DecodeError::Analysis {
470 reason: AnalysisErrorKind::CounterOverflow {
471 counter: AnalysisCounter::CompressedBits,
472 },
473 })
474 } else {
475 Ok(())
476 }
477 }
478
479 #[inline(always)]
480 fn consume_buffered(&mut self, count: u8) -> Result<(), DecodeError> {
481 if count > self.buffered_bits {
482 return Err(self.deflate_error(deflate::Error::UnexpectedEof));
483 }
484 self.bits >>= count;
485 self.buffered_bits -= count;
486 self.bit_position =
487 self.bit_position
488 .checked_add(u64::from(count))
489 .ok_or(DecodeError::Analysis {
490 reason: AnalysisErrorKind::CounterOverflow {
491 counter: AnalysisCounter::CompressedBits,
492 },
493 })?;
494 Ok(())
495 }
496
497 fn align_to_byte(&mut self) -> Result<(), DecodeError> {
498 let padding = ((8 - self.bit_position % 8) % 8) as u8;
499 if padding != 0 {
500 self.read_bits(padding)?;
501 }
502 Ok(())
503 }
504
505 fn deflate_error(&self, error: deflate::Error) -> DecodeError {
506 let reason = match error {
507 deflate::Error::UnexpectedEof => DeflateErrorKind::Truncated,
508 _ => DeflateErrorKind::InvalidData,
509 };
510 DecodeError::InvalidDeflate {
511 bit_offset: self.bit_position,
512 reason,
513 }
514 }
515}
516
517impl<C: InputCursor> DeflateBits for AnalysisCursor<C> {
518 type Error = DecodeError;
519
520 #[inline(always)]
521 fn read_bits(&mut self, count: u8) -> Result<u32, Self::Error> {
522 debug_assert!(count <= 24);
523 self.fill_to(count)?;
524 if self.buffered_bits < count {
525 return Err(self.deflate_error(deflate::Error::UnexpectedEof));
526 }
527 let mask = if count == 0 { 0 } else { (1_u64 << count) - 1 };
528 let value = (self.bits & mask) as u32;
529 self.consume_buffered(count)?;
530 Ok(value)
531 }
532
533 #[inline(always)]
534 fn peek_bits_padded(&mut self, count: u8) -> Result<(u32, u8), Self::Error> {
535 self.fill_to(count)?;
536 let available = self.buffered_bits.min(count);
537 let mask = if count == 0 { 0 } else { (1_u64 << count) - 1 };
538 Ok(((self.bits & mask) as u32, available))
539 }
540
541 #[inline(always)]
542 fn advance_bits(&mut self, count: u8) -> Result<(), Self::Error> {
543 self.consume_buffered(count)
544 }
545
546 #[inline(always)]
547 fn error(&self, error: deflate::Error) -> Self::Error {
548 self.deflate_error(error)
549 }
550}
551
552impl<C: InputCursor> InputCursor for AnalysisCursor<C> {
553 fn position(&self) -> u64 {
554 self.bit_position / 8
555 }
556
557 fn is_at_end(&mut self) -> Result<bool, DecodeError> {
558 self.check_position()?;
559 if self.buffered_bits >= 8 {
560 return Ok(false);
561 }
562 self.inner.is_at_end()
563 }
564
565 fn available(&mut self) -> Result<&[u8], DecodeError> {
566 self.check_position()?;
567 debug_assert_eq!(self.bit_position % 8, 0);
568 if self.buffered_bits >= 8 {
569 let byte_count = usize::from(self.buffered_bits / 8);
570 for (index, byte) in self.scratch[..byte_count].iter_mut().enumerate() {
571 *byte = (self.bits >> (index * 8)) as u8;
572 }
573 self.serving_buffer = true;
574 return Ok(&self.scratch[..byte_count]);
575 }
576 self.serving_buffer = false;
577 self.inner.available()
578 }
579
580 fn advance(&mut self, count: usize) {
581 if self.serving_buffer {
582 let bits = u8::try_from(count.saturating_mul(8))
583 .expect("the analysis scratch buffer contains at most eight bytes");
584 debug_assert!(bits <= self.buffered_bits);
585 self.bits >>= bits;
586 self.buffered_bits -= bits;
587 } else {
588 self.inner.advance(count);
589 }
590 let additional = u64::try_from(count)
591 .ok()
592 .and_then(|count| count.checked_mul(8));
593 match additional.and_then(|additional| self.bit_position.checked_add(additional)) {
594 Some(position) => self.bit_position = position,
595 None => self.position_overflowed = true,
596 }
597 self.serving_buffer = false;
598 }
599
600 fn verify_source_unchanged(&self) -> Result<(), DecodeError> {
601 self.check_position()?;
602 self.inner.verify_source_unchanged()
603 }
604
605 fn peek_two(&mut self) -> Result<Option<[u8; 2]>, DecodeError> {
606 self.check_position()?;
607 debug_assert_eq!(self.bit_position % 8, 0);
608 self.fill_to(16)?;
609 Ok((self.buffered_bits >= 16).then_some([self.bits as u8, (self.bits >> 8) as u8]))
610 }
611}
612
613enum StreamChecksum {
614 Gzip(Crc32),
615 Zlib(Adler32),
616 None,
617}
618
619struct OutputState {
620 bytes: [u8; WINDOW_SIZE + CHECKSUM_BUFFER_SIZE],
625 history_length: usize,
626 write_position: usize,
627 checksum_start: usize,
628 maximum_distance: usize,
629 checksum: StreamChecksum,
630 stream_size: u64,
631}
632
633impl OutputState {
634 fn new(maximum_distance: usize, checksum: StreamChecksum) -> Self {
635 Self {
636 bytes: [0; WINDOW_SIZE + CHECKSUM_BUFFER_SIZE],
637 history_length: 0,
638 write_position: 0,
639 checksum_start: 0,
640 maximum_distance,
641 checksum,
642 stream_size: 0,
643 }
644 }
645
646 #[inline(always)]
647 fn prepare_output<const CHECK_CONFIGURED_LIMITS: bool>(
648 &mut self,
649 total_output: &mut u64,
650 additional: usize,
651 config: &crate::config::Config,
652 ) -> Result<(), DecodeError> {
653 let actual = if CHECK_CONFIGURED_LIMITS {
654 config.checked_output_total(*total_output, additional)?
655 } else {
656 total_output
657 .checked_add(additional as u64)
658 .ok_or(DecodeError::OutputLimitExceeded { limit: u64::MAX })?
659 };
660 *total_output = actual;
661 self.stream_size += additional as u64;
664 Ok(())
665 }
666
667 #[inline(always)]
668 fn ensure_space(&mut self, additional: usize) {
669 debug_assert!(additional <= 258);
670 if self.write_position + additional > self.bytes.len() {
671 self.roll_buffer();
672 }
673 debug_assert!(self.write_position + additional <= self.bytes.len());
674 }
675
676 #[inline(always)]
677 fn emit(&mut self, byte: u8) {
678 self.ensure_space(1);
679 self.bytes[self.write_position] = byte;
680 self.write_position += 1;
681 self.history_length = (self.history_length + 1).min(WINDOW_SIZE);
682 }
683
684 fn append_bytes(&mut self, bytes: &[u8]) {
685 let mut offset = 0;
686 while offset < bytes.len() {
687 if self.write_position == self.bytes.len() {
688 self.roll_buffer();
689 }
690 let count = (bytes.len() - offset).min(self.bytes.len() - self.write_position);
691 self.bytes[self.write_position..self.write_position + count]
692 .copy_from_slice(&bytes[offset..offset + count]);
693 self.write_position += count;
694 self.history_length = (self.history_length + count).min(WINDOW_SIZE);
695 offset += count;
696 }
697 }
698
699 #[inline]
700 fn copy_match(&mut self, distance: usize, length: usize) {
701 debug_assert!(distance != 0);
702 debug_assert!(distance <= self.maximum_distance);
703 debug_assert!(distance <= self.history_length);
704 self.ensure_space(length);
705 let destination = self.write_position;
706 let source = destination - distance;
707 if distance == 1 {
708 let byte = self.bytes[source];
709 self.bytes[destination..destination + length].fill(byte);
710 } else if length <= distance {
711 self.bytes.copy_within(source..source + length, destination);
712 } else {
713 self.bytes
714 .copy_within(source..source + distance, destination);
715 let mut produced = distance;
716 while produced < length {
717 let count = produced.min(length - produced);
718 self.bytes
719 .copy_within(destination..destination + count, destination + produced);
720 produced += count;
721 }
722 }
723 self.write_position += length;
724 self.history_length = (self.history_length + length).min(WINDOW_SIZE);
725 }
726
727 fn flush_checksum(&mut self) {
728 let bytes = &self.bytes[self.checksum_start..self.write_position];
729 match &mut self.checksum {
730 StreamChecksum::Gzip(checksum) => checksum.update(bytes),
731 StreamChecksum::Zlib(checksum) => checksum.update(bytes),
732 StreamChecksum::None => {}
733 }
734 self.checksum_start = self.write_position;
735 }
736
737 #[cold]
738 fn roll_buffer(&mut self) {
739 self.flush_checksum();
740 let history_start = self.write_position - self.history_length;
741 self.bytes
742 .copy_within(history_start..self.write_position, 0);
743 self.write_position = self.history_length;
744 self.checksum_start = self.write_position;
745 }
746
747 fn finish_checksum(mut self) -> (StreamChecksum, u64) {
748 self.flush_checksum();
749 (self.checksum, self.stream_size)
750 }
751}
752
753struct AnalyzeState<'a> {
754 config: &'a crate::config::Config,
755 options: AnalyzeOptions,
756 analysis: Analysis,
757 total_output: u64,
758 remaining_backreferences: usize,
759 retained_header_bytes: usize,
760}
761
762impl AnalyzeState<'_> {
763 fn reserve_stream(&mut self) -> Result<(), DecodeError> {
764 reserve_item(
765 &mut self.analysis.streams,
766 self.options.maximum_streams,
767 AnalysisResource::Streams,
768 )
769 }
770
771 fn reserve_block(&mut self) -> Result<(), DecodeError> {
772 reserve_item(
773 &mut self.analysis.blocks,
774 self.options.maximum_blocks,
775 AnalysisResource::Blocks,
776 )
777 }
778}
779
780fn reserve_item<T>(
781 values: &mut Vec<T>,
782 limit: usize,
783 resource: AnalysisResource,
784) -> Result<(), DecodeError> {
785 if values.len() >= limit {
786 return Err(DecodeError::Analysis {
787 reason: AnalysisErrorKind::ResourceLimit { resource, limit },
788 });
789 }
790 values.try_reserve(1).map_err(|_| DecodeError::Analysis {
791 reason: AnalysisErrorKind::AllocationFailed {
792 resource,
793 additional: 1,
794 },
795 })
796}
797
798fn alphabet_shape(bytes: &[u8], declared_count: usize) -> Result<AlphabetShape, DecodeError> {
799 let mut code_lengths = Vec::new();
800 code_lengths
801 .try_reserve_exact(bytes.len())
802 .map_err(|_| DecodeError::Analysis {
803 reason: AnalysisErrorKind::AllocationFailed {
804 resource: AnalysisResource::AlphabetCodeLengths,
805 additional: bytes.len(),
806 },
807 })?;
808 code_lengths.extend_from_slice(bytes);
809 Ok(AlphabetShape {
810 code_lengths,
811 declared_count,
812 })
813}
814
815fn record_window_reference(
816 block: &mut BlockAnalysis,
817 coverage: &mut [bool; WINDOW_SIZE],
818 global_lengths: &mut [u64; 259],
819 remaining: &mut usize,
820 distance: usize,
821 length: usize,
822) -> Result<(), DecodeError> {
823 let reference = Backreference {
824 distance: u16::try_from(distance).expect("DEFLATE distances fit u16"),
825 length: u16::try_from(length).expect("DEFLATE lengths fit u16"),
826 };
827 block.window_backreference_count += 1;
830 block.farthest_backreference = block.farthest_backreference.max(distance as u64);
831 let length_count = global_lengths
832 .get_mut(length)
833 .expect("DEFLATE reference lengths do not exceed 258");
834 *length_count += 1;
835
836 let begin = WINDOW_SIZE - distance;
837 let end = begin.saturating_add(length).min(WINDOW_SIZE);
838 coverage[begin..end].fill(true);
839
840 if *remaining == 0 {
841 block.omitted_backreference_count += 1;
842 return Ok(());
843 }
844 block
845 .retained_backreferences
846 .try_reserve(1)
847 .map_err(|_| DecodeError::Analysis {
848 reason: AnalysisErrorKind::AllocationFailed {
849 resource: AnalysisResource::Backreferences,
850 additional: 1,
851 },
852 })?;
853 block.retained_backreferences.push(reference);
854 *remaining -= 1;
855 Ok(())
856}
857
858fn coverage_groups(coverage: &[bool; WINDOW_SIZE]) -> u64 {
859 coverage
860 .iter()
861 .copied()
862 .fold((false, 0_u64), |(inside, groups), covered| {
863 (covered, groups + u64::from(covered && !inside))
864 })
865 .1
866}
867
868fn analyze_compressed_symbols<C: InputCursor, const CHECK_CONFIGURED_LIMITS: bool>(
869 cursor: &mut AnalysisCursor<C>,
870 trees: (&Huffman, &Huffman),
871 output: &mut OutputState,
872 state: &mut AnalyzeState<'_>,
873 block: &mut BlockAnalysis,
874 block_output_start: u64,
875 coverage: &mut [bool; WINDOW_SIZE],
876) -> Result<(), DecodeError> {
877 let (literal_tree, distance_tree) = trees;
878 loop {
879 match literal_tree.decode(cursor)? {
880 symbol @ 0..=255 => {
881 output.prepare_output::<CHECK_CONFIGURED_LIMITS>(
882 &mut state.total_output,
883 1,
884 state.config,
885 )?;
886 block.literal_symbols += 1;
888 output.emit(symbol as u8);
889 }
890 END_OF_BLOCK => return Ok(()),
891 symbol @ 257..=285 => {
892 let length_index = symbol - 257;
893 let length = LENGTH_BASE[length_index]
894 + cursor.read_bits(LENGTH_EXTRA[length_index])? as usize;
895 let distance_symbol = distance_tree.decode(cursor)?;
896 if distance_symbol >= DISTANCE_BASE.len() {
897 return Err(cursor.deflate_error(deflate::Error::InvalidDistance));
898 }
899 let distance = DISTANCE_BASE[distance_symbol]
900 + cursor.read_bits(DISTANCE_EXTRA[distance_symbol])? as usize;
901 if distance == 0
902 || distance > output.maximum_distance
903 || distance > output.history_length
904 {
905 return Err(cursor.deflate_error(deflate::Error::InvalidDistance));
906 }
907 let position_in_block = state.total_output - block_output_start;
908 output.prepare_output::<CHECK_CONFIGURED_LIMITS>(
909 &mut state.total_output,
910 length,
911 state.config,
912 )?;
913 block.backreference_symbols += 1;
915 block.copied_bytes += length as u64;
916 if distance as u64 > position_in_block {
917 let preceding_distance = distance as u64 - position_in_block;
918 let reported_length = length.min(distance);
919 record_window_reference(
920 block,
921 coverage,
922 &mut state.analysis.backreference_length_counts,
923 &mut state.remaining_backreferences,
924 preceding_distance as usize,
925 reported_length,
926 )?;
927 }
928
929 output.copy_match(distance, length);
930 }
931 _ => return Err(cursor.deflate_error(deflate::Error::InvalidSymbol)),
932 }
933 }
934}
935
936fn analyze_block<C: InputCursor, const CHECK_CONFIGURED_LIMITS: bool>(
937 cursor: &mut AnalysisCursor<C>,
938 output: &mut OutputState,
939 state: &mut AnalyzeState<'_>,
940 stream_index: u64,
941 index_in_stream: u64,
942) -> Result<BlockAnalysis, DecodeError> {
943 let block_start = cursor.bit_position();
944 let block_output_start = state.total_output;
945 let is_final = cursor.read_bits(1)? != 0;
946 let encoding = cursor.read_bits(2)?;
947 let mut block = BlockAnalysis {
948 stream_index,
949 index_in_stream,
950 is_final,
951 compressed_offset_in_bits: block_start,
952 uncompressed_offset_in_bytes: block_output_start,
953 ..BlockAnalysis::default()
954 };
955 let mut coverage = [false; WINDOW_SIZE];
956
957 match encoding {
958 0 => {
959 block.block_type = BlockType::Uncompressed;
960 cursor.align_to_byte()?;
961 let length = cursor.read_bits(16)? as u16;
962 let complement = cursor.read_bits(16)? as u16;
963 if length != !complement {
964 return Err(cursor.deflate_error(deflate::Error::InvalidStoredLength));
965 }
966 block.compressed_data_offset_in_bits = cursor.bit_position();
967 output.prepare_output::<CHECK_CONFIGURED_LIMITS>(
968 &mut state.total_output,
969 usize::from(length),
970 state.config,
971 )?;
972 let mut remaining = usize::from(length);
973 while remaining != 0 {
974 let available = cursor.available()?;
975 if available.is_empty() {
976 return Err(cursor.deflate_error(deflate::Error::UnexpectedEof));
977 }
978 let count = remaining.min(available.len());
979 output.append_bytes(&available[..count]);
980 cursor.advance(count);
981 remaining -= count;
982 }
983 }
984 1 => {
985 block.block_type = BlockType::FixedHuffman;
986 block.compressed_data_offset_in_bits = cursor.bit_position();
987 let (literal, distance) = fixed_trees();
988 analyze_compressed_symbols::<_, CHECK_CONFIGURED_LIMITS>(
989 cursor,
990 (literal, distance),
991 output,
992 state,
993 &mut block,
994 block_output_start,
995 &mut coverage,
996 )?;
997 }
998 2 => {
999 block.block_type = BlockType::DynamicHuffman;
1000 let (literal_tree, distance_tree, declared) = dynamic_trees_with_lengths(cursor)?;
1001 block.compressed_data_offset_in_bits = cursor.bit_position();
1002 block.precode = Some(alphabet_shape(&declared.precode, declared.precode_count)?);
1003 let literal_end = declared.literal_count;
1004 let distance_end = literal_end + declared.distance_count;
1005 block.literal = Some(alphabet_shape(
1006 &declared.lengths[..literal_end],
1007 declared.literal_count,
1008 )?);
1009 block.distance = Some(alphabet_shape(
1010 &declared.lengths[literal_end..distance_end],
1011 declared.distance_count,
1012 )?);
1013 analyze_compressed_symbols::<_, CHECK_CONFIGURED_LIMITS>(
1014 cursor,
1015 (&literal_tree, &distance_tree),
1016 output,
1017 state,
1018 &mut block,
1019 block_output_start,
1020 &mut coverage,
1021 )?;
1022 }
1023 _ => return Err(cursor.deflate_error(deflate::Error::InvalidBlockType)),
1024 }
1025 cursor.check_position()?;
1026
1027 block.compressed_size_in_bits =
1028 cursor
1029 .bit_position()
1030 .checked_sub(block_start)
1031 .ok_or(DecodeError::Analysis {
1032 reason: AnalysisErrorKind::CounterOverflow {
1033 counter: AnalysisCounter::CompressedBits,
1034 },
1035 })?;
1036 block.uncompressed_size_in_bytes =
1037 state
1038 .total_output
1039 .checked_sub(block_output_start)
1040 .ok_or(DecodeError::Analysis {
1041 reason: AnalysisErrorKind::CounterOverflow {
1042 counter: AnalysisCounter::DecompressedBytes,
1043 },
1044 })?;
1045 block.merged_window_backreference_count = coverage_groups(&coverage);
1046 if block.uncompressed_size_in_bytes >= WINDOW_SIZE as u64 {
1047 block.used_window_symbols = Some(coverage.iter().filter(|&&used| used).count() as u64);
1048 }
1049 Ok(block)
1050}
1051
1052fn gzip_header(details: DetailedMemberHeader) -> StreamHeader {
1053 StreamHeader::Gzip(GzipHeaderFields {
1054 flags: details.flags,
1055 modification_time: details.modification_time,
1056 extra_flags: details.extra_flags,
1057 operating_system: details.operating_system,
1058 file_name: details.file_name,
1059 comment: details.comment,
1060 extra: details.extra,
1061 header_crc16: details.header_crc16,
1062 bgzf_block_size: details.member.bgzf_block_size,
1063 })
1064}
1065
1066fn read_zlib_header<C: InputCursor>(
1067 cursor: &mut AnalysisCursor<C>,
1068) -> Result<(StreamHeader, usize), DecodeError> {
1069 let offset = cursor.position();
1070 let mut bytes = [0_u8; 2];
1071 for byte in &mut bytes {
1072 let Some(&value) = cursor.available()?.first() else {
1073 return Err(DecodeError::InvalidZlib {
1074 offset,
1075 reason: ZlibErrorKind::Truncated,
1076 });
1077 };
1078 cursor.advance(1);
1079 *byte = value;
1080 }
1081 let window_bits = crate::zlib::parse_header(bytes, offset)?;
1082 Ok((
1083 StreamHeader::Zlib(ZlibHeaderFields {
1084 window_size: 1_u32 << window_bits,
1085 compression_level: bytes[1] >> 6,
1086 dictionary_id: None,
1087 }),
1088 1_usize << window_bits,
1089 ))
1090}
1091
1092fn read_zlib_footer<C: InputCursor>(
1093 cursor: &mut AnalysisCursor<C>,
1094) -> Result<[u8; 4], DecodeError> {
1095 let offset = cursor.position();
1096 let mut bytes = [0_u8; 4];
1097 for byte in &mut bytes {
1098 let Some(&value) = cursor.available()?.first() else {
1099 return Err(DecodeError::InvalidZlib {
1100 offset,
1101 reason: ZlibErrorKind::Truncated,
1102 });
1103 };
1104 cursor.advance(1);
1105 *byte = value;
1106 }
1107 Ok(bytes)
1108}
1109
1110fn analyze_one_stream<C: InputCursor, const CHECK_CONFIGURED_LIMITS: bool>(
1111 cursor: &mut AnalysisCursor<C>,
1112 state: &mut AnalyzeState<'_>,
1113 format: Format,
1114) -> Result<(), DecodeError> {
1115 state.reserve_stream()?;
1116 let stream_index = state.analysis.streams.len() as u64;
1117 let header_offset = cursor.bit_position();
1118 let stream_output_start = state.total_output;
1119 let first_block_index = state.analysis.blocks.len();
1120
1121 let (header, maximum_distance, checksum) = match format {
1122 Format::Gzip => {
1123 let details = parse_member_header_detailed(
1124 cursor,
1125 stream_index == 0,
1126 state.options.maximum_header_bytes,
1127 state.retained_header_bytes,
1128 )?;
1129 state.retained_header_bytes = details.retained_metadata_bytes;
1130 (
1131 gzip_header(details),
1132 WINDOW_SIZE,
1133 StreamChecksum::Gzip(Crc32::new()),
1134 )
1135 }
1136 Format::Zlib => {
1137 let (header, maximum_distance) = read_zlib_header(cursor)?;
1138 (
1139 header,
1140 maximum_distance,
1141 StreamChecksum::Zlib(Adler32::new()),
1142 )
1143 }
1144 Format::RawDeflate => (StreamHeader::RawDeflate, WINDOW_SIZE, StreamChecksum::None),
1145 };
1146 let deflate_offset = cursor.bit_position();
1147 let mut output = OutputState::new(maximum_distance, checksum);
1148 let mut block_index = 0_u64;
1149 loop {
1150 state.reserve_block()?;
1151 let block = analyze_block::<_, CHECK_CONFIGURED_LIMITS>(
1152 cursor,
1153 &mut output,
1154 state,
1155 stream_index,
1156 block_index,
1157 )?;
1158 let final_block = block.is_final;
1159 state.analysis.blocks.push(block);
1160 block_index = block_index.checked_add(1).ok_or(DecodeError::Analysis {
1161 reason: AnalysisErrorKind::CounterOverflow {
1162 counter: AnalysisCounter::StructuralItems,
1163 },
1164 })?;
1165 if final_block {
1166 break;
1167 }
1168 }
1169 cursor.align_to_byte()?;
1170 let footer_offset = cursor.bit_position();
1171 let (checksum, stream_size) = output.finish_checksum();
1172
1173 let footer = match (format, checksum) {
1174 (Format::Gzip, StreamChecksum::Gzip(checksum)) => {
1175 let bytes = cursor.read_exact::<8>(cursor.position())?;
1176 let expected_crc = u32::from_le_bytes(bytes[..4].try_into().expect("four bytes"));
1177 let expected_size = u32::from_le_bytes(bytes[4..].try_into().expect("four bytes"));
1178 let actual_crc = checksum.finish();
1179 if expected_crc != actual_crc {
1180 return Err(DecodeError::ChecksumMismatch {
1181 member: stream_index,
1182 expected: expected_crc,
1183 actual: actual_crc,
1184 });
1185 }
1186 if expected_size != stream_size as u32 {
1187 return Err(DecodeError::SizeMismatch {
1188 member: stream_index,
1189 expected: expected_size,
1190 actual_mod32: stream_size as u32,
1191 });
1192 }
1193 StreamFooter::Gzip {
1194 crc32: expected_crc,
1195 uncompressed_size: expected_size,
1196 }
1197 }
1198 (Format::Zlib, StreamChecksum::Zlib(checksum)) => {
1199 let expected = u32::from_be_bytes(read_zlib_footer(cursor)?);
1200 let actual = checksum.finish();
1201 if expected != actual {
1202 return Err(DecodeError::InvalidZlib {
1203 offset: footer_offset / 8,
1204 reason: ZlibErrorKind::ChecksumMismatch { expected, actual },
1205 });
1206 }
1207 StreamFooter::Zlib { adler32: expected }
1208 }
1209 (Format::RawDeflate, StreamChecksum::None) => StreamFooter::None,
1210 _ => unreachable!("the stream checksum follows the selected format"),
1211 };
1212 let stream_end = cursor.bit_position();
1213 let compressed_size_in_bits =
1214 stream_end
1215 .checked_sub(header_offset)
1216 .ok_or(DecodeError::Analysis {
1217 reason: AnalysisErrorKind::CounterOverflow {
1218 counter: AnalysisCounter::CompressedBits,
1219 },
1220 })?;
1221 let block_count = state.analysis.blocks.len() - first_block_index;
1222 state.analysis.streams.push(StreamAnalysis {
1223 index: stream_index,
1224 header,
1225 header_offset_in_bits: header_offset,
1226 deflate_offset_in_bits: deflate_offset,
1227 footer_offset_in_bits: footer_offset,
1228 uncompressed_offset_in_bytes: stream_output_start,
1229 footer,
1230 compressed_size_in_bits,
1231 uncompressed_size_in_bytes: stream_size,
1232 first_block_index,
1233 block_count,
1234 });
1235 Ok(())
1236}
1237
1238fn analyze_cursor_mode<C: InputCursor, const CHECK_CONFIGURED_LIMITS: bool>(
1239 cursor: C,
1240 config: &crate::config::Config,
1241 options: AnalyzeOptions,
1242) -> Result<Analysis, DecodeError> {
1243 let mut cursor = AnalysisCursor::new(cursor)?;
1244 let format = resolve_cursor_format(&mut cursor, config.format)?;
1245 let mut state = AnalyzeState {
1246 config,
1247 options,
1248 analysis: Analysis {
1249 format,
1250 streams: Vec::new(),
1251 blocks: Vec::new(),
1252 uncompressed_size_in_bytes: 0,
1253 compressed_size_in_bytes: 0,
1254 backreference_length_counts: [0; 259],
1255 },
1256 total_output: 0,
1257 remaining_backreferences: options.maximum_retained_backreferences,
1258 retained_header_bytes: 0,
1259 };
1260
1261 match format {
1262 Format::Gzip => {
1263 if cursor.is_at_end()? {
1264 return Err(DecodeError::InvalidGzip {
1265 offset: 0,
1266 reason: GzipErrorKind::BadMagic,
1267 });
1268 }
1269 while !cursor.is_at_end()? {
1270 analyze_one_stream::<_, CHECK_CONFIGURED_LIMITS>(&mut cursor, &mut state, format)?;
1271 }
1272 }
1273 Format::Zlib | Format::RawDeflate => {
1274 analyze_one_stream::<_, CHECK_CONFIGURED_LIMITS>(&mut cursor, &mut state, format)?;
1275 if !cursor.is_at_end()? {
1276 return Err(match format {
1277 Format::Zlib => DecodeError::InvalidZlib {
1278 offset: cursor.position(),
1279 reason: ZlibErrorKind::TrailingGarbage,
1280 },
1281 Format::RawDeflate => DecodeError::InvalidDeflate {
1282 bit_offset: cursor.bit_position(),
1283 reason: DeflateErrorKind::TrailingGarbage,
1284 },
1285 Format::Gzip => unreachable!(),
1286 });
1287 }
1288 }
1289 }
1290
1291 cursor.verify_source_unchanged()?;
1292 config.verify_expected_output(state.total_output)?;
1293 state.analysis.uncompressed_size_in_bytes = state.total_output;
1294 state.analysis.compressed_size_in_bytes = cursor.position();
1295 Ok(state.analysis)
1296}
1297
1298fn analyze_cursor<C: InputCursor>(
1299 cursor: C,
1300 config: &crate::config::Config,
1301 options: AnalyzeOptions,
1302) -> Result<Analysis, DecodeError> {
1303 if config.output_limit.is_some() || config.expected_uncompressed_size.is_some() {
1307 analyze_cursor_mode::<_, true>(cursor, config, options)
1308 } else {
1309 analyze_cursor_mode::<_, false>(cursor, config, options)
1310 }
1311}
1312
1313pub(crate) fn analyze_source<R: ReadAt + ?Sized>(
1314 source: &R,
1315 config: &crate::config::Config,
1316 options: AnalyzeOptions,
1317) -> Result<Analysis, DecodeError> {
1318 let cursor = SourceCursor::new(source, config.input_page_size)?;
1319 if cursor.length() > u64::MAX / 8 {
1320 return Err(DecodeError::Analysis {
1321 reason: AnalysisErrorKind::CounterOverflow {
1322 counter: AnalysisCounter::CompressedBits,
1323 },
1324 });
1325 }
1326 analyze_cursor(cursor, config, options)
1327}
1328
1329pub(crate) fn analyze_stream<R: Read>(
1330 source: R,
1331 config: &crate::config::Config,
1332 options: AnalyzeOptions,
1333) -> Result<Analysis, DecodeError> {
1334 analyze_cursor(
1335 StreamCursor::new(source, config.input_page_size),
1336 config,
1337 options,
1338 )
1339}
1340
1341#[cfg(test)]
1342mod tests {
1343 use super::{OutputState, StreamChecksum, WINDOW_SIZE};
1344
1345 fn ordered_history(output: &OutputState) -> Vec<u8> {
1346 let start = output.write_position - output.history_length;
1347 output.bytes[start..output.write_position].to_vec()
1348 }
1349
1350 #[test]
1351 fn bulk_match_copy_matches_naive_overlap_across_buffer_rolls() {
1352 let mut expected: Vec<u8> = (0..WINDOW_SIZE + 173)
1353 .map(|index| (index.wrapping_mul(37) >> 3) as u8)
1354 .collect();
1355 let mut output = OutputState::new(WINDOW_SIZE, StreamChecksum::None);
1356 output.append_bytes(&expected);
1357
1358 for (distance, length) in [
1359 (1, 258),
1360 (3, 257),
1361 (257, 258),
1362 (258, 17),
1363 (WINDOW_SIZE - 1, 258),
1364 (WINDOW_SIZE, 258),
1365 ] {
1366 for _ in 0..length {
1367 let byte = expected[expected.len() - distance];
1368 expected.push(byte);
1369 }
1370 output.copy_match(distance, length);
1371 let suffix = &expected[expected.len() - WINDOW_SIZE..];
1372 assert_eq!(ordered_history(&output), suffix);
1373 }
1374 }
1375}