Skip to main content

structured_zstd/decoding/
errors.rs

1//! Errors that might occur while decoding zstd formatted data
2
3use crate::bit_io::GetBitsError;
4use crate::blocks::block::BlockType;
5use crate::blocks::literals_section::LiteralsSectionType;
6use crate::io::Error;
7use alloc::vec::Vec;
8use core::fmt;
9#[cfg(feature = "std")]
10use std::error::Error as StdError;
11
12#[derive(Debug)]
13#[non_exhaustive]
14pub enum FrameDescriptorError {
15    InvalidFrameContentSizeFlag { got: u8 },
16}
17
18impl fmt::Display for FrameDescriptorError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            Self::InvalidFrameContentSizeFlag { got } => write!(
22                f,
23                "Invalid Frame_Content_Size_Flag; Is: {got}, Should be one of: 0, 1, 2, 3"
24            ),
25        }
26    }
27}
28
29#[cfg(feature = "std")]
30impl StdError for FrameDescriptorError {}
31
32#[derive(Debug)]
33#[non_exhaustive]
34pub enum FrameHeaderError {
35    WindowTooBig { got: u64 },
36    WindowTooSmall { got: u64 },
37    FrameDescriptorError(FrameDescriptorError),
38    DictIdTooSmall { got: usize, expected: usize },
39    MismatchedFrameSize { got: usize, expected: u8 },
40    FrameSizeIsZero,
41    InvalidFrameSize { got: u8 },
42}
43
44impl fmt::Display for FrameHeaderError {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        match self {
47            Self::WindowTooBig { got } => write!(
48                f,
49                "window_size bigger than allowed maximum. Is: {}, Should be lower than: {}",
50                got,
51                crate::common::MAX_WINDOW_SIZE
52            ),
53            Self::WindowTooSmall { got } => write!(
54                f,
55                "window_size smaller than allowed minimum. Is: {}, Should be greater than: {}",
56                got,
57                crate::common::MIN_WINDOW_SIZE
58            ),
59            Self::FrameDescriptorError(e) => write!(f, "{e:?}"),
60            Self::DictIdTooSmall { got, expected } => write!(
61                f,
62                "Not enough bytes in dict_id. Is: {got}, Should be: {expected}"
63            ),
64            Self::MismatchedFrameSize { got, expected } => write!(
65                f,
66                "frame_content_size does not have the right length. Is: {got}, Should be: {expected}"
67            ),
68            Self::FrameSizeIsZero => write!(f, "frame_content_size was zero"),
69            Self::InvalidFrameSize { got } => write!(
70                f,
71                "Invalid frame_content_size. Is: {got}, Should be one of 1, 2, 4, 8 bytes"
72            ),
73        }
74    }
75}
76
77#[cfg(feature = "std")]
78impl StdError for FrameHeaderError {
79    fn source(&self) -> Option<&(dyn StdError + 'static)> {
80        match self {
81            FrameHeaderError::FrameDescriptorError(source) => Some(source),
82            _ => None,
83        }
84    }
85}
86
87impl From<FrameDescriptorError> for FrameHeaderError {
88    fn from(error: FrameDescriptorError) -> Self {
89        Self::FrameDescriptorError(error)
90    }
91}
92
93#[derive(Debug)]
94#[non_exhaustive]
95pub enum ReadFrameHeaderError {
96    MagicNumberReadError(Error),
97    BadMagicNumber(u32),
98    FrameDescriptorReadError(Error),
99    InvalidFrameDescriptor(FrameDescriptorError),
100    WindowDescriptorReadError(Error),
101    DictionaryIdReadError(Error),
102    FrameContentSizeReadError(Error),
103    SkipFrame { magic_number: u32, length: u32 },
104}
105
106impl fmt::Display for ReadFrameHeaderError {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            Self::MagicNumberReadError(e) => write!(f, "Error while reading magic number: {e}"),
110            Self::BadMagicNumber(e) => write!(f, "Read wrong magic number: 0x{e:X}"),
111            Self::FrameDescriptorReadError(e) => {
112                write!(f, "Error while reading frame descriptor: {e}")
113            }
114            Self::InvalidFrameDescriptor(e) => write!(f, "{e:?}"),
115            Self::WindowDescriptorReadError(e) => {
116                write!(f, "Error while reading window descriptor: {e}")
117            }
118            Self::DictionaryIdReadError(e) => write!(f, "Error while reading dictionary id: {e}"),
119            Self::FrameContentSizeReadError(e) => {
120                write!(f, "Error while reading frame content size: {e}")
121            }
122            Self::SkipFrame {
123                magic_number,
124                length,
125            } => write!(
126                f,
127                "SkippableFrame encountered with MagicNumber 0x{magic_number:X} and length {length} bytes"
128            ),
129        }
130    }
131}
132
133#[cfg(feature = "std")]
134impl StdError for ReadFrameHeaderError {
135    fn source(&self) -> Option<&(dyn StdError + 'static)> {
136        match self {
137            ReadFrameHeaderError::MagicNumberReadError(source) => Some(source),
138            ReadFrameHeaderError::FrameDescriptorReadError(source) => Some(source),
139            ReadFrameHeaderError::InvalidFrameDescriptor(source) => Some(source),
140            ReadFrameHeaderError::WindowDescriptorReadError(source) => Some(source),
141            ReadFrameHeaderError::DictionaryIdReadError(source) => Some(source),
142            ReadFrameHeaderError::FrameContentSizeReadError(source) => Some(source),
143            _ => None,
144        }
145    }
146}
147
148impl From<FrameDescriptorError> for ReadFrameHeaderError {
149    fn from(error: FrameDescriptorError) -> Self {
150        Self::InvalidFrameDescriptor(error)
151    }
152}
153
154#[derive(Debug)]
155#[non_exhaustive]
156pub enum BlockHeaderReadError {
157    ReadError(Error),
158    FoundReservedBlock,
159    BlockTypeError(BlockTypeError),
160    BlockSizeError(BlockSizeError),
161}
162
163#[cfg(feature = "std")]
164impl std::error::Error for BlockHeaderReadError {
165    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
166        match self {
167            BlockHeaderReadError::ReadError(source) => Some(source),
168            BlockHeaderReadError::BlockTypeError(source) => Some(source),
169            BlockHeaderReadError::BlockSizeError(source) => Some(source),
170            BlockHeaderReadError::FoundReservedBlock => None,
171        }
172    }
173}
174
175impl ::core::fmt::Display for BlockHeaderReadError {
176    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
177        match self {
178            BlockHeaderReadError::ReadError(_) => write!(f, "Error while reading the block header"),
179            BlockHeaderReadError::FoundReservedBlock => write!(
180                f,
181                "Reserved block occured. This is considered corruption by the documentation"
182            ),
183            BlockHeaderReadError::BlockTypeError(e) => write!(f, "Error getting block type: {e}"),
184            BlockHeaderReadError::BlockSizeError(e) => {
185                write!(f, "Error getting block content size: {e}")
186            }
187        }
188    }
189}
190
191impl From<Error> for BlockHeaderReadError {
192    fn from(val: Error) -> Self {
193        Self::ReadError(val)
194    }
195}
196
197impl From<BlockTypeError> for BlockHeaderReadError {
198    fn from(val: BlockTypeError) -> Self {
199        Self::BlockTypeError(val)
200    }
201}
202
203impl From<BlockSizeError> for BlockHeaderReadError {
204    fn from(val: BlockSizeError) -> Self {
205        Self::BlockSizeError(val)
206    }
207}
208
209#[derive(Debug)]
210#[non_exhaustive]
211pub enum BlockTypeError {
212    InvalidBlocktypeNumber { num: u8 },
213}
214
215#[cfg(feature = "std")]
216impl std::error::Error for BlockTypeError {}
217
218impl core::fmt::Display for BlockTypeError {
219    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220        match self {
221            BlockTypeError::InvalidBlocktypeNumber { num } => {
222                write!(
223                    f,
224                    "Invalid Blocktype number. Is: {num}. Should be one of: 0, 1, 2, 3 (3 is reserved).",
225                )
226            }
227        }
228    }
229}
230
231#[derive(Debug)]
232#[non_exhaustive]
233pub enum BlockSizeError {
234    BlockSizeTooLarge { size: u32 },
235}
236
237#[cfg(feature = "std")]
238impl std::error::Error for BlockSizeError {}
239
240impl core::fmt::Display for BlockSizeError {
241    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242        match self {
243            BlockSizeError::BlockSizeTooLarge { size } => {
244                write!(
245                    f,
246                    "Blocksize was bigger than the absolute maximum {} (128kb). Is: {}",
247                    crate::common::MAX_BLOCK_SIZE,
248                    size,
249                )
250            }
251        }
252    }
253}
254
255#[derive(Debug)]
256#[non_exhaustive]
257pub enum DecompressBlockError {
258    BlockContentReadError(Error),
259    MalformedSectionHeader {
260        expected_len: usize,
261        remaining_bytes: usize,
262    },
263    DecompressLiteralsError(DecompressLiteralsError),
264    LiteralsSectionParseError(LiteralsSectionParseError),
265    SequencesHeaderParseError(SequencesHeaderParseError),
266    DecodeSequenceError(DecodeSequenceError),
267    ExecuteSequencesError(ExecuteSequencesError),
268}
269
270#[cfg(feature = "std")]
271impl std::error::Error for DecompressBlockError {
272    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
273        match self {
274            DecompressBlockError::BlockContentReadError(source) => Some(source),
275            DecompressBlockError::DecompressLiteralsError(source) => Some(source),
276            DecompressBlockError::LiteralsSectionParseError(source) => Some(source),
277            DecompressBlockError::SequencesHeaderParseError(source) => Some(source),
278            DecompressBlockError::DecodeSequenceError(source) => Some(source),
279            DecompressBlockError::ExecuteSequencesError(source) => Some(source),
280            _ => None,
281        }
282    }
283}
284
285impl core::fmt::Display for DecompressBlockError {
286    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
287        match self {
288            DecompressBlockError::BlockContentReadError(e) => {
289                write!(f, "Error while reading the block content: {e}")
290            }
291            DecompressBlockError::MalformedSectionHeader {
292                expected_len,
293                remaining_bytes,
294            } => {
295                write!(
296                    f,
297                    "Malformed section header. Says literals would be this long: {expected_len} but there are only {remaining_bytes} bytes left",
298                )
299            }
300            DecompressBlockError::DecompressLiteralsError(e) => write!(f, "{e:?}"),
301            DecompressBlockError::LiteralsSectionParseError(e) => write!(f, "{e:?}"),
302            DecompressBlockError::SequencesHeaderParseError(e) => write!(f, "{e:?}"),
303            DecompressBlockError::DecodeSequenceError(e) => write!(f, "{e:?}"),
304            DecompressBlockError::ExecuteSequencesError(e) => write!(f, "{e:?}"),
305        }
306    }
307}
308
309impl From<Error> for DecompressBlockError {
310    fn from(val: Error) -> Self {
311        Self::BlockContentReadError(val)
312    }
313}
314
315impl From<DecompressLiteralsError> for DecompressBlockError {
316    fn from(val: DecompressLiteralsError) -> Self {
317        Self::DecompressLiteralsError(val)
318    }
319}
320
321impl From<LiteralsSectionParseError> for DecompressBlockError {
322    fn from(val: LiteralsSectionParseError) -> Self {
323        Self::LiteralsSectionParseError(val)
324    }
325}
326
327impl From<SequencesHeaderParseError> for DecompressBlockError {
328    fn from(val: SequencesHeaderParseError) -> Self {
329        Self::SequencesHeaderParseError(val)
330    }
331}
332
333impl From<DecodeSequenceError> for DecompressBlockError {
334    fn from(val: DecodeSequenceError) -> Self {
335        Self::DecodeSequenceError(val)
336    }
337}
338
339impl From<ExecuteSequencesError> for DecompressBlockError {
340    fn from(val: ExecuteSequencesError) -> Self {
341        Self::ExecuteSequencesError(val)
342    }
343}
344
345#[derive(Debug)]
346#[non_exhaustive]
347pub enum DecodeBlockContentError {
348    DecoderStateIsFailed,
349    ExpectedHeaderOfPreviousBlock,
350    ReadError {
351        step: BlockType,
352        source: Error,
353    },
354    DecompressBlockError(DecompressBlockError),
355    /// The block's decompressed payload would not fit in the
356    /// caller-provided output buffer (only reachable via the
357    /// direct-decode path with a fixed-capacity backend). Internal
358    /// diagnostic variant — the frame-level decoder always
359    /// converts this into
360    /// `FrameDecoderError::FrameContentSizeMismatch` before
361    /// returning to the user, so external callers never observe
362    /// it. The `step: BlockType` field is kept for in-crate
363    /// debugging (which decode arm triggered the overshoot) but is
364    /// not part of any caller-visible distinction.
365    BackendOverflow {
366        step: BlockType,
367    },
368}
369
370#[cfg(feature = "std")]
371impl std::error::Error for DecodeBlockContentError {
372    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
373        match self {
374            DecodeBlockContentError::ReadError { step: _, source } => Some(source),
375            DecodeBlockContentError::DecompressBlockError(source) => Some(source),
376            _ => None,
377        }
378    }
379}
380
381impl core::fmt::Display for DecodeBlockContentError {
382    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
383        match self {
384            DecodeBlockContentError::DecoderStateIsFailed => {
385                write!(
386                    f,
387                    "Can't decode next block if failed along the way. Results will be nonsense",
388                )
389            }
390            DecodeBlockContentError::ExpectedHeaderOfPreviousBlock => {
391                write!(
392                    f,
393                    "Can't decode next block body, while expecting to decode the header of the previous block. Results will be nonsense",
394                )
395            }
396            DecodeBlockContentError::ReadError { step, source } => {
397                write!(f, "Error while reading bytes for {step}: {source}",)
398            }
399            DecodeBlockContentError::DecompressBlockError(e) => write!(f, "{e:?}"),
400            DecodeBlockContentError::BackendOverflow { step } => write!(
401                f,
402                "{step} block's decompressed payload exceeds the caller-provided output buffer",
403            ),
404        }
405    }
406}
407
408impl From<DecompressBlockError> for DecodeBlockContentError {
409    fn from(val: DecompressBlockError) -> Self {
410        Self::DecompressBlockError(val)
411    }
412}
413
414#[derive(Debug)]
415#[non_exhaustive]
416pub enum DecodeBufferError {
417    NotEnoughBytesInDictionary {
418        got: usize,
419        need: usize,
420    },
421    OffsetTooBig {
422        offset: usize,
423        buf_len: usize,
424    },
425    ZeroOffset,
426    /// Legacy unit variant kept for binary compatibility with earlier
427    /// snapshots of this enum. Not surfaced from any current
428    /// production path — `BufferBackend::try_extend` /
429    /// `try_extend_from_within` and the new `try_reserve` (used by
430    /// `DecodeBuffer::repeat`) carry their failures through the
431    /// richer [`Self::OutputBufferOverflow`] variant below, which
432    /// reports the offending `tail` / `requested` / `capacity`
433    /// triple. New code should pattern-match on
434    /// `OutputBufferOverflow`; this unit variant is retained to keep
435    /// the `#[non_exhaustive]` enum's existing discriminant set
436    /// stable.
437    BackendOverflow,
438    /// Repeat-side match copy would write past the writable tail of
439    /// a fixed-capacity backend (`UserSliceBackend`). Surfaced by
440    /// [`super::decode_buffer::DecodeBuffer::repeat`] / `_lookahead`
441    /// when the new `BufferBackend::try_reserve` rejects the
442    /// pre-write capacity check — keeping the safe public decode
443    /// APIs error-returning instead of panicking via the per-call
444    /// `assert!` inside `extend_from_within_unchecked`. Growable
445    /// backends (`FlatBuf`, `RingBuffer`) never produce this; their
446    /// `try_reserve` falls through to infallible `reserve`.
447    OutputBufferOverflow {
448        tail: usize,
449        requested: usize,
450        capacity: usize,
451    },
452}
453
454#[cfg(feature = "std")]
455impl std::error::Error for DecodeBufferError {}
456
457impl core::fmt::Display for DecodeBufferError {
458    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
459        match self {
460            DecodeBufferError::NotEnoughBytesInDictionary { got, need } => {
461                write!(
462                    f,
463                    "Need {need} bytes from the dictionary but it is only {got} bytes long",
464                )
465            }
466            DecodeBufferError::OffsetTooBig { offset, buf_len } => {
467                write!(f, "offset: {offset} bigger than buffer: {buf_len}",)
468            }
469            DecodeBufferError::ZeroOffset => {
470                write!(f, "Illegal offset: 0 found")
471            }
472            DecodeBufferError::BackendOverflow => {
473                write!(
474                    f,
475                    "Match repeat would overflow the output buffer's fixed capacity"
476                )
477            }
478            DecodeBufferError::OutputBufferOverflow {
479                tail,
480                requested,
481                capacity,
482            } => {
483                write!(
484                    f,
485                    "Match repeat would write past fixed-capacity buffer: tail={tail}, requested={requested}, capacity={capacity}"
486                )
487            }
488        }
489    }
490}
491
492#[derive(Debug)]
493#[non_exhaustive]
494pub enum DictionaryDecodeError {
495    BadMagicNum { got: [u8; 4] },
496    DictionaryTooSmall { got: usize, need: usize },
497    ZeroDictionaryId,
498    ZeroRepeatOffsetInDictionary { index: u8 },
499    FSETableError(FSETableError),
500    HuffmanTableError(HuffmanTableError),
501}
502
503#[cfg(feature = "std")]
504impl std::error::Error for DictionaryDecodeError {
505    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
506        match self {
507            DictionaryDecodeError::FSETableError(source) => Some(source),
508            DictionaryDecodeError::HuffmanTableError(source) => Some(source),
509            _ => None,
510        }
511    }
512}
513
514impl core::fmt::Display for DictionaryDecodeError {
515    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
516        match self {
517            DictionaryDecodeError::BadMagicNum { got } => {
518                write!(
519                    f,
520                    "Bad magic_num at start of the dictionary; Got: {:#04X?}, Expected: {:#04x?}",
521                    got,
522                    crate::decoding::dictionary::MAGIC_NUM,
523                )
524            }
525            DictionaryDecodeError::DictionaryTooSmall { got, need } => {
526                write!(
527                    f,
528                    "Dictionary is too small: got {got} bytes, need at least {need} bytes",
529                )
530            }
531            DictionaryDecodeError::ZeroDictionaryId => {
532                write!(f, "Dictionary id must be non-zero")
533            }
534            DictionaryDecodeError::ZeroRepeatOffsetInDictionary { index } => {
535                write!(f, "Dictionary repeat offset rep{index} must be non-zero")
536            }
537            DictionaryDecodeError::FSETableError(e) => write!(f, "{e:?}"),
538            DictionaryDecodeError::HuffmanTableError(e) => write!(f, "{e:?}"),
539        }
540    }
541}
542
543impl From<FSETableError> for DictionaryDecodeError {
544    fn from(val: FSETableError) -> Self {
545        Self::FSETableError(val)
546    }
547}
548
549impl From<HuffmanTableError> for DictionaryDecodeError {
550    fn from(val: HuffmanTableError) -> Self {
551        Self::HuffmanTableError(val)
552    }
553}
554
555#[derive(Debug)]
556#[non_exhaustive]
557pub enum FrameDecoderError {
558    ReadFrameHeaderError(ReadFrameHeaderError),
559    FrameHeaderError(FrameHeaderError),
560    WindowSizeTooBig {
561        requested: u64,
562    },
563    DictionaryDecodeError(DictionaryDecodeError),
564    FailedToReadBlockHeader(BlockHeaderReadError),
565    FailedToReadBlockBody(DecodeBlockContentError),
566    FailedToReadChecksum(Error),
567    NotYetInitialized,
568    FailedToInitialize(FrameHeaderError),
569    FailedToDrainDecodebuffer(Error),
570    FailedToSkipFrame,
571    TargetTooSmall,
572    /// Decoded block sizes don't sum to the frame's declared
573    /// `frame_content_size` (either a block claims to expand past
574    /// FCS, or the stream ends before reaching FCS). Indicates a
575    /// malformed or corrupt frame — distinct from
576    /// [`Self::TargetTooSmall`] (which is the caller's
577    /// responsibility) so callers can tell decoder-side issues
578    /// apart from their own buffer sizing mistakes.
579    FrameContentSizeMismatch {
580        declared: u64,
581        produced: u64,
582    },
583    DictNotProvided {
584        dict_id: u32,
585    },
586    DictIdMismatch {
587        expected: u32,
588        provided: u32,
589    },
590    DictAlreadyRegistered {
591        dict_id: u32,
592    },
593    /// Frame header's `dict_id` did not match the value pinned via
594    /// `FrameDecoder::expect_dict_id`. Returned BEFORE any block
595    /// decode and BEFORE any output is produced — no XXH64 init,
596    /// no partial output. Scratch buffer allocation / reservation
597    /// for the decode pipeline happens during frame-header parsing,
598    /// which is already complete when this validation fires, so
599    /// the cost of scratch sizing is paid even on a mismatched
600    /// header. `expected` is the pinned value (`Some(0)` is
601    /// treated as "no dictionary expected", matching a frame whose
602    /// header omits the optional `Dictionary_ID` field); `found`
603    /// reports what the frame actually carried (`None` when the
604    /// header omits the field, `Some(id)` when it does not).
605    #[cfg(feature = "lsm")]
606    UnexpectedDictId {
607        expected: Option<u32>,
608        found: Option<u32>,
609    },
610    /// Frame header's raw `Window_Descriptor` byte did not match
611    /// the value pinned via `FrameDecoder::expect_window_descriptor`.
612    /// Returned BEFORE any block decode work. Single-segment frames
613    /// (which omit the `Window_Descriptor` byte from the wire) are
614    /// reported via `found: None` so callers can distinguish
615    /// "wrong descriptor" from "no descriptor on the wire".
616    #[cfg(feature = "lsm")]
617    UnexpectedWindowDescriptor {
618        expected: u8,
619        found: Option<u8>,
620    },
621}
622
623#[cfg(feature = "std")]
624impl StdError for FrameDecoderError {
625    fn source(&self) -> Option<&(dyn StdError + 'static)> {
626        match self {
627            FrameDecoderError::ReadFrameHeaderError(source) => Some(source),
628            FrameDecoderError::FrameHeaderError(source) => Some(source),
629            FrameDecoderError::DictionaryDecodeError(source) => Some(source),
630            FrameDecoderError::FailedToReadBlockHeader(source) => Some(source),
631            FrameDecoderError::FailedToReadBlockBody(source) => Some(source),
632            FrameDecoderError::FailedToReadChecksum(source) => Some(source),
633            FrameDecoderError::FailedToInitialize(source) => Some(source),
634            FrameDecoderError::FailedToDrainDecodebuffer(source) => Some(source),
635            _ => None,
636        }
637    }
638}
639
640impl core::fmt::Display for FrameDecoderError {
641    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
642        match self {
643            FrameDecoderError::ReadFrameHeaderError(e) => {
644                write!(f, "{e:?}")
645            }
646            FrameDecoderError::FrameHeaderError(e) => {
647                write!(f, "{e:?}")
648            }
649            FrameDecoderError::WindowSizeTooBig { requested } => {
650                write!(
651                    f,
652                    "Specified window_size is too big; Requested: {}, Allowed: {}",
653                    requested,
654                    crate::common::MAXIMUM_ALLOWED_WINDOW_SIZE,
655                )
656            }
657            FrameDecoderError::DictionaryDecodeError(e) => {
658                write!(f, "{e:?}")
659            }
660            FrameDecoderError::FailedToReadBlockHeader(e) => {
661                write!(f, "Failed to parse/decode block body: {e}")
662            }
663            FrameDecoderError::FailedToReadBlockBody(e) => {
664                write!(f, "Failed to parse block header: {e}")
665            }
666            FrameDecoderError::FailedToReadChecksum(e) => {
667                write!(f, "Failed to read checksum: {e}")
668            }
669            FrameDecoderError::NotYetInitialized => {
670                write!(f, "Decoder must initialized or reset before using it",)
671            }
672            FrameDecoderError::FailedToInitialize(e) => {
673                write!(f, "Decoder encountered error while initializing: {e}")
674            }
675            FrameDecoderError::FailedToDrainDecodebuffer(e) => {
676                write!(
677                    f,
678                    "Decoder encountered error while draining the decodebuffer: {e}",
679                )
680            }
681            FrameDecoderError::FailedToSkipFrame => {
682                write!(
683                    f,
684                    "Failed to skip bytes for the length given in the frame header"
685                )
686            }
687            FrameDecoderError::TargetTooSmall => {
688                write!(
689                    f,
690                    "Target must have at least as many bytes as the content size reported by the frame"
691                )
692            }
693            FrameDecoderError::FrameContentSizeMismatch { declared, produced } => {
694                write!(
695                    f,
696                    "Frame content size mismatch (corrupt frame): declared {declared} bytes, blocks summed to {produced} bytes"
697                )
698            }
699            FrameDecoderError::DictNotProvided { dict_id } => {
700                write!(
701                    f,
702                    "Frame header specified dictionary id 0x{dict_id:X} that wasn't provided via add_dict()/add_dict_from_bytes() (or add_dict_handle() on atomic targets) or reset_with_dict_handle()/decode_all_with_dict_handle()/decode_all_with_dict_bytes()"
703                )
704            }
705            FrameDecoderError::DictIdMismatch { expected, provided } => {
706                write!(
707                    f,
708                    "Frame header dictionary id 0x{expected:X} does not match provided dictionary id 0x{provided:X}"
709                )
710            }
711            FrameDecoderError::DictAlreadyRegistered { dict_id } => {
712                write!(
713                    f,
714                    "Dictionary id 0x{dict_id:X} already registered in decoder"
715                )
716            }
717            #[cfg(feature = "lsm")]
718            FrameDecoderError::UnexpectedDictId { expected, found } => {
719                write!(f, "Frame header dict_id mismatch: expected ")?;
720                match expected {
721                    Some(id) => write!(f, "0x{id:X}")?,
722                    None => write!(f, "<none>")?,
723                }
724                write!(f, ", found ")?;
725                match found {
726                    Some(id) => write!(f, "0x{id:X}"),
727                    None => write!(f, "<none>"),
728                }
729            }
730            #[cfg(feature = "lsm")]
731            FrameDecoderError::UnexpectedWindowDescriptor { expected, found } => {
732                write!(
733                    f,
734                    "Frame header window_descriptor mismatch: expected 0x{expected:02X}, found "
735                )?;
736                match found {
737                    Some(byte) => write!(f, "0x{byte:02X}"),
738                    None => write!(f, "<none> (single-segment frame omits window_descriptor)"),
739                }
740            }
741        }
742    }
743}
744
745impl From<DictionaryDecodeError> for FrameDecoderError {
746    fn from(val: DictionaryDecodeError) -> Self {
747        Self::DictionaryDecodeError(val)
748    }
749}
750
751impl From<BlockHeaderReadError> for FrameDecoderError {
752    fn from(val: BlockHeaderReadError) -> Self {
753        Self::FailedToReadBlockHeader(val)
754    }
755}
756
757impl From<FrameHeaderError> for FrameDecoderError {
758    fn from(val: FrameHeaderError) -> Self {
759        Self::FrameHeaderError(val)
760    }
761}
762
763impl From<ReadFrameHeaderError> for FrameDecoderError {
764    fn from(val: ReadFrameHeaderError) -> Self {
765        Self::ReadFrameHeaderError(val)
766    }
767}
768
769#[derive(Debug)]
770#[non_exhaustive]
771pub enum DecompressLiteralsError {
772    MissingCompressedSize,
773    MissingNumStreams,
774    GetBitsError(GetBitsError),
775    HuffmanTableError(HuffmanTableError),
776    HuffmanDecoderError(HuffmanDecoderError),
777    UninitializedHuffmanTable,
778    MissingBytesForJumpHeader { got: usize },
779    MissingBytesForLiterals { got: usize, needed: usize },
780    ExtraPadding { skipped_bits: i32 },
781    BitstreamReadMismatch { read_til: isize, expected: isize },
782    DecodedLiteralCountMismatch { decoded: usize, expected: usize },
783}
784
785#[cfg(feature = "std")]
786impl std::error::Error for DecompressLiteralsError {
787    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
788        match self {
789            DecompressLiteralsError::GetBitsError(source) => Some(source),
790            DecompressLiteralsError::HuffmanTableError(source) => Some(source),
791            DecompressLiteralsError::HuffmanDecoderError(source) => Some(source),
792            _ => None,
793        }
794    }
795}
796impl core::fmt::Display for DecompressLiteralsError {
797    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
798        match self {
799            DecompressLiteralsError::MissingCompressedSize => {
800                write!(
801                    f,
802                    "compressed size was none even though it must be set to something for compressed literals",
803                )
804            }
805            DecompressLiteralsError::MissingNumStreams => {
806                write!(
807                    f,
808                    "num_streams was none even though it must be set to something (1 or 4) for compressed literals",
809                )
810            }
811            DecompressLiteralsError::GetBitsError(e) => write!(f, "{e:?}"),
812            DecompressLiteralsError::HuffmanTableError(e) => write!(f, "{e:?}"),
813            DecompressLiteralsError::HuffmanDecoderError(e) => write!(f, "{e:?}"),
814            DecompressLiteralsError::UninitializedHuffmanTable => {
815                write!(
816                    f,
817                    "Tried to reuse huffman table but it was never initialized",
818                )
819            }
820            DecompressLiteralsError::MissingBytesForJumpHeader { got } => {
821                write!(f, "Need 6 bytes to decode jump header, got {got} bytes",)
822            }
823            DecompressLiteralsError::MissingBytesForLiterals { got, needed } => {
824                write!(
825                    f,
826                    "Need at least {needed} bytes to decode literals. Have: {got} bytes",
827                )
828            }
829            DecompressLiteralsError::ExtraPadding { skipped_bits } => {
830                write!(
831                    f,
832                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
833                )
834            }
835            DecompressLiteralsError::BitstreamReadMismatch { read_til, expected } => {
836                write!(
837                    f,
838                    "Bitstream was read till: {read_til}, should have been: {expected}",
839                )
840            }
841            DecompressLiteralsError::DecodedLiteralCountMismatch { decoded, expected } => {
842                write!(
843                    f,
844                    "Did not decode enough literals: {decoded}, Should have been: {expected}",
845                )
846            }
847        }
848    }
849}
850
851impl From<HuffmanDecoderError> for DecompressLiteralsError {
852    fn from(val: HuffmanDecoderError) -> Self {
853        Self::HuffmanDecoderError(val)
854    }
855}
856
857impl From<GetBitsError> for DecompressLiteralsError {
858    fn from(val: GetBitsError) -> Self {
859        Self::GetBitsError(val)
860    }
861}
862
863impl From<HuffmanTableError> for DecompressLiteralsError {
864    fn from(val: HuffmanTableError) -> Self {
865        Self::HuffmanTableError(val)
866    }
867}
868
869#[derive(Debug)]
870#[non_exhaustive]
871pub enum ExecuteSequencesError {
872    DecodebufferError(DecodeBufferError),
873    NotEnoughBytesForSequence {
874        wanted: usize,
875        have: usize,
876    },
877    ZeroOffset,
878    /// A donor-shape inline sequence (`BufferBackend::exec_sequence_inline`)
879    /// would have written past the writable tail of a fixed-size backend
880    /// (`UserSliceBackend`). Indicates the frame is corrupt — its sequences
881    /// expand past the declared `frame_content_size` plus the caller-supplied
882    /// `WILDCOPY_OVERLENGTH` slack. The fast-path check is per-sequence; the
883    /// post-block FCS overflow check would also catch the same shape, but the
884    /// per-sequence guard is what keeps the unsafe write surface inside the
885    /// user-provided slice on the way to the post-block check.
886    OutputBufferOverflow {
887        tail: usize,
888        requested: usize,
889        capacity: usize,
890    },
891}
892
893impl core::fmt::Display for ExecuteSequencesError {
894    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
895        match self {
896            ExecuteSequencesError::DecodebufferError(e) => {
897                write!(f, "{e:?}")
898            }
899            ExecuteSequencesError::NotEnoughBytesForSequence { wanted, have } => {
900                write!(
901                    f,
902                    "Sequence wants to copy up to byte {wanted}. Bytes in literalsbuffer: {have}"
903                )
904            }
905            ExecuteSequencesError::ZeroOffset => {
906                write!(f, "Illegal offset: 0 found")
907            }
908            ExecuteSequencesError::OutputBufferOverflow {
909                tail,
910                requested,
911                capacity,
912            } => {
913                write!(
914                    f,
915                    "Donor-path sequence would write past fixed-size buffer: tail={tail}, requested={requested}, capacity={capacity}"
916                )
917            }
918        }
919    }
920}
921
922#[cfg(feature = "std")]
923impl std::error::Error for ExecuteSequencesError {
924    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
925        match self {
926            ExecuteSequencesError::DecodebufferError(source) => Some(source),
927            _ => None,
928        }
929    }
930}
931
932impl From<DecodeBufferError> for ExecuteSequencesError {
933    fn from(val: DecodeBufferError) -> Self {
934        Self::DecodebufferError(val)
935    }
936}
937
938impl From<crate::decoding::buffer_backend::BackendOverflow> for ExecuteSequencesError {
939    fn from(val: crate::decoding::buffer_backend::BackendOverflow) -> Self {
940        Self::OutputBufferOverflow {
941            tail: val.tail,
942            requested: val.requested,
943            capacity: val.capacity,
944        }
945    }
946}
947
948#[derive(Debug)]
949#[non_exhaustive]
950pub enum DecodeSequenceError {
951    GetBitsError(GetBitsError),
952    FSEDecoderError(FSEDecoderError),
953    FSETableError(FSETableError),
954    ExtraPadding { skipped_bits: i32 },
955    UnsupportedOffset { offset_code: u8 },
956    ZeroOffset,
957    NotEnoughBytesForNumSequences,
958    ExtraBits { bits_remaining: isize },
959    MissingCompressionMode,
960    MissingByteForRleLlTable,
961    MissingByteForRleOfTable,
962    MissingByteForRleMlTable,
963}
964
965#[cfg(feature = "std")]
966impl std::error::Error for DecodeSequenceError {
967    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
968        match self {
969            DecodeSequenceError::GetBitsError(source) => Some(source),
970            DecodeSequenceError::FSEDecoderError(source) => Some(source),
971            DecodeSequenceError::FSETableError(source) => Some(source),
972            _ => None,
973        }
974    }
975}
976
977impl core::fmt::Display for DecodeSequenceError {
978    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
979        match self {
980            DecodeSequenceError::GetBitsError(e) => write!(f, "{e:?}"),
981            DecodeSequenceError::FSEDecoderError(e) => write!(f, "{e:?}"),
982            DecodeSequenceError::FSETableError(e) => write!(f, "{e:?}"),
983            DecodeSequenceError::ExtraPadding { skipped_bits } => {
984                write!(
985                    f,
986                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
987                )
988            }
989            DecodeSequenceError::UnsupportedOffset { offset_code } => {
990                write!(
991                    f,
992                    "Do not support offsets bigger than 1<<32; got: {offset_code}",
993                )
994            }
995            DecodeSequenceError::ZeroOffset => write!(
996                f,
997                "Read an offset == 0. That is an illegal value for offsets"
998            ),
999            DecodeSequenceError::NotEnoughBytesForNumSequences => write!(
1000                f,
1001                "Bytestream did not contain enough bytes to decode num_sequences"
1002            ),
1003            DecodeSequenceError::ExtraBits { bits_remaining } => write!(f, "{bits_remaining}"),
1004            DecodeSequenceError::MissingCompressionMode => write!(
1005                f,
1006                "compression modes are none but they must be set to something"
1007            ),
1008            DecodeSequenceError::MissingByteForRleLlTable => {
1009                write!(f, "Need a byte to read for RLE ll table")
1010            }
1011            DecodeSequenceError::MissingByteForRleOfTable => {
1012                write!(f, "Need a byte to read for RLE of table")
1013            }
1014            DecodeSequenceError::MissingByteForRleMlTable => {
1015                write!(f, "Need a byte to read for RLE ml table")
1016            }
1017        }
1018    }
1019}
1020
1021impl From<GetBitsError> for DecodeSequenceError {
1022    fn from(val: GetBitsError) -> Self {
1023        Self::GetBitsError(val)
1024    }
1025}
1026
1027impl From<FSETableError> for DecodeSequenceError {
1028    fn from(val: FSETableError) -> Self {
1029        Self::FSETableError(val)
1030    }
1031}
1032
1033impl From<FSEDecoderError> for DecodeSequenceError {
1034    fn from(val: FSEDecoderError) -> Self {
1035        Self::FSEDecoderError(val)
1036    }
1037}
1038
1039#[derive(Debug)]
1040#[non_exhaustive]
1041pub enum LiteralsSectionParseError {
1042    IllegalLiteralSectionType { got: u8 },
1043    GetBitsError(GetBitsError),
1044    NotEnoughBytes { have: usize, need: u8 },
1045}
1046
1047#[cfg(feature = "std")]
1048impl std::error::Error for LiteralsSectionParseError {
1049    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1050        match self {
1051            LiteralsSectionParseError::GetBitsError(source) => Some(source),
1052            _ => None,
1053        }
1054    }
1055}
1056impl core::fmt::Display for LiteralsSectionParseError {
1057    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1058        match self {
1059            LiteralsSectionParseError::IllegalLiteralSectionType { got } => {
1060                write!(
1061                    f,
1062                    "Illegal literalssectiontype. Is: {got}, must be in: 0, 1, 2, 3"
1063                )
1064            }
1065            LiteralsSectionParseError::GetBitsError(e) => write!(f, "{e:?}"),
1066            LiteralsSectionParseError::NotEnoughBytes { have, need } => {
1067                write!(
1068                    f,
1069                    "Not enough byte to parse the literals section header. Have: {have}, Need: {need}",
1070                )
1071            }
1072        }
1073    }
1074}
1075
1076impl From<GetBitsError> for LiteralsSectionParseError {
1077    fn from(val: GetBitsError) -> Self {
1078        Self::GetBitsError(val)
1079    }
1080}
1081
1082impl core::fmt::Display for LiteralsSectionType {
1083    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
1084        match self {
1085            LiteralsSectionType::Compressed => write!(f, "Compressed"),
1086            LiteralsSectionType::Raw => write!(f, "Raw"),
1087            LiteralsSectionType::RLE => write!(f, "RLE"),
1088            LiteralsSectionType::Treeless => write!(f, "Treeless"),
1089        }
1090    }
1091}
1092
1093#[derive(Debug)]
1094#[non_exhaustive]
1095pub enum SequencesHeaderParseError {
1096    NotEnoughBytes { need_at_least: u8, got: usize },
1097}
1098
1099#[cfg(feature = "std")]
1100impl std::error::Error for SequencesHeaderParseError {}
1101
1102impl core::fmt::Display for SequencesHeaderParseError {
1103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1104        match self {
1105            SequencesHeaderParseError::NotEnoughBytes { need_at_least, got } => {
1106                write!(
1107                    f,
1108                    "source must have at least {need_at_least} bytes to parse header; got {got} bytes",
1109                )
1110            }
1111        }
1112    }
1113}
1114
1115#[derive(Debug)]
1116#[non_exhaustive]
1117pub enum FSETableError {
1118    AccLogIsZero,
1119    AccLogTooBig {
1120        got: u8,
1121        max: u8,
1122    },
1123    GetBitsError(GetBitsError),
1124    ProbabilityCounterMismatch {
1125        got: u32,
1126        expected_sum: u32,
1127        symbol_probabilities: Vec<i32>,
1128    },
1129    TooManySymbols {
1130        got: usize,
1131    },
1132    /// Probability value outside the RFC 8878 §4.1.1 allowed set
1133    /// `{-1, 0, 1..=table_size}`. Carries the violating value, the
1134    /// table size (`1 << accuracy_log`) and `accuracy_log` so the
1135    /// caller can pinpoint the failure without re-deriving the bound.
1136    InvalidProbability {
1137        value: i32,
1138        table_size: u32,
1139        accuracy_log: u8,
1140    },
1141    /// `calc_baseline_and_numbits` produced a state-entry whose bit
1142    /// width exceeds the table's accuracy log, violating the
1143    /// `new_state + (1 << num_bits) - 1 < table_size` invariant that
1144    /// the unchecked `read_entry` decode hot path relies on. The
1145    /// triggering probability is in-range per RFC 8878 §4.1.1; the
1146    /// failure is an internal table-shape inconsistency surfaced
1147    /// against the public `build_from_probabilities` API.
1148    TableInvariantViolation {
1149        prob: i32,
1150        symbol: u8,
1151        num_bits: u8,
1152        accuracy_log: u8,
1153    },
1154}
1155
1156#[cfg(feature = "std")]
1157impl std::error::Error for FSETableError {
1158    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1159        match self {
1160            FSETableError::GetBitsError(source) => Some(source),
1161            _ => None,
1162        }
1163    }
1164}
1165
1166impl core::fmt::Display for FSETableError {
1167    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1168        match self {
1169            FSETableError::AccLogIsZero => write!(f, "Acclog must be at least 1"),
1170            FSETableError::AccLogTooBig { got, max } => {
1171                write!(
1172                    f,
1173                    "Found FSE acc_log: {got} bigger than allowed maximum in this case: {max}"
1174                )
1175            }
1176            FSETableError::GetBitsError(e) => write!(f, "{e:?}"),
1177            FSETableError::ProbabilityCounterMismatch {
1178                got,
1179                expected_sum,
1180                symbol_probabilities,
1181            } => {
1182                write!(
1183                    f,
1184                    "FSE probability sum mismatch: got {got}, expected {expected_sum}. Indicates corrupted data or an invalid distribution\n {symbol_probabilities:?}",
1185                )
1186            }
1187            FSETableError::TooManySymbols { got } => {
1188                write!(
1189                    f,
1190                    "There are too many symbols in this distribution: {got}. Max: 256",
1191                )
1192            }
1193            FSETableError::InvalidProbability {
1194                value,
1195                table_size,
1196                accuracy_log,
1197            } => {
1198                write!(
1199                    f,
1200                    "FSE probability value {value} is outside the RFC 8878 allowed set (must be -1, 0, or in 1..={table_size}; accuracy_log={accuracy_log})",
1201                )
1202            }
1203            FSETableError::TableInvariantViolation {
1204                prob,
1205                symbol,
1206                num_bits,
1207                accuracy_log,
1208            } => {
1209                write!(
1210                    f,
1211                    "FSE table invariant violation: symbol {symbol} (prob {prob}) produced num_bits {num_bits} > accuracy_log {accuracy_log}",
1212                )
1213            }
1214        }
1215    }
1216}
1217
1218impl From<GetBitsError> for FSETableError {
1219    fn from(val: GetBitsError) -> Self {
1220        Self::GetBitsError(val)
1221    }
1222}
1223
1224#[derive(Debug)]
1225#[non_exhaustive]
1226pub enum FSEDecoderError {
1227    GetBitsError(GetBitsError),
1228    TableIsUninitialized,
1229    /// Externally constructed `FSETable` violates the
1230    /// `decode.len() == 1 << accuracy_log` shape invariant. Only
1231    /// reachable under `feature = "fuzz_exports"`, where fuzz
1232    /// harnesses can set the `FSETable.decode` / `accuracy_log`
1233    /// fields directly and skip `build_decoding_table`.
1234    InvalidTableShape {
1235        decode_len: usize,
1236        accuracy_log: u8,
1237    },
1238}
1239
1240#[cfg(feature = "std")]
1241impl std::error::Error for FSEDecoderError {
1242    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1243        match self {
1244            FSEDecoderError::GetBitsError(source) => Some(source),
1245            _ => None,
1246        }
1247    }
1248}
1249
1250impl core::fmt::Display for FSEDecoderError {
1251    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1252        match self {
1253            FSEDecoderError::GetBitsError(e) => write!(f, "{e:?}"),
1254            FSEDecoderError::TableIsUninitialized => {
1255                write!(f, "Tried to use an uninitialized table!")
1256            }
1257            FSEDecoderError::InvalidTableShape {
1258                decode_len,
1259                accuracy_log,
1260            } => match 1usize.checked_shl((*accuracy_log).into()) {
1261                Some(expected) => write!(
1262                    f,
1263                    "FSETable shape invariant violated: decode.len() = {decode_len}, expected 1 << accuracy_log = {expected} (accuracy_log = {accuracy_log})",
1264                ),
1265                None => write!(
1266                    f,
1267                    "FSETable shape invariant violated: decode.len() = {decode_len}, accuracy_log = {accuracy_log} overflows 1 << accuracy_log for usize",
1268                ),
1269            },
1270        }
1271    }
1272}
1273
1274impl From<GetBitsError> for FSEDecoderError {
1275    fn from(val: GetBitsError) -> Self {
1276        Self::GetBitsError(val)
1277    }
1278}
1279
1280#[derive(Debug)]
1281#[non_exhaustive]
1282pub enum HuffmanTableError {
1283    GetBitsError(GetBitsError),
1284    FSEDecoderError(FSEDecoderError),
1285    FSETableError(FSETableError),
1286    SourceIsEmpty,
1287    NotEnoughBytesForWeights {
1288        got_bytes: usize,
1289        expected_bytes: u8,
1290    },
1291    ExtraPadding {
1292        skipped_bits: i32,
1293    },
1294    TooManyWeights {
1295        got: usize,
1296    },
1297    MissingWeights,
1298    LeftoverIsNotAPowerOf2 {
1299        got: u32,
1300    },
1301    NotEnoughBytesToDecompressWeights {
1302        have: usize,
1303        need: usize,
1304    },
1305    FSETableUsedTooManyBytes {
1306        used: usize,
1307        available_bytes: u8,
1308    },
1309    NotEnoughBytesInSource {
1310        got: usize,
1311        need: usize,
1312    },
1313    WeightBiggerThanMaxNumBits {
1314        got: u8,
1315    },
1316    MaxBitsTooHigh {
1317        got: u8,
1318    },
1319}
1320
1321#[cfg(feature = "std")]
1322impl StdError for HuffmanTableError {
1323    fn source(&self) -> Option<&(dyn StdError + 'static)> {
1324        match self {
1325            HuffmanTableError::GetBitsError(source) => Some(source),
1326            HuffmanTableError::FSEDecoderError(source) => Some(source),
1327            HuffmanTableError::FSETableError(source) => Some(source),
1328            _ => None,
1329        }
1330    }
1331}
1332
1333impl core::fmt::Display for HuffmanTableError {
1334    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
1335        match self {
1336            HuffmanTableError::GetBitsError(e) => write!(f, "{e:?}"),
1337            HuffmanTableError::FSEDecoderError(e) => write!(f, "{e:?}"),
1338            HuffmanTableError::FSETableError(e) => write!(f, "{e:?}"),
1339            HuffmanTableError::SourceIsEmpty => write!(f, "Source needs to have at least one byte"),
1340            HuffmanTableError::NotEnoughBytesForWeights {
1341                got_bytes,
1342                expected_bytes,
1343            } => {
1344                write!(
1345                    f,
1346                    "Header says there should be {expected_bytes} bytes for the weights but there are only {got_bytes} bytes in the stream"
1347                )
1348            }
1349            HuffmanTableError::ExtraPadding { skipped_bits } => {
1350                write!(
1351                    f,
1352                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
1353                )
1354            }
1355            HuffmanTableError::TooManyWeights { got } => {
1356                write!(
1357                    f,
1358                    "More than 255 weights decoded (got {got} weights). Stream is probably corrupted",
1359                )
1360            }
1361            HuffmanTableError::MissingWeights => {
1362                write!(f, "Can\'t build huffman table without any weights")
1363            }
1364            HuffmanTableError::LeftoverIsNotAPowerOf2 { got } => {
1365                write!(f, "Leftover must be power of two but is: {got}")
1366            }
1367            HuffmanTableError::NotEnoughBytesToDecompressWeights { have, need } => {
1368                write!(
1369                    f,
1370                    "Not enough bytes in stream to decompress weights. Is: {have}, Should be: {need}",
1371                )
1372            }
1373            HuffmanTableError::FSETableUsedTooManyBytes {
1374                used,
1375                available_bytes,
1376            } => {
1377                write!(
1378                    f,
1379                    "FSE table used more bytes: {used} than were meant to be used for the whole stream of huffman weights ({available_bytes})",
1380                )
1381            }
1382            HuffmanTableError::NotEnoughBytesInSource { got, need } => {
1383                write!(f, "Source needs to have at least {need} bytes, got: {got}",)
1384            }
1385            HuffmanTableError::WeightBiggerThanMaxNumBits { got } => {
1386                write!(
1387                    f,
1388                    "Cant have weight: {} bigger than max_num_bits: {}",
1389                    got,
1390                    crate::huff0::MAX_MAX_NUM_BITS,
1391                )
1392            }
1393            HuffmanTableError::MaxBitsTooHigh { got } => {
1394                write!(
1395                    f,
1396                    "max_bits derived from weights is: {} should be lower than: {}",
1397                    got,
1398                    crate::huff0::MAX_MAX_NUM_BITS,
1399                )
1400            }
1401        }
1402    }
1403}
1404
1405impl From<GetBitsError> for HuffmanTableError {
1406    fn from(val: GetBitsError) -> Self {
1407        Self::GetBitsError(val)
1408    }
1409}
1410
1411impl From<FSEDecoderError> for HuffmanTableError {
1412    fn from(val: FSEDecoderError) -> Self {
1413        Self::FSEDecoderError(val)
1414    }
1415}
1416
1417impl From<FSETableError> for HuffmanTableError {
1418    fn from(val: FSETableError) -> Self {
1419        Self::FSETableError(val)
1420    }
1421}
1422
1423#[derive(Debug)]
1424#[non_exhaustive]
1425pub enum HuffmanDecoderError {
1426    GetBitsError(GetBitsError),
1427}
1428
1429impl core::fmt::Display for HuffmanDecoderError {
1430    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1431        match self {
1432            HuffmanDecoderError::GetBitsError(e) => write!(f, "{e:?}"),
1433        }
1434    }
1435}
1436
1437#[cfg(feature = "std")]
1438impl StdError for HuffmanDecoderError {
1439    fn source(&self) -> Option<&(dyn StdError + 'static)> {
1440        match self {
1441            HuffmanDecoderError::GetBitsError(source) => Some(source),
1442        }
1443    }
1444}
1445
1446impl From<GetBitsError> for HuffmanDecoderError {
1447    fn from(val: GetBitsError) -> Self {
1448        Self::GetBitsError(val)
1449    }
1450}
1451
1452#[cfg(test)]
1453mod tests {
1454    use alloc::{string::ToString, vec};
1455
1456    use super::{
1457        BlockTypeError, DecodeBlockContentError, DecodeSequenceError, DecompressBlockError,
1458        DecompressLiteralsError, FSETableError, FrameDecoderError, HuffmanTableError,
1459    };
1460
1461    #[test]
1462    fn block_and_sequence_display_messages_are_specific() {
1463        assert_eq!(
1464            BlockTypeError::InvalidBlocktypeNumber { num: 7 }.to_string(),
1465            "Invalid Blocktype number. Is: 7. Should be one of: 0, 1, 2, 3 (3 is reserved)."
1466        );
1467        assert_eq!(
1468            DecompressBlockError::MalformedSectionHeader {
1469                expected_len: 12,
1470                remaining_bytes: 3,
1471            }
1472            .to_string(),
1473            "Malformed section header. Says literals would be this long: 12 but there are only 3 bytes left"
1474        );
1475        assert_eq!(
1476            DecodeBlockContentError::ExpectedHeaderOfPreviousBlock.to_string(),
1477            "Can't decode next block body, while expecting to decode the header of the previous block. Results will be nonsense"
1478        );
1479        assert_eq!(
1480            DecodeSequenceError::ExtraPadding { skipped_bits: 11 }.to_string(),
1481            "Padding at the end of the sequence_section was more than a byte long: 11 bits. Probably caused by data corruption"
1482        );
1483    }
1484
1485    #[test]
1486    fn frame_decoder_display_messages_are_specific() {
1487        assert_eq!(
1488            FrameDecoderError::TargetTooSmall.to_string(),
1489            "Target must have at least as many bytes as the content size reported by the frame"
1490        );
1491        assert_eq!(
1492            FrameDecoderError::DictNotProvided { dict_id: 0xABCD }.to_string(),
1493            "Frame header specified dictionary id 0xABCD that wasn't provided via add_dict()/add_dict_from_bytes() (or add_dict_handle() on atomic targets) or reset_with_dict_handle()/decode_all_with_dict_handle()/decode_all_with_dict_bytes()"
1494        );
1495        assert_eq!(
1496            FrameDecoderError::DictIdMismatch {
1497                expected: 0xABCD,
1498                provided: 0x1234
1499            }
1500            .to_string(),
1501            "Frame header dictionary id 0xABCD does not match provided dictionary id 0x1234"
1502        );
1503        assert_eq!(
1504            FrameDecoderError::DictAlreadyRegistered { dict_id: 0xABCD }.to_string(),
1505            "Dictionary id 0xABCD already registered in decoder"
1506        );
1507        assert_eq!(
1508            FrameDecoderError::FrameContentSizeMismatch {
1509                declared: 100,
1510                produced: 87,
1511            }
1512            .to_string(),
1513            "Frame content size mismatch (corrupt frame): declared 100 bytes, blocks summed to 87 bytes"
1514        );
1515    }
1516
1517    #[test]
1518    fn decode_block_content_backend_overflow_display_names_the_step() {
1519        use crate::blocks::block::BlockType;
1520        assert_eq!(
1521            DecodeBlockContentError::BackendOverflow {
1522                step: BlockType::RLE
1523            }
1524            .to_string(),
1525            "RLE block's decompressed payload exceeds the caller-provided output buffer"
1526        );
1527        assert_eq!(
1528            DecodeBlockContentError::BackendOverflow {
1529                step: BlockType::Raw
1530            }
1531            .to_string(),
1532            "Raw block's decompressed payload exceeds the caller-provided output buffer"
1533        );
1534    }
1535
1536    #[test]
1537    fn literal_display_messages_are_specific() {
1538        assert_eq!(
1539            DecompressLiteralsError::MissingCompressedSize.to_string(),
1540            "compressed size was none even though it must be set to something for compressed literals"
1541        );
1542        assert_eq!(
1543            DecompressLiteralsError::MissingNumStreams.to_string(),
1544            "num_streams was none even though it must be set to something (1 or 4) for compressed literals"
1545        );
1546        assert_eq!(
1547            DecompressLiteralsError::ExtraPadding { skipped_bits: 9 }.to_string(),
1548            "Padding at the end of the sequence_section was more than a byte long: 9 bits. Probably caused by data corruption"
1549        );
1550    }
1551
1552    #[test]
1553    fn fse_and_huffman_display_messages_are_specific() {
1554        assert_eq!(
1555            FSETableError::ProbabilityCounterMismatch {
1556                got: 4,
1557                expected_sum: 3,
1558                symbol_probabilities: vec![1, -1],
1559            }
1560            .to_string(),
1561            "FSE probability sum mismatch: got 4, expected 3. Indicates corrupted data or an invalid distribution\n [1, -1]"
1562        );
1563        assert_eq!(
1564            HuffmanTableError::NotEnoughBytesForWeights {
1565                got_bytes: 2,
1566                expected_bytes: 5,
1567            }
1568            .to_string(),
1569            "Header says there should be 5 bytes for the weights but there are only 2 bytes in the stream"
1570        );
1571        assert_eq!(
1572            HuffmanTableError::ExtraPadding { skipped_bits: 13 }.to_string(),
1573            "Padding at the end of the sequence_section was more than a byte long: 13 bits. Probably caused by data corruption"
1574        );
1575        assert_eq!(
1576            HuffmanTableError::FSETableUsedTooManyBytes {
1577                used: 7,
1578                available_bytes: 6,
1579            }
1580            .to_string(),
1581            "FSE table used more bytes: 7 than were meant to be used for the whole stream of huffman weights (6)"
1582        );
1583    }
1584}