Skip to main content

structured_zstd/decoding/
errors.rs

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