Skip to main content

Module zstd

Module zstd 

Source
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:

rulelibzstdbare ruzstd 0.9this module
reserved frame-descriptor bitrefusedacceptedrefused at open
content checksum comparedrefused on mismatchread, never comparedcompared
Frame_Content_Size equals outputrefused on mismatchignoredcompared
block header size above Block_Maximum_Sizerefusedrefused since 0.9.1refused
sequences regenerating past Block_Maximum_Sizerefusedrefused since 0.9.1(ruzstd’s)
several frames, skippable framesdecoded / skippedfirst frame onlydecoded / skipped
bytes after the last framerefusedignoredrefused 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; ruzstd panics 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§

ZstdDecoder
One Zstandard decoder over an admitted input.