# `gno.land/p/moul/x/daily/bencode/v0` **Bencoding, the BitTorrent serialization format**: `Decode`, `Value.Encode`, `NewInt`, `NewString`, `NewList`, `NewDict`. ```go import "gno.land/p/moul/x/daily/bencode/v0" v, err := bencode.Decode("d3:cow3:moo4:spam4:eggse") v.Get("cow").Text() // "moo" v.Keys() // ["cow", "spam"] v.Encode() // "d3:cow3:moo4:spam4:eggse", byte for byte d := bencode.NewDict() d.Set("spam", bencode.NewString("eggs")) d.Set("cow", bencode.NewString("moo")) d.Encode() // "d3:cow3:moo4:spam4:eggse": insertion order does not matter ``` **Four types and no escape syntax.** Integers (`i42e`), byte strings (`5:hello`), lists (`l...e`) and dictionaries (`d...e`). No float, no boolean, no null. A string carries its length instead of quoting rules, so a value can contain any byte, including the delimiters. **The point is canonicality, and that is why the decoder is strict.** One value has exactly one encoding: dictionary keys sorted bytewise, no leading zeros, no negative zero, no trailing bytes. A BitTorrent infohash is the digest of a re-encoded `info` dictionary, so a decoder that accepts a sloppy encoding and re-emits a tidy one silently changes the identity of what it parsed. This one **rejects** non-canonical input rather than normalizing it, which makes the round trip a guarantee: for every input `Decode` accepts, `Encode` returns the original bytes. `TestCanonicalIsAFixedPoint` pins that property. `ErrNotCanonical` is kept apart from `ErrSyntax` on purpose: "this parses but no encoder would emit it" is a different message to show a user than "this is not bencode". Nothing that encodes to nothing can be stored: `Set` and `NewList` ignore a nil and the zero `Value`, because either one would emit a key with no value, or a list whose `Len` disagrees with its encoding, and `Decode` would then reject what `Encode` produced. `Encode` is total. It returns `""` for anything with no canonical encoding, which means a value nested deeper than `MaxDepth` and therefore any cycle a caller assembled by hand. `Set` refuses the direct case (a dictionary holding itself) outright; two dictionaries holding each other cannot be caught at insert time without walking the whole structure, so the depth bound in `Encode` is what keeps them from aborting the call. `""` is not a legal bencode document, so a caller can test for it. `Set` also never writes through the slices an earlier `Set` produced, so copying the struct behind the pointer cannot leave two values partially aliased. Accessors never panic. `Int`, `Text`, `Len`, `At` and `Get` return the zero value for the wrong kind or a nil receiver, so walking a document of unknown shape cannot abort a realm call. Input is bounded by `MaxLen` (8192 bytes) and `MaxDepth` (16) so gas stays predictable. Not implemented: streaming, raw `[]byte` values distinct from strings, and integers wider than `int64`. **Live demo:** [`r/moul/x/daily/bencodedemo`](https://github.com/moul/gno-contracts/tree/main/r/moul/x/daily/bencodedemo) · render it at [`/r/moul/x/daily/bencodedemo/v0`](https://gno.land/r/moul/x/daily/bencodedemo/v0). --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).