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