@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
- Protocolos de rede: Converter entre host byte order e network byte order (big-endian).
- Formatos de arquivo: Ler/escrever arquivos com endianness diferente do host.
- Serialização: Garantir formato consistente independente da plataforma.
- 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
- @bitReverse — Inverter ordem dos bits
- @clz — Contar zeros à esquerda
- @ctz — Contar zeros à direita
- @popCount — Contar bits em 1