zstd-zero 0.2.1

A no-std, allocation-free streaming Zstandard decoder
Documentation
use core::fmt;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum DecodeError {
    DecoderPoisoned,
    InvalidMagic,
    InvalidFrameHeader,
    UnsupportedDictionary { id: u32 },
    WindowTooLarge,
    HistoryTooSmall { required: usize, provided: usize },
    BlockScratchTooSmall { required: usize, provided: usize },
    LiteralScratchTooSmall { required: usize, provided: usize },
    InvalidBlock,
    InvalidEntropyTable,
    InvalidBitstream,
    InvalidOffset,
    ChecksumMismatch { expected: u32, actual: u32 },
    ContentSizeMismatch { expected: u64, actual: u64 },
    UnexpectedEof,
    ArithmeticOverflow,
}

impl fmt::Display for DecodeError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::DecoderPoisoned => formatter.write_str("decoder is poisoned"),
            Self::InvalidMagic => formatter.write_str("invalid zstd frame magic"),
            Self::InvalidFrameHeader => formatter.write_str("invalid zstd frame header"),
            Self::UnsupportedDictionary { id } => {
                write!(formatter, "zstd dictionary {id} is not supported")
            }
            Self::WindowTooLarge => formatter.write_str("zstd window does not fit this platform"),
            Self::HistoryTooSmall { required, provided } => write!(
                formatter,
                "history buffer is too small: need {required} bytes, have {provided}"
            ),
            Self::BlockScratchTooSmall { required, provided } => write!(
                formatter,
                "block scratch is too small: need {required} bytes, have {provided}"
            ),
            Self::LiteralScratchTooSmall { required, provided } => write!(
                formatter,
                "literal scratch is too small: need {required} bytes, have {provided}"
            ),
            Self::InvalidBlock => formatter.write_str("invalid zstd block"),
            Self::InvalidEntropyTable => formatter.write_str("invalid zstd entropy table"),
            Self::InvalidBitstream => formatter.write_str("invalid zstd bitstream"),
            Self::InvalidOffset => formatter.write_str("invalid zstd match offset"),
            Self::ChecksumMismatch { expected, actual } => write!(
                formatter,
                "zstd checksum mismatch: expected {expected:08x}, got {actual:08x}"
            ),
            Self::ContentSizeMismatch { expected, actual } => write!(
                formatter,
                "zstd content-size mismatch: expected {expected}, got {actual}"
            ),
            Self::UnexpectedEof => formatter.write_str("unexpected end of zstd stream"),
            Self::ArithmeticOverflow => formatter.write_str("zstd size arithmetic overflow"),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::string::ToString;

    use super::DecodeError;

    #[test]
    fn formats_every_decode_error() {
        let cases = [
            (DecodeError::DecoderPoisoned, "decoder is poisoned"),
            (DecodeError::InvalidMagic, "invalid zstd frame magic"),
            (DecodeError::InvalidFrameHeader, "invalid zstd frame header"),
            (
                DecodeError::UnsupportedDictionary { id: 42 },
                "zstd dictionary 42 is not supported",
            ),
            (
                DecodeError::WindowTooLarge,
                "zstd window does not fit this platform",
            ),
            (
                DecodeError::HistoryTooSmall {
                    required: 8,
                    provided: 3,
                },
                "history buffer is too small: need 8 bytes, have 3",
            ),
            (
                DecodeError::BlockScratchTooSmall {
                    required: 8,
                    provided: 3,
                },
                "block scratch is too small: need 8 bytes, have 3",
            ),
            (
                DecodeError::LiteralScratchTooSmall {
                    required: 8,
                    provided: 3,
                },
                "literal scratch is too small: need 8 bytes, have 3",
            ),
            (DecodeError::InvalidBlock, "invalid zstd block"),
            (
                DecodeError::InvalidEntropyTable,
                "invalid zstd entropy table",
            ),
            (DecodeError::InvalidBitstream, "invalid zstd bitstream"),
            (DecodeError::InvalidOffset, "invalid zstd match offset"),
            (
                DecodeError::ChecksumMismatch {
                    expected: 0x1234,
                    actual: 0xabcd,
                },
                "zstd checksum mismatch: expected 00001234, got 0000abcd",
            ),
            (
                DecodeError::ContentSizeMismatch {
                    expected: 12,
                    actual: 34,
                },
                "zstd content-size mismatch: expected 12, got 34",
            ),
            (DecodeError::UnexpectedEof, "unexpected end of zstd stream"),
            (
                DecodeError::ArithmeticOverflow,
                "zstd size arithmetic overflow",
            ),
        ];

        for (error, expected) in cases {
            assert_eq!(error.to_string(), expected);
        }
    }
}