Pular para o conteúdo

Um protocolo binário

Mensagens TLV — tipo, tamanho, valor — com varint: escrever, ler, e o campo desconhecido que não quebra ninguém.

TLV (tipo-tamanho-valor) é o desenho que sobrevive a versões: cada campo diz o próprio tamanho, então quem lê pode pular um campo que não conhece. É a mesma ideia do Protocol Buffers, e o motivo pelo qual um cliente antigo lê a mensagem de um servidor novo.

dataforge
adopt Arcane.Estrutura as Est
adopt Arcane.Bytes as Bytes

steady NOME := 1
steady IDADE := 2
steady FOTO := 9                     // um campo que a versão antiga não conhece

action campo(tipo, valor):
    yield Bytes.concatenar(Est.varint(tipo), Est.varint(len(valor)), valor)

action mensagem(campos):
    yield campos >> distill acc, c: Bytes.concatenar(acc, c) Bytes.de_texto("")

action ler(dados, conhecidos):
    saida := {}
    pos := 0
    persist pos smaller len(dados):
        t := Est.ler_varint(dados, pos)
        n := Est.ler_varint(dados, pos + t["tamanho"])
        inicio := pos + t["tamanho"] + n["tamanho"]
        given inicio + n["valor"] bigger len(dados):
            trigger $"o campo {t["valor"]} diz ter {n["valor"]} bytes e a mensagem acaba antes"
        given t["valor"] in conhecidos:
            saida[conhecidos[t["valor"]]] := Bytes.fatiar(dados, inicio, inicio + n["valor"])
        pos := inicio + n["valor"]            // o desconhecido é PULADO
    yield saida

m := mensagem([campo(NOME, Bytes.de_texto("Ana")), campo(FOTO, Bytes.de_hex("ffd8ffe0")),
               campo(IDADE, Est.varint(30))])
lido := ler(m, {NOME: "nome", IDADE: "idade"})
assert Bytes.para_texto(lido["nome"]) is "Ana"
assert Est.ler_varint(lido["idade"])["valor"] is 30