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
| Algoritmo | Taxa Compressão | Velocidade Comp. | Velocidade Decomp. | Uso Ideal |
|---|---|---|---|---|
| Deflate/gzip | Boa | Média | Rápida | HTTP, arquivos gerais |
| Zstd | Excelente | Rápida | Muito rápida | Logs, backups, DB |
| LZ4 | Moderada | Muito rápida | Extremamente rápida | Real-time, cache |
| XZ/LZMA | Excelente | Lenta | Média | Distribuição, arquivamento |
| Brotli | Excelente | Lenta | Rápida | Web (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:
| Biblioteca | Deflate (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
- Escolha o algoritmo certo: Zstd para uso geral, LZ4 para velocidade, gzip para compatibilidade
- Use streaming para grandes arquivos: Evite carregar arquivos inteiros na memória
- Considere o nível de compressão: Níveis mais altos comprimem mais mas são mais lentos
- Teste com dados reais: A taxa de compressão varia enormemente com o tipo de dado
- 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.