···11-# protobuf_decode_gleam
11+# protobin
2233-Decode the protobuf wire format!
33+Decode the protobuf wire format using gleam `Decoder`s!
4455This is just a little toy I made mostly for myself. I would be happy to
66polish it and make it work better, but for now this should not be used at all.
···991010Also, there is currently no support for encoding the protobuf wire format.
1111<!--
1212-[](https://hex.pm/packages/protobuf_decode_gleam)
1313-[](https://hexdocs.pm/protobuf_decode_gleam/)
1212+[](https://hex.pm/packages/protobin)
1313+[](https://hexdocs.pm/protobin/)
141415151616```sh
1717-gleam add protobuf_decode_gleam@1
1717+gleam add protobin@1
1818```
1919-->
2020```gleam
2121-import protobuf_decode_gleam.{parse, read_varint}
2222-import protobuf_decode_gleam/decoders
2121+import protobin.{parse, read_varint}
2222+import protobin/decoders
23232424import gleam/option
2525···6565```
66666767<!--
6868-Further documentation can be found at <https://hexdocs.pm/protobuf_decode_gleam>.
6868+Further documentation can be found at <https://hexdocs.pm/protobin>.
6969-->
70707171## Development
+2-2
gleam.toml
···11-name = "protobuf_decode_gleam"
11+name = "protobin"
22version = "1.0.0"
3344# Fill out these fields if you intend to generate HTML documentation or publish
···66#
77description = "Decode the protobuf wire format."
88licences = ["Apache-2.0"]
99-repository = { type = "github", user = "gavinmorrow", repo = "protobuf-decode-gleam" }
99+repository = { type = "github", user = "gavinmorrow", repo = "gleam-protobin" }
1010# links = [{ title = "Website", href = "" }]
1111#
1212# For a full reference of all the available options, you can have a look at
+3-3
src/decoders.gleam
···55import gleam/list
66import gleam/result
7788-import internal/util
99-import protobuf_decode_gleam.{
88+import protobin.{
109 type BytePos, type DecodeResult, type ValueParser, Parsed, parse, parse_varint,
1110}
1111+import internal/util
12121313/// Decode a repeated field that may be either packed or expanded.
1414///
···3636 values
3737 |> list.map(dynamic.bit_array)
3838 |> list.try_map(fn(value) { decode.run(value, decoder) })
3939- |> result.map_error(protobuf_decode_gleam.UnableToDecode)
3939+ |> result.map_error(protobin.UnableToDecode)
4040 }
41414242 case values {
+228
src/protobin.gleam
···11+import gleam/bit_array
22+import gleam/dict
33+import gleam/dynamic.{type Dynamic}
44+import gleam/dynamic/decode.{type Decoder}
55+import gleam/int
66+import gleam/list
77+import gleam/option
88+import gleam/result
99+1010+import internal/util
1111+import internal/wire_type.{type WireType}
1212+1313+pub fn parse(
1414+ from bits: BitArray,
1515+ using decoder: Decoder(t),
1616+) -> DecodeResult(Parsed(t)) {
1717+ use Parsed(value: data, rest:, pos:) <- result.try(read_fields(bits, [], 0))
1818+ decode.run(data, decoder)
1919+ |> result.map(fn(value) { Parsed(value:, rest:, pos:) })
2020+ |> result.map_error(UnableToDecode)
2121+}
2222+2323+pub type ParseError {
2424+ UnknownWireType(Int, pos: BytePos)
2525+ InvalidVarInt(leftover_bits: BitArray, acc: BitArray, pos: BytePos)
2626+ InvalidFixed(size: Int, bits: BitArray, pos: BytePos)
2727+ InvalidLen(len: Int, value_bits: BitArray, pos: BytePos)
2828+ UnableToDecode(List(decode.DecodeError))
2929+}
3030+3131+fn read_fields(
3232+ bits: BitArray,
3333+ acc: List(Field),
3434+ pos: BytePos,
3535+) -> DecodeResult(Parsed(Dynamic)) {
3636+ case bits {
3737+ <<>> ->
3838+ acc
3939+ |> repeated_to_list
4040+ |> dict.to_list
4141+ |> dynamic.properties
4242+ |> Parsed(value: _, rest: bits, pos:)
4343+ |> Ok
4444+ bits -> {
4545+ use Parsed(value: prop, rest: bits, pos:) <- result.try(read_field(
4646+ bits,
4747+ pos,
4848+ ))
4949+ read_fields(bits, [prop, ..acc], pos)
5050+ }
5151+ }
5252+}
5353+5454+fn repeated_to_list(reversed_fields: List(Field)) -> dict.Dict(Dynamic, Dynamic) {
5555+ let fields = {
5656+ // Every field is a list of values for two reasons:
5757+ // a) expanded repeated values are encoded as repeated fields
5858+ // b) if a non-repeating field is defined twice, then the last value should
5959+ // be considered the correct one. since the parser doesn't know which
6060+ // fields are repeating, it parses all fields as a list and then the
6161+ // decoders will handle choosing which value(s) to keep.
6262+ let acc: dict.Dict(Dynamic, List(Dynamic)) = dict.new()
6363+ use fields, Field(key:, value:) <- list.fold(
6464+ over: reversed_fields,
6565+ from: acc,
6666+ )
6767+6868+ use existing_values <- dict.upsert(in: fields, update: key)
6969+ let existing_values = option.unwrap(existing_values, or: [])
7070+ // The repeated values were previously reversed, now being reversed again
7171+ [value, ..existing_values]
7272+ }
7373+7474+ dict.map_values(in: fields, with: fn(_key, field) { field |> dynamic.list })
7575+}
7676+7777+pub type DecodeResult(t) =
7878+ Result(t, ParseError)
7979+8080+pub type BytePos =
8181+ Int
8282+8383+pub type Parsed(t) {
8484+ Parsed(value: t, rest: BitArray, pos: BytePos)
8585+}
8686+8787+fn parsed_map(of parsed: Parsed(t), with fun: fn(t) -> u) -> Parsed(u) {
8888+ let Parsed(value:, rest:, pos:) = parsed
8989+ Parsed(value: fun(value), rest:, pos:)
9090+}
9191+9292+type Field {
9393+ Field(key: Dynamic, value: Dynamic)
9494+}
9595+9696+fn wire_type_read_fn(ty: WireType) -> ValueParser {
9797+ case ty {
9898+ wire_type.VarInt -> read_varint
9999+ wire_type.I64 -> read_fixed(64)
100100+ wire_type.Len -> read_len
101101+ wire_type.I32 -> read_fixed(32)
102102+ }
103103+}
104104+105105+fn read_field(bits: BitArray, tag_pos: BytePos) -> DecodeResult(Parsed(Field)) {
106106+ use Parsed(value: tag, rest: bits, pos:) <- result.try(parse_varint(
107107+ bits,
108108+ tag_pos,
109109+ ))
110110+ let tag = util.bit_array_to_uint(tag)
111111+112112+ let field_id = tag |> int.bitwise_shift_right(3)
113113+ let wire_type = tag |> int.bitwise_and(0b111)
114114+115115+ case wire_type {
116116+ 6 -> {
117117+ echo tag as "tag"
118118+ Nil
119119+ }
120120+ _ -> Nil
121121+ }
122122+ use wire_type <- result.try(option.to_result(
123123+ wire_type.parse(wire_type),
124124+ UnknownWireType(wire_type, pos: tag_pos),
125125+ ))
126126+127127+ let read = wire_type_read_fn(wire_type)
128128+ use value: Parsed(Dynamic) <- result.try(
129129+ read(bits, pos) |> result.map(parsed_map(_, dynamic.bit_array)),
130130+ )
131131+132132+ let field =
133133+ parsed_map(value, fn(value) { Field(key: dynamic.int(field_id), value:) })
134134+135135+ Ok(field)
136136+}
137137+138138+pub type ValueResult =
139139+ DecodeResult(Parsed(BitArray))
140140+141141+pub type ValueParser =
142142+ fn(BitArray, BytePos) -> ValueResult
143143+144144+/// Reads the bits from a varint and returns *all* of them. They cannot be
145145+/// parsed as a uint.
146146+///
147147+/// Specifically, the continuation bits are included, so the value's bit size
148148+/// will be a multiple of 8.
149149+pub fn read_varint(bits: BitArray, pos: BytePos) -> ValueResult {
150150+ read_varint_acc(bits, <<>>, pos)
151151+}
152152+153153+fn read_varint_acc(bits: BitArray, acc: BitArray, pos: BytePos) -> ValueResult {
154154+ case bits {
155155+ <<0:size(1), n:bits-size(7), rest:bytes>> -> {
156156+ let bit = <<0:size(1), n:bits>>
157157+ let acc = bit_array.concat([acc, bit])
158158+ Ok(Parsed(value: acc, rest:, pos: pos + 1))
159159+ }
160160+ <<1:size(1), n:bits-size(7), rest:bytes>> -> {
161161+ let bit = <<1:size(1), n:bits>>
162162+ read_varint_acc(rest, bit_array.concat([acc, bit]), pos + 1)
163163+ }
164164+ bits -> Error(InvalidVarInt(leftover_bits: bits, acc:, pos:))
165165+ }
166166+}
167167+168168+/// Reads the bits from a varint and parses them a BitArray. The returned bits
169169+/// can be parsed as a uint.
170170+///
171171+/// For a decoder that does this, use `decoders.uint()`.
172172+///
173173+/// The continuation bits are not included, so the value's bit size will be a
174174+/// multiple of 7.
175175+pub fn parse_varint(bits: BitArray, pos: BytePos) -> ValueResult {
176176+ parse_varint_acc(bits, <<>>, pos)
177177+}
178178+179179+fn parse_varint_acc(bits: BitArray, acc: BitArray, pos: BytePos) -> ValueResult {
180180+ case bits {
181181+ <<0:size(1), n:bits-size(7), rest:bytes>> -> {
182182+ let acc = bit_array.concat([n, acc])
183183+ Ok(Parsed(value: acc, rest:, pos: pos + 1))
184184+ }
185185+ <<1:size(1), n:bits-size(7), rest:bytes>> ->
186186+ parse_varint_acc(rest, bit_array.concat([n, acc]), pos + 1)
187187+ bits -> {
188188+ Error(InvalidVarInt(leftover_bits: bits, acc:, pos:))
189189+ }
190190+ }
191191+}
192192+193193+/// Size must be a multiple of 8.
194194+pub fn read_fixed(size: Int) -> ValueParser {
195195+ assert size % 8 == 0
196196+ fn(bits: BitArray, pos: BytePos) -> ValueResult {
197197+ case bits {
198198+ <<num:bits-size(size), rest:bytes>> ->
199199+ Ok(Parsed(value: num, rest:, pos: pos + size / 8))
200200+ bits -> Error(InvalidFixed(size:, bits:, pos:))
201201+ }
202202+ }
203203+}
204204+205205+fn read_len(bits: BitArray, len_pos: BytePos) -> ValueResult {
206206+ // First, read the length of the value
207207+ // It is encoded as a varint immediately after the tag
208208+ use Parsed(value: len, rest: bits, pos:) <- result.try(parse_varint(
209209+ bits,
210210+ len_pos,
211211+ ))
212212+ let len = util.bit_array_to_uint(len)
213213+214214+ // Just decoded a uint, so should be safe
215215+ assert len > 0
216216+217217+ use value <- result.try(
218218+ bit_array.slice(from: bits, at: 0, take: len)
219219+ |> result.map_error(fn(_) {
220220+ InvalidLen(len:, value_bits: bits, pos: len_pos)
221221+ }),
222222+ )
223223+ // Assert b/c if the len was too long, it would've errored in the prev slice
224224+ let assert Ok(rest) =
225225+ bit_array.slice(from: bits, at: len, take: bit_array.byte_size(bits) - len)
226226+227227+ Ok(Parsed(value:, rest:, pos: pos + len))
228228+}
-228
src/protobuf_decode_gleam.gleam
···11-import gleam/bit_array
22-import gleam/dict
33-import gleam/dynamic.{type Dynamic}
44-import gleam/dynamic/decode.{type Decoder}
55-import gleam/int
66-import gleam/list
77-import gleam/option
88-import gleam/result
99-1010-import internal/util
1111-import internal/wire_type.{type WireType}
1212-1313-pub fn parse(
1414- from bits: BitArray,
1515- using decoder: Decoder(t),
1616-) -> DecodeResult(Parsed(t)) {
1717- use Parsed(value: data, rest:, pos:) <- result.try(read_fields(bits, [], 0))
1818- decode.run(data, decoder)
1919- |> result.map(fn(value) { Parsed(value:, rest:, pos:) })
2020- |> result.map_error(UnableToDecode)
2121-}
2222-2323-pub type ParseError {
2424- UnknownWireType(Int, pos: BytePos)
2525- InvalidVarInt(leftover_bits: BitArray, acc: BitArray, pos: BytePos)
2626- InvalidFixed(size: Int, bits: BitArray, pos: BytePos)
2727- InvalidLen(len: Int, value_bits: BitArray, pos: BytePos)
2828- UnableToDecode(List(decode.DecodeError))
2929-}
3030-3131-fn read_fields(
3232- bits: BitArray,
3333- acc: List(Field),
3434- pos: BytePos,
3535-) -> DecodeResult(Parsed(Dynamic)) {
3636- case bits {
3737- <<>> ->
3838- acc
3939- |> repeated_to_list
4040- |> dict.to_list
4141- |> dynamic.properties
4242- |> Parsed(value: _, rest: bits, pos:)
4343- |> Ok
4444- bits -> {
4545- use Parsed(value: prop, rest: bits, pos:) <- result.try(read_field(
4646- bits,
4747- pos,
4848- ))
4949- read_fields(bits, [prop, ..acc], pos)
5050- }
5151- }
5252-}
5353-5454-fn repeated_to_list(reversed_fields: List(Field)) -> dict.Dict(Dynamic, Dynamic) {
5555- let fields = {
5656- // Every field is a list of values for two reasons:
5757- // a) expanded repeated values are encoded as repeated fields
5858- // b) if a non-repeating field is defined twice, then the last value should
5959- // be considered the correct one. since the parser doesn't know which
6060- // fields are repeating, it parses all fields as a list and then the
6161- // decoders will handle choosing which value(s) to keep.
6262- let acc: dict.Dict(Dynamic, List(Dynamic)) = dict.new()
6363- use fields, Field(key:, value:) <- list.fold(
6464- over: reversed_fields,
6565- from: acc,
6666- )
6767-6868- use existing_values <- dict.upsert(in: fields, update: key)
6969- let existing_values = option.unwrap(existing_values, or: [])
7070- // The repeated values were previously reversed, now being reversed again
7171- [value, ..existing_values]
7272- }
7373-7474- dict.map_values(in: fields, with: fn(_key, field) { field |> dynamic.list })
7575-}
7676-7777-pub type DecodeResult(t) =
7878- Result(t, ParseError)
7979-8080-pub type BytePos =
8181- Int
8282-8383-pub type Parsed(t) {
8484- Parsed(value: t, rest: BitArray, pos: BytePos)
8585-}
8686-8787-fn parsed_map(of parsed: Parsed(t), with fun: fn(t) -> u) -> Parsed(u) {
8888- let Parsed(value:, rest:, pos:) = parsed
8989- Parsed(value: fun(value), rest:, pos:)
9090-}
9191-9292-type Field {
9393- Field(key: Dynamic, value: Dynamic)
9494-}
9595-9696-fn wire_type_read_fn(ty: WireType) -> ValueParser {
9797- case ty {
9898- wire_type.VarInt -> read_varint
9999- wire_type.I64 -> read_fixed(64)
100100- wire_type.Len -> read_len
101101- wire_type.I32 -> read_fixed(32)
102102- }
103103-}
104104-105105-fn read_field(bits: BitArray, tag_pos: BytePos) -> DecodeResult(Parsed(Field)) {
106106- use Parsed(value: tag, rest: bits, pos:) <- result.try(parse_varint(
107107- bits,
108108- tag_pos,
109109- ))
110110- let tag = util.bit_array_to_uint(tag)
111111-112112- let field_id = tag |> int.bitwise_shift_right(3)
113113- let wire_type = tag |> int.bitwise_and(0b111)
114114-115115- case wire_type {
116116- 6 -> {
117117- echo tag as "tag"
118118- Nil
119119- }
120120- _ -> Nil
121121- }
122122- use wire_type <- result.try(option.to_result(
123123- wire_type.parse(wire_type),
124124- UnknownWireType(wire_type, pos: tag_pos),
125125- ))
126126-127127- let read = wire_type_read_fn(wire_type)
128128- use value: Parsed(Dynamic) <- result.try(
129129- read(bits, pos) |> result.map(parsed_map(_, dynamic.bit_array)),
130130- )
131131-132132- let field =
133133- parsed_map(value, fn(value) { Field(key: dynamic.int(field_id), value:) })
134134-135135- Ok(field)
136136-}
137137-138138-pub type ValueResult =
139139- DecodeResult(Parsed(BitArray))
140140-141141-pub type ValueParser =
142142- fn(BitArray, BytePos) -> ValueResult
143143-144144-/// Reads the bits from a varint and returns *all* of them. They cannot be
145145-/// parsed as a uint.
146146-///
147147-/// Specifically, the continuation bits are included, so the value's bit size
148148-/// will be a multiple of 8.
149149-pub fn read_varint(bits: BitArray, pos: BytePos) -> ValueResult {
150150- read_varint_acc(bits, <<>>, pos)
151151-}
152152-153153-fn read_varint_acc(bits: BitArray, acc: BitArray, pos: BytePos) -> ValueResult {
154154- case bits {
155155- <<0:size(1), n:bits-size(7), rest:bytes>> -> {
156156- let bit = <<0:size(1), n:bits>>
157157- let acc = bit_array.concat([acc, bit])
158158- Ok(Parsed(value: acc, rest:, pos: pos + 1))
159159- }
160160- <<1:size(1), n:bits-size(7), rest:bytes>> -> {
161161- let bit = <<1:size(1), n:bits>>
162162- read_varint_acc(rest, bit_array.concat([acc, bit]), pos + 1)
163163- }
164164- bits -> Error(InvalidVarInt(leftover_bits: bits, acc:, pos:))
165165- }
166166-}
167167-168168-/// Reads the bits from a varint and parses them a BitArray. The returned bits
169169-/// can be parsed as a uint.
170170-///
171171-/// For a decoder that does this, use `decoders.uint()`.
172172-///
173173-/// The continuation bits are not included, so the value's bit size will be a
174174-/// multiple of 7.
175175-pub fn parse_varint(bits: BitArray, pos: BytePos) -> ValueResult {
176176- parse_varint_acc(bits, <<>>, pos)
177177-}
178178-179179-fn parse_varint_acc(bits: BitArray, acc: BitArray, pos: BytePos) -> ValueResult {
180180- case bits {
181181- <<0:size(1), n:bits-size(7), rest:bytes>> -> {
182182- let acc = bit_array.concat([n, acc])
183183- Ok(Parsed(value: acc, rest:, pos: pos + 1))
184184- }
185185- <<1:size(1), n:bits-size(7), rest:bytes>> ->
186186- parse_varint_acc(rest, bit_array.concat([n, acc]), pos + 1)
187187- bits -> {
188188- Error(InvalidVarInt(leftover_bits: bits, acc:, pos:))
189189- }
190190- }
191191-}
192192-193193-/// Size must be a multiple of 8.
194194-pub fn read_fixed(size: Int) -> ValueParser {
195195- assert size % 8 == 0
196196- fn(bits: BitArray, pos: BytePos) -> ValueResult {
197197- case bits {
198198- <<num:bits-size(size), rest:bytes>> ->
199199- Ok(Parsed(value: num, rest:, pos: pos + size / 8))
200200- bits -> Error(InvalidFixed(size:, bits:, pos:))
201201- }
202202- }
203203-}
204204-205205-fn read_len(bits: BitArray, len_pos: BytePos) -> ValueResult {
206206- // First, read the length of the value
207207- // It is encoded as a varint immediately after the tag
208208- use Parsed(value: len, rest: bits, pos:) <- result.try(parse_varint(
209209- bits,
210210- len_pos,
211211- ))
212212- let len = util.bit_array_to_uint(len)
213213-214214- // Just decoded a uint, so should be safe
215215- assert len > 0
216216-217217- use value <- result.try(
218218- bit_array.slice(from: bits, at: 0, take: len)
219219- |> result.map_error(fn(_) {
220220- InvalidLen(len:, value_bits: bits, pos: len_pos)
221221- }),
222222- )
223223- // Assert b/c if the len was too long, it would've errored in the prev slice
224224- let assert Ok(rest) =
225225- bit_array.slice(from: bits, at: len, take: bit_array.byte_size(bits) - len)
226226-227227- Ok(Parsed(value:, rest:, pos: pos + len))
228228-}