protobuf#
Protocol Buffers codec for OCaml, for hand-written message schemas.
Define a codec once as a value of type 'a t and use it for both encoding
and decoding. No .proto code generation yet — the target use case is
speaking specific gRPC or protobuf protocols by hand, where the schema
change cadence is low and the combinator definition is short.
Installation#
Install with opam:
$ opam install nox-protobuf
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install nox-protobuf
Usage#
type person = { name : string; age : int32; hobbies : string list }
let person_codec : person Protobuf.t =
let open Protobuf.Message in
v (fun name age hobbies -> { name; age; hobbies })
|> required 1 Protobuf.string ~enc:(fun p -> p.name)
|> required 2 Protobuf.int32 ~enc:(fun p -> p.age)
|> repeated 3 Protobuf.string ~enc:(fun p -> p.hobbies)
|> seal
let wire =
Protobuf.to_string person_codec
{ name = "Ada"; age = 36l; hobbies = [ "math"; "sewing" ] }
let () =
match Protobuf.of_string person_codec wire with
| Ok p -> Printf.printf "%s, %ld, %d hobbies\n" p.name p.age (List.length p.hobbies)
| Error e -> prerr_endline (Protobuf.Error.to_string e)
Scalar codecs#
| Codec | OCaml type | Wire |
|---|---|---|
int32 |
int32 |
varint, signed |
int64 |
int64 |
varint, signed |
uint32 |
int32 |
varint, unsigned bit pattern |
uint64 |
int64 |
varint, unsigned bit pattern |
sint32 |
int32 |
varint, zig-zag |
sint64 |
int64 |
varint, zig-zag |
fixed32 |
int32 |
4 bytes little-endian |
fixed64 |
int64 |
8 bytes little-endian |
sfixed32 |
int32 |
4 bytes little-endian, signed |
sfixed64 |
int64 |
8 bytes little-endian, signed |
float |
float |
4 bytes IEEE 754 |
double |
float |
8 bytes IEEE 754 |
bool |
bool |
varint |
string |
string |
length-delimited UTF-8 |
bytes |
string |
length-delimited bytes |
Field combinators#
The pipeline starts with v ctor and each step adds one field:
required n c ~enc— a field at tagn, codecc. Absent decodes as the scalar's default (0,"",false, etc., matching proto3 semantics).optional n c ~enc— the value as'a option; decoded asNoneiff the tag is absent.repeated n c ~enc— multiple values (non-packed). Each occurrence of tagnappends to the list.packed n c ~enc— packed repeated: one length-delimited blob holds all values concatenated. Required for numeric repeated fields in proto3.map n kc vc ~enc— amap<K, V>field.oneof ~default ~enc cases— at most one of a set of alternative fields.
Each step takes a builder ('o, 'a -> 'dec) t_ and returns ('o, 'dec) t_;
seal closes ('o, 'o) t_ into an 'o Protobuf.t.
Limitations (v0.1)#
- No
.protocode generation. Write codecs by hand. - No
oneof. - No
map<K, V>. - Unknown fields are discarded on decode (not preserved for re-encode).
int32/int64(not zig-zag) use 10-byte varints for negative values, matching the protobuf spec.