Decompression Guide
lzma2.zig implements an adaptive, defensive LZMA2 stream decoder. It processes chunked streams, restores dictionary sliding history, and validates property and size headers.
1. One-Shot Decompression
The simplest way to decode an LZMA2 stream is using lzma2.decompress():
const std = @import("std");
const lzma2 = @import("lzma2");
pub fn main() !void {
const allocator = std.heap.page_allocator;
const decompressed = try lzma2.decompress(allocator, compressed_data, .{});
defer allocator.free(decompressed);
std.debug.print("Restored: {s}\n", .{decompressed});
}2. Zero-Allocation Caller Buffers
When decoding into an existing stack or static buffer, use lzma2.decompressInto():
var restored_buf: [1024]u8 = undefined;
const decomp_len = try lzma2.decompressInto(&restored_buf, compressed_data, .{});
const restored_slice = restored_buf[0..decomp_len];If the provided destination slice is smaller than the unpacked stream, error.OutputTooSmall is returned.
3. Protecting Against Decompression Bombs
Malicious actors can craft tiny LZMA2 streams that expand into gigabytes of data. lzma2.zig guards against this attack with the max_output_size option:
const decompressed = try lzma2.decompress(allocator, untrusted_data, .{
.max_output_size = 50 * 1024 * 1024, // Limit to 50 MiB
}) catch |err| switch (err) {
error.ExceededSizeLimit => {
std.debug.print("Decompression aborted: payload exceeded limit!\n", .{});
return;
},
else => return err,
};
defer allocator.free(decompressed);4. Dictionary Size Constraints
LZMA2 streams depend on a sliding dictionary history. You can constrain memory allocation by setting dict_size_limit on DecompressOptions:
const decompressed = try lzma2.decompress(allocator, compressed_data, .{
.dict_size_limit = 8 * 1024 * 1024, // 8 MiB maximum dictionary window
});
defer allocator.free(decompressed);If a compressed match refers to distance beyond the configured dictionary size, the decoder immediately stops and returns error.CorruptData or error.ExceededSizeLimit.
5. Detecting LZMA2 Streams
Before invoking decompression, you can quickly verify whether a byte slice begins with a plausible LZMA2 chunk header using lzma2.isLzma2Stream:
if (!lzma2.isLzma2Stream(maybe_lzma2)) {
std.debug.print("Not an LZMA2 stream!\n", .{});
return;
}6. Legacy .lzma (Alone) Container Decompression
To decompress files created by legacy LZMA tools:
const decompressed = try lzma2.alone.decompress(allocator, alone_file_bytes, .{
.max_output_size = 64 * 1024 * 1024,
});
defer allocator.free(decompressed);The decoder automatically validates the 13-byte header, resolves known or unknown uncompressed sizes, and stops at the EOS marker if present.
7. Raw LZMA Stream Decompression
For raw headerless LZMA payloads:
const lzma = @import("lzma");
const decompressed = try lzma.decompressRaw(allocator, raw_payload, .{
.lc = 3,
.lp = 0,
.pb = 2,
.uncompressed_size = 1024, // or null for streams ending with an EOS marker
});
defer allocator.free(decompressed);