philiprehberger-gzip_kit
Gzip compression and decompression with streaming support
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-gzip_kit"
Or install directly:
gem install philiprehberger-gzip_kit
Usage
require "philiprehberger/gzip_kit"
compressed = Philiprehberger::GzipKit.compress("Hello, world!")
original = Philiprehberger::GzipKit.decompress(compressed)
Compression Levels
require "philiprehberger/gzip_kit"
# Fast compression
fast = Philiprehberger::GzipKit.compress(data, level: Zlib::BEST_SPEED)
# Maximum compression
small = Philiprehberger::GzipKit.compress(data, level: Zlib::BEST_COMPRESSION)
# No compression (store only)
raw = Philiprehberger::GzipKit.compress(data, level: Zlib::NO_COMPRESSION)
Compression Stats
require "philiprehberger/gzip_kit"
result = Philiprehberger::GzipKit.compress(data, stats: true)
# => { data: "...", ratio: 0.85, original_size: 10000, compressed_size: 1500 }
Gzip Detection
require "philiprehberger/gzip_kit"
Philiprehberger::GzipKit.compressed?(gzip_data) # => true
Philiprehberger::GzipKit.compressed?("plain text") # => false
File Operations
require "philiprehberger/gzip_kit"
# Compress a file
Philiprehberger::GzipKit.compress_file("data.txt", "data.txt.gz")
# Decompress a file
Philiprehberger::GzipKit.decompress_file("data.txt.gz", "data.txt")
# Compress with progress callback
Philiprehberger::GzipKit.compress_file("data.txt", "data.txt.gz") do |bytes_processed, total_bytes|
puts "#{bytes_processed}/#{total_bytes} bytes"
end
# Decompress with progress callback (total_bytes is nil)
Philiprehberger::GzipKit.decompress_file("data.txt.gz", "data.txt") do |bytes_processed, total_bytes|
puts "#{bytes_processed} bytes decompressed"
end
Streaming
require "philiprehberger/gzip_kit"
# Compress from one IO to another
File.open("input.txt", "rb") do |input|
File.open("output.gz", "wb") do |output|
Philiprehberger::GzipKit.compress_stream(input, output)
end
end
# Decompress from one IO to another
File.open("output.gz", "rb") do |input|
File.open("restored.txt", "wb") do |output|
Philiprehberger::GzipKit.decompress_stream(input, output)
end
end
Custom chunk size
Streaming and file methods read in 64 KB chunks by default. Override with chunk_size: to tune for memory constraints or throughput:
require "philiprehberger/gzip_kit"
# Smaller chunks for memory-constrained environments
File.open("input.txt", "rb") do |input|
File.open("output.gz", "wb") do |output|
Philiprehberger::GzipKit.compress_stream(input, output, chunk_size: 4 * 1024)
end
end
# Larger chunks for bulk throughput
Philiprehberger::GzipKit.compress_file("input.bin", "output.gz", chunk_size: 1 * 1024 * 1024)
Philiprehberger::GzipKit.decompress_file("output.gz", "restored.bin", chunk_size: 1 * 1024 * 1024)
chunk_size: must be a positive Integer; any other value raises ArgumentError.
Decompress with stats
Mirror of compress(..., stats: true) — returns a hash with the decompressed payload and the compressed-to-decompressed size ratio:
require "philiprehberger/gzip_kit"
compressed = Philiprehberger::GzipKit.compress("a" * 10_000)
result = Philiprehberger::GzipKit.decompress(compressed, stats: true)
# => { data: "aaaa...", ratio: 0.0041 }
The ratio is compressed_size / decompressed_size (inverse of compression ratio). For zero-length output, ratio is 0.0.
Stream Concatenation
require "philiprehberger/gzip_kit"
part_a = Philiprehberger::GzipKit.compress("Hello, ")
part_b = Philiprehberger::GzipKit.compress("world!")
combined = Philiprehberger::GzipKit.concat(part_a, part_b)
Philiprehberger::GzipKit.decompress(combined) # => "Hello, world!"
Equivalence Check
Compare two gzip blobs by their decompressed payload — useful when the same source is recompressed at different levels or with different metadata:
require "philiprehberger/gzip_kit"
fast = Philiprehberger::GzipKit.compress("hello", level: Zlib::BEST_SPEED)
best = Philiprehberger::GzipKit.compress("hello", level: Zlib::BEST_COMPRESSION)
Philiprehberger::GzipKit.equivalent?(fast, best) # => true
Philiprehberger::GzipKit.equivalent?(fast, Philiprehberger::GzipKit.compress("world")) # => false
Header Inspection
require "philiprehberger/gzip_kit"
header = Philiprehberger::GzipKit.inspect_header(gzip_data)
# => { method: :deflate, mtime: 2026-03-28 12:00:00 +0000, os: 255, original_name: nil, comment: nil }
API
| Method | Description |
|---|---|
GzipKit.compress(string, level:, stats:) |
Compress a string to gzip bytes; returns stats hash when stats: true |
GzipKit.decompress(data, stats:) |
Decompress gzip bytes to a string; returns { data:, ratio: } when stats: true |
GzipKit.compressed?(data) |
Check if data is gzip-compressed via magic bytes |
GzipKit.compress_file(src, dest, level:, chunk_size:, &block) |
Compress a file to a gzip file with optional progress callback and chunk size |
GzipKit.decompress_file(src, dest, chunk_size:, &block) |
Decompress a gzip file with optional progress callback and chunk size |
GzipKit.compress_stream(io_in, io_out, level:, chunk_size:) |
Streaming compression (64 KB chunks by default) |
GzipKit.decompress_stream(io_in, io_out, chunk_size:) |
Streaming decompression (64 KB chunks by default) |
GzipKit.concat(data_a, data_b) |
Concatenate two gzip-compressed strings |
GzipKit.equivalent?(blob_a, blob_b) |
Check whether two gzip blobs decompress to equal byte strings |
GzipKit.inspect_header(data) |
Read gzip header metadata without decompressing |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: