@byteSwap em Zig — Referência e Exemplos

@byteSwap em Zig

O @byteSwap inverte a ordem dos bytes de um valor inteiro. É a operação fundamental para converter entre big-endian e little-endian, essencial para protocolos de rede, formatos de arquivo e interoperabilidade entre arquiteturas. Mapeia diretamente para instruções como BSWAP (x86).

Sintaxe

@byteSwap(value: T) T

Parâmetros

  • value (T): Valor inteiro cujos bytes serão invertidos. Deve ser um tipo inteiro com tamanho múltiplo de 8 bits.

Valor de retorno

Retorna T — o valor com a ordem dos bytes invertida.

Exemplos práticos

Exemplo 1: Inversão básica de bytes

const std = @import("std");

pub fn main() void {
    const valor: u32 = 0xAABBCCDD;
    const invertido = @byteSwap(valor);

    std.debug.print("Original:  0x{X:0>8}\n", .{valor});     // 0xAABBCCDD
    std.debug.print("Invertido: 0x{X:0>8}\n", .{invertido}); // 0xDDCCBBAA

    const v16: u16 = 0x1234;
    std.debug.print("u16: 0x{X:0>4} -> 0x{X:0>4}\n", .{ v16, @byteSwap(v16) });
    // u16: 0x1234 -> 0x3412
}

Exemplo 2: Conversão host para network byte order

const std = @import("std");
const builtin = @import("builtin");

fn hostToNetwork(valor: u32) u32 {
    // Network byte order é big-endian
    if (builtin.cpu.arch.endian() == .little) {
        return @byteSwap(valor);
    }
    return valor;
}

fn networkToHost(valor: u32) u32 {
    return hostToNetwork(valor); // A operação é simétrica
}

pub fn main() void {
    const ip = (192 << 24) | (168 << 16) | (1 << 8) | 1; // 192.168.1.1
    const rede = hostToNetwork(ip);

    std.debug.print("Host:    0x{X:0>8}\n", .{ip});
    std.debug.print("Network: 0x{X:0>8}\n", .{rede});
}

Exemplo 3: Ler inteiro de bytes em formato big-endian

const std = @import("std");

fn lerU32BigEndian(bytes: *const [4]u8) u32 {
    const valor = std.mem.bytesToValue(u32, bytes);
    // Se estamos em little-endian, inverter
    return std.mem.bigToNative(u32, valor);
}

pub fn main() void {
    const dados = [_]u8{ 0x00, 0x01, 0x00, 0x00 }; // 65536 em big-endian
    const valor = lerU32BigEndian(&dados);
    std.debug.print("Valor: {}\n", .{valor}); // 65536
}

Casos de uso comuns

  1. Protocolos de rede: Converter entre host byte order e network byte order (big-endian).
  2. Formatos de arquivo: Ler/escrever arquivos com endianness diferente do host.
  3. Serialização: Garantir formato consistente independente da plataforma.
  4. Interop entre arquiteturas: Comunicação entre sistemas big e little endian.

Endianness na prática

A maioria dos processadores modernos (x86, ARM em modo padrão) usa little-endian: o byte menos significativo vem primeiro na memória. Protocolos de rede como TCP/IP usam big-endian (também chamado de “network byte order”). Formatos de arquivo variam.

Valor u32: 0x12345678

Em memória little-endian:  [78] [56] [34] [12]  ← byte baixo primeiro
Em memória big-endian:     [12] [34] [56] [78]  ← byte alto primeiro

@byteSwap converte entre os dois formatos.

A biblioteca padrão e endianness

Zig oferece funções convenientes em std.mem para conversão de endianness que usam @byteSwap internamente:

const std = @import("std");

pub fn main() void {
    const valor: u32 = 0x12345678;

    // Converter para big-endian
    const be = std.mem.nativeToBig(u32, valor);

    // Converter de big-endian para native
    const native = std.mem.bigToNative(u32, be);

    // Converter para little-endian
    const le = std.mem.nativeToLittle(u32, valor);

    _ = native;
    _ = le;
}

Prefira usar as funções de std.mem em código de alto nível — elas tornam a intenção mais clara e são portáveis.

Equivalente em C

Em C, as funções htonl, htons, ntohl, ntohs da POSIX fazem conversão host/network. Porém não são disponíveis em todas as plataformas e exigem <arpa/inet.h>:

#include <arpa/inet.h>
uint32_t valor_rede = htonl(valor_host);

GCC e Clang oferecem __builtin_bswap16/32/64 como alternativa portável. O @byteSwap do Zig mapeia para essas instruções — geralmente uma única instrução BSWAP no x86-64.

Tipos suportados

@byteSwap funciona com qualquer inteiro cujo tamanho seja múltiplo de 8 bits. Para u8 e i8, a operação é identidade (um único byte não tem “ordem”):

@byteSwap(@as(u8, 0xAB))   // 0xAB (sem efeito)
@byteSwap(@as(u16, 0x1234)) // 0x3412
@byteSwap(@as(u32, 0x12345678)) // 0x78563412
@byteSwap(@as(u64, 0x123456789ABCDEF0)) // 0xF0DEBC9A78563412
@byteSwap(@as(u128, val)) // também funciona

Erros comuns

Aplicar @byteSwap desnecessariamente em sistemas big-endian: O código que usa @byteSwap incondicionalmente fica incorreto em sistemas big-endian. Sempre verifique builtin.cpu.arch.endian() antes de aplicar a conversão, ou use as funções de std.mem que fazem isso automaticamente.

Confundir com @bitReverse: @byteSwap(0xAABBCCDD) retorna 0xDDCCBBAA — apenas a ordem dos bytes muda. @bitReverse inverte cada bit individualmente, o que é uma operação muito diferente.

Perguntas Frequentes

Posso usar @byteSwap com floats?

Não diretamente — @byteSwap aceita apenas inteiros. Para floats, converta para inteiro, aplique o swap, e converta de volta:

fn swapF32(v: f32) f32 {
    return @bitCast(@byteSwap(@as(u32, @bitCast(v))));
}

@byteSwap é uma operação cara?

Não. Em x86-64, o compilador emite uma única instrução BSWAP ou MOVBE. É uma das operações mais baratas possíveis — custo de 1 ciclo de CPU na maioria das arquiteturas modernas.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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