Skip to main content

Module workspace

Module workspace 

Source
Available on crate feature caller-workspace only.
Expand description

Streaming decoder that runs entirely in caller-owned memory.

WorkspaceDecoder decodes a stream of zstd frames chunk by chunk without touching the heap: the window, the entropy tables, the literals and the staging for blocks split across input chunks all live in one byte slice the caller supplies. workspace_bytes says how large that slice must be for a given window limit, so the caller can admit the memory in its own accounting before decoding starts, and keep the charge for as long as the slice lives.

Every step is bounded three ways, all chosen by the caller: the window a frame may declare and the total output (DecodeLimits), and the work a step may do (WorkBudget). A refusal is reported apart from malformed input, an unsupported feature, and input that ends mid-frame, and it ends the stream: every later step fails with WorkspaceDecodeError::Failed until WorkspaceDecoder::reset.

Frames that name a dictionary are refused with Unsupported::Dictionary; this decoder keeps no dictionary registry. With the legacy feature, v0.5-v0.7 frames are refused with Unsupported::LegacyFrame, since their decoders allocate.

The decoding itself is the same code the other decoders run, so output is identical to theirs.

§Examples

use structured_zstd::decoding::workspace::{
    workspace_bytes, DecodeLimits, StepState, WorkBudget, WorkspaceDecoder,
};
use structured_zstd::encoding::{compress_slice_to_vec, CompressionLevel};

let data = b"the same words, the same words, the same words".repeat(100);
let compressed = compress_slice_to_vec(&data, CompressionLevel::Default);

let limits = DecodeLimits::new(1 << 20).with_max_output(1 << 20);
let mut workspace = vec![0u8; workspace_bytes(limits.max_window_size).unwrap()];
let mut decoder = WorkspaceDecoder::new(&mut workspace, limits).unwrap();
let mut work = WorkBudget::new(1 << 24);

let mut decoded = Vec::new();
let mut out = [0u8; 512];
let mut input = &compressed[..];
loop {
    // Feed at most 100 bytes at a time; say when nothing more will come.
    let chunk = &input[..input.len().min(100)];
    let last = chunk.len() == input.len();
    let progress = decoder.step(chunk, last, &mut out, &mut work).unwrap();
    decoded.extend_from_slice(&out[..progress.produced]);
    input = &input[progress.consumed..];
    if progress.state == StepState::Done {
        break;
    }
}
assert_eq!(decoded, data);

Structs§

DecodeLimits
Limits a WorkspaceDecoder holds every frame of the stream to.
Progress
What one WorkspaceDecoder::step did.
WorkBudget
Work a caller allows its decode steps, in units charged before the work they pay for runs.
WorkspaceDecoder
Allocation-free streaming zstd decoder over a caller-owned workspace.

Enums§

LimitExceeded
A limit from DecodeLimits or WorkBudget that refused the stream.
MalformedInput
How the input breaks the zstd format.
StepState
Why a WorkspaceDecoder::step returned.
Unsupported
Something this decoder does not do, met in otherwise valid input.
WorkspaceConfigError
A workspace size or limit workspace_bytes or WorkspaceDecoder::new refused.
WorkspaceDecodeError
Why a WorkspaceDecoder::step failed. Every variant ends the stream: later steps return Self::Failed until WorkspaceDecoder::reset.

Constants§

COMPRESSED_BLOCK_UNITS
Work units charged before a compressed block decodes, on top of its input bytes and its output bound: the most its entropy-table builds can cost (one Huffman table of 1 << 12 entries and the literal-length, match-length and offset tables at 1 << 9, 1 << 9 and 1 << 8).

Functions§

workspace_bytes
Bytes of workspace a WorkspaceDecoder needs to decode frames whose window is at most max_window_size.