Skip to main content

otf_pixels_compress/
lib.rs

1//! Compression and checksum primitives for `otf-pixels` codecs.
2//!
3//! Per ADR-0010 these are written from scratch, and per ADR-0012 they live
4//! here rather than inside one codec, because three formats now share them:
5//! PNG needs zlib, TIFF needs zlib *and* LZW, GIF needs LZW.
6//!
7//! # Scope
8//!
9//! This crate knows about bit streams and byte buffers. It knows nothing about
10//! images, pixels or descriptors, and deliberately does not depend on
11//! `otf-pixels-core`. That boundary is what lets it be tested directly against
12//! reference implementations — the validation ADR-0010 requires — rather than
13//! only through a codec.
14//!
15//! The consequence is a local [`Error`] type. Codecs translate it into their
16//! own error at the boundary, which is one small helper per codec and keeps
17//! the stable [`ErrorCode`] mapping where it belongs.
18//!
19//! [`ErrorCode`]: https://docs.rs/otf-pixels-core
20//!
21//! # Safety
22//!
23//! Every parser here reads attacker-controlled bytes and returns errors rather
24//! than panicking. `unsafe_code = "forbid"` means the classic decompressor
25//! failure — an out-of-bounds write through a back-reference — is
26//! unrepresentable rather than merely avoided.
27
28mod checksum;
29mod deflate;
30mod inflate;
31mod lzw;
32
33pub use checksum::{Adler32, Crc32};
34pub use deflate::{Level, deflate, zlib_compress};
35pub use inflate::{Inflater, ZlibStream, inflate_to, zlib_decompress};
36pub use lzw::{BitOrder, LzwDecoder, LzwEncoder};
37
38use core::fmt;
39
40/// A compression or checksum failure.
41///
42/// Always caused by malformed input or by a caller-declared bound being
43/// exceeded — never by an internal invariant, which is why there is no
44/// "internal error" variant to handle.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Error {
47    /// Which format the bytes claimed to be, for the caller's message.
48    format: &'static str,
49    detail: String,
50}
51
52impl Error {
53    /// Report bytes that are invalid for `format`.
54    #[must_use]
55    pub fn malformed(format: &'static str, detail: impl Into<String>) -> Self {
56        Self {
57            format,
58            detail: detail.into(),
59        }
60    }
61
62    /// The format tag, suitable for a codec's own error type.
63    #[must_use]
64    pub const fn format(&self) -> &'static str {
65        self.format
66    }
67
68    /// What went wrong.
69    #[must_use]
70    pub fn detail(&self) -> &str {
71        &self.detail
72    }
73}
74
75impl fmt::Display for Error {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(f, "malformed {} data: {}", self.format, self.detail)
78    }
79}
80
81impl std::error::Error for Error {}
82
83/// The result of a compression operation.
84pub type Result<T> = std::result::Result<T, Error>;