File Compression Example
This guide explains how to compress and decompress on-disk files using snappy.zig and Zig 0.17.0's std.Io engine. Snappy compressed files conventionally use the .sz extension and strictly follow the official Google Snappy Framed Stream format.
Snappy .sz File Format Architecture
When writing a compressed file to disk:
- File Header: Every valid
.szfile begins with the 10-byte sequence:[0xff, 0x06, 0x00, 0x00, 's', 'N', 'a', 'P', 'p', 'Y'] - Chunking: Input data is split into blocks of at most 65,536 bytes (
snappy.MAX_BLOCK_SIZE). - Integrity Protection: Each chunk is stamped with its Castagnoli CRC-32C checksum masked with: $$\text{MaskedCRC} = ((\text{CRC} \gg 15) \mid (\text{CRC} \ll 17)) + \text{0xa282ead8}$$
- Interoperability: Any
.szfile produced bysnappy.zigcan be decompressed by Google's C++ toolsnappy, Linux command-line tools, Pythonsnappy, or Go'sszpackage.
Compressing a File (examples/compress_file.zig)
zig
const std = @import("std");
const snappy = @import("snappy");
pub fn main() !void {
const allocator = std.heap.page_allocator;
var threaded = std.Io.Threaded.init(allocator, .{});
defer threaded.deinit();
const io = threaded.io();
const cwd = std.Io.Dir.cwd();
const sample_file = "temp_example_input.txt";
const comp_file = "temp_example_output.sz";
defer {
cwd.deleteFile(io, sample_file) catch {};
cwd.deleteFile(io, comp_file) catch {};
}
// Create a sample text file
const sample_content =
"This is a demonstration of file compression using snappy.zig on Windows/Linux/macOS.\n" ++
"Snappy prioritizes extreme compression and decompression throughput.\n" ++
"Native Zig implementation with zero external dependencies and memory safety.\n";
try cwd.writeFile(io, .{ .sub_path = sample_file, .data = sample_content });
std.debug.print("Created sample input file '{s}' ({d} bytes)\n", .{ sample_file, sample_content.len });
// Read and compress
const in_data = try cwd.readFileAlloc(io, sample_file, allocator, @fromBackingInt(@intCast(10 * 1024 * 1024)));
defer allocator.free(in_data);
var reader = std.Io.Reader.fixed(in_data);
var out_storage: [4096]u8 = undefined;
var writer = std.Io.Writer.fixed(&out_storage);
try snappy.compressStream(allocator, &reader, &writer, .{});
const compressed_data = writer.buffered();
try cwd.writeFile(io, .{ .sub_path = comp_file, .data = compressed_data });
std.debug.print("Successfully compressed into '{s}' ({d} bytes)\n", .{ comp_file, compressed_data.len });
}Step-by-Step Breakdown (Compression)
std.Io.ThreadedInitialization: Zig 0.17.0 unifies non-blocking and threaded filesystem operations understd.Io.threaded.io()provides theio: std.Iohandle passed to all filesystem operations.- File Creation:
cwd.writeFilecreates the source filetemp_example_input.txt. - Data Ingestion:
cwd.readFileAllocreads up to 10MB into memory. - Stream Compression (
snappy.compressStream): Reads throughstd.Io.Reader.fixed, writes the framed stream intostd.Io.Writer.fixed, and handles chunk boundaries and CRC checksums. - Disk Write: The resulting framed
.szpayload is persisted usingcwd.writeFile. - Cleanup: Both temporary files are unlinked in the
deferblock.
Decompressing a File (examples/decompress_file.zig)
zig
const std = @import("std");
const snappy = @import("snappy");
pub fn main() !void {
const allocator = std.heap.page_allocator;
var threaded = std.Io.Threaded.init(allocator, .{});
defer threaded.deinit();
const io = threaded.io();
const cwd = std.Io.Dir.cwd();
const comp_file = "temp_example_comp.sz";
const decomp_file = "temp_example_restored.txt";
defer {
cwd.deleteFile(io, comp_file) catch {};
cwd.deleteFile(io, decomp_file) catch {};
}
// 1. Create a compressed file
const sample_data =
"Demonstration of decompressing a Snappy framed .sz file from disk back to plain text.\n" ++
"Snappy frames include stream identifiers, block tags, and CRC-32C data checksums.\n" ++
"Native Zig 0.17.0 streaming I/O pipeline verified for roundtrip correctness.\n";
const comp_bytes = try snappy.compress(allocator, sample_data);
defer allocator.free(comp_bytes);
try cwd.writeFile(io, .{ .sub_path = comp_file, .data = comp_bytes });
std.debug.print("Wrote compressed file '{s}' ({d} bytes)\n", .{ comp_file, comp_bytes.len });
// 2. Read compressed file from disk
const read_comp = try cwd.readFileAlloc(io, comp_file, allocator, @fromBackingInt(@intCast(10 * 1024 * 1024)));
defer allocator.free(read_comp);
// 3. Decompress using streaming pipeline
var reader = std.Io.Reader.fixed(read_comp);
var out_storage: [4096]u8 = undefined;
var writer = std.Io.Writer.fixed(&out_storage);
try snappy.decompressStream(allocator, &reader, &writer, .{});
const restored_bytes = writer.buffered();
try cwd.writeFile(io, .{ .sub_path = decomp_file, .data = restored_bytes });
std.debug.print("Successfully restored into '{s}' ({d} bytes)\n", .{ decomp_file, restored_bytes.len });
}Step-by-Step Breakdown (Decompression)
- File Reading:
cwd.readFileAllocreads the.szcompressed file from disk. - Stream Validation & Decoding:
snappy.decompressStreamprocesses the stream byte-by-byte:- Validates the 10-byte
\xff\x06\x00\x00sNaPpYheader. - Computes CRC-32C on decoded payload and verifies against the chunk checksum.
- Emits uncompressed plaintext to the writer.
- Validates the 10-byte
- Restoration:
cwd.writeFilewritestemp_example_restored.txtcontaining the restored content.
One-Shot Disk File Helpers
snappy.zig also provides convenience functions to compress or decompress on-disk files in a single call:
zig
// Compress directly from disk file to .sz file:
try snappy.compressFile(allocator, "data.csv", "data.csv.sz", .{});
// Decompress directly from .sz file to disk file:
try snappy.decompressFile(allocator, "data.csv.sz", "data.csv", .{});These functions automatically initialize std.Io.Threaded, open streaming readers and writers, and flush all compressed/decompressed chunks to disk.
Running the Examples
Compile and run with the Zig build system:
bash
zig build run-compress_file
zig build run-decompress_fileExpected Output
run-compress_file:
console
Created sample input file 'temp_example_input.txt' (231 bytes)
Successfully compressed into 'temp_example_output.sz' (226 bytes)run-decompress_file:
console
Wrote compressed file 'temp_example_comp.sz' (258 bytes)
Successfully restored into 'temp_example_restored.txt' (245 bytes)