Expand description
Window-admitted Zstandard streams.
The open_zstd / ZstdDecoder interface, built on the pure-Rust ruzstd crate rather
than on zstd::zstd_safe (libzstd through zstd-sys and cc), so the workspace builds
with no C code.
ruzstd is a block decoder with a ring buffer, not a port of libzstd, so everything
libzstd checks and ruzstd does not is checked HERE, from this module’s own reading of
the frame, and each was measured against the Zstandard CLI v1.5.5 on 2026-10-12:
| rule | libzstd | bare ruzstd 0.9 | this module |
|---|---|---|---|
| reserved frame-descriptor bit | refused | accepted | refused at open |
| content checksum compared | refused on mismatch | read, never compared | compared |
Frame_Content_Size equals output | refused on mismatch | ignored | compared |
block header size above Block_Maximum_Size | refused | refused since 0.9.1 | refused |
sequences regenerating past Block_Maximum_Size | refused | refused since 0.9.1 | (ruzstd’s) |
| several frames, skippable frames | decoded / skipped | first frame only | decoded / skipped |
| bytes after the last frame | refused | ignored | refused at open |
One thing ruzstd does not reproduce, stated rather than hidden: a compressed block’s
LITERALS are not held to Block_Maximum_Size. ruzstd 0.9.1 refuses a block whose
sequences regenerate past the maximum, as it executes them: a 227-byte frame of a hundred
maximal matches is refused with 0.27 MB of heap, where 0.9.0 built its 13 MiB first (29.9
MB; both measured, 2026-10-13). That is why the requirement is 0.9.1 and not 0.9. What
it still does not check is the literals: a block with no sequences, and the literals after
a block’s last sequence. A literals section states at most 2^20 - 1 bytes, so the excess is
bounded by 1 MiB a block, is ALLOCATED before anything can refuse it, and is refused
afterwards by this module whenever the block’s size is determinable (see decode_block).
It is not determinable for a compressed block decoded while the buffer is still under its
window and its length unknown; the test zstd_known_gap_... pins the frame that gets
through.
Two differences in memory behaviour, also stated rather than hidden:
- The admission bounds the window, not peak heap. libzstd allocates one window per
frame.
ruzstd’s ring buffer grows by reallocation and holds the old and new buffers while copying, so a frame that fills its window peaks near two windows plus three blocks: about twice the window the admission charged. - A failed allocation is not returned. libzstd reported an allocation failure as an
error, which this module turned into
retained_allocation_failed;ruzstdpanics on a null from the allocator for its ring buffer and aborts through the global allocation error handler elsewhere, so an allocation that fails inside the decoder now panics or aborts instead of returning an error.
Structs§
- Zstd
Decoder - One Zstandard decoder over an admitted input.