Packed Struct em Zig — O que é e Como Usar

Packed Struct em Zig — O que é e Como Usar

Definição

Uma packed struct em Zig é uma struct declarada com a palavra-chave packed que possui um layout de memória exato e previsível, sem padding (bytes de preenchimento) entre os campos. Os campos são empacotados bit a bit, permitindo definir campos com tamanhos arbitrários em bits — como campos de 3 bits, 1 bit (booleano) ou 5 bits.

Packed structs são fundamentais para trabalhar com protocolos de rede, formatos de arquivo binário, registradores de hardware e qualquer situação onde o layout exato dos bits importa.

Por que Packed Structs Importam

  1. Layout garantido: Você sabe exatamente onde cada bit está na memória.
  2. Bitfields: Campos podem ter tamanhos menores que 1 byte (ex: u3, u1).
  3. Interoperabilidade: Mapeiam diretamente para estruturas de protocolos binários.
  4. Conversão segura: Podem ser convertidas de/para inteiros com @bitCast.
  5. Hardware: Permitem mapear registradores de dispositivos com precisão bit a bit.

Exemplo Prático

Bitfields para Protocolo

const std = @import("std");

const CabecalhoTCP = packed struct {
    porta_origem: u16,
    porta_destino: u16,
    numero_sequencia: u32,
    numero_ack: u32,
    offset: u4,
    reservado: u3,
    flags: packed struct {
        ns: u1,
        cwr: u1,
        ece: u1,
        urg: u1,
        ack: u1,
        psh: u1,
        rst: u1,
        syn: u1,
        fin: u1,
    },
    janela: u16,
    checksum: u16,
    ponteiro_urgente: u16,
};

pub fn main() void {
    std.debug.print("Tamanho do cabeçalho: {} bytes\n", .{@sizeOf(CabecalhoTCP)});
}

Conversão para Inteiro

const Flags = packed struct {
    leitura: bool,    // 1 bit
    escrita: bool,    // 1 bit
    execucao: bool,   // 1 bit
    _padding: u5 = 0, // 5 bits para completar 1 byte
};

pub fn main() void {
    const flags = Flags{
        .leitura = true,
        .escrita = true,
        .execucao = false,
    };

    // Converter para inteiro
    const valor: u8 = @bitCast(flags);
    std.debug.print("Flags como byte: 0b{b:0>8}\n", .{valor});
    // Saída: 0b00000011
}

Lendo Dados Binários

fn lerCabecalho(dados: []const u8) *const CabecalhoTCP {
    return @ptrCast(@alignCast(dados.ptr));
}

Packed vs Extern vs Normal

// Normal: compilador pode reordenar e adicionar padding
const Normal = struct {
    a: u8,
    b: u32,  // padding de 3 bytes antes de b
    c: u8,
};

// Packed: sem padding, campos empacotados bit a bit
const Packed = packed struct {
    a: u8,
    b: u32,
    c: u8,
};

// Extern: layout compatível com C ABI
const Extern = extern struct {
    a: u8,
    b: u32,
    c: u8,
};

// @sizeOf(Normal) >= 12 (com padding)
// @sizeOf(Packed) == 5 (sem padding)
// @sizeOf(Extern) == 12 (padding C)

Armadilhas Comuns

  • Performance: Acessar campos não alinhados em packed structs pode ser mais lento que structs normais. O processador pode precisar de instruções extras.
  • Ponteiros para campos: Não é possível obter ponteiros para campos individuais de uma packed struct que não estejam alinhados a bytes.
  • Endianness: Packed structs usam a endianness nativa. Para protocolos de rede (big-endian), use std.mem.bigToNative para converter.
  • Confundir com extern struct: Para interop com C, use extern struct. Packed structs são para controle bit-a-bit.
  • Bool em packed struct: bool ocupa 1 bit, não 1 byte como em structs normais.

Termos Relacionados

  • Struct — Structs regulares em Zig
  • Alignment — Alinhamento de memória
  • Union — Tipos union em Zig
  • Slice — Referências a sequências de memória

Tutoriais Relacionados

Continue aprendendo Zig

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