Skip to content

Compression ​

One-Shot Compression ​

The simplest way to compress data - default level 3:

zig
const zstd = @import("zstd");

const compressed = try zstd.compress(allocator, data);
defer allocator.free(compressed);

compress signature from:

zig
pub fn compress(allocator: std.mem.Allocator, src: []const u8) anyerror![]u8

Compressed Block Encoding ​

Compression follows the Zstandard 1.6.0 specification: block headers, literal sections, and FSE-encoded sequence sections all conform exactly.

compress emits spec-compliant Compressed_Blocks natively:

  • LZ77 match finding over a hash chain (4-byte hash, configurable depth)
  • Raw/RLE literal sections per the literals header spec (1/2/3-byte sizes)
  • Sequence section with predefined FSE tables for literal-length, offset and match-length codes, plus extra-bit tails - encoded through native Zig FSE table construction and symbol encoding
  • Automatic fallback to Raw_Block when compression does not help

Compression Levels (i32) ​

Levels are plain i32, range -131072 to 22 (see constants.c_level_min/max). Use zstd.compressWithLevel for numeric control:

zig
// Numeric levels 1-22 (and negative levels for fast modes)
const fast = try zstd.compressWithLevel(allocator, data, 1);
const balanced = try zstd.compressWithLevel(allocator, data, 3);
const best = try zstd.compressWithLevel(allocator, data, 19);
const custom = try zstd.compressWithLevel(allocator, data, 12);

// Helpers
const min = zstd.minCLevel();     // -131072
const max = zstd.maxCLevel();     // 22
const def = zstd.defaultCLevel(); // 3

compressWithLevel signature:

zig
pub fn compressWithLevel(allocator: std.mem.Allocator, src: []const u8, level: i32) anyerror![]u8

Note: There is no CLevel enum. Earlier docs referenced CLevel.fastest/default/best; use plain i32 instead.

CompressionOptions ​

For fine-grained control use zstd.compressWithOptions:

zig
pub const CompressionOptions = struct {
    level: i32 = 3,
    windowLog: u8 = 0,
    hashLog: u8 = 0,
    chainLog: u8 = 0,
    searchLog: u8 = 0,
    minMatch: u8 = 0,
    targetLength: u32 = 0,
    strategy: Strategy = .fast,
    checksum: bool = false,
    dictId: u32 = 0,
    contentSize: ?u64 = null,

};
zig
const opts = zstd.CompressionOptions{
    .level = 9,
    .checksum = true,
    .windowLog = 20,
    .strategy = .lazy2,
};
const compressed = try zstd.compressWithOptions(allocator, data, opts);
defer allocator.free(compressed);

// Or derive tuned options for a level + source size
var tuned = zstd.getCompressionParameters(12, data.len, 0);
tuned.checksum = true;
const c2 = try zstd.compressWithOptions(allocator, data, tuned);
defer allocator.free(c2);
FieldTypeDefaultDescription
leveli323Compression level (minCLevel..maxCLevel)
windowLogu80Window log override (0 = auto)
hashLogu80Hash log override
chainLogu80Chain log override
searchLogu80Search log override
minMatchu80Minimum match length
targetLengthu320Target length
strategyStrategy.fastfast, dfast, greedy, lazy, lazy2, btlazy2, btopt, btultra, btultra2
checksumboolfalseEnable XXH64 checksum
dictIdu320Dictionary ID for frame header
contentSize?u64nullPledged source size (null = auto)

Reusable CompressionContext ​

For compressing multiple buffers with the same settings:

zig
var cctx = zstd.CompressionContext.init(allocator);
defer cctx.deinit();

// Or with level
var cctx2 = zstd.CompressionContext.initWithLevel(allocator, 9);
defer cctx2.deinit();

// Compress with allocator
const c1 = try cctx.compressAlloc(data1);
defer allocator.free(c1);

const c2 = try cctx.compressAlloc(data2);
defer allocator.free(c2);

// Compress into preallocated buffer
var buf: [4096]u8 = undefined;
const written = try cctx.compress(&buf, data);

CompressionContext Methods ​

MethodSignatureDescription
initinit(allocator: Allocator) CompressionContextCreate with default level 3
initWithLevelinitWithLevel(allocator: Allocator, level: i32) CompressionContextCreate with numeric level
deinitdeinit(self: *CompressionContext) voidRelease resources
setLevelsetLevel(self: *CompressionContext, level: i32) voidChange compression level
setChecksumsetChecksum(self: *CompressionContext, flag: bool) voidEnable/disable checksum
setWindowLogsetWindowLog(self: *CompressionContext, log: u8) voidSet window log
setPledgedSrcSizesetPledgedSrcSize(self: *CompressionContext, size: ?u64) voidSet content size for header
compresscompress(self: *CompressionContext, dst: []u8, src: []const u8) !usizeCompress into preallocated buffer
compressAlloccompressAlloc(self: *CompressionContext, src: []const u8) anyerror![]u8Compress with allocator
resetreset(self: *CompressionContext) voidReset streaming state
zig
cctx.setLevel(5);
cctx.setChecksum(true);
cctx.setWindowLog(22);
cctx.setPledgedSrcSize(@as(?u64, data.len));
cctx.reset(); // reuse for new job

compressBound / compressInto ​

Pre-allocate output buffers. compressBound is a worst-case guarantee, not an estimate: a buffer of exactly that size holds the frame for any input of that length, at any level. It fails only for a size the format cannot represent (at or above MAX_INPUT_SIZE), rather than wrapping.

zig
const bound = try zstd.compressBound(src.len);
var buf = try allocator.alloc(u8, bound);
defer allocator.free(buf);

const written = try zstd.compressInto(allocator, buf, src, 3);

compressInto returns error.DstSizeTooSmall when dst is below the bound, so a short buffer is reported rather than yielding a truncated frame.

Checksum ​

Enable frame checksum for data integrity verification:

zig
const opts = zstd.CompressionOptions{ .checksum = true };
const compressed = try zstd.compressWithOptions(allocator, data, opts);
// The decompressor will verify the checksum automatically; use DecompressionOptions.forceIgnoreChecksum to skip

Released under the MIT License.