Como Codificar e Decodificar Base64 em Zig

Introdução

Base64 é um esquema de codificação que converte dados binários em texto ASCII, usando 64 caracteres imprimíveis. É amplamente usado em e-mails (MIME), tokens JWT, incorporação de imagens em HTML e transmissão de dados em APIs. Zig oferece suporte a Base64 na biblioteca padrão via std.base64.

Nesta receita, você aprenderá a codificar e decodificar dados em Base64.

Pré-requisitos

Codificar para Base64

const std = @import("std");

pub fn main() !void {
    const encoder = std.base64.standard.Encoder;

    // Codificar string
    const texto = "Olá, Zig Brasil!";
    var buf: [256]u8 = undefined;
    const encoded = encoder.encode(&buf, texto);

    std.debug.print("Original: {s}\n", .{texto});
    std.debug.print("Base64:   {s}\n", .{encoded});

    // Codificar bytes arbitrários
    const bytes = [_]u8{ 0x00, 0xFF, 0x42, 0x13, 0x37, 0xAB, 0xCD, 0xEF };
    var buf2: [256]u8 = undefined;
    const encoded2 = encoder.encode(&buf2, &bytes);

    std.debug.print("\nBytes:  ", .{});
    for (bytes) |b| std.debug.print("{X:0>2} ", .{b});
    std.debug.print("\nBase64: {s}\n", .{encoded2});
}

Saída esperada

Original: Olá, Zig Brasil!
Base64:   T2zDoSwgWmlnIEJyYXNpbCE=

Decodificar de Base64

const std = @import("std");

pub fn main() !void {
    const decoder = std.base64.standard.Decoder;

    // Decodificar string Base64
    const encoded = "T2zDoSwgWmlnIEJyYXNpbCE=";

    var buf: [256]u8 = undefined;
    const decoded = decoder.decode(&buf, encoded) catch |err| {
        std.debug.print("Erro ao decodificar: {}\n", .{err});
        return;
    };

    std.debug.print("Base64:      {s}\n", .{encoded});
    std.debug.print("Decodificado: {s}\n", .{decoded});

    // Decodificar com alocador
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const encoded2 = "SGVsbG8gV29ybGQh";
    const calc_len = try decoder.calcSizeForSlice(encoded2);
    const decoded_buf = try allocator.alloc(u8, calc_len);
    defer allocator.free(decoded_buf);

    const resultado = decoder.decode(decoded_buf, encoded2) catch |err| {
        std.debug.print("Erro: {}\n", .{err});
        return;
    };

    std.debug.print("\n{s} -> {s}\n", .{ encoded2, resultado });
}

Codificação URL-Safe

A variante URL-safe usa - e _ em vez de + e /:

const std = @import("std");

pub fn main() !void {
    const dados = "dados com +/= especiais?sim&outro=valor";

    // Codificação padrão
    var buf_std: [256]u8 = undefined;
    const standard = std.base64.standard.Encoder.encode(&buf_std, dados);

    // Codificação URL-safe
    var buf_url: [256]u8 = undefined;
    const url_safe = std.base64.url_safe.Encoder.encode(&buf_url, dados);

    std.debug.print("Original:  {s}\n", .{dados});
    std.debug.print("Standard:  {s}\n", .{standard});
    std.debug.print("URL-safe:  {s}\n", .{url_safe});
}

Exemplo Prático: Codificar Credenciais HTTP Basic

const std = @import("std");

fn gerarBasicAuth(buf: []u8, usuario: []const u8, senha: []const u8) ![]const u8 {
    // Construir "usuario:senha"
    var cred_buf: [256]u8 = undefined;
    const credenciais = try std.fmt.bufPrint(&cred_buf, "{s}:{s}", .{ usuario, senha });

    // Codificar em Base64
    const encoder = std.base64.standard.Encoder;
    const encoded = encoder.encode(buf, credenciais);

    return encoded;
}

pub fn main() !void {
    var buf: [256]u8 = undefined;
    const auth = try gerarBasicAuth(&buf, "admin", "senha123");

    std.debug.print("Authorization: Basic {s}\n", .{auth});

    // Verificar decodificando
    const decoder = std.base64.standard.Decoder;
    var dec_buf: [256]u8 = undefined;
    const decoded = try decoder.decode(&dec_buf, auth);
    std.debug.print("Decodificado: {s}\n", .{decoded});
}

Calcular Tamanho do Buffer

const std = @import("std");

pub fn main() !void {
    const encoder = std.base64.standard.Encoder;
    const decoder = std.base64.standard.Decoder;

    // Calcular tamanho do encode
    const tamanhos = [_]usize{ 1, 3, 10, 100, 1000 };

    std.debug.print("{s:>10} {s:>15}\n", .{ "Input", "Base64" });
    for (tamanhos) |t| {
        const encoded_len = encoder.calcSize(t);
        std.debug.print("{d:>10} {d:>15}\n", .{ t, encoded_len });
    }

    // Calcular tamanho do decode
    const encoded = "SGVsbG8gV29ybGQh"; // "Hello World!"
    const decoded_len = try decoder.calcSizeForSlice(encoded);
    std.debug.print("\n\"{s}\" decodifica para {d} bytes\n", .{ encoded, decoded_len });
}

Dicas e Boas Práticas

  1. Use URL-safe para URLs e tokens: Evita problemas com +, / e = em URLs.

  2. Calcule o tamanho do buffer: Use calcSize para alocar o buffer correto.

  3. Base64 aumenta o tamanho em ~33%: Cada 3 bytes viram 4 caracteres.

  4. Trate erros de decode: Dados Base64 inválidos causam erros ao decodificar.

  5. Não é criptografia: Base64 é codificação, não encriptação. Os dados são facilmente reversíveis.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

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