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:
Bytes.empacotar(">i32", 1) // 00 00 00 01 ordem de rede
Bytes.empacotar("<i32", 1) // 01 00 00 00 ordem do IntelOs tipos#
| Escrita | O quê | Bytes |
|---|---|---|
i8 u8 | inteiro de 8 bits, com e sem sinal | 1 |
i16 u16 | 16 bits | 2 |
i32 u32 | 32 bits | 4 |
i64 u64 | 64 bits | 8 |
f32 f64 | ponto flutuante | 4 / 8 |
bool | verdadeiro ou falso | 1 |
bytesN | um bloco de tamanho fixo | N |
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#
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.
leitor := Bytes.ler(mensagem)
versao := leitor.ler("u8")
tipo := leitor.ler("u8")
tamanho := leitor.ler("u32")
corpo := leitor.ler_bytes(tamanho)
assert leitor.acabou| Chamada | Faz |
|---|---|
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.acabou | onde estamos |
Ler além do fim diz quanto falta, em vez de estourar calado:
erro: faltam bytes para ler 'u32': o cursor está em 1, o campo pede 4
e só há 1 até o fim.Escrever#
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:
pedaco := Bytes.janela(arquivo_inteiro, 100, 108) // não copia
guardado := Bytes.copiar(pedaco) // agora simVer 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:
out Bytes.despejo(mensagem)00000000 01 07 00 00 04 00 44 61 74 61 46 6f 72 67 65 |......DataForge|Segredo se compara em tempo fixo#
given Bytes.igual_em_tempo_fixo(token_recebido, token_certo):
out "ok"O resto#
| Chamada | Faz |
|---|---|
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 |