Sign in

    flate

    Pure-MoonBit, runtime-agnostic DEFLATE (RFC 1951) engine with gzip/zlib/zip wrappers — suspendable and io-free.

    compression
    deflate
    inflate
    gzip
    zlib
    zip
    rfc1951
    Download zip
    Version
    0.9.0
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    32K

    #flate

    Pure-MoonBit DEFLATE (RFC 1951) — a runtime-agnostic, suspendable, io-free compression engine, with gzip (RFC 1952), zlib (RFC 1950), and zip (APPNOTE.TXT) container wrappers.

    CRC-32 and Adler-32 are provided by the moonbit-community/flate/checksum package. It depends only on MoonBit core and is shared by gzip, zlib, and zip.

    #Install

    moon add moonbit-community/flate

    #Quick start

    The one-shot API is useful when the complete payload is already in memory. Read-only codec inputs accept BytesView, so a slice of a larger buffer needs no ownership conversion at the call site:

    ///|
    test "README raw DEFLATE round-trip" {
    let source = b"runtime-agnostic streaming compression"
    let compressed = @flate.deflate_all(source)
    @test.assert_eq(@flate.inflate_all(compressed), source)
    }

    For whole-buffer callers that already own their output storage, use the direct buffer API. deflate_bound supplies a conservative DEFLATE capacity (it panics for a negative input length or an unrepresentable bound); compression returns None if its encoded output does not fit, while decompression raises OutputLimitExceeded if its decoded output does not fit. Reuse Compressor and Decompressor across streams to retain their workspace.

    ///|
    test "README caller-buffer DEFLATE" {
    let source = b"direct input and caller-owned output"
    let compressor = @flate.Compressor(level=6)
    let compressed_buffer = FixedArray::make(
    @flate.deflate_bound(source.length()),
    b'\x00',
    )
    guard compressor.compress_into(source, compressed_buffer)
    is Some(compressed_len) else {
    @test.fail("deflate_bound was too small")
    }
    let compressed = Bytes::from_array(compressed_buffer[:compressed_len])
    let decompressor = @flate.Decompressor()
    let output = FixedArray::make(source.length(), b'\x00')
    let decoded_len = decompressor.decompress_into(compressed, output)
    @test.assert_eq(Bytes::from_array(output[:decoded_len]), source)
    }

    deflate_into and inflate_into are single-use convenience forms of those methods. Like inflate_all, inflate_into ignores trailing bytes after a valid final DEFLATE block; after Decompressor::decompress_into, Decompressor::last_consumed() is the number of input bytes the stream took, so a caller can find a trailer that follows it. Use the streaming APIs when input or output must suspend under backpressure.

    Compressor::compress(input) returns independent Bytes and reuses its workspace across calls, without requiring a caller-owned output buffer. Decompressor::decompress(input) is its counterpart: the output grows as in inflate_all, and last_consumed() says where the stream ended. That is how to read a stream of unknown length from the front of a buffer and go on behind it:

    ///|
    test "README prefix decode" {
    // A record: a raw DEFLATE stream, then whatever follows it.
    let payload = b"the stream ends with its final block"
    let record = @flate.deflate_all(payload) + b"what follows"
    let decompressor = @flate.Decompressor()
    @test.assert_eq(decompressor.decompress(record), payload)
    let rest = record[decompressor.last_consumed():]
    @test.assert_eq(rest.to_owned(), b"what follows")
    }

    decompress neither bounds its output nor takes a cancellation callback; with untrusted input use decompress_into, which reports its consumption the same way, or the streaming Inflater.

    The streaming API is a pure push state machine. It owns no I/O object, so both synchronous and asynchronous callers can supply input and drain output:

    ///|
    test "README streaming Deflater" {
    let source = b"small buffers exercise suspension and resume"
    let encoder = @flate.Deflater()
    let chunk = FixedArray::make(3, b'\x00')
    let compressed = @buffer.Buffer()
    let mut first = true
    for ;; {
    let input = if first { source[:] } else { b"" }
    let status = encoder.step(
    input,
    chunk.mut_view(),
    action=if first { Finish } else { Continue },
    )
    let consumed = encoder.last_consumed()
    let produced = encoder.last_produced()
    @test.assert_eq(consumed, if first { source.length() } else { 0 })
    first = false
    for i in 0..<produced {
    compressed.write_byte(chunk[i])
    }
    if status is Done {
    break
    }
    }
    @test.assert_eq(@flate.inflate_all(compressed.to_bytes()), source)
    }

    #State-machine contract

    • step returns the state-machine Status; last_consumed() and last_produced() describe exactly the prefixes accepted and written by that call. Drop only input[:consumed]; large input views may be partially consumed when output is backpressured, keeping internal memory bounded.
    • NeedMoreOutput preserves all pending work. Resume with another non-empty output view; one byte is sufficient.
    • Inflater owns the bounded tail of an incomplete atomic unit. On NeedMoreInput, feed the next non-overlapping chunk; no growing replay window is required.
    • Encoders take one DeflateAction: Continue, SyncFlush, or Finish. Pass the requested action with the final input of that batch. If consumed < input.length(), re-present that suffix with the same action. Once the complete view is accepted, the request remains latched across output backpressure.
    • Inflater::step_into(input, output) accepts a complete FixedArray[Byte] and uses bulk window copies. It has the same consumption and backpressure semantics as step, which accepts arbitrary mutable output views. Both release caller buffers after each call.
    • gzip and zlib Decoder::step_into(input, output) accept a complete FixedArray[Byte] and checksum decoded output in batches. Use it when the caller owns a fixed output buffer; step accepts arbitrary mutable views. Both drain pending body input before accepting more, so output can be produced while last_consumed() is zero. Re-present the unaccepted input on the next call.
    • Raw Inflater::step(..., end=true) and the container decoders turn physical EOF before Done into a stable truncation error. After any decoder error, discard or reset the raw engine; container decoder instances stably rethrow the same error.
    • Once Done is returned, new input is not consumed; reset the raw engine or the zlib Encoder (reset(dictionary?)), or create a new wrapper, before reuse. A reset encoder produces exactly the bytes of a freshly constructed one with the same options and driving schedule, while reusing its workspace.
    • Configure a raw preset dictionary through Deflater(...)/Inflater(...), or while starting a fresh stream through reset(dictionary=...); dictionary selection cannot be mutated after a stream begins.
    • gzip/zlib decoders may accept transport read-ahead beyond their trailer; after Done, recover that suffix with unused_input() before parsing the next protocol frame. For an exact one-member gzip boundary, construct the decoder with multistream=false.
    • Raw failures expose a stable InflateErrorKind (Truncated, Corrupt, TrailingData, OutputLimitExceeded, or Cancelled) alongside diagnostic text; gzip/zlib similarly expose GzipErrorKind/ZlibErrorKind.
    • One-shot decoding (inflate_all, inflate_exact) accepts an optional cancelled callback, polled at entry and then every ~4-8 KiB of decoded output; returning true raises InflateError(Cancelled, _), so untrusted-input loops can react to cancellation without streaming plumbing.

    #Exact and bounded one-shot decoding

    inflate_all remains the convenient trusted-input API: it accepts a raw DEFLATE prefix and grows its output without a limit. Use inflate_exact when the input must contain exactly one raw stream. Both accept max_output to bound the decoded size:

    ///|
    test "README exact and limited inflate" {
    let source = b"bounded convenience API"
    let compressed = @flate.deflate_all(source)
    @test.assert_eq(@flate.inflate_exact(compressed), source)
    @test.assert_eq(
    @flate.inflate_all(compressed, max_output=source.length()),
    source,
    )
    @test.assert_eq(
    @flate.inflate_exact(compressed, max_output=source.length()),
    source,
    )
    }

    inflate_exact combines exact framing with optional bounds: max_output caps the decoded size (raising OutputLimitExceeded before any oversized result is returned; negative limits are rejected), and preallocated=true replays the deterministic decode a second time into one exactly sized allocation. The preallocated mode trades roughly double the decode work for a peak memory of about one decoded output (instead of a growing buffer plus a final copy), which matters for very large single streams. The default stays single-pass for callers that do not need the memory bound. Called with the input alone, inflate_exact runs on the in-memory decoder of inflate_all; a limit, a callback or preallocated selects the stepping decoder, which gives the same result and the same error for a malformed, truncated or extended stream.

    #ZIP container

    The @zip package reads and writes ZIP archives using the same compression engine. Use Archive for in-memory editing and serialization:

    ///|
    test "README zip round-trip" {
    let archive = @zip.Archive()
    archive.add("hello.txt", b"hello, zip")
    let bytes = @zip.write(archive)
    let parsed = @zip.read(bytes)
    @test.assert_eq(parsed.get("hello.txt"), Some(b"hello, zip"))
    }

    Supported features include STORED and DEFLATE entries, ZIP64, data descriptors, UTF-8 names, and archive comments. read accepts ReadLimits to bound archive size, entry count, decompressed sizes, and the complete retained source, plus a cancelled callback. Exceeding a limit raises ZipError(LimitExceeded(kind, limit, actual), message).

    read verifies each entry's CRC-32, raising ChecksumMismatch for a damaged payload. Cancellation remains Cancelled throughout parsing, decoding and checksum verification.

    write(archive, preserve=true) reuses unchanged source records; an unmodified archive round-trips byte-for-byte, including prefixes, gaps and directory order. After an edit, local records and directory entries keep their respective order; bytes outside records, such as prefixes and gaps, are omitted. max_preserved_source_bytes covers the full source, including these bytes. Supply max_output_bytes to bound the serialized size before allocating it. write returns the complete ZIP as Bytes.

    For incremental output, use Writer with a synchronous sink. add accepts a complete file; begin_entry / write / end_entry accept chunks. Supply size when known; unknown sizes use ZIP64.

    ///|
    test "README incremental ZIP writer" {
    // A real folder adapter passes a buffered file-write callback here.
    let output = @buffer.Buffer()
    let writer = @zip.Writer(chunk => output.write_bytes(chunk))
    writer.add("small.txt", b"small file")
    writer.begin_entry("chunked.txt", size=6UL)
    writer.write(b"abc")
    writer.write(b"def")
    writer.end_entry()
    writer.finish()
    let archive = @zip.read(output.to_bytes())
    @test.assert_eq(archive.get("chunked.txt"), Some(b"abcdef"))
    }

    Call finish to write the central directory. The sink must consume each chunk before returning. If it raises an error, the writer becomes unusable and the partial output must be discarded.

    Writer retains directory metadata, current-entry metadata and codec buffers. add processes a complete file during the call; write feeds chunks through the codec or directly to the sink. Folder traversal and file I/O belong to the caller.

    #Architecture

    The raw engine shares Huffman construction, block writing and format validation across its encoding and decoding paths:

    PathImplementation
    Whole-buffer compressiondeflate_all.mbt, lz77.mbt
    Streaming compressiondeflate.mbt, block_planner.mbt, lz77.mbt
    Content-driven block splittingsplit_plan.mbt
    Iterated optimal parsing and splittingoptimal_parse.mbt, optimal_plan.mbt
    Block encoding and bit outputblock_writer.mbt, huffman_build.mbt, bitwriter.mbt
    Whole-buffer decompressioninflate_all.mbt
    Streaming decompressioninflate.mbt
    Shared decoding tables and validationhuffman_table.mbt, tables.mbt, decode_rules.mbt

    Unbounded inflate_all uses the direct MemDecoder path, and so does inflate_exact called with the input alone. Bounded inflate_all, and inflate_exact with a limit, a callback or preallocated, drive the streaming inflater. Decompressor::decompress_into uses the direct decoder with caller-owned output, Decompressor::decompress with an output that grows. Differential tests compare one-shot and streaming decoding across randomized chunk/output schedules, truncations and bit flips.

    The gzip and zlib packages add framing, checksums and streaming wrappers. ZIP provides in-memory archives and a synchronous incremental writer. The checksum package supplies CRC-32 and Adler-32 independently of the codecs.

    #Optional fast stored blocks

    fast_store=true opts into a heuristic speed/ratio trade-off. It samples byte variety, frequency skew and local repetition; small inputs also get a periodicity check. Inputs below 1 KiB skip the classifier. A candidate judged unlikely to compress is emitted as DEFLATE stored blocks without match search or Huffman construction. The default is false: existing calls retain their normal compression behavior.

    ///|
    let codec = @flate.Compressor(level=6, fast_store=true)

    ///|
    let raw = @flate.deflate_all(input, fast_store=true)

    ///|
    let stream = @flate.Deflater(level=6, fast_store=true)

    ///|
    let archive_bytes = @zip.write(archive, fast_store=true)

    The option is also available on deflate_into, split/optimal convenience APIs, gzip/zlib compression functions and streaming encoders, all ZIP serialization functions, and zip.Writer. Whole-buffer APIs classify the entire input; streaming encoders decide per block and retain stored bytes as match history. ZIP entries declared Deflate remain method 8; this option selects stored blocks inside DEFLATE, rather than changing the entry to ZIP method 0. Preserved ZIP payloads are reused; record offsets may be patched.

    This is not a proof of incompressibility. Long repeated patterns or useful dictionary matches can be missed, potentially making output much larger. Enable it only when that compression-ratio trade-off is acceptable. Decoding remains lossless, and the normal output bounds and output limits still apply.

    #Effort tiers

    All standard DEFLATE on the wire. Levels 0-9 are available on deflate_all / Deflater. The default level 6 uses distance-aware lazy matching and a sampled minimum match length; the other levels retain their zlib-derived tuning. Whole-buffer compression uses 65,535-byte block targets, while streaming keeps its bounded 16 KiB input blocks. deflate_all_split tokenizes each input range once with the full 32 KB history and cuts blocks where the literal/match observation distribution drifts (libdeflate's observation-divergence splitter, split_plan.mbt), arbitrating each chunk against fixed-cadence cuts using estimated block costs. deflate_all_optimal adds zopfli-style iterated optimal parsing (optimal_parse.mbt) plus content-driven block splitting (optimal_plan.mbt). Both paths use the shared block writer. Output size depends on the input and chosen parsing strategy.

    The containers expose both tiers: @gzip.compress / @zlib.compress and their streaming Encoders take level 0-9; @gzip.compress_optimal / @zlib.compress_optimal apply the offline zopfli path (one-shot only — a streaming encoder cannot suspend a whole-input optimal parse), for compress-once, serve-forever artifacts.

    #API conventions

    Read-only inputs accept BytesView; existing Bytes values convert at the call site without copying. Compressor::compress and compress_into reuse workspace for repeated streams, and @checksum.adler32 accepts views directly.

    Both inflate_all and inflate_exact take a plain optional integer ceiling: max_output=n. Omit it for unbounded output. inflate_all accepts a raw stream prefix; inflate_exact rejects trailing bytes.

    ZIP writing uses write(archive, preserve=true, max_output_bytes=n) for byte-preserving, bounded output. The options are independent. Construct read bounds with ReadLimits(max_entries=..., max_package_bytes=...,max_entry_uncompressed_bytes=..., max_total_uncompressed_bytes=...,max_preserved_source_bytes=...).

    Internal format and tuning constants are private. Use deflate_bound to size compression output. Filename-based compression selection belongs to the caller; the CLI supplies its own policy.

    ZIP payload CRC and gzip FHCRC are verified. A damaged checksum raises ChecksumMismatch; malformed ZIP DEFLATE data raises InvalidDeflate, and cancellation during ZIP decompression remains Cancelled. deflate_bound rejects negative or unrepresentable capacities.

    Use != for inequality and @debug.Repr(value) for debugging.

    InflateError

    pub suberror InflateError {
    InflateError(InflateErrorKind, String)
    }

    Error raised while decoding a raw DEFLATE stream.

    Compressor

    pub struct Compressor {
    // private fields
    }

    A reusable, whole-buffer raw DEFLATE compressor. Unlike the suspendable Deflater, it reads the input directly and writes one complete stream into either a caller-provided buffer or reusable growable output storage.

    Compressor::Compressor

    fn Compressor::Compressor(level? : Int, fast_store? : Bool) -> Compressor

    Create a reusable whole-buffer compressor. level 0-9 is clamped, with level 0 selecting stored blocks only. fast_store defaults to false; when enabled, sampled high-diversity inputs may bypass compression and use stored blocks. It trades compression ratio for speed without changing the data.

    Compressor::compress

    fn Compressor::compress(self : Compressor, input : BytesView) -> Bytes

    Compress a complete stream, reusing the parsing and output workspace. The returned bytes are independent of subsequent calls to this compressor.

    Compressor::compress_into

    fn Compressor::compress_into(self : Compressor, input : BytesView, output : FixedArray[Byte]) -> Int?

    Compress input directly into output, returning its byte length. None means that output was too small; any prefix written before exhaustion is unspecified. Reuse one Compressor and one caller-owned output buffer to avoid per-stream workspace and result-buffer allocation.

    Decompressor

    pub struct Decompressor {
    // private fields
    }

    A reusable, whole-buffer raw DEFLATE decompressor. It decodes the stream at the front of its input, into caller-owned storage (decompress_into) or into new Bytes (decompress), and says how much input the stream took (last_consumed), while retaining Huffman and dynamic-header workspace across streams.

    Decompressor::Decompressor

    fn Decompressor::Decompressor() -> Decompressor

    Create a reusable whole-buffer decompressor.

    Decompressor::decompress

    fn Decompressor::decompress(self : Decompressor, input : BytesView) -> Bytes raise InflateError

    Decompress input into new Bytes that grow with the output, as inflate_all does, while retaining the Huffman and dynamic-header workspace across streams. Bytes after the final DEFLATE block are ignored; last_consumed tells where they start, which is how a container finds the trailer of a member whose length it does not know.

    The output is unbounded and the call cannot be cancelled: a convenience for trusted input. To reject bytes after the stream, use inflate_exact. For a prefix decode with a bound, use decompress_into (an output of the size allowed, and a cancelled callback) or the streaming Inflater; both report the input they consumed.

    After an error the decompressor is ready for the next stream as it is, and last_consumed() is zero.

    Decompressor::decompress_into

    fn Decompressor::decompress_into(self : Decompressor, input : BytesView, output : FixedArray[Byte], cancelled? : () -> Bool) -> Int raise InflateError

    Decompress input into output, returning the number of bytes written. The complete output must fit in output; otherwise this raises InflateError(OutputLimitExceeded, _). As with inflate_all, bytes after the final DEFLATE block are ignored; last_consumed tells where they start. Reuse one Decompressor and output buffer to avoid per-stream decoder and result-buffer allocation.

    Decompressor::last_consumed

    fn Decompressor::last_consumed(self : Decompressor) -> Int

    Number of input bytes the latest decompress_into or decompress took: the DEFLATE stream up to and including the byte that holds the last bit of its final block. input[last_consumed():] is what the call ignored, a container's trailer for one. Zero initially and after a call raises.

    DeflateAction

    pub(all) enum DeflateAction {
    Continue
    SyncFlush
    Finish
    } derive(Eq,
    Debug
    )

    Requested action for one streaming compression step.

    The action applies after the complete input view has been consumed. If a step reports partial consumption, re-present the remaining suffix with the same action.

    DeflateAction::equal

    Deflater

    pub struct Deflater {
    // private fields
    }

    Streaming DEFLATE compressor : a push-based, suspendable state machine. It emits the stream block-by-block as input arrives, and chooses fixed- or dynamic-Huffman per block.

    Deflater::Deflater

    fn Deflater::Deflater(level? : Int, dictionary? : BytesView, fast_store? : Bool) -> Deflater

    Create a fresh compressor. level 0-9 (clamped) trades speed for ratio with level-specific tuning; 0 emits stored blocks only, 6 is the default. dictionary, when supplied, preloads the match window with its last 32 KB; the decoder must use the same preset dictionary. Optional fast_store samples each block and may skip compression; it is off by default and may increase output size, including when a dictionary could help.

    Deflater::is_finished

    fn Deflater::is_finished(self : Deflater) -> Bool

    Whether the final compressed stream has been fully emitted.

    Deflater::last_consumed

    fn Deflater::last_consumed(self : Deflater) -> Int

    Number of input bytes accepted by the latest step call. This is zero initially, after reset, and for the stable Done state.

    Deflater::last_produced

    fn Deflater::last_produced(self : Deflater) -> Int

    Number of output bytes written by the latest step call. This is zero initially, after reset, and for the stable Done state.

    Deflater::reset

    fn Deflater::reset(self : Deflater, dictionary? : BytesView) -> Unit

    Reset to the start of a fresh DEFLATE stream, keeping the compression level and fast_store policy, reusing allocations across streams (e.g. ZIP entries). With no dictionary, the history is cleared. Supplying one immediately preloads its last 32 KB for the new stream; the decoder must use the same preset dictionary.

    Deflater::step

    fn Deflater::step(self : Deflater, input : BytesView, output : MutArrayView[Byte], action? : DeflateAction) -> Status

    Run one compression step. Accepts as much of input as fits in one bounded block window, emits blocks, and drains compressed bytes into output. Returns the status; read last_consumed() and last_produced() immediately afterwards for this call's counts. Drop only input[:last_consumed()] and re-present the suffix. With a small output buffer a large input may be consumed only partially, which propagates output backpressure instead of growing internal memory with the caller's input size. Once a final block has been emitted, calls only drain that block and consume no new input; after completion they idempotently return Done with both counts zero.

    Use action=Finish with the final input view. The action is accepted after that entire view has been consumed, then remains latched across NeedMoreOutput; if a call reports partial consumption, re-present the suffix with action=Finish. Later input is left unconsumed.

    action=SyncFlush similarly requests zlib-style Z_SYNC_FLUSH: everything buffered is compressed and the bit stream is padded to a byte boundary with an empty stored block, so all produced bytes are final and transmittable while the stream continues. A sync-flush is sticky across output backpressure once accepted. Continue only accepts input. An empty output may accept at most one bounded block window before returning NeedMoreOutput.

    InflateErrorKind

    pub(all) enum InflateErrorKind {
    Truncated
    Corrupt
    TrailingData
    OutputLimitExceeded
    Cancelled
    } derive(Eq,
    Debug
    )

    Stable classification for failures raised by the raw DEFLATE APIs.

    InflateErrorKind::equal

    Inflater

    pub struct Inflater {
    // private fields
    }

    Streaming DEFLATE decompressor (RFC 1951): a push-based, suspendable state machine. All cross-call state lives here, so it pauses on NeedMoreInput/NeedMoreOutput and resumes on the next step. Supports stored, fixed- and dynamic-Huffman blocks. step makes progress with as little as one byte of output room (the output view): a match too large for the buffer is copied in pieces across calls.

    Inflater::Inflater

    fn Inflater::Inflater(dictionary? : BytesView) -> Inflater

    Create a fresh decompressor positioned at the start of a DEFLATE stream. If supplied, dictionary preloads the history window with its final 32 KiB.

    Inflater::is_finished

    fn Inflater::is_finished(self : Inflater) -> Bool

    Whether the end of the DEFLATE stream has been reached.

    Inflater::last_consumed

    fn Inflater::last_consumed(self : Inflater) -> Int

    Number of input bytes accepted by the latest step call. This is zero initially, after reset, after a call raises, and in the stable Done state.

    Inflater::last_produced

    fn Inflater::last_produced(self : Inflater) -> Int

    Number of output bytes written by the latest step call. This is zero initially, after reset, after a call raises, and in the stable Done state.

    Inflater::reset

    fn Inflater::reset(self : Inflater, dictionary? : BytesView) -> Unit

    Reset to the start of a fresh DEFLATE stream, reusing allocations (the 32 KB window and Huffman table storage) across many streams (e.g. ZIP entries). The stale window contents are unreachable afterwards: any distance reaching past the new stream's start is rejected (dist > wpos). Pass dictionary to preload the fresh stream's history window.

    Inflater::step

    fn Inflater::step(self : Inflater, input : BytesView, output : MutArrayView[Byte], end? : Bool) -> Status raise InflateError

    Run one decompression step. Reads from input, writes into output, and returns the status. After a normal return, read last_consumed() and last_produced() for this call's counts, and drop exactly input[:last_consumed()]. An empty output makes no progress (NeedMoreOutput, zero produced); one output byte always suffices to advance. Both counts are cleared before every call and remain zero if the call raises.

    Headers and Huffman symbols remain atomic internally, but an incomplete unit's small tail is retained by the engine. Therefore callers may feed ordinary non-overlapping chunks: on NeedMoreInput the complete supplied view has been accepted and need not be re-presented. Pass end=true with the physical final input view; the signal is sticky across output backpressure, and an incomplete stream then raises InflateError instead of returning NeedMoreInput forever. Once any error is raised, later calls stably raise the same error until reset().

    Inflater::step_into

    fn Inflater::step_into(self : Inflater, input : BytesView, output : FixedArray[Byte], end? : Bool) -> Status raise InflateError

    Advance the streaming decoder into a complete fixed output array. This has the same consumption, backpressure and EOF semantics as step, with bulk window copies. The array is borrowed only for this call; inspect last_consumed and last_produced after a successful step.

    Status

    pub(all) enum Status {
    Done
    NeedMoreInput
    NeedMoreOutput
    } derive(Eq,
    Debug
    )

    Outcome of one step of the streaming DEFLATE state machine.

    The engine is runtime-agnostic: it moves bytes between an input view and an output buffer and suspends by returning one of these, so any driver — a synchronous loop or an async adapter — can resume it.

    Status::equal

    fn Status::equal(Status, Status) -> Bool

    deflate_all

    fn deflate_all(input : BytesView, level? : Int, fast_store? : Bool) -> Bytes

    Compress bytes into a complete raw DEFLATE stream held entirely in memory. The whole input is tokenized directly, advancing a single offset and a session-owned fixed matchfinder; unlike Deflater, no input staging ring is needed. Opt-in fast_store may emit stored blocks after a heuristic check; false (the default) preserves normal compression behavior.

    deflate_all_optimal

    fn deflate_all_optimal(input : BytesView, iterations? : Int, fast_store? : Bool) -> Bytes

    Compress with zopfli-style effort: content-driven block splitting plus iterated optimal parsing, with a second splitting pass over the optimal tokens (blocksplittinglast). iterations is the squeeze count per block, clamped to at least 1; the default is 15. fast_store is disabled by default. Enabling it can bypass this strategy for heuristically incompressible input, trading output size for speed.

    deflate_all_split

    fn deflate_all_split(input : BytesView, level? : Int, fast_store? : Bool) -> Bytes

    Compress with content-driven block splitting: tokenize each master chunk once (full 32 KB cross-block history), cut the token stream where the symbol statistics drift, and select stored, fixed or dynamic encoding for each segment. A mid-tier between the fixed-cadence deflate_all and the offline zopfli deflate_all_optimal. fast_store is disabled by default. Enabling it can bypass this strategy for heuristically incompressible input, trading output size for speed.

    deflate_bound

    fn deflate_bound(input_size : Int) -> Int

    A conservative output capacity for deflate_into or Compressor::compress_into. The returned value covers the fixed 16 KiB minimum block cadence and final bit padding for a non-negative input length. Panics if the input length is negative or the bound cannot fit in Int.

    deflate_into

    fn deflate_into(input : BytesView, output : FixedArray[Byte], level? : Int, fast_store? : Bool) -> Int?

    One-shot convenience form of Compressor::compress_into. It creates a compressor for this call; use Compressor when encoding many streams. Optional fast_store has the same opt-in speed/ratio trade-off as deflate_all.

    inflate_all

    fn inflate_all(input : BytesView, max_output? : Int, cancelled? : () -> Bool) -> Bytes raise InflateError

    Decompress a raw DEFLATE stream held entirely in memory. Bytes after the final block are ignored; use inflate_exact when an exact framing boundary is required. Omit max_output for unbounded output, or supply a non-negative ceiling to raise OutputLimitExceeded before returning an oversized result. If supplied, cancelled is polled at entry and every 4096 produced bytes (8192 with max_output); returning true raises InflateError(Cancelled, _).

    inflate_exact

    fn inflate_exact(input : BytesView, max_output? : Int, cancelled? : () -> Bool, preallocated? : Bool) -> Bytes raise InflateError

    Decompress exactly one raw DEFLATE stream. Unlike inflate_all, this rejects any bytes after the final block.

    Optional controls:
    • max_output: decoded output beyond this size raises InflateError(OutputLimitExceeded, _). A negative value is rejected the same way.
    • cancelled: polled at entry and then once per internal step (every 8192 produced bytes); returning true raises InflateError(Cancelled, _).
    • preallocated: when true, decode runs twice — a counting pass that validates the whole stream, then a second pass filling one exactly sized allocation. Peak memory stays at roughly one decoded output, at the cost of roughly double the decode work; the default false grows one buffer in a single pass, trading transient memory for one decode pass.

    With none of the three there is nothing to poll, to bound or to count, and the stream is decoded by the in-memory decoder of inflate_all instead of in steps; the input it consumed tells whether anything follows the final block. The result and the errors of a malformed, truncated or extended stream are those of the stepping decoder. An output beyond the 2 GiB that Bytes can hold raises InflateError(OutputLimitExceeded, _) on this path, as it does with preallocated.

    inflate_into

    fn inflate_into(input : BytesView, output : FixedArray[Byte], cancelled? : () -> Bool) -> Int raise InflateError

    One-shot convenience form of Decompressor::decompress_into. It creates a decompressor for this call; use Decompressor when decoding many streams.