Revision control
Copy as Markdown
Other Tools
#![doc = core::include_str!("../README.md")]
#![cfg_attr(not(any(test, feature = "rust-allocator")), no_std)]
#[cfg(any(feature = "rust-allocator", feature = "c-allocator"))]
extern crate alloc;
mod adler32;
pub mod allocate;
pub mod c_api;
mod cpu_features;
pub mod crc32;
pub mod deflate;
pub mod inflate;
pub mod read_buf;
pub use adler32::{adler32, adler32_combine};
pub use crc32::{crc32, crc32_combine};
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {
#[cfg(feature = "ZLIB_DEBUG")]
{
eprint!($($arg)*)
}
};
}
/// Maximum size of the dynamic table. The maximum number of code structures is
/// 1924, which is the sum of 1332 for literal/length codes and 592 for distance
/// codes. These values were found by exhaustive searches using the program
/// examples/enough.c found in the zlib distributions. The arguments to that
/// program are the number of symbols, the initial root table size, and the
/// maximum bit length of a code. "enough 286 10 15" for literal/length codes
/// returns 1332, and "enough 30 9 15" for distance codes returns 592.
/// The initial root table size (10 or 9) is found in the fifth argument of the
/// inflate_table() calls in inflate.c and infback.c. If the root table size is
/// changed, then these maximum sizes would be need to be recalculated and
/// updated.
#[allow(unused)]
pub(crate) const ENOUGH: usize = ENOUGH_LENS + ENOUGH_DISTS;
pub(crate) const ENOUGH_LENS: usize = 1332;
pub(crate) const ENOUGH_DISTS: usize = 592;
/// initial adler-32 hash value
pub(crate) const ADLER32_INITIAL_VALUE: usize = 1;
/// initial crc-32 hash value
pub(crate) const CRC32_INITIAL_VALUE: u32 = 0;
pub const MIN_WBITS: i32 = 8; // 256b LZ77 window
pub const MAX_WBITS: i32 = 15; // 32kb LZ77 window
pub(crate) const DEF_WBITS: i32 = MAX_WBITS;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DeflateFlush {
#[default]
/// if flush is set to `NoFlush`, that allows deflate to decide how much data
/// to accumulate before producing output, in order to maximize compression.
NoFlush = 0,
/// If flush is set to `PartialFlush`, all pending output is flushed to the
/// output buffer, but the output is not aligned to a byte boundary. All of the
/// input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
/// This completes the current deflate block and follows it with an empty fixed
/// codes block that is 10 bits long. This assures that enough bytes are output
/// in order for the decompressor to finish the block before the empty fixed
/// codes block.
PartialFlush = 1,
/// If the parameter flush is set to `SyncFlush`, all pending output is
/// flushed to the output buffer and the output is aligned on a byte boundary, so
/// that the decompressor can get all input data available so far. (In
/// particular avail_in is zero after the call if enough output space has been
/// provided before the call.) Flushing may degrade compression for some
/// compression algorithms and so it should be used only when necessary. This
/// completes the current deflate block and follows it with an empty stored block
/// that is three bits plus filler bits to the next byte, followed by four bytes
/// (00 00 ff ff).
SyncFlush = 2,
/// If flush is set to `FullFlush`, all output is flushed as with
/// Z_SYNC_FLUSH, and the compression state is reset so that decompression can
/// restart from this point if previous compressed data has been damaged or if
/// random access is desired. Using `FullFlush` too often can seriously degrade
/// compression.
FullFlush = 3,
/// If the parameter flush is set to `Finish`, pending input is processed,
/// pending output is flushed and deflate returns with `StreamEnd` if there was
/// enough output space. If deflate returns with `Ok` or `BufError`, this
/// function must be called again with `Finish` and more output space (updated
/// avail_out) but no more input data, until it returns with `StreamEnd` or an
/// error. After deflate has returned `StreamEnd`, the only possible operations
/// on the stream are deflateReset or deflateEnd.
///
/// `Finish` can be used in the first deflate call after deflateInit if all the
/// compression is to be done in a single step. In order to complete in one
/// call, avail_out must be at least the value returned by deflateBound (see
/// below). Then deflate is guaranteed to return `StreamEnd`. If not enough
/// output space is provided, deflate will not return `StreamEnd`, and it must
/// be called again as described above.
Finish = 4,
/// If flush is set to `Block`, a deflate block is completed and emitted, as
/// for `SyncFlush`, but the output is not aligned on a byte boundary, and up to
/// seven bits of the current block are held to be written as the next byte after
/// the next deflate block is completed. In this case, the decompressor may not
/// be provided enough bits at this point in order to complete decompression of
/// the data provided so far to the compressor. It may need to wait for the next
/// block to be emitted. This is for advanced applications that need to control
/// the emission of deflate blocks.
Block = 5,
}
impl TryFrom<i32> for DeflateFlush {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::NoFlush),
1 => Ok(Self::PartialFlush),
2 => Ok(Self::SyncFlush),
3 => Ok(Self::FullFlush),
4 => Ok(Self::Finish),
5 => Ok(Self::Block),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InflateFlush {
#[default]
NoFlush = 0,
SyncFlush = 2,
Finish = 4,
Block = 5,
Trees = 6,
}
impl TryFrom<i32> for InflateFlush {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::NoFlush),
2 => Ok(Self::SyncFlush),
4 => Ok(Self::Finish),
5 => Ok(Self::Block),
6 => Ok(Self::Trees),
_ => Err(()),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Code {
/// operation, extra bits, table bits
pub op: u8,
/// bits in this part of the code
pub bits: u8,
/// offset in table or code value
pub val: u16,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum ReturnCode {
Ok = 0,
StreamEnd = 1,
NeedDict = 2,
ErrNo = -1,
StreamError = -2,
DataError = -3,
MemError = -4,
BufError = -5,
VersionError = -6,
}
impl From<i32> for ReturnCode {
fn from(value: i32) -> Self {
match Self::try_from_c_int(value) {
Some(value) => value,
None => panic!("invalid return code {value}"),
}
}
}
impl ReturnCode {
const fn error_message_str(self) -> &'static str {
match self {
ReturnCode::Ok => "\0",
ReturnCode::StreamEnd => "stream end\0",
ReturnCode::NeedDict => "need dictionary\0",
ReturnCode::ErrNo => "file error\0",
ReturnCode::StreamError => "stream error\0",
ReturnCode::DataError => "data error\0",
ReturnCode::MemError => "insufficient memory\0",
ReturnCode::BufError => "buffer error\0",
ReturnCode::VersionError => "incompatible version\0",
}
}
pub const fn error_message(self) -> *const core::ffi::c_char {
let msg = self.error_message_str();
msg.as_ptr().cast::<core::ffi::c_char>()
}
pub const fn try_from_c_int(err: core::ffi::c_int) -> Option<Self> {
match err {
0 => Some(Self::Ok),
1 => Some(Self::StreamEnd),
2 => Some(Self::NeedDict),
-1 => Some(Self::ErrNo),
-2 => Some(Self::StreamError),
-3 => Some(Self::DataError),
-4 => Some(Self::MemError),
-5 => Some(Self::BufError),
-6 => Some(Self::VersionError),
_ => None,
}
}
}