v2 / vlib / encoding / cbor / cbor.v
54 lines · 52 sloc · 2.3 KB · 468855eef1db0ff73c62be2d1bf176ffa0e1478e
Raw
1// Package cbor implements RFC 8949 (Concise Binary Object Representation).
2//
3// Three layers of API are available:
4//
5// * `encode[T]` / `decode[T]` — comptime-driven generic API. Works on
6// primitives, strings, arrays, maps with any scalar key, structs
7// (with `@[cbor: 'name']` and `@[skip]` attributes), enums,
8// `time.Time` (auto-tagged), and any type implementing
9// `Marshaler` / `Unmarshaler`.
10//
11// * `Packer` / `Unpacker` — manual streaming API. Use when the schema
12// isn't known at compile time, or when you need full control over
13// tags, indefinite-length items and simple values.
14//
15// * `Value` sumtype — dynamic representation for round-tripping
16// unknown payloads or inspecting tagged data.
17//
18// Defaults follow RFC 8949 *preferred serialisation* (§4.2.2): floats
19// shrink to the shortest IEEE 754 width that preserves their value, and
20// every length argument uses the shortest encoding. Set
21// `EncodeOpts.canonical = true` to additionally sort map keys for
22// hash/signature stability (§4.2.1, deterministic encoding).
23module cbor
24
25// encode serialises any V value into CBOR bytes. The returned slice
26// owns its backing buffer (V's GC tracks it) — no copy, so the returned
27// bytes are safe to keep across calls and to pass to other modules.
28pub fn encode[T](val T, opts EncodeOpts) ![]u8 {
29 mut p := new_packer(opts)
30 p.pack[T](val)!
31 return p.bytes()
32}
33
34// decode parses CBOR bytes into a value of type T. Rejects extra bytes
35// after the top-level item by default — callers feeding a buffer that
36// holds multiple concatenated items (or that may carry an unrelated
37// suffix) must opt in via `DecodeOpts.allow_trailing_bytes = true` and
38// drive an `Unpacker` themselves.
39//
40// A leading self-describe tag (`d9 d9 f7`, RFC 8949 §3.4.6) is stripped
41// transparently so payloads encoded with `EncodeOpts.self_describe`
42// round-trip through `decode[T]` without the caller having to peel it.
43pub fn decode[T](data []u8, opts DecodeOpts) !T {
44 mut u := new_unpacker(data, opts)
45 if u.data.len - u.pos >= 3 && u.data[u.pos] == 0xd9 && u.data[u.pos + 1] == 0xd9
46 && u.data[u.pos + 2] == 0xf7 {
47 u.pos += 3
48 }
49 value := u.unpack[T]()!
50 if !opts.allow_trailing_bytes && !u.done() {
51 return malformed(u.pos, '${u.remaining()} trailing byte(s) after top-level item')
52 }
53 return value
54}
55