Skip to content

Zlib & Deflate

Zlib and Deflate are closely related formats based on the DEFLATE algorithm. They’re used extensively in PNG images, ZIP files, HTTP, and many other contexts.

FormatDescriptionChecksumAuto-detect
DeflateRaw DEFLATE streamNoneNo
ZlibDEFLATE + zlib header/trailerAdler-32Yes
GzipDEFLATE + gzip header/trailerCRC32Yes

All three use the same compression algorithm but different wrappers.

FormatCompressDecompressRatio
Deflate2.4 GB/s2.4 GB/s99.2%
Zlib2.4 GB/s2.4 GB/s99.2%
Use CaseFormat
PNG imagesZlib
ZIP filesDeflate (raw)
HTTP Content-EncodingGzip
Custom protocolDeflate (minimal overhead)
Data integrity neededZlib (Adler-32)
General file compressionGzip

Zlib wraps DEFLATE with a small header and Adler-32 checksum.

const cz = @import("compressionz");
// Compress
const compressed = try cz.compress(.zlib, data, allocator);
defer allocator.free(compressed);
// Decompress
const decompressed = try cz.decompress(.zlib, compressed, allocator);
defer allocator.free(decompressed);
// Compression level
const best = try cz.compressWithOptions(.zlib, data, allocator, .{
.level = .best,
});
// With dictionary
const dict = try cz.compressWithOptions(.zlib, data, allocator, .{
.dictionary = my_dictionary,
});
┌─────────────────────────────────────────────────────┐
│ Zlib Header (2 bytes) │
│ - CMF: compression method and flags │
│ - FLG: flags (check bits, preset dict, level) │
│ - [Dictionary ID] (4 bytes, if FDICT set) │
├─────────────────────────────────────────────────────┤
│ Compressed Data (DEFLATE) │
├─────────────────────────────────────────────────────┤
│ Adler-32 Checksum (4 bytes) │
└─────────────────────────────────────────────────────┘
// Zlib detection (CMF * 256 + FLG must be divisible by 31)
fn isZlib(data: []const u8) bool {
if (data.len < 2) return false;
const cmf = data[0];
const flg = data[1];
return (cmf & 0x0F) == 8 and // DEFLATE method
(@as(u16, cmf) * 256 + flg) % 31 == 0;
}

Raw DEFLATE stream without any wrapper.

const cz = @import("compressionz");
// Compress
const compressed = try cz.compress(.deflate, data, allocator);
defer allocator.free(compressed);
// Decompress
const decompressed = try cz.decompress(.deflate, compressed, allocator);
defer allocator.free(decompressed);

Both Zlib and Deflate support dictionary compression:

const dictionary = "common patterns in your data...";
// Compress with dictionary
const compressed = try cz.compressWithOptions(.deflate, data, allocator, .{
.dictionary = dictionary,
});
defer allocator.free(compressed);
// Decompress with same dictionary
const decompressed = try cz.decompressWithOptions(.deflate, compressed, allocator, .{
.dictionary = dictionary,
});
defer allocator.free(decompressed);
┌─────────────────────────────────────────────────────┐
│ DEFLATE Blocks │
│ │
│ Block Header (3 bits) │
│ - BFINAL: 1 if last block │
│ - BTYPE: block type (00=stored, 01=fixed, 10=dyn) │
│ │
│ Block Data │
│ - Huffman-coded literals and length/distance pairs │
│ │
│ ... more blocks ... │
│ │
│ Final block (BFINAL=1) │
└─────────────────────────────────────────────────────┘

Zlib and Deflate are the only codecs in compressionz (besides Zstd) that support dictionary compression.

The dictionary provides a “seed” of common patterns that the compressor can reference:

// Without dictionary: compressor builds patterns from scratch
// With dictionary: compressor starts with known patterns
const json_dict =
\\{"id":,"name":,"email":,"created_at":,"updated_at":
\\,"status":"active","status":"inactive","status":"pending"
\\,"type":"user","type":"admin","type":"guest"
;
// Small JSON objects compress much better with dictionary
const compressed = try cz.compressWithOptions(.zlib, json_data, allocator, .{
.dictionary = json_dict,
});
Data SizeWithout DictWith DictImprovement
100 bytes95 bytes45 bytes53% smaller
500 bytes380 bytes210 bytes45% smaller
1 KB720 bytes520 bytes28% smaller
10 KB6.5 KB5.8 KB11% smaller

Dictionary compression is most effective for small data with predictable patterns.

Both formats support streaming:

const cz = @import("compressionz");
const std = @import("std");
// Streaming compression
pub fn compressStream(allocator: std.mem.Allocator, input: anytype, output: anytype) !void {
var comp = try cz.compressor(.zlib, allocator, output, .{});
defer comp.deinit();
var buf: [65536]u8 = undefined;
while (true) {
const n = try input.read(&buf);
if (n == 0) break;
try comp.writer().writeAll(buf[0..n]);
}
try comp.finish();
}
// Streaming decompression
pub fn decompressStream(allocator: std.mem.Allocator, input: anytype) ![]u8 {
var decomp = try cz.decompressor(.zlib, allocator, input);
defer decomp.deinit();
return decomp.reader().readAllAlloc(allocator, 1024 * 1024 * 1024);
}
const cz = @import("compressionz");
// Zlib
const zlib_compressed = try cz.zlib_codec.compress(data, allocator, .default, .zlib);
const zlib_decompressed = try cz.zlib_codec.decompress(compressed, allocator, .zlib);
// Deflate
const deflate_compressed = try cz.zlib_codec.compress(data, allocator, .default, .deflate);
const deflate_decompressed = try cz.zlib_codec.decompress(compressed, allocator, .deflate);
// With dictionary
const dict_compressed = try cz.zlib_codec.compressWithDict(
data, allocator, .default, .zlib, dictionary
);
const dict_decompressed = try cz.zlib_codec.decompressWithDict(
compressed, allocator, .zlib, max_size, dictionary
);

DEFLATE combines two compression techniques:

  1. LZ77 — Replace repeated sequences with (length, distance) pairs
  2. Huffman Coding — Use shorter codes for more frequent symbols
Input: "ABRACADABRA"
LZ77 output:
A B R A C A D [match: length=4, distance=7] A
(The "ABRA" at the end matches "ABRA" from 7 positions back)
Huffman coding:
Assign short bit sequences to common symbols
A=0, B=10, R=110, C=1110, D=11110, etc.
TypeNameUse Case
00StoredIncompressible data (passed through)
01FixedPre-defined Huffman codes
10DynamicCustom Huffman codes per block

PNG uses Zlib internally:

// PNG file structure (simplified)
// IHDR chunk (image header)
// IDAT chunk (zlib-compressed image data) <-- Zlib here
// IEND chunk (end marker)

ZIP uses raw Deflate:

// ZIP local file header
// Deflate-compressed file data <-- Raw Deflate here
// Data descriptor (optional)

For minimal overhead, use raw Deflate:

fn sendCompressed(socket: *Socket, data: []const u8, allocator: Allocator) !void {
// 4-byte size prefix + deflate data (no zlib overhead)
const compressed = try cz.compress(.deflate, data, allocator);
defer allocator.free(compressed);
var header: [4]u8 = undefined;
std.mem.writeInt(u32, &header, @intCast(compressed.len), .little);
try socket.writeAll(&header);
try socket.writeAll(compressed);
}
AspectDeflateZlibGzip
Overhead0 bytes6 bytes18+ bytes
ChecksumNoneAdler-32CRC32
Dictionary
Auto-detect
Use caseEmbeddingLibrariesFiles