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