Guide

Containers

gzip and zlib and raw deflate are one compression in three wrappers. Why that's an option and what each wrapper adds

One compression, several wrappers

A container is the packaging around a compressed stream: a magic number so tools recognize it, a header with metadata, a checksum at the end. The compression inside doesn't change. gzip and zlib carry the exact same deflate data, they just wrap it differently.

So here a container is an option of its format, never a format of its own:

ts
compress("deflate", data);                          // raw deflate, RFC 1951
compress("deflate", data, { container: "zlib" });   // RFC 1950
compress("deflate", data, { container: "gzip" });   // RFC 1952

The first container is the default. Asking for gzip as a format gets you an error that names deflate and the container, so nobody has to guess.

Every container

FormatContainerMagicHeaderCheck
deflaterawnonenonenone
deflatezlibnone, two header bytes with a checkmethod, window, level hintAdler-32
deflategzip1f 8b 08flags, mtime, OS, optional name and commentCRC-32 and size
bzip2bzip2BZh and the block size digitper block magic and CRCCRC-32/BZIP2 per block and per stream
lzmaalonenone, the properties byte is usually 5dproperties, dictionary size, sizenone
lzmaxzfd 37 7a 58 5a 00stream flags, block headers with filtersCRC-32, CRC-64 or SHA-256 per block, CRC-32 everywhere else
zstdzstd28 b5 2f fdwindow, dictionary id, content sizeXXH64, low 32 bits
brotlibrotlinonewindow bitsnone
lz4frame04 22 4d 18flags, block size, content sizeXXH32 of the header, blocks and content
lz4legacy02 21 4c 18nonenone
lzwcompress1f 9dmaximum code width, block modenone

What each one adds

gzip

It stores an optional file name and comment, a modification time and the OS. compress writes name and mtime when you pass them and OS 255, unknown, so the bytes don't depend on the machine. decompress reads every member, as gunzip does.

zlib

Two header bytes and an Adler-32. The second byte carries a level hint, which decompress reports as fastest, fast, default or best. A stream that needs a preset dictionary is refused with UnsupportedError.

xz

The elaborate one. Blocks, each with its own filter chain and check, an index of the blocks and a footer that points back at it. This package reads LZMA2 with the delta and x86 filters before it, and every check type it knows how to compute. It writes one block with LZMA2 and the check you pick, CRC-64 by default.

.lzma

alone here. It's what LZMA Utils wrote before xz: thirteen header bytes and the raw range coded stream. No checksum, so a damaged file decodes into garbage instead of an error. xz --format=lzma still writes it.

LZ4 frame

It checks itself three ways: a byte of XXH32 closes the header, and the frame can carry XXH32 after every block and after the content. compress writes the content checksum, like the lz4 command.

LZ4 legacy

It's the old lz4 -l format. Blocks of up to 8 MiB, no checksum, no end mark. Linux still boots kernels packed in it.