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    /// The frame carried a trailing XXH64 content checksum and the decoder
584    /// was set to [`ContentChecksum::Verify`](crate::decoding::ContentChecksum::Verify),
585    /// but the digest computed over the decompressed output did not match the
586    /// stored value. Indicates corruption in the compressed stream or its
587    /// trailing checksum. `expected` is the value read from the frame tail;
588    /// `calculated` is the digest the decoder computed (both low 32 bits).
589    ChecksumMismatch {
590        expected: u32,
591        calculated: u32,
592    },
593    DictNotProvided {
594        dict_id: u32,
595    },
596    DictIdMismatch {
597        expected: u32,
598        provided: u32,
599    },
600    DictAlreadyRegistered {
601        dict_id: u32,
602    },
603    /// Frame header's `dict_id` did not match the value pinned via
604    /// `FrameDecoder::expect_dict_id`. Returned BEFORE any block
605    /// decode and BEFORE any output is produced — no XXH64 init,
606    /// no partial output. Scratch buffer allocation / reservation
607    /// for the decode pipeline happens during frame-header parsing,
608    /// which is already complete when this validation fires, so
609    /// the cost of scratch sizing is paid even on a mismatched
610    /// header. `expected` is the pinned value (`Some(0)` is
611    /// treated as "no dictionary expected", matching a frame whose
612    /// header omits the optional `Dictionary_ID` field); `found`
613    /// reports what the frame actually carried (`None` when the
614    /// header omits the field, `Some(id)` when it does not).
615    #[cfg(feature = "lsm")]
616    UnexpectedDictId {
617        expected: Option<u32>,
618        found: Option<u32>,
619    },
620    /// Frame header's raw `Window_Descriptor` byte did not match
621    /// the value pinned via `FrameDecoder::expect_window_descriptor`.
622    /// Returned BEFORE any block decode work. Single-segment frames
623    /// (which omit the `Window_Descriptor` byte from the wire) are
624    /// reported via `found: None` so callers can distinguish
625    /// "wrong descriptor" from "no descriptor on the wire".
626    #[cfg(feature = "lsm")]
627    UnexpectedWindowDescriptor {
628        expected: u8,
629        found: Option<u8>,
630    },
631    /// Block-precise variant of [`Self::FailedToReadBlockHeader`]: a block
632    /// header read failed and the decoder captured WHERE. `block_index` is
633    /// the 0-based index of the failing block in the frame; `frame_offset`
634    /// is the frame-absolute byte offset of that block's `Block_Header`
635    /// (matches `FrameEmitInfo.blocks[block_index].offset_in_frame` from the
636    /// encode side). Lets per-block recovery (ECC repair) target the one bad
637    /// block instead of re-fetching the whole frame.
638    #[cfg(feature = "lsm")]
639    FailedToReadBlockHeaderAt {
640        source: BlockHeaderReadError,
641        block_index: u32,
642        frame_offset: u32,
643    },
644    /// Block-precise variant of [`Self::FailedToReadBlockBody`]: a block
645    /// body decode failed. Carries the same `block_index` / `frame_offset`
646    /// coordinates plus the failing block's structural metadata
647    /// ([`FrameBlock`]) reconstructed from its header, so a consumer can
648    /// locate and repair exactly this block.
649    ///
650    /// [`FrameBlock`]: crate::encoding::frame_emit_info::FrameBlock
651    #[cfg(feature = "lsm")]
652    FailedToReadBlockBodyAt {
653        source: DecodeBlockContentError,
654        block_index: u32,
655        frame_offset: u32,
656        block: crate::encoding::frame_emit_info::FrameBlock,
657    },
658    /// `FrameDecoder::decode_blocks_partial` was called with
659    /// `start_block > end_block` (the half-open block range is
660    /// empty-or-inverted and cannot describe a valid subset). API
661    /// misuse, surfaced as `Err` rather than a `PartialDecode`
662    /// outcome — distinct from a corrupt-frame stop, which is
663    /// reported via `PartialDecode::stopped_at`.
664    #[cfg(feature = "lsm")]
665    InvalidBlockRange {
666        start_block: u32,
667        end_block: u32,
668    },
669    /// A resuming [`FrameDecoder::decode_blocks_partial`] was given a
670    /// `window_prime` (via [`ResumeInput`]) shorter than the match window the
671    /// resume block can reach back into. The resumed decode would read past the
672    /// primed history and silently mis-resolve matches, so it is rejected up
673    /// front. `got` is the supplied prime length; `need` is the required
674    /// minimum (`min(window_size, output_offset)`).
675    ///
676    /// [`FrameDecoder::decode_blocks_partial`]: crate::decoding::FrameDecoder::decode_blocks_partial
677    /// [`ResumeInput`]: crate::decoding::ResumeInput
678    #[cfg(feature = "lsm")]
679    ResumeWindowTooShort {
680        got: usize,
681        need: usize,
682    },
683    /// A resuming [`FrameDecoder::decode_blocks_partial`] was given a
684    /// [`ResumeInput`] whose [`ResumeState`] was captured from a frame with a
685    /// different decode-relevant shape (window size, dictionary id,
686    /// single-segment flag, content-checksum flag, or magicless mode) than the
687    /// frame currently [`reset`](crate::decoding::FrameDecoder::reset) into the
688    /// decoder. Applying entropy/repcode state across mismatched frames would
689    /// yield byte-wrong output, so it is rejected up front.
690    ///
691    /// [`FrameDecoder::decode_blocks_partial`]: crate::decoding::FrameDecoder::decode_blocks_partial
692    /// [`ResumeInput`]: crate::decoding::ResumeInput
693    /// [`ResumeState`]: crate::decoding::ResumeState
694    #[cfg(feature = "lsm")]
695    ResumeFrameMismatch,
696}
697
698#[cfg(feature = "std")]
699impl StdError for FrameDecoderError {
700    fn source(&self) -> Option<&(dyn StdError + 'static)> {
701        match self {
702            FrameDecoderError::ReadFrameHeaderError(source) => Some(source),
703            FrameDecoderError::FrameHeaderError(source) => Some(source),
704            FrameDecoderError::DictionaryDecodeError(source) => Some(source),
705            FrameDecoderError::FailedToReadBlockHeader(source) => Some(source),
706            FrameDecoderError::FailedToReadBlockBody(source) => Some(source),
707            #[cfg(feature = "lsm")]
708            FrameDecoderError::FailedToReadBlockHeaderAt { source, .. } => Some(source),
709            #[cfg(feature = "lsm")]
710            FrameDecoderError::FailedToReadBlockBodyAt { source, .. } => Some(source),
711            FrameDecoderError::FailedToReadChecksum(source) => Some(source),
712            FrameDecoderError::FailedToInitialize(source) => Some(source),
713            FrameDecoderError::FailedToDrainDecodebuffer(source) => Some(source),
714            _ => None,
715        }
716    }
717}
718
719impl core::fmt::Display for FrameDecoderError {
720    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
721        match self {
722            FrameDecoderError::ReadFrameHeaderError(e) => {
723                write!(f, "{e:?}")
724            }
725            FrameDecoderError::FrameHeaderError(e) => {
726                write!(f, "{e:?}")
727            }
728            FrameDecoderError::WindowSizeTooBig { requested } => {
729                write!(
730                    f,
731                    "Specified window_size is too big; Requested: {}, Allowed: {}",
732                    requested,
733                    crate::common::MAXIMUM_ALLOWED_WINDOW_SIZE,
734                )
735            }
736            FrameDecoderError::DictionaryDecodeError(e) => {
737                write!(f, "{e:?}")
738            }
739            FrameDecoderError::FailedToReadBlockHeader(e) => {
740                write!(f, "Failed to parse/decode block body: {e}")
741            }
742            FrameDecoderError::FailedToReadBlockBody(e) => {
743                write!(f, "Failed to parse block header: {e}")
744            }
745            #[cfg(feature = "lsm")]
746            FrameDecoderError::FailedToReadBlockHeaderAt {
747                source,
748                block_index,
749                frame_offset,
750            } => {
751                write!(
752                    f,
753                    "Failed to read block header at block {block_index} (frame offset {frame_offset}): {source}"
754                )
755            }
756            #[cfg(feature = "lsm")]
757            FrameDecoderError::FailedToReadBlockBodyAt {
758                source,
759                block_index,
760                frame_offset,
761                ..
762            } => {
763                write!(
764                    f,
765                    "Failed to decode block body at block {block_index} (frame offset {frame_offset}): {source}"
766                )
767            }
768            FrameDecoderError::FailedToReadChecksum(e) => {
769                write!(f, "Failed to read checksum: {e}")
770            }
771            FrameDecoderError::NotYetInitialized => {
772                write!(f, "Decoder must initialized or reset before using it",)
773            }
774            FrameDecoderError::FailedToInitialize(e) => {
775                write!(f, "Decoder encountered error while initializing: {e}")
776            }
777            FrameDecoderError::FailedToDrainDecodebuffer(e) => {
778                write!(
779                    f,
780                    "Decoder encountered error while draining the decodebuffer: {e}",
781                )
782            }
783            FrameDecoderError::FailedToSkipFrame => {
784                write!(
785                    f,
786                    "Failed to skip bytes for the length given in the frame header"
787                )
788            }
789            FrameDecoderError::TargetTooSmall => {
790                write!(
791                    f,
792                    "Target must have at least as many bytes as the content size reported by the frame"
793                )
794            }
795            FrameDecoderError::FrameContentSizeMismatch { declared, produced } => {
796                write!(
797                    f,
798                    "Frame content size mismatch (corrupt frame): declared {declared} bytes, blocks summed to {produced} bytes"
799                )
800            }
801            FrameDecoderError::ChecksumMismatch {
802                expected,
803                calculated,
804            } => {
805                write!(
806                    f,
807                    "Content checksum mismatch (corrupt frame): frame stored 0x{expected:08X}, decoder calculated 0x{calculated:08X}"
808                )
809            }
810            FrameDecoderError::DictNotProvided { dict_id } => {
811                write!(
812                    f,
813                    "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()"
814                )
815            }
816            FrameDecoderError::DictIdMismatch { expected, provided } => {
817                write!(
818                    f,
819                    "Frame header dictionary id 0x{expected:X} does not match provided dictionary id 0x{provided:X}"
820                )
821            }
822            FrameDecoderError::DictAlreadyRegistered { dict_id } => {
823                write!(
824                    f,
825                    "Dictionary id 0x{dict_id:X} already registered in decoder"
826                )
827            }
828            #[cfg(feature = "lsm")]
829            FrameDecoderError::UnexpectedDictId { expected, found } => {
830                write!(f, "Frame header dict_id mismatch: expected ")?;
831                match expected {
832                    Some(id) => write!(f, "0x{id:X}")?,
833                    None => write!(f, "<none>")?,
834                }
835                write!(f, ", found ")?;
836                match found {
837                    Some(id) => write!(f, "0x{id:X}"),
838                    None => write!(f, "<none>"),
839                }
840            }
841            #[cfg(feature = "lsm")]
842            FrameDecoderError::UnexpectedWindowDescriptor { expected, found } => {
843                write!(
844                    f,
845                    "Frame header window_descriptor mismatch: expected 0x{expected:02X}, found "
846                )?;
847                match found {
848                    Some(byte) => write!(f, "0x{byte:02X}"),
849                    None => write!(f, "<none> (single-segment frame omits window_descriptor)"),
850                }
851            }
852            #[cfg(feature = "lsm")]
853            FrameDecoderError::InvalidBlockRange {
854                start_block,
855                end_block,
856            } => {
857                write!(
858                    f,
859                    "Invalid block range for partial decode: start_block {start_block} > end_block {end_block}"
860                )
861            }
862            #[cfg(feature = "lsm")]
863            FrameDecoderError::ResumeWindowTooShort { got, need } => {
864                write!(
865                    f,
866                    "resume window_prime too short: got {got} bytes, need at least {need}"
867                )
868            }
869            #[cfg(feature = "lsm")]
870            FrameDecoderError::ResumeFrameMismatch => {
871                write!(
872                    f,
873                    "resume state was captured from a frame with a different decode shape than the current frame"
874                )
875            }
876        }
877    }
878}
879
880impl From<DictionaryDecodeError> for FrameDecoderError {
881    fn from(val: DictionaryDecodeError) -> Self {
882        Self::DictionaryDecodeError(val)
883    }
884}
885
886impl From<BlockHeaderReadError> for FrameDecoderError {
887    fn from(val: BlockHeaderReadError) -> Self {
888        Self::FailedToReadBlockHeader(val)
889    }
890}
891
892impl From<FrameHeaderError> for FrameDecoderError {
893    fn from(val: FrameHeaderError) -> Self {
894        Self::FrameHeaderError(val)
895    }
896}
897
898impl From<ReadFrameHeaderError> for FrameDecoderError {
899    fn from(val: ReadFrameHeaderError) -> Self {
900        Self::ReadFrameHeaderError(val)
901    }
902}
903
904#[derive(Debug)]
905#[non_exhaustive]
906pub enum DecompressLiteralsError {
907    MissingCompressedSize,
908    MissingNumStreams,
909    GetBitsError(GetBitsError),
910    HuffmanTableError(HuffmanTableError),
911    HuffmanDecoderError(HuffmanDecoderError),
912    UninitializedHuffmanTable,
913    MissingBytesForJumpHeader { got: usize },
914    MissingBytesForLiterals { got: usize, needed: usize },
915    ExtraPadding { skipped_bits: i32 },
916    BitstreamReadMismatch { read_til: isize, expected: isize },
917    DecodedLiteralCountMismatch { decoded: usize, expected: usize },
918}
919
920#[cfg(feature = "std")]
921impl std::error::Error for DecompressLiteralsError {
922    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
923        match self {
924            DecompressLiteralsError::GetBitsError(source) => Some(source),
925            DecompressLiteralsError::HuffmanTableError(source) => Some(source),
926            DecompressLiteralsError::HuffmanDecoderError(source) => Some(source),
927            _ => None,
928        }
929    }
930}
931impl core::fmt::Display for DecompressLiteralsError {
932    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
933        match self {
934            DecompressLiteralsError::MissingCompressedSize => {
935                write!(
936                    f,
937                    "compressed size was none even though it must be set to something for compressed literals",
938                )
939            }
940            DecompressLiteralsError::MissingNumStreams => {
941                write!(
942                    f,
943                    "num_streams was none even though it must be set to something (1 or 4) for compressed literals",
944                )
945            }
946            DecompressLiteralsError::GetBitsError(e) => write!(f, "{e:?}"),
947            DecompressLiteralsError::HuffmanTableError(e) => write!(f, "{e:?}"),
948            DecompressLiteralsError::HuffmanDecoderError(e) => write!(f, "{e:?}"),
949            DecompressLiteralsError::UninitializedHuffmanTable => {
950                write!(
951                    f,
952                    "Tried to reuse huffman table but it was never initialized",
953                )
954            }
955            DecompressLiteralsError::MissingBytesForJumpHeader { got } => {
956                write!(f, "Need 6 bytes to decode jump header, got {got} bytes",)
957            }
958            DecompressLiteralsError::MissingBytesForLiterals { got, needed } => {
959                write!(
960                    f,
961                    "Need at least {needed} bytes to decode literals. Have: {got} bytes",
962                )
963            }
964            DecompressLiteralsError::ExtraPadding { skipped_bits } => {
965                write!(
966                    f,
967                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
968                )
969            }
970            DecompressLiteralsError::BitstreamReadMismatch { read_til, expected } => {
971                write!(
972                    f,
973                    "Bitstream was read till: {read_til}, should have been: {expected}",
974                )
975            }
976            DecompressLiteralsError::DecodedLiteralCountMismatch { decoded, expected } => {
977                write!(
978                    f,
979                    "Did not decode enough literals: {decoded}, Should have been: {expected}",
980                )
981            }
982        }
983    }
984}
985
986impl From<HuffmanDecoderError> for DecompressLiteralsError {
987    fn from(val: HuffmanDecoderError) -> Self {
988        Self::HuffmanDecoderError(val)
989    }
990}
991
992impl From<GetBitsError> for DecompressLiteralsError {
993    fn from(val: GetBitsError) -> Self {
994        Self::GetBitsError(val)
995    }
996}
997
998impl From<HuffmanTableError> for DecompressLiteralsError {
999    fn from(val: HuffmanTableError) -> Self {
1000        Self::HuffmanTableError(val)
1001    }
1002}
1003
1004#[derive(Debug)]
1005#[non_exhaustive]
1006pub enum ExecuteSequencesError {
1007    DecodebufferError(DecodeBufferError),
1008    NotEnoughBytesForSequence {
1009        wanted: usize,
1010        have: usize,
1011    },
1012    ZeroOffset,
1013    /// An inline sequence (`exec_sequence_inline`) would have written past
1014    /// the writable tail of the output buffer. Raised by every capacity-bounded
1015    /// backend that runs the inline executor (the fixed-capacity user slice and
1016    /// the pre-reserved flat buffer alike). Indicates the frame is corrupt: its
1017    /// sequences expand past the declared `frame_content_size` plus the
1018    /// caller-supplied `WILDCOPY_OVERLENGTH` slack. The fast-path check is
1019    /// per-sequence; the post-block FCS overflow check would also catch the
1020    /// same shape, but the per-sequence guard is what keeps the unsafe write
1021    /// surface inside the buffer on the way to the post-block check.
1022    OutputBufferOverflow {
1023        tail: usize,
1024        requested: usize,
1025        capacity: usize,
1026    },
1027}
1028
1029impl core::fmt::Display for ExecuteSequencesError {
1030    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1031        match self {
1032            ExecuteSequencesError::DecodebufferError(e) => {
1033                write!(f, "{e:?}")
1034            }
1035            ExecuteSequencesError::NotEnoughBytesForSequence { wanted, have } => {
1036                write!(
1037                    f,
1038                    "Sequence wants to copy up to byte {wanted}. Bytes in literalsbuffer: {have}"
1039                )
1040            }
1041            ExecuteSequencesError::ZeroOffset => {
1042                write!(f, "Illegal offset: 0 found")
1043            }
1044            ExecuteSequencesError::OutputBufferOverflow {
1045                tail,
1046                requested,
1047                capacity,
1048            } => {
1049                write!(
1050                    f,
1051                    "Inline sequence would write past the output buffer: tail={tail}, requested={requested}, capacity={capacity}"
1052                )
1053            }
1054        }
1055    }
1056}
1057
1058#[cfg(feature = "std")]
1059impl std::error::Error for ExecuteSequencesError {
1060    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1061        match self {
1062            ExecuteSequencesError::DecodebufferError(source) => Some(source),
1063            _ => None,
1064        }
1065    }
1066}
1067
1068impl From<DecodeBufferError> for ExecuteSequencesError {
1069    fn from(val: DecodeBufferError) -> Self {
1070        Self::DecodebufferError(val)
1071    }
1072}
1073
1074impl From<crate::decoding::buffer_backend::BackendOverflow> for ExecuteSequencesError {
1075    fn from(val: crate::decoding::buffer_backend::BackendOverflow) -> Self {
1076        Self::OutputBufferOverflow {
1077            tail: val.tail,
1078            requested: val.requested,
1079            capacity: val.capacity,
1080        }
1081    }
1082}
1083
1084impl ExecuteSequencesError {
1085    /// `Some(requested)` when this error is a fixed-capacity output-buffer
1086    /// overshoot from the Compressed-block sequence executor — either the
1087    /// inline-sequence path ([`Self::OutputBufferOverflow`]) or the
1088    /// match-repeat fallback ([`DecodeBufferError::OutputBufferOverflow`]
1089    /// wrapped in [`Self::DecodebufferError`]). `requested` is the byte
1090    /// count the failing write tried to append past the slice end.
1091    ///
1092    /// `None` for every non-overflow variant. Used by
1093    /// `FrameDecoder::run_direct_decode` to fold an in-block Compressed
1094    /// overshoot into the same `FrameContentSizeMismatch` contract the
1095    /// Raw/RLE [`DecodeBlockContentError::BackendOverflow`] arm already
1096    /// produces — both mean "the frame's content expands past the
1097    /// declared `frame_content_size`".
1098    pub(crate) fn output_overflow_requested(&self) -> Option<usize> {
1099        match self {
1100            ExecuteSequencesError::OutputBufferOverflow { requested, .. } => Some(*requested),
1101            ExecuteSequencesError::DecodebufferError(DecodeBufferError::OutputBufferOverflow {
1102                requested,
1103                ..
1104            }) => Some(*requested),
1105            _ => None,
1106        }
1107    }
1108}
1109
1110#[derive(Debug)]
1111#[non_exhaustive]
1112pub enum DecodeSequenceError {
1113    GetBitsError(GetBitsError),
1114    FSEDecoderError(FSEDecoderError),
1115    FSETableError(FSETableError),
1116    ExtraPadding {
1117        skipped_bits: i32,
1118    },
1119    UnsupportedOffset {
1120        offset_code: u8,
1121    },
1122    ZeroOffset,
1123    NotEnoughBytesForNumSequences,
1124    ExtraBits {
1125        bits_remaining: isize,
1126    },
1127    MissingCompressionMode,
1128    MissingByteForRleLlTable,
1129    MissingByteForRleOfTable,
1130    MissingByteForRleMlTable,
1131    /// An RLE-mode sequence table's single symbol code is out of range
1132    /// for its axis (LL/ML/OF). `axis` names the axis; `code` is the
1133    /// offending value read from the stream.
1134    InvalidRleCode {
1135        axis: &'static str,
1136        code: u8,
1137    },
1138}
1139
1140#[cfg(feature = "std")]
1141impl std::error::Error for DecodeSequenceError {
1142    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1143        match self {
1144            DecodeSequenceError::GetBitsError(source) => Some(source),
1145            DecodeSequenceError::FSEDecoderError(source) => Some(source),
1146            DecodeSequenceError::FSETableError(source) => Some(source),
1147            _ => None,
1148        }
1149    }
1150}
1151
1152impl core::fmt::Display for DecodeSequenceError {
1153    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1154        match self {
1155            DecodeSequenceError::GetBitsError(e) => write!(f, "{e:?}"),
1156            DecodeSequenceError::FSEDecoderError(e) => write!(f, "{e:?}"),
1157            DecodeSequenceError::FSETableError(e) => write!(f, "{e:?}"),
1158            DecodeSequenceError::ExtraPadding { skipped_bits } => {
1159                write!(
1160                    f,
1161                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
1162                )
1163            }
1164            DecodeSequenceError::UnsupportedOffset { offset_code } => {
1165                write!(
1166                    f,
1167                    "Do not support offsets bigger than 1<<32; got: {offset_code}",
1168                )
1169            }
1170            DecodeSequenceError::ZeroOffset => write!(
1171                f,
1172                "Read an offset == 0. That is an illegal value for offsets"
1173            ),
1174            DecodeSequenceError::NotEnoughBytesForNumSequences => write!(
1175                f,
1176                "Bytestream did not contain enough bytes to decode num_sequences"
1177            ),
1178            DecodeSequenceError::ExtraBits { bits_remaining } => write!(f, "{bits_remaining}"),
1179            DecodeSequenceError::MissingCompressionMode => write!(
1180                f,
1181                "compression modes are none but they must be set to something"
1182            ),
1183            DecodeSequenceError::MissingByteForRleLlTable => {
1184                write!(f, "Need a byte to read for RLE ll table")
1185            }
1186            DecodeSequenceError::MissingByteForRleOfTable => {
1187                write!(f, "Need a byte to read for RLE of table")
1188            }
1189            DecodeSequenceError::MissingByteForRleMlTable => {
1190                write!(f, "Need a byte to read for RLE ml table")
1191            }
1192            DecodeSequenceError::InvalidRleCode { axis, code } => {
1193                write!(f, "RLE {axis} table code {code} is out of range")
1194            }
1195        }
1196    }
1197}
1198
1199impl From<GetBitsError> for DecodeSequenceError {
1200    fn from(val: GetBitsError) -> Self {
1201        Self::GetBitsError(val)
1202    }
1203}
1204
1205impl From<FSETableError> for DecodeSequenceError {
1206    fn from(val: FSETableError) -> Self {
1207        Self::FSETableError(val)
1208    }
1209}
1210
1211impl From<FSEDecoderError> for DecodeSequenceError {
1212    fn from(val: FSEDecoderError) -> Self {
1213        Self::FSEDecoderError(val)
1214    }
1215}
1216
1217#[derive(Debug)]
1218#[non_exhaustive]
1219pub enum LiteralsSectionParseError {
1220    IllegalLiteralSectionType { got: u8 },
1221    GetBitsError(GetBitsError),
1222    NotEnoughBytes { have: usize, need: u8 },
1223}
1224
1225#[cfg(feature = "std")]
1226impl std::error::Error for LiteralsSectionParseError {
1227    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1228        match self {
1229            LiteralsSectionParseError::GetBitsError(source) => Some(source),
1230            _ => None,
1231        }
1232    }
1233}
1234impl core::fmt::Display for LiteralsSectionParseError {
1235    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1236        match self {
1237            LiteralsSectionParseError::IllegalLiteralSectionType { got } => {
1238                write!(
1239                    f,
1240                    "Illegal literalssectiontype. Is: {got}, must be in: 0, 1, 2, 3"
1241                )
1242            }
1243            LiteralsSectionParseError::GetBitsError(e) => write!(f, "{e:?}"),
1244            LiteralsSectionParseError::NotEnoughBytes { have, need } => {
1245                write!(
1246                    f,
1247                    "Not enough byte to parse the literals section header. Have: {have}, Need: {need}",
1248                )
1249            }
1250        }
1251    }
1252}
1253
1254impl From<GetBitsError> for LiteralsSectionParseError {
1255    fn from(val: GetBitsError) -> Self {
1256        Self::GetBitsError(val)
1257    }
1258}
1259
1260impl core::fmt::Display for LiteralsSectionType {
1261    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
1262        match self {
1263            LiteralsSectionType::Compressed => write!(f, "Compressed"),
1264            LiteralsSectionType::Raw => write!(f, "Raw"),
1265            LiteralsSectionType::RLE => write!(f, "RLE"),
1266            LiteralsSectionType::Treeless => write!(f, "Treeless"),
1267        }
1268    }
1269}
1270
1271#[derive(Debug)]
1272#[non_exhaustive]
1273pub enum SequencesHeaderParseError {
1274    NotEnoughBytes { need_at_least: u8, got: usize },
1275}
1276
1277#[cfg(feature = "std")]
1278impl std::error::Error for SequencesHeaderParseError {}
1279
1280impl core::fmt::Display for SequencesHeaderParseError {
1281    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1282        match self {
1283            SequencesHeaderParseError::NotEnoughBytes { need_at_least, got } => {
1284                write!(
1285                    f,
1286                    "source must have at least {need_at_least} bytes to parse header; got {got} bytes",
1287                )
1288            }
1289        }
1290    }
1291}
1292
1293#[derive(Debug)]
1294#[non_exhaustive]
1295pub enum FSETableError {
1296    AccLogIsZero,
1297    AccLogTooBig {
1298        got: u8,
1299        max: u8,
1300    },
1301    GetBitsError(GetBitsError),
1302    ProbabilityCounterMismatch {
1303        got: u32,
1304        expected_sum: u32,
1305        symbol_probabilities: Vec<i32>,
1306    },
1307    TooManySymbols {
1308        got: usize,
1309    },
1310    /// Probability value outside the RFC 8878 §4.1.1 allowed set
1311    /// `{-1, 0, 1..=table_size}`. Carries the violating value, the
1312    /// table size (`1 << accuracy_log`) and `accuracy_log` so the
1313    /// caller can pinpoint the failure without re-deriving the bound.
1314    InvalidProbability {
1315        value: i32,
1316        table_size: u32,
1317        accuracy_log: u8,
1318    },
1319    /// `calc_baseline_and_numbits` produced a state-entry whose bit
1320    /// width exceeds the table's accuracy log, violating the
1321    /// `new_state + (1 << num_bits) - 1 < table_size` invariant that
1322    /// the unchecked `read_entry` decode hot path relies on. The
1323    /// triggering probability is in-range per RFC 8878 §4.1.1; the
1324    /// failure is an internal table-shape inconsistency surfaced
1325    /// against the public `build_from_probabilities` API.
1326    TableInvariantViolation {
1327        prob: i32,
1328        symbol: u8,
1329        num_bits: u8,
1330        accuracy_log: u8,
1331    },
1332}
1333
1334#[cfg(feature = "std")]
1335impl std::error::Error for FSETableError {
1336    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1337        match self {
1338            FSETableError::GetBitsError(source) => Some(source),
1339            _ => None,
1340        }
1341    }
1342}
1343
1344impl core::fmt::Display for FSETableError {
1345    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1346        match self {
1347            FSETableError::AccLogIsZero => write!(f, "Acclog must be at least 1"),
1348            FSETableError::AccLogTooBig { got, max } => {
1349                write!(
1350                    f,
1351                    "Found FSE acc_log: {got} bigger than allowed maximum in this case: {max}"
1352                )
1353            }
1354            FSETableError::GetBitsError(e) => write!(f, "{e:?}"),
1355            FSETableError::ProbabilityCounterMismatch {
1356                got,
1357                expected_sum,
1358                symbol_probabilities,
1359            } => {
1360                write!(
1361                    f,
1362                    "FSE probability sum mismatch: got {got}, expected {expected_sum}. Indicates corrupted data or an invalid distribution\n {symbol_probabilities:?}",
1363                )
1364            }
1365            FSETableError::TooManySymbols { got } => {
1366                write!(
1367                    f,
1368                    "There are too many symbols in this distribution: {got}. Max: 256",
1369                )
1370            }
1371            FSETableError::InvalidProbability {
1372                value,
1373                table_size,
1374                accuracy_log,
1375            } => {
1376                write!(
1377                    f,
1378                    "FSE probability value {value} is outside the RFC 8878 allowed set (must be -1, 0, or in 1..={table_size}; accuracy_log={accuracy_log})",
1379                )
1380            }
1381            FSETableError::TableInvariantViolation {
1382                prob,
1383                symbol,
1384                num_bits,
1385                accuracy_log,
1386            } => {
1387                write!(
1388                    f,
1389                    "FSE table invariant violation: symbol {symbol} (prob {prob}) produced num_bits {num_bits} > accuracy_log {accuracy_log}",
1390                )
1391            }
1392        }
1393    }
1394}
1395
1396impl From<GetBitsError> for FSETableError {
1397    fn from(val: GetBitsError) -> Self {
1398        Self::GetBitsError(val)
1399    }
1400}
1401
1402#[derive(Debug)]
1403#[non_exhaustive]
1404pub enum FSEDecoderError {
1405    GetBitsError(GetBitsError),
1406    TableIsUninitialized,
1407    /// Externally constructed `FSETable` violates the
1408    /// `decode.len() == 1 << accuracy_log` shape invariant. Only
1409    /// reachable under `feature = "fuzz_exports"`, where fuzz
1410    /// harnesses can set the `FSETable.decode` / `accuracy_log`
1411    /// fields directly and skip `build_decoding_table`.
1412    InvalidTableShape {
1413        decode_len: usize,
1414        accuracy_log: u8,
1415    },
1416}
1417
1418#[cfg(feature = "std")]
1419impl std::error::Error for FSEDecoderError {
1420    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1421        match self {
1422            FSEDecoderError::GetBitsError(source) => Some(source),
1423            _ => None,
1424        }
1425    }
1426}
1427
1428impl core::fmt::Display for FSEDecoderError {
1429    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1430        match self {
1431            FSEDecoderError::GetBitsError(e) => write!(f, "{e:?}"),
1432            FSEDecoderError::TableIsUninitialized => {
1433                write!(f, "Tried to use an uninitialized table!")
1434            }
1435            FSEDecoderError::InvalidTableShape {
1436                decode_len,
1437                accuracy_log,
1438            } => match 1usize.checked_shl((*accuracy_log).into()) {
1439                Some(expected) => write!(
1440                    f,
1441                    "FSETable shape invariant violated: decode.len() = {decode_len}, expected 1 << accuracy_log = {expected} (accuracy_log = {accuracy_log})",
1442                ),
1443                None => write!(
1444                    f,
1445                    "FSETable shape invariant violated: decode.len() = {decode_len}, accuracy_log = {accuracy_log} overflows 1 << accuracy_log for usize",
1446                ),
1447            },
1448        }
1449    }
1450}
1451
1452impl From<GetBitsError> for FSEDecoderError {
1453    fn from(val: GetBitsError) -> Self {
1454        Self::GetBitsError(val)
1455    }
1456}
1457
1458#[derive(Debug)]
1459#[non_exhaustive]
1460pub enum HuffmanTableError {
1461    GetBitsError(GetBitsError),
1462    FSEDecoderError(FSEDecoderError),
1463    FSETableError(FSETableError),
1464    SourceIsEmpty,
1465    NotEnoughBytesForWeights {
1466        got_bytes: usize,
1467        expected_bytes: u8,
1468    },
1469    ExtraPadding {
1470        skipped_bits: i32,
1471    },
1472    TooManyWeights {
1473        got: usize,
1474    },
1475    MissingWeights,
1476    LeftoverIsNotAPowerOf2 {
1477        got: u32,
1478    },
1479    NotEnoughBytesToDecompressWeights {
1480        have: usize,
1481        need: usize,
1482    },
1483    FSETableUsedTooManyBytes {
1484        used: usize,
1485        available_bytes: u8,
1486    },
1487    NotEnoughBytesInSource {
1488        got: usize,
1489        need: usize,
1490    },
1491    WeightBiggerThanMaxNumBits {
1492        got: u8,
1493    },
1494    MaxBitsTooHigh {
1495        got: u8,
1496    },
1497}
1498
1499#[cfg(feature = "std")]
1500impl StdError for HuffmanTableError {
1501    fn source(&self) -> Option<&(dyn StdError + 'static)> {
1502        match self {
1503            HuffmanTableError::GetBitsError(source) => Some(source),
1504            HuffmanTableError::FSEDecoderError(source) => Some(source),
1505            HuffmanTableError::FSETableError(source) => Some(source),
1506            _ => None,
1507        }
1508    }
1509}
1510
1511impl core::fmt::Display for HuffmanTableError {
1512    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
1513        match self {
1514            HuffmanTableError::GetBitsError(e) => write!(f, "{e:?}"),
1515            HuffmanTableError::FSEDecoderError(e) => write!(f, "{e:?}"),
1516            HuffmanTableError::FSETableError(e) => write!(f, "{e:?}"),
1517            HuffmanTableError::SourceIsEmpty => write!(f, "Source needs to have at least one byte"),
1518            HuffmanTableError::NotEnoughBytesForWeights {
1519                got_bytes,
1520                expected_bytes,
1521            } => {
1522                write!(
1523                    f,
1524                    "Header says there should be {expected_bytes} bytes for the weights but there are only {got_bytes} bytes in the stream"
1525                )
1526            }
1527            HuffmanTableError::ExtraPadding { skipped_bits } => {
1528                write!(
1529                    f,
1530                    "Padding at the end of the sequence_section was more than a byte long: {skipped_bits} bits. Probably caused by data corruption",
1531                )
1532            }
1533            HuffmanTableError::TooManyWeights { got } => {
1534                write!(
1535                    f,
1536                    "More than 255 weights decoded (got {got} weights). Stream is probably corrupted",
1537                )
1538            }
1539            HuffmanTableError::MissingWeights => {
1540                write!(f, "Can\'t build huffman table without any weights")
1541            }
1542            HuffmanTableError::LeftoverIsNotAPowerOf2 { got } => {
1543                write!(f, "Leftover must be power of two but is: {got}")
1544            }
1545            HuffmanTableError::NotEnoughBytesToDecompressWeights { have, need } => {
1546                write!(
1547                    f,
1548                    "Not enough bytes in stream to decompress weights. Is: {have}, Should be: {need}",
1549                )
1550            }
1551            HuffmanTableError::FSETableUsedTooManyBytes {
1552                used,
1553                available_bytes,
1554            } => {
1555                write!(
1556                    f,
1557                    "FSE table used more bytes: {used} than were meant to be used for the whole stream of huffman weights ({available_bytes})",
1558                )
1559            }
1560            HuffmanTableError::NotEnoughBytesInSource { got, need } => {
1561                write!(f, "Source needs to have at least {need} bytes, got: {got}",)
1562            }
1563            HuffmanTableError::WeightBiggerThanMaxNumBits { got } => {
1564                write!(
1565                    f,
1566                    "Cant have weight: {} bigger than max_num_bits: {}",
1567                    got,
1568                    crate::huff0::MAX_MAX_NUM_BITS,
1569                )
1570            }
1571            HuffmanTableError::MaxBitsTooHigh { got } => {
1572                write!(
1573                    f,
1574                    "max_bits derived from weights is: {} should be lower than: {}",
1575                    got,
1576                    crate::huff0::MAX_MAX_NUM_BITS,
1577                )
1578            }
1579        }
1580    }
1581}
1582
1583impl From<GetBitsError> for HuffmanTableError {
1584    fn from(val: GetBitsError) -> Self {
1585        Self::GetBitsError(val)
1586    }
1587}
1588
1589impl From<FSEDecoderError> for HuffmanTableError {
1590    fn from(val: FSEDecoderError) -> Self {
1591        Self::FSEDecoderError(val)
1592    }
1593}
1594
1595impl From<FSETableError> for HuffmanTableError {
1596    fn from(val: FSETableError) -> Self {
1597        Self::FSETableError(val)
1598    }
1599}
1600
1601#[derive(Debug)]
1602#[non_exhaustive]
1603pub enum HuffmanDecoderError {
1604    GetBitsError(GetBitsError),
1605}
1606
1607impl core::fmt::Display for HuffmanDecoderError {
1608    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1609        match self {
1610            HuffmanDecoderError::GetBitsError(e) => write!(f, "{e:?}"),
1611        }
1612    }
1613}
1614
1615#[cfg(feature = "std")]
1616impl StdError for HuffmanDecoderError {
1617    fn source(&self) -> Option<&(dyn StdError + 'static)> {
1618        match self {
1619            HuffmanDecoderError::GetBitsError(source) => Some(source),
1620        }
1621    }
1622}
1623
1624impl From<GetBitsError> for HuffmanDecoderError {
1625    fn from(val: GetBitsError) -> Self {
1626        Self::GetBitsError(val)
1627    }
1628}
1629
1630#[cfg(test)]
1631mod tests {
1632    use alloc::{string::ToString, vec};
1633
1634    use super::{
1635        BlockTypeError, DecodeBlockContentError, DecodeBufferError, DecodeSequenceError,
1636        DecompressBlockError, DecompressLiteralsError, ExecuteSequencesError, FSETableError,
1637        FrameDecoderError, HuffmanTableError,
1638    };
1639
1640    #[test]
1641    fn execute_sequences_output_overflow_requested_covers_all_arms() {
1642        // #246: `run_direct_decode` folds a Compressed-block overshoot into
1643        // `FrameContentSizeMismatch` by reading `requested` from whichever
1644        // overflow shape the executor produced. Cover all three arms:
1645        //   1. inline-sequence path -> `OutputBufferOverflow` directly,
1646        //   2. match-repeat path -> `DecodebufferError(OutputBufferOverflow)`,
1647        //   3. any other variant -> None (no fold).
1648        let inline = ExecuteSequencesError::OutputBufferOverflow {
1649            tail: 10,
1650            requested: 7,
1651            capacity: 12,
1652        };
1653        assert_eq!(inline.output_overflow_requested(), Some(7));
1654
1655        let repeat =
1656            ExecuteSequencesError::DecodebufferError(DecodeBufferError::OutputBufferOverflow {
1657                tail: 3,
1658                requested: 99,
1659                capacity: 4,
1660            });
1661        assert_eq!(repeat.output_overflow_requested(), Some(99));
1662
1663        // Non-overflow variants (and non-overflow DecodeBufferError) -> None.
1664        assert_eq!(
1665            ExecuteSequencesError::ZeroOffset.output_overflow_requested(),
1666            None
1667        );
1668        assert_eq!(
1669            ExecuteSequencesError::DecodebufferError(DecodeBufferError::ZeroOffset)
1670                .output_overflow_requested(),
1671            None
1672        );
1673    }
1674
1675    #[test]
1676    fn block_and_sequence_display_messages_are_specific() {
1677        assert_eq!(
1678            BlockTypeError::InvalidBlocktypeNumber { num: 7 }.to_string(),
1679            "Invalid Blocktype number. Is: 7. Should be one of: 0, 1, 2, 3 (3 is reserved)."
1680        );
1681        assert_eq!(
1682            DecompressBlockError::MalformedSectionHeader {
1683                expected_len: 12,
1684                remaining_bytes: 3,
1685            }
1686            .to_string(),
1687            "Malformed section header. Says literals would be this long: 12 but there are only 3 bytes left"
1688        );
1689        assert_eq!(
1690            DecodeBlockContentError::ExpectedHeaderOfPreviousBlock.to_string(),
1691            "Can't decode next block body, while expecting to decode the header of the previous block. Results will be nonsense"
1692        );
1693        assert_eq!(
1694            DecodeSequenceError::ExtraPadding { skipped_bits: 11 }.to_string(),
1695            "Padding at the end of the sequence_section was more than a byte long: 11 bits. Probably caused by data corruption"
1696        );
1697    }
1698
1699    #[test]
1700    fn frame_decoder_display_messages_are_specific() {
1701        assert_eq!(
1702            FrameDecoderError::TargetTooSmall.to_string(),
1703            "Target must have at least as many bytes as the content size reported by the frame"
1704        );
1705        assert_eq!(
1706            FrameDecoderError::DictNotProvided { dict_id: 0xABCD }.to_string(),
1707            "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()"
1708        );
1709        assert_eq!(
1710            FrameDecoderError::DictIdMismatch {
1711                expected: 0xABCD,
1712                provided: 0x1234
1713            }
1714            .to_string(),
1715            "Frame header dictionary id 0xABCD does not match provided dictionary id 0x1234"
1716        );
1717        assert_eq!(
1718            FrameDecoderError::DictAlreadyRegistered { dict_id: 0xABCD }.to_string(),
1719            "Dictionary id 0xABCD already registered in decoder"
1720        );
1721        assert_eq!(
1722            FrameDecoderError::FrameContentSizeMismatch {
1723                declared: 100,
1724                produced: 87,
1725            }
1726            .to_string(),
1727            "Frame content size mismatch (corrupt frame): declared 100 bytes, blocks summed to 87 bytes"
1728        );
1729        // Locks the wasm-exposed checksum-mismatch contract (exact string).
1730        assert_eq!(
1731            FrameDecoderError::ChecksumMismatch {
1732                expected: 0xDEAD_BEEF,
1733                calculated: 0x0BAD_F00D,
1734            }
1735            .to_string(),
1736            "Content checksum mismatch (corrupt frame): frame stored 0xDEADBEEF, decoder calculated 0x0BADF00D"
1737        );
1738    }
1739
1740    #[test]
1741    fn decode_block_content_backend_overflow_display_names_the_step() {
1742        use crate::blocks::block::BlockType;
1743        assert_eq!(
1744            DecodeBlockContentError::BackendOverflow {
1745                step: BlockType::RLE
1746            }
1747            .to_string(),
1748            "RLE block's decompressed payload exceeds the caller-provided output buffer"
1749        );
1750        assert_eq!(
1751            DecodeBlockContentError::BackendOverflow {
1752                step: BlockType::Raw
1753            }
1754            .to_string(),
1755            "Raw block's decompressed payload exceeds the caller-provided output buffer"
1756        );
1757    }
1758
1759    #[test]
1760    fn literal_display_messages_are_specific() {
1761        assert_eq!(
1762            DecompressLiteralsError::MissingCompressedSize.to_string(),
1763            "compressed size was none even though it must be set to something for compressed literals"
1764        );
1765        assert_eq!(
1766            DecompressLiteralsError::MissingNumStreams.to_string(),
1767            "num_streams was none even though it must be set to something (1 or 4) for compressed literals"
1768        );
1769        assert_eq!(
1770            DecompressLiteralsError::ExtraPadding { skipped_bits: 9 }.to_string(),
1771            "Padding at the end of the sequence_section was more than a byte long: 9 bits. Probably caused by data corruption"
1772        );
1773    }
1774
1775    #[test]
1776    fn fse_and_huffman_display_messages_are_specific() {
1777        assert_eq!(
1778            FSETableError::ProbabilityCounterMismatch {
1779                got: 4,
1780                expected_sum: 3,
1781                symbol_probabilities: vec![1, -1],
1782            }
1783            .to_string(),
1784            "FSE probability sum mismatch: got 4, expected 3. Indicates corrupted data or an invalid distribution\n [1, -1]"
1785        );
1786        assert_eq!(
1787            HuffmanTableError::NotEnoughBytesForWeights {
1788                got_bytes: 2,
1789                expected_bytes: 5,
1790            }
1791            .to_string(),
1792            "Header says there should be 5 bytes for the weights but there are only 2 bytes in the stream"
1793        );
1794        assert_eq!(
1795            HuffmanTableError::ExtraPadding { skipped_bits: 13 }.to_string(),
1796            "Padding at the end of the sequence_section was more than a byte long: 13 bits. Probably caused by data corruption"
1797        );
1798        assert_eq!(
1799            HuffmanTableError::FSETableUsedTooManyBytes {
1800                used: 7,
1801                available_bytes: 6,
1802            }
1803            .to_string(),
1804            "FSE table used more bytes: 7 than were meant to be used for the whole stream of huffman weights (6)"
1805        );
1806    }
1807}