Pular para o conteúdo

Arcane.Bytes

Dados binários: empacotar com a ordem declarada, um cursor que anda, e a janela que olha sem copiar.

A linguagem tem o tipo Bytes, e não havia o que fazer com ele. Ler um arquivo binário, falar um protocolo, montar um cabeçalho de quatro bytes — tudo isso pedia sair da linguagem.

A ordem dos bytes é obrigatória#

empacotar("i32", 1) não existe aqui. O formato sempre diz a ordem:

dataforge
Bytes.empacotar(">i32", 1)     // 00 00 00 01   ordem de rede
Bytes.empacotar("<i32", 1)     // 01 00 00 00   ordem do Intel

Os tipos#

EscritaO quêBytes
i8 u8inteiro de 8 bits, com e sem sinal1
i16 u1616 bits2
i32 u3232 bits4
i64 u6464 bits8
f32 f64ponto flutuante4 / 8
boolverdadeiro ou falso1
bytesNum bloco de tamanho fixoN

Os nomes dizem o tamanho em bits, e não uma letra. i32 é um inteiro de 32 bits em qualquer máquina; o int do C não é — e descobrir isso depois de gravar um arquivo é caro.

Um cabeçalho de protocolo#

dataforge
adopt Arcane.Bytes as Bytes

// versão (1), tipo (1), tamanho (4) — escrito assim, e não com contas
cabecalho := Bytes.empacotar(">u8 u8 u32", 1, 7, 1024)
out Bytes.hex(cabecalho, " ")          // 01 07 00 00 04 00

partes := Bytes.desempacotar(">u8 u8 u32", cabecalho)
assert partes is [1, 7, 1024]

O cursor anda sozinho#

Ler com fatias exige calcular o deslocamento de cada campo, e um erro num deles desalinha tudo o que vem depois — com o programa entregando números plausíveis e errados.

dataforge
leitor := Bytes.ler(mensagem)

versao := leitor.ler("u8")
tipo := leitor.ler("u8")
tamanho := leitor.ler("u32")
corpo := leitor.ler_bytes(tamanho)

assert leitor.acabou
ChamadaFaz
leitor.ler(tipo)um campo, e anda
leitor.ler(tipo, n)n campos iguais
leitor.ler_bytes(n)um bloco
leitor.ler_texto(n)um bloco, como texto
leitor.ler_ate_zero()texto terminado em zero, como num formato antigo
leitor.espiar(n)olha sem andar, para decidir o que vem
leitor.pular(n) · leitor.ir_para(p)move o cursor
leitor.resto()tudo o que sobrou
leitor.sobrou · leitor.acabouonde estamos

Ler além do fim diz quanto falta, em vez de estourar calado:

text
erro: faltam bytes para ler 'u32': o cursor está em 1, o campo pede 4
      e só há 1 até o fim.

Escrever#

dataforge
e := Bytes.escrever()
e.escrever("u8", 2)
e.escrever("u32", 99)
e.escrever_texto("fim", com_zero := yes)
bloco := e.finalizar()

A janela não copia#

Copiar um arquivo de 200 MB para ler 8 bytes é o jeito mais fácil de estourar a memória. A janela aponta para os mesmos bytes:

dataforge
pedaco := Bytes.janela(arquivo_inteiro, 100, 108)   // não copia
guardado := Bytes.copiar(pedaco)                    // agora sim

Ver o que está errado#

O despejo é o que se olha quando o protocolo não bate — posição, hexadecimal e o texto legível, lado a lado:

dataforge
out Bytes.despejo(mensagem)
text
00000000  01 07 00 00 04 00 44 61 74 61 46 6f 72 67 65     |......DataForge|

Segredo se compara em tempo fixo#

dataforge
given Bytes.igual_em_tempo_fixo(token_recebido, token_certo):
    out "ok"

O resto#

ChamadaFaz
hex(dados[, sep]) · de_hex(texto)hexadecimal, ida e volta
base64(dados) · de_base64(texto)base64
bits(dados) · de_bits(texto)a representação em bits
de_texto(t) · para_texto(d)texto ⇄ bytes
concatenar(...) · fatiar(d, i, f)juntar e cortar
ou_exclusivo(a, b)XOR byte a byte
preencher(d, n, com, a_esquerda)completa até o tamanho
achar(d, agulha) · dividir(d, sep)procurar
inverter(d)de trás para frente