Introdução
Compressão de dados é uma necessidade comum em aplicações de rede, armazenamento e processamento de arquivos. Zig oferece suporte a deflate/gzip diretamente na biblioteca padrão via std.compress, e também permite usar a libz do sistema via interoperabilidade C.
Para interop com C em geral, veja Chamar Funções C de Zig e Interoperabilidade C-Zig.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Compressão com std.compress (Deflate)
A biblioteca padrão de Zig inclui implementação de deflate:
const std = @import("std");
pub fn comprimir(allocator: std.mem.Allocator, dados: []const u8) ![]u8 {
var resultado = std.ArrayList(u8).init(allocator);
errdefer resultado.deinit();
var compressor = try std.compress.deflate.compressor(
resultado.writer(),
.{},
);
try compressor.writer().writeAll(dados);
try compressor.finish();
return resultado.toOwnedSlice();
}
pub fn descomprimir(allocator: std.mem.Allocator, dados_comprimidos: []const u8) ![]u8 {
var stream = std.io.fixedBufferStream(dados_comprimidos);
var decompressor = std.compress.deflate.decompressor(stream.reader());
var resultado = std.ArrayList(u8).init(allocator);
errdefer resultado.deinit();
const reader = decompressor.reader();
while (true) {
var buf: [4096]u8 = undefined;
const n = reader.read(&buf) catch |err| switch (err) {
error.EndOfStream => break,
else => return err,
};
if (n == 0) break;
try resultado.appendSlice(buf[0..n]);
}
return resultado.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const original = "Zig Brasil - linguagem de programação de sistemas moderna e eficiente!";
const comprimido = try comprimir(allocator, original);
defer allocator.free(comprimido);
std.debug.print("Original: {} bytes\n", .{original.len});
std.debug.print("Comprimido: {} bytes\n", .{comprimido.len});
const restaurado = try descomprimir(allocator, comprimido);
defer allocator.free(restaurado);
std.debug.print("Restaurado: {s}\n", .{restaurado});
std.debug.print("Igual: {}\n", .{std.mem.eql(u8, original, restaurado)});
}
Compressão Gzip
Para gzip (usado em HTTP e arquivos .gz):
const std = @import("std");
pub fn comprimirGzip(allocator: std.mem.Allocator, dados: []const u8) ![]u8 {
var resultado = std.ArrayList(u8).init(allocator);
errdefer resultado.deinit();
var compressor = try std.compress.gzip.compressor(
resultado.writer(),
.{},
);
try compressor.writer().writeAll(dados);
try compressor.finish();
return resultado.toOwnedSlice();
}
pub fn descomprimirGzip(allocator: std.mem.Allocator, dados: []const u8) ![]u8 {
var stream = std.io.fixedBufferStream(dados);
var decompressor = std.compress.gzip.decompressor(stream.reader());
var resultado = std.ArrayList(u8).init(allocator);
errdefer resultado.deinit();
while (true) {
var buf: [4096]u8 = undefined;
const n = decompressor.reader().read(&buf) catch break;
if (n == 0) break;
try resultado.appendSlice(buf[0..n]);
}
return resultado.toOwnedSlice();
}
Usando libz do Sistema via @cImport
Para projetos que precisam de compatibilidade total com zlib:
const std = @import("std");
const c = @cImport(@cInclude("zlib.h"));
pub fn comprimirZlib(dest: []u8, src: []const u8) ![]u8 {
var dest_len: c_ulong = @intCast(dest.len);
const resultado = c.compress(
dest.ptr,
&dest_len,
src.ptr,
@intCast(src.len),
);
if (resultado != c.Z_OK) return error.CompressaoFalhou;
return dest[0..@intCast(dest_len)];
}
No build.zig, linkar com zlib:
exe.linkSystemLibrary("z");
exe.linkLibC();
Comprimir Arquivo
const std = @import("std");
pub fn comprimirArquivo(
allocator: std.mem.Allocator,
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();
}
Teste
test "comprimir e descomprimir" {
const allocator = std.testing.allocator;
const original = "Dados de teste para compressão em Zig";
const comprimido = try comprimir(allocator, original);
defer allocator.free(comprimido);
try std.testing.expect(comprimido.len < original.len);
const restaurado = try descomprimir(allocator, comprimido);
defer allocator.free(restaurado);
try std.testing.expectEqualStrings(original, restaurado);
}
Veja Testes Unitários Básicos para mais sobre testes. Para leitura e escrita de arquivos, consulte Ler Arquivo e Escrever em Arquivo.