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