Bibliotecas de Compressão em Zig — zlib, zstd, lz4 e Mais

Bibliotecas de Compressão em Zig — zlib, gzip, zstd, lz4 e Mais

A compressão de dados é fundamental em praticamente toda aplicação moderna — desde servidores web que comprimem respostas HTTP até bancos de dados que otimizam armazenamento em disco. O Zig se destaca nessa área com implementações de compressão na biblioteca padrão que são frequentemente mais rápidas que as implementações C originais, além de oferecer acesso fácil a bibliotecas C de compressão existentes.

Compressão na Biblioteca Padrão

A std.compress oferece implementações puras em Zig de diversos algoritmos:

Deflate/Inflate

const std = @import("std");

pub fn comprimir(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var output = std.ArrayList(u8).init(allocator);
    var compressor = try std.compress.deflate.compressor(
        output.writer(),
        .{},
    );
    try compressor.write(dados);
    try compressor.finish();
    return output.toOwnedSlice();
}

pub fn descomprimir(dados_comprimidos: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var stream = std.io.fixedBufferStream(dados_comprimidos);
    var decompressor = std.compress.deflate.decompressor(stream.reader());

    var output = std.ArrayList(u8).init(allocator);
    var buf: [4096]u8 = undefined;

    while (true) {
        const n = try decompressor.read(&buf);
        if (n == 0) break;
        try output.appendSlice(buf[0..n]);
    }

    return output.toOwnedSlice();
}

Gzip

const std = @import("std");

pub fn comprimirGzip(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var output = std.ArrayList(u8).init(allocator);

    var compressor = try std.compress.gzip.compressor(output.writer(), .{});
    try compressor.writer().writeAll(dados);
    try compressor.finish();

    return output.toOwnedSlice();
}

pub fn descomprimirGzip(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var stream = std.io.fixedBufferStream(dados);
    var decompressor = std.compress.gzip.decompressor(stream.reader());

    var output = std.ArrayList(u8).init(allocator);
    var buf: [4096]u8 = undefined;

    while (true) {
        const n = try decompressor.read(&buf);
        if (n == 0) break;
        try output.appendSlice(buf[0..n]);
    }

    return output.toOwnedSlice();
}

Zstandard (zstd)

const std = @import("std");

pub fn comprimirZstd(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var output = std.ArrayList(u8).init(allocator);

    var compressor = try std.compress.zstd.compressor(output.writer(), .{
        .compression_level = .default,
    });
    try compressor.writer().writeAll(dados);
    try compressor.finish();

    return output.toOwnedSlice();
}

pub fn descomprimirZstd(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var stream = std.io.fixedBufferStream(dados);
    var decompressor = std.compress.zstd.decompressor(stream.reader());

    var output = std.ArrayList(u8).init(allocator);
    var buf: [4096]u8 = undefined;

    while (true) {
        const n = try decompressor.read(&buf);
        if (n == 0) break;
        try output.appendSlice(buf[0..n]);
    }

    return output.toOwnedSlice();
}

XZ/LZMA

const std = @import("std");

pub fn descomprimirXz(dados: []const u8, allocator: std.mem.Allocator) ![]u8 {
    var stream = std.io.fixedBufferStream(dados);
    var decompressor = try std.compress.xz.decompress(allocator, stream.reader());
    defer decompressor.deinit();

    var output = std.ArrayList(u8).init(allocator);
    var buf: [4096]u8 = undefined;

    while (true) {
        const n = try decompressor.read(&buf);
        if (n == 0) break;
        try output.appendSlice(buf[0..n]);
    }

    return output.toOwnedSlice();
}

Compressão de Arquivos

Comprimindo e Descomprimindo Arquivos

pub fn comprimirArquivo(caminho_entrada: []const u8, caminho_saida: []const u8) !void {
    const entrada = try std.fs.cwd().openFile(caminho_entrada, .{});
    defer entrada.close();

    const saida = try std.fs.cwd().createFile(caminho_saida, .{});
    defer saida.close();

    var compressor = try std.compress.gzip.compressor(saida.writer(), .{});

    var buf: [8192]u8 = undefined;
    while (true) {
        const n = try entrada.read(&buf);
        if (n == 0) break;
        try compressor.writer().writeAll(buf[0..n]);
    }

    try compressor.finish();
}

pub fn descomprimirArquivo(caminho_gz: []const u8, caminho_saida: []const u8) !void {
    const entrada = try std.fs.cwd().openFile(caminho_gz, .{});
    defer entrada.close();

    const saida = try std.fs.cwd().createFile(caminho_saida, .{});
    defer saida.close();

    var decompressor = std.compress.gzip.decompressor(entrada.reader());

    var buf: [8192]u8 = undefined;
    while (true) {
        const n = try decompressor.read(&buf);
        if (n == 0) break;
        try saida.writeAll(buf[0..n]);
    }
}

Bibliotecas C via Interop

LZ4

const c = @cImport({
    @cInclude("lz4.h");
});

pub fn comprimirLz4(dados: []const u8) ![]u8 {
    const max_dst = c.LZ4_compressBound(@intCast(dados.len));
    var output = try allocator.alloc(u8, @intCast(max_dst));

    const compressed_size = c.LZ4_compress_default(
        dados.ptr,
        output.ptr,
        @intCast(dados.len),
        max_dst,
    );

    if (compressed_size <= 0) return error.CompressionFailed;

    return output[0..@intCast(compressed_size)];
}

Comparação de Algoritmos

AlgoritmoTaxa CompressãoVelocidade Comp.Velocidade Decomp.Uso Ideal
Deflate/gzipBoaMédiaRápidaHTTP, arquivos gerais
ZstdExcelenteRápidaMuito rápidaLogs, backups, DB
LZ4ModeradaMuito rápidaExtremamente rápidaReal-time, cache
XZ/LZMAExcelenteLentaMédiaDistribuição, arquivamento
BrotliExcelenteLentaRápidaWeb (HTTP/2, HTTP/3)

Middleware HTTP de Compressão

Exemplo prático integrando compressão com frameworks web:

fn compressionMiddleware(request: *Request, response: *Response) !void {
    const accept_encoding = request.headers.get("Accept-Encoding") orelse "";

    if (std.mem.indexOf(u8, accept_encoding, "gzip") != null) {
        const body_original = response.body;
        const body_comprimido = try comprimirGzip(body_original, allocator);
        response.body = body_comprimido;
        response.headers.put("Content-Encoding", "gzip");
    }
}

Performance do Zig vs C

As implementações de compressão em Zig puro frequentemente igualam ou superam as implementações C:

BibliotecaDeflate (MB/s)Inflate (MB/s)
Zig std.compress~180~450
zlib (C)~160~420
libdeflate (C)~350~800

A implementação Zig se beneficia de otimizações SIMD e da ausência de overhead de chamadas de função C.

Boas Práticas

  1. Escolha o algoritmo certo: Zstd para uso geral, LZ4 para velocidade, gzip para compatibilidade
  2. Use streaming para grandes arquivos: Evite carregar arquivos inteiros na memória
  3. Considere o nível de compressão: Níveis mais altos comprimem mais mas são mais lentos
  4. Teste com dados reais: A taxa de compressão varia enormemente com o tipo de dado
  5. Monitore uso de memória: Alguns descompressores precisam de buffers proporcionais ao dado

Próximos Passos

Explore as bibliotecas de serialização que frequentemente usam compressão internamente, os frameworks web para compressão HTTP, e os drivers de banco de dados que se beneficiam de compressão. Consulte nossos tutoriais para exemplos práticos.

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.