Skip to content

Legacy LZMA & Container Compression ​

This example demonstrates how to use the legacy .lzma container format (LZMA-Alone) and raw headerless LZMA1 bitstreams using the @import("lzma") module export from lzma2.zig.


Use Case ​

Legacy systems, firmware images, and historical archive formats frequently use the classic 13-byte header .lzma file format or raw headerless LZMA streams rather than chunked LZMA2. lzma2.zig provides first-class native support for both without any external C library.

This example covers:

  1. Legacy .lzma Container: 13-byte header encoding properties, dictionary size, and 64-bit uncompressed size.
  2. Raw LZMA Stream with EOS Marker: Headerless continuous stream terminated by an End-of-Stream range-coder symbol (0xFFFFFFFF).
  3. Raw LZMA Stream without EOS Marker: Headerless stream where the uncompressed size is known out-of-band, saving ~6 bytes.
  4. Zero-Allocation Buffers: Pre-allocated caller buffers via lzma.compressInto and lzma.decompressInto.
  5. Streaming I/O Pipelines: Streaming via lzma.compressStream and lzma.decompressStream.

Source Code ​

zig
//! Comprehensive LZMA Compression and Decompression Example using @import("lzma").

const std = @import("std");
const lzma = @import("lzma");

const MemoryReader = struct {
    slice: []const u8,
    pos: usize = 0,

    pub fn read(self: *MemoryReader, dest: []u8) !usize {
        if (self.pos >= self.slice.len) return 0;
        const n = @min(self.slice.len - self.pos, dest.len);
        @memcpy(dest[0..n], self.slice[self.pos .. self.pos + n]);
        self.pos += n;
        return n;
    }
};

const MemoryWriter = struct {
    list: *std.ArrayList(u8),
    allocator: std.mem.Allocator,

    pub fn writeAll(self: *MemoryWriter, bytes: []const u8) !void {
        self.list.appendSlice(self.allocator, bytes) catch return error.OutOfMemory;
    }
};

pub fn main() !void {
    const allocator = std.heap.smp_allocator;

    const document: []const u8 =
        "The LZMA algorithm combines LZ77 dictionary compression with range encoding " ++
        "and adaptive Markov probability trees. The standalone .lzma container wraps " ++
        "the raw bitstream with a 13-byte header encoding properties, dictionary size, " ++
        "and 64-bit uncompressed size.";

    std.debug.print("LZMA Comprehensive Example\n", .{});
    std.debug.print("Original input size: {d} bytes\n\n", .{document.len});

    // 1. Legacy .lzma Container Format (13-byte header)
    std.debug.print("1. Legacy .lzma Container (13-byte header)\n", .{});
    const container_comp = try lzma.compress(allocator, document, .{ .level = 6 });
    defer allocator.free(container_comp);
    std.debug.print("Container compressed size: {d} bytes (header 13 bytes + compressed stream)\n", .{container_comp.len});

    const parsed_header = try lzma.AloneHeader.decode(container_comp[0..13]);
    std.debug.print("Parsed header: dict_size={d} bytes, uncompressed_size={?d}, props=0x{X:0>2}\n", .{
        parsed_header.dict_size,
        parsed_header.uncompressed_size,
        parsed_header.getPropsByte(),
    });

    const container_decomp = try lzma.decompress(allocator, container_comp, .{});
    defer allocator.free(container_decomp);
    if (!std.mem.eql(u8, document, container_decomp)) return error.VerificationFailed;
    std.debug.print("Container roundtrip: PASS\n\n", .{});

    // 2. Raw LZMA Stream (No header, with EOS marker)
    std.debug.print("2. Raw Headerless LZMA Stream (EOS marker)\n", .{});
    const raw_eos = try lzma.compressRaw(allocator, document, .{ .write_end_mark = true });
    defer allocator.free(raw_eos);
    std.debug.print("Raw stream with EOS marker size: {d} bytes\n", .{raw_eos.len});

    const raw_eos_decomp = try lzma.decompressRaw(allocator, raw_eos, .{ .uncompressed_size = null });
    defer allocator.free(raw_eos_decomp);
    if (!std.mem.eql(u8, document, raw_eos_decomp)) return error.VerificationFailed;
    std.debug.print("Raw stream (EOS) roundtrip: PASS\n\n", .{});

    // 3. Raw LZMA Stream (No header, known uncompressed size, no EOS)
    std.debug.print("3. Raw Headerless LZMA Stream (Known size, no EOS)\n", .{});
    const raw_no_eos = try lzma.compressRaw(allocator, document, .{ .write_end_mark = false });
    defer allocator.free(raw_no_eos);
    std.debug.print("Raw stream without EOS size: {d} bytes (saved ~6 bytes EOS marker)\n", .{raw_no_eos.len});

    const raw_no_eos_decomp = try lzma.decompressRaw(allocator, raw_no_eos, .{ .uncompressed_size = document.len });
    defer allocator.free(raw_no_eos_decomp);
    if (!std.mem.eql(u8, document, raw_no_eos_decomp)) return error.VerificationFailed;
    std.debug.print("Raw stream (Known size) roundtrip: PASS\n\n", .{});

    // 4. Zero-Allocation Caller Buffer Operations
    std.debug.print("4. Zero-Allocation Caller Buffers\n", .{});
    var comp_buffer: [1024]u8 = undefined;
    var decomp_buffer: [1024]u8 = undefined;

    const enc_len = try lzma.compressInto(&comp_buffer, document, .{ .level = 5 });
    std.debug.print("compressInto caller buffer: {d} bytes\n", .{enc_len});

    const dec_len = try lzma.decompressInto(&decomp_buffer, comp_buffer[0..enc_len], .{});
    std.debug.print("decompressInto caller buffer: {d} bytes\n", .{dec_len});
    if (!std.mem.eql(u8, document, decomp_buffer[0..dec_len])) return error.VerificationFailed;
    std.debug.print("Caller-buffer roundtrip: PASS\n\n", .{});

    // 5. Streaming I/O Pipelines
    std.debug.print("5. Streaming I/O Pipelines\n", .{});
    var in_stream = MemoryReader{ .slice = document };
    var out_stream_storage = std.ArrayList(u8).empty;
    defer out_stream_storage.deinit(allocator);
    var out_stream = MemoryWriter{ .list = &out_stream_storage, .allocator = allocator };

    try lzma.compressStream(allocator, &in_stream, &out_stream, .{});
    std.debug.print("lzma.compressStream written {d} bytes\n", .{out_stream_storage.items.len});

    var comp_reader = MemoryReader{ .slice = out_stream_storage.items };
    var final_storage = std.ArrayList(u8).empty;
    defer final_storage.deinit(allocator);
    var final_writer = MemoryWriter{ .list = &final_storage, .allocator = allocator };

    try lzma.decompressStream(allocator, &comp_reader, &final_writer, .{});
    if (!std.mem.eql(u8, document, final_storage.items)) return error.VerificationFailed;
    std.debug.print("Streaming pipeline roundtrip: PASS\n\n", .{});

    std.debug.print("All LZMA operations completed successfully!\n", .{});
}

Expected Output ​

text
LZMA Comprehensive Example
Original input size: 259 bytes

1. Legacy .lzma Container (13-byte header)
Container compressed size: 215 bytes (header 13 bytes + compressed stream)
Parsed header: dict_size=8388608 bytes, uncompressed_size=259, props=0x5D
Container roundtrip: PASS

2. Raw Headerless LZMA Stream (EOS marker)
Raw stream with EOS marker size: 207 bytes
Raw stream (EOS) roundtrip: PASS

3. Raw Headerless LZMA Stream (Known size, no EOS)
Raw stream without EOS size: 202 bytes (saved ~6 bytes EOS marker)
Raw stream (Known size) roundtrip: PASS

4. Zero-Allocation Caller Buffers
compressInto caller buffer: 215 bytes
decompressInto caller buffer: 259 bytes
Caller-buffer roundtrip: PASS

5. Streaming I/O Pipelines
lzma.compressStream written 215 bytes
Streaming pipeline roundtrip: PASS

All LZMA operations completed successfully!

How to Run ​

bash
zig build run-lzma_compression

Released under the MIT License.