zstd
import zstd from "zstd";
let packed: string = fs.read_file("level1.pak.zst") or { return 1; };
var data: collections.Vec_u8 = zstd.decompress(packed, 256 << 20) or {
io.println("not good zstd: {err}");
return 1;
};
// data.slice() is what the .zst file holdszstd reads Zstandard (RFC 8878):
.zst files, asset packs, Content-Encoding: zstd bodies, and the
supercompressed levels of KTX2 textures (which texture
decodes with it). Every frame the RFC defines decodes:
- raw, RLE and compressed blocks, any window size;
- Huffman-coded literals, FSE-coded sequences, repeated tables and trees;
- the content checksum, checked whenever a frame has one;
- several frames one after the other, and skippable frames (skipped).
Decompression is strict: anything that isn’t valid zstd is an error,
including bytes left over after the last frame. A frame made with a
dictionary is refused with Unsupported. zstd decompresses only; it
has no encoder. It is a pure module, separate from
compress and brotli.
Errors
pub enum ZstdError : u8 {
BadData = 1: "not valid zstd data";
Truncated = 2: "zstd data ends early";
TooLarge = 3: "decompressed data over the limit";
Checksum = 4: "zstd content checksum does not match";
Unsupported = 5: "a zstd frame that needs a dictionary";
}BadData: the bytes are not zstd. Causes include a broken table or Huffman tree, a match reaching before the start of the data, a frame that produces a different size than its header says, or bytes after the end.Truncated: the data ends inside a frame (or is empty).TooLarge: the output would pass the limit you gave.Checksum: a frame decoded, but its content checksum differs: the data was damaged.Unsupported: the frame needs a dictionary.
Functions
decompress
pub fn decompress(data: []const u8, max_out: i64) -> collections.Vec_u8 or ZstdErrorDecompresses every frame in data into one byte vector. max_out is
the most output you accept: a small input that would expand to
gigabytes stops with TooLarge at that size, and the vector never grows
past it. Traps when max_out is negative.
decompress_into
pub fn decompress_into(data: []const u8, out: []u8) -> i64 or ZstdErrorDecompresses into your buffer and returns how many bytes it wrote. Use
it when you know the size, as a texture’s level index tells it. Output
that does not fit is TooLarge.
var pixels: [65536]u8;
let n: i64 = zstd.decompress_into(level_bytes, pixels[0..65536]) or { return 1; };content_size
pub fn content_size(data: []const u8) -> ?i64The size the first frame says it decompresses to, read from its header
without decoding anything; null when the header doesn’t say (a frame
written as a stream) or data is not zstd. A frame can lie: the size is
checked when you decompress.
let n: i64 = zstd.content_size(packed) or { 1 << 20 };Memory and speed
Each call allocates about 150 KiB for its tables and one block’s
literals, and frees it before returning; decompress also allocates its
result, at the frame’s content size when the header gives one. Natively
on an x64 desktop, 4 MB of text, JSON and binary records decompress in
about 10 ms.
Last modified: