1use crate::{DeflateIndex, Format, IndexError, IndexKind};
2use std::error::Error;
3use std::fmt::{self, Display, Formatter};
4use std::io;
5use std::sync::Arc;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9#[non_exhaustive]
10pub enum AnalysisResource {
11 Streams,
13 Blocks,
15 HeaderBytes,
17 AlphabetCodeLengths,
19 Backreferences,
21}
22
23impl Display for AnalysisResource {
24 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
25 formatter.write_str(match self {
26 Self::Streams => "streams",
27 Self::Blocks => "DEFLATE blocks",
28 Self::HeaderBytes => "gzip header bytes",
29 Self::AlphabetCodeLengths => "Huffman code lengths",
30 Self::Backreferences => "back-reference records",
31 })
32 }
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37#[non_exhaustive]
38pub enum AnalysisCounter {
39 CompressedBits,
41 DecompressedBytes,
43 StructuralItems,
45}
46
47impl Display for AnalysisCounter {
48 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
49 formatter.write_str(match self {
50 Self::CompressedBits => "compressed-bit",
51 Self::DecompressedBytes => "decompressed-byte",
52 Self::StructuralItems => "structural-item",
53 })
54 }
55}
56
57#[derive(Clone, Debug, Eq, PartialEq)]
59#[non_exhaustive]
60pub enum AnalysisErrorKind {
61 ResourceLimit {
63 resource: AnalysisResource,
65 limit: usize,
67 },
68 AllocationFailed {
70 resource: AnalysisResource,
72 additional: usize,
74 },
75 CounterOverflow {
77 counter: AnalysisCounter,
79 },
80}
81
82impl Display for AnalysisErrorKind {
83 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
84 match self {
85 Self::ResourceLimit { resource, limit } => {
86 write!(
87 formatter,
88 "{resource} exceeded the configured limit of {limit}"
89 )
90 }
91 Self::AllocationFailed {
92 resource,
93 additional,
94 } => write!(
95 formatter,
96 "could not reserve space for {additional} additional {resource}"
97 ),
98 Self::CounterOverflow { counter } => {
99 write!(formatter, "the {counter} analysis counter overflowed")
100 }
101 }
102 }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq)]
107#[non_exhaustive]
108pub enum GzipErrorKind {
109 BadMagic,
111 UnsupportedCompressionMethod(u8),
113 ReservedFlags(u8),
115 HeaderChecksumMismatch {
117 expected: u16,
119 actual: u16,
121 },
122 UnterminatedHeaderField,
124 Truncated,
126 TrailingGarbage,
128}
129
130impl Display for GzipErrorKind {
131 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
132 match self {
133 Self::BadMagic => formatter.write_str("missing gzip magic bytes"),
134 Self::UnsupportedCompressionMethod(method) => {
135 write!(formatter, "unsupported gzip compression method {method}")
136 }
137 Self::ReservedFlags(flags) => {
138 write!(formatter, "reserved gzip flag bits are set: {flags:#04x}")
139 }
140 Self::HeaderChecksumMismatch { expected, actual } => write!(
141 formatter,
142 "gzip header checksum mismatch: expected {expected:#06x}, got {actual:#06x}"
143 ),
144 Self::UnterminatedHeaderField => formatter.write_str("unterminated gzip header field"),
145 Self::Truncated => formatter.write_str("truncated gzip header or footer"),
146 Self::TrailingGarbage => formatter.write_str("trailing non-gzip data"),
147 }
148 }
149}
150
151#[derive(Clone, Debug, Eq, PartialEq)]
153#[non_exhaustive]
154pub enum ZlibErrorKind {
155 UnsupportedCompressionMethod(u8),
157 UnsupportedWindowSize(u8),
159 BadHeaderCheck,
161 PresetDictionary,
163 Truncated,
165 ChecksumMismatch {
167 expected: u32,
169 actual: u32,
171 },
172 TrailingGarbage,
174}
175
176impl Display for ZlibErrorKind {
177 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
178 match self {
179 Self::UnsupportedCompressionMethod(method) => {
180 write!(formatter, "unsupported zlib compression method {method}")
181 }
182 Self::UnsupportedWindowSize(cinfo) => {
183 write!(formatter, "unsupported zlib CINFO window value {cinfo}")
184 }
185 Self::BadHeaderCheck => formatter.write_str("invalid zlib FCHECK header residue"),
186 Self::PresetDictionary => {
187 formatter.write_str("zlib preset dictionaries are not supported")
188 }
189 Self::Truncated => formatter.write_str("truncated zlib header or trailer"),
190 Self::ChecksumMismatch { expected, actual } => write!(
191 formatter,
192 "zlib Adler-32 mismatch: expected {expected:#010x}, got {actual:#010x}"
193 ),
194 Self::TrailingGarbage => formatter.write_str("trailing data after zlib stream"),
195 }
196 }
197}
198
199#[derive(Clone, Debug, Eq, PartialEq)]
201#[non_exhaustive]
202pub enum DeflateErrorKind {
203 InvalidData,
205 UnexpectedDictionary,
207 BackendStatus(i32),
209 Truncated,
211 Stalled,
213 TrailingGarbage,
215}
216
217impl Display for DeflateErrorKind {
218 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
219 match self {
220 Self::InvalidData => formatter.write_str("invalid DEFLATE data"),
221 Self::UnexpectedDictionary => {
222 formatter.write_str("DEFLATE stream requested a preset dictionary")
223 }
224 Self::BackendStatus(status) => {
225 write!(formatter, "unexpected DEFLATE backend status {status}")
226 }
227 Self::Truncated => formatter.write_str("truncated DEFLATE stream"),
228 Self::Stalled => formatter.write_str("DEFLATE decoder made no progress"),
229 Self::TrailingGarbage => formatter.write_str("trailing data after raw DEFLATE stream"),
230 }
231 }
232}
233
234#[derive(Clone, Debug)]
239#[non_exhaustive]
240pub enum DecodeError {
241 Io {
243 offset: Option<u64>,
245 source: Arc<io::Error>,
247 },
248 InvalidGzip {
250 offset: u64,
252 reason: GzipErrorKind,
254 },
255 UnrecognizedFormat,
257 InvalidZlib {
259 offset: u64,
261 reason: ZlibErrorKind,
263 },
264 InvalidDeflate {
266 bit_offset: u64,
268 reason: DeflateErrorKind,
270 },
271 Analysis {
273 reason: AnalysisErrorKind,
275 },
276 ChecksumMismatch {
278 member: u64,
280 expected: u32,
282 actual: u32,
284 },
285 SizeMismatch {
287 member: u64,
289 expected: u32,
291 actual_mod32: u32,
293 },
294 OutputLimitExceeded {
296 limit: u64,
298 },
299 UnexpectedOutputSize {
301 expected: u64,
303 actual: u64,
305 },
306 IndexBoundaryMismatch {
308 expected_bit_offset: u64,
310 actual_bit_offset: u64,
312 },
313 IndexOutputMismatch {
315 checkpoint_bit_offset: u64,
317 expected_bytes: u64,
319 actual_bytes: u64,
321 },
322 IndexLineMismatch {
324 checkpoint_byte_offset: u64,
326 expected_lines: u64,
328 actual_lines: u64,
330 },
331 IndexTotalLineMismatch {
333 expected_lines: u64,
335 actual_lines: u64,
337 },
338 WorkerPanicked,
340 Cancelled,
342}
343
344impl DecodeError {
345 pub(crate) fn input_io(offset: u64, source: io::Error) -> Self {
346 Self::Io {
347 offset: Some(offset),
348 source: Arc::new(source),
349 }
350 }
351
352 pub(crate) fn output_io(source: io::Error) -> Self {
353 Self::Io {
354 offset: None,
355 source: Arc::new(source),
356 }
357 }
358
359 pub(crate) fn io_kind(&self) -> io::ErrorKind {
360 match self {
361 Self::Io { source, .. } => source.kind(),
362 Self::InvalidGzip {
363 reason: GzipErrorKind::Truncated,
364 ..
365 }
366 | Self::InvalidZlib {
367 reason: ZlibErrorKind::Truncated,
368 ..
369 }
370 | Self::InvalidDeflate {
371 reason: DeflateErrorKind::Truncated,
372 ..
373 } => io::ErrorKind::UnexpectedEof,
374 Self::InvalidGzip { .. }
375 | Self::UnrecognizedFormat
376 | Self::InvalidZlib { .. }
377 | Self::InvalidDeflate { .. }
378 | Self::ChecksumMismatch { .. }
379 | Self::SizeMismatch { .. }
380 | Self::UnexpectedOutputSize { .. }
381 | Self::IndexBoundaryMismatch { .. }
382 | Self::IndexOutputMismatch { .. }
383 | Self::IndexLineMismatch { .. }
384 | Self::IndexTotalLineMismatch { .. } => io::ErrorKind::InvalidData,
385 Self::OutputLimitExceeded { .. }
386 | Self::Analysis {
387 reason:
388 AnalysisErrorKind::ResourceLimit { .. } | AnalysisErrorKind::AllocationFailed { .. },
389 } => io::ErrorKind::FileTooLarge,
390 Self::Analysis {
391 reason: AnalysisErrorKind::CounterOverflow { .. },
392 } => io::ErrorKind::InvalidData,
393 Self::WorkerPanicked => io::ErrorKind::Other,
394 Self::Cancelled => io::ErrorKind::Interrupted,
395 }
396 }
397
398 pub(crate) fn to_io_error(&self) -> io::Error {
399 io::Error::new(self.io_kind(), self.clone())
400 }
401}
402
403impl Display for DecodeError {
404 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
405 match self {
406 Self::Io {
407 offset: Some(offset),
408 source,
409 } => write!(
410 formatter,
411 "I/O error at compressed offset {offset}: {source}"
412 ),
413 Self::Io {
414 offset: None,
415 source,
416 } => write!(formatter, "output I/O error: {source}"),
417 Self::InvalidGzip { offset, reason } => {
418 write!(formatter, "invalid gzip data at byte {offset}: {reason}")
419 }
420 Self::UnrecognizedFormat => {
421 formatter.write_str("input is neither recognizable gzip nor zlib data")
422 }
423 Self::InvalidZlib { offset, reason } => {
424 write!(formatter, "invalid zlib data at byte {offset}: {reason}")
425 }
426 Self::InvalidDeflate { bit_offset, reason } => {
427 write!(
428 formatter,
429 "invalid DEFLATE data at bit {bit_offset}: {reason}"
430 )
431 }
432 Self::Analysis { reason } => write!(formatter, "analysis failed: {reason}"),
433 Self::ChecksumMismatch {
434 member,
435 expected,
436 actual,
437 } => write!(
438 formatter,
439 "gzip member {member} CRC32 mismatch: expected {expected:#010x}, got {actual:#010x}"
440 ),
441 Self::SizeMismatch {
442 member,
443 expected,
444 actual_mod32,
445 } => write!(
446 formatter,
447 "gzip member {member} ISIZE mismatch: expected {expected}, got {actual_mod32}"
448 ),
449 Self::OutputLimitExceeded { limit } => {
450 write!(formatter, "decoded output exceeded the {limit}-byte limit")
451 }
452 Self::UnexpectedOutputSize { expected, actual } => write!(
453 formatter,
454 "decoded output size mismatch: expected {expected} bytes, got {actual}"
455 ),
456 Self::IndexBoundaryMismatch {
457 expected_bit_offset,
458 actual_bit_offset,
459 } => write!(
460 formatter,
461 "index checkpoint at bit {expected_bit_offset} did not match the inflater boundary at bit {actual_bit_offset}"
462 ),
463 Self::IndexOutputMismatch {
464 checkpoint_bit_offset,
465 expected_bytes,
466 actual_bytes,
467 } => write!(
468 formatter,
469 "index span ending at bit {checkpoint_bit_offset} declared {expected_bytes} output bytes but produced {actual_bytes}"
470 ),
471 Self::IndexLineMismatch {
472 checkpoint_byte_offset,
473 expected_lines,
474 actual_lines,
475 } => write!(
476 formatter,
477 "index checkpoint at decoded byte {checkpoint_byte_offset} declared {expected_lines} preceding newlines but output contains {actual_lines}"
478 ),
479 Self::IndexTotalLineMismatch {
480 expected_lines,
481 actual_lines,
482 } => write!(
483 formatter,
484 "index declared {expected_lines} total newlines but output contains {actual_lines}"
485 ),
486 Self::WorkerPanicked => formatter.write_str("a decoder worker panicked"),
487 Self::Cancelled => formatter.write_str("decoding was cancelled"),
488 }
489 }
490}
491
492impl Error for DecodeError {
493 fn source(&self) -> Option<&(dyn Error + 'static)> {
494 match self {
495 Self::Io { source, .. } => Some(source.as_ref()),
496 _ => None,
497 }
498 }
499}
500
501#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
503pub struct DecodeReport {
504 pub compressed_bytes: u64,
506 pub decompressed_bytes: u64,
508 pub member_count: u64,
513 pub decoder_threads: usize,
515 pub format: Format,
517 pub line_count: Option<u64>,
524}
525
526impl AsRef<DecodeReport> for DecodeReport {
527 fn as_ref(&self) -> &DecodeReport {
528 self
529 }
530}
531
532#[derive(Clone, Debug, Eq, PartialEq)]
534pub struct IndexedDecodeReport {
535 pub decode: DecodeReport,
537 pub index: DeflateIndex,
539}
540
541impl IndexedDecodeReport {
542 #[must_use]
544 pub const fn report(&self) -> &DecodeReport {
545 &self.decode
546 }
547
548 #[must_use]
550 pub const fn index(&self) -> &DeflateIndex {
551 &self.index
552 }
553
554 #[must_use]
556 pub fn into_parts(self) -> (DecodeReport, DeflateIndex) {
557 (self.decode, self.index)
558 }
559}
560
561impl AsRef<DecodeReport> for IndexedDecodeReport {
562 fn as_ref(&self) -> &DecodeReport {
563 &self.decode
564 }
565}
566
567#[derive(Clone, Debug)]
569#[non_exhaustive]
570pub enum IndexingError {
571 Decode(DecodeError),
573 Index(IndexError),
575}
576
577#[derive(Clone, Debug)]
582#[non_exhaustive]
583pub enum IndexDecodeError {
584 Decode(DecodeError),
586 Index(IndexError),
588 FormatMismatch {
590 selected: Format,
592 indexed: IndexKind,
594 },
595}
596
597impl From<DecodeError> for IndexDecodeError {
598 fn from(error: DecodeError) -> Self {
599 Self::Decode(error)
600 }
601}
602
603impl From<IndexError> for IndexDecodeError {
604 fn from(error: IndexError) -> Self {
605 Self::Index(error)
606 }
607}
608
609impl Display for IndexDecodeError {
610 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
611 match self {
612 Self::Decode(error) => Display::fmt(error, formatter),
613 Self::Index(error) => {
614 write!(formatter, "index-driven decode rejected the index: {error}")
615 }
616 Self::FormatMismatch { selected, indexed } => write!(
617 formatter,
618 "decoder selected {selected}, but the index describes {indexed:?} data"
619 ),
620 }
621 }
622}
623
624impl Error for IndexDecodeError {
625 fn source(&self) -> Option<&(dyn Error + 'static)> {
626 match self {
627 Self::Decode(error) => Some(error),
628 Self::Index(error) => Some(error),
629 Self::FormatMismatch { .. } => None,
630 }
631 }
632}
633
634impl IndexingError {
635 pub(crate) fn to_io_error(&self) -> io::Error {
636 match self {
637 Self::Decode(error) => error.to_io_error(),
638 Self::Index(_) => io::Error::other(self.clone()),
639 }
640 }
641}
642
643impl From<DecodeError> for IndexingError {
644 fn from(error: DecodeError) -> Self {
645 Self::Decode(error)
646 }
647}
648
649impl From<IndexError> for IndexingError {
650 fn from(error: IndexError) -> Self {
651 Self::Index(error)
652 }
653}
654
655impl Display for IndexingError {
656 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
657 match self {
658 Self::Decode(error) => Display::fmt(error, formatter),
659 Self::Index(error) => write!(formatter, "index construction failed: {error}"),
660 }
661 }
662}
663
664impl Error for IndexingError {
665 fn source(&self) -> Option<&(dyn Error + 'static)> {
666 match self {
667 Self::Decode(error) => Some(error),
668 Self::Index(error) => Some(error),
669 }
670 }
671}