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>;