A .zst file is one or more frames, Zstandard frames or skippable frames,
one after another; decompressing it gives the frames’ contents joined. This page follows
RFC 8878. Numbers are
little-endian. Compressed blocks are described on
compressed blocks.
Zstandard frame
| Field | Size | Description |
|---|---|---|
Magic_Number | 4 | FD2FB528, stored 28 B5 2F FD. |
Frame_Header_Descriptor | 1 | See below. |
Window_Descriptor | 0 or 1 | Absent when Single_Segment_Flag is set. See window size. |
Dictionary_ID | 0, 1, 2 or 4 | Which dictionary the frame needs. 0 or absent means none. |
Frame_Content_Size | 0, 1, 2, 4 or 8 | Size of the decompressed content. A 2-byte value has 256 added (256 to 65,791). |
| Blocks | … | One or more blocks; the last has Last_Block set. |
Content_Checksum | 0 or 4 | With Content_Checksum_Flag: the low 32 bits of XXH64, seed 0, of the content. |
Frame header descriptor
| Bits | Name | Meaning |
|---|---|---|
| 7 and 6 | Frame_Content_Size_Flag | Size of Frame_Content_Size: 0 → 0 bytes, or 1 byte when Single_Segment_Flag is set; 1 → 2; 2 → 4; 3 → 8. |
| 5 | Single_Segment_Flag | The content is one segment: no window descriptor, and the window size is the content size, which must be present. |
| 4 | Unused | Ignored by decoders; may be used in future. |
| 3 | Reserved | Must be 0. |
| 2 | Content_Checksum_Flag | A 4-byte checksum ends the frame. |
| 1 and 0 | Dictionary_ID_Flag | Size of Dictionary_ID: 0 → 0, 1 → 1, 2 → 2, 3 → 4 bytes. |
Window size
The window descriptor’s bits 7 to 3 are the exponent and bits 2 to 0 the mantissa.
windowLog = 10 + Exponent; windowBase = 1 << windowLog;
Window_Size = windowBase + (windowBase / 8) × Mantissa. That is 1 KB to
3.75 TB. Decoders should support at least 8 MB; the RFC recommends that encoders not
go above that for general use.
Blocks
Each block starts with a 3-byte little-endian header:
| Bits | Name | Meaning |
|---|---|---|
| 0 | Last_Block | This is the frame’s last block. |
| 1 and 2 | Block_Type | 0 Raw_Block: the bytes as they are. 1 RLE_Block: one byte, repeated Block_Size times. 2 Compressed_Block. 3 Reserved, invalid. |
| 3 to 23 | Block_Size | Bytes of content after the header; for an RLE block, the number of repeats (the content is 1 byte). |
A block holds at most Block_Maximum_Size of content, the smaller of the
window size and 128 KB. A compressed block may not be larger than that either. The
block 01 00 00 is an empty last raw block.
Skippable frames
| Field | Size | Description |
|---|---|---|
Magic_Number | 4 | Any of 184D2A50 to 184D2A5F. |
Frame_Size | 4 | Size of the user data. |
User_Data | … | Skipped by decoders. Used for metadata, such as the seek tables of the seekable format. |
Dictionaries
A dictionary prepares the decoder with content and entropy tables, which helps on small inputs. A frame names its dictionary by ID; the dictionary itself is a separate file:
| Field | Size | Description |
|---|---|---|
Magic_Number | 4 | EC30A437, stored 37 A4 30 EC. |
Dictionary_ID | 4 | Not 0. IDs up to 32,767 and from 231 are reserved for registration. |
Entropy_Tables | … | A Huffman table for literals, then FSE tables for offsets, match lengths and literal lengths (as in a compressed block), then three 4-byte repeat offsets that replace the default 1, 4 and 8. |
Content | … | Data treated as if decoded just before the frame. |
Any file without that magic can be used as a raw content-only dictionary.
Example
A small file compressed with default settings:
28 B5 2F FD magic
24 descriptor: Single_Segment (20), Content_Checksum (04), 1-byte content size
0C Frame_Content_Size 12
… blocks
xx xx xx xx checksum