z

Z

Pure Crystal implementation of DEFLATE (RFC 1951), zlib (RFC 1950), and gzip (RFC 1952) compression. Zero native dependencies — no libz, no C bindings.

The API mirrors Crystal's Compress::Deflate, Compress::Zlib, and Compress::Gzip so you can swap them in with minimal changes.

Installation

Add the dependency to your shard.yml:

dependencies:
  z:
    github: jkthorne/z

Then run shards install.

Usage

require "z"

Gzip

# Compress
compressed = IO::Memory.new
Z::Gzip::Writer.open(compressed) do |gz|
  gz.print "Hello, World!"
end

# Decompress
compressed.rewind
Z::Gzip::Reader.open(compressed) do |gz|
  puts gz.gets_to_end # => "Hello, World!"
end

Zlib

compressed = IO::Memory.new
Z::Zlib::Writer.open(compressed) do |zl|
  zl.print "Hello, World!"
end

compressed.rewind
Z::Zlib::Reader.open(compressed) do |zl|
  puts zl.gets_to_end # => "Hello, World!"
end

Raw DEFLATE

compressed = IO::Memory.new
Z::Deflate::Writer.open(compressed) do |deflate|
  deflate.print "Hello, World!"
end

compressed.rewind
Z::Deflate::Reader.open(compressed) do |inflate|
  puts inflate.gets_to_end # => "Hello, World!"
end

Compression levels

Pass level: to any writer. Levels range from 0 (store, no compression) to 9 (best compression, slowest). The default is 6.

# Fast compression
Z::Gzip::Writer.open(io, level: Z::Deflate::BEST_SPEED) { |gz| gz.print data }

# Maximum compression
Z::Gzip::Writer.open(io, level: Z::Deflate::BEST_COMPRESSION) { |gz| gz.print data }

# No compression (stored)
Z::Gzip::Writer.open(io, level: Z::Deflate::NO_COMPRESSION) { |gz| gz.print data }
Constant Value Description
Z::Deflate::NO_COMPRESSION 0 Store without compressing
Z::Deflate::BEST_SPEED 1 Fastest compression
Z::Deflate::DEFAULT_COMPRESSION 6 Balanced speed/ratio
Z::Deflate::BEST_COMPRESSION 9 Smallest output

Gzip header metadata

header = Z::Gzip::Header.new
header.name = "data.txt"
header.modification_time = Time.utc

compressed = IO::Memory.new
Z::Gzip::Writer.new(compressed, header: header) do |gz|
  gz.print "file contents"
end

When reading, access the header through the reader:

Z::Gzip::Reader.open(io) do |gz|
  gz.header.name            # => "data.txt" or nil
  gz.header.modification_time
  gz.header.os
  gz.header.comment         # => String or nil
  gz.gets_to_end
end

Checksums

The checksum modules are available standalone:

Z::Adler32.checksum("Hello".to_slice) # => UInt32
Z::CRC32.checksum("Hello".to_slice)   # => UInt32

# Incremental
adler = Z::Adler32.initial
adler = Z::Adler32.update(chunk1, adler)
adler = Z::Adler32.update(chunk2, adler)

crc = Z::CRC32.initial
crc = Z::CRC32.update(chunk1, crc)
crc = Z::CRC32.update(chunk2, crc)
checksum = Z::CRC32.finalize(crc)

Working with files

# Compress a file
File.open("data.txt") do |input|
  File.open("data.txt.gz", "w") do |output|
    Z::Gzip::Writer.open(output) do |gz|
      IO.copy(input, gz)
    end
  end
end

# Decompress a file
File.open("data.txt.gz") do |input|
  Z::Gzip::Reader.open(input) do |gz|
    File.open("data.txt", "w") do |output|
      IO.copy(gz, output)
    end
  end
end

Interoperability

Output from Z is valid and can be read by any compliant implementation. These all work:

# Compress with Z, decompress with Crystal stdlib
compressed = IO::Memory.new
Z::Gzip::Writer.open(compressed) { |gz| gz.print data }
compressed.rewind
Compress::Gzip::Reader.open(compressed) { |gz| gz.gets_to_end }

# Compress with Crystal stdlib, decompress with Z
compressed = IO::Memory.new
Compress::Gzip::Writer.open(compressed) { |gz| gz.print data }
compressed.rewind
Z::Gzip::Reader.open(compressed) { |gz| gz.gets_to_end }

Output is also compatible with command-line tools like gzip, gunzip, and zlib-flate.

How it works

The implementation is fully compliant with the RFC specifications:

  • LZ77 sliding window (32 KB) with hash-chain match finding and lazy matching at levels >= 4
  • Huffman coding with two-level lookup tables for decoding (9-bit primary + secondary) and canonical code generation for encoding
  • Block selection automatically chooses between stored, fixed Huffman, and dynamic Huffman blocks based on estimated encoded size
  • Checksums are pure Crystal: Adler-32 with the Nmax=5552 deferred-modulo optimization, CRC-32 with a precomputed 256-entry table
  • RFC compliance includes reserved-bit validation, header CRC16 (FHCRC) verification, trailer checksum enforcement, and correct XFL compression hints

Development

crystal spec

License

MIT

Repository

z

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 0
  • about 5 hours ago
  • April 8, 2026
License

MIT License

Links
Synced at

Wed, 08 Apr 2026 16:26:14 GMT

Languages