A robust, standards-compliant implementation of Bencode.
2025-09-26 17:12:35 +02:00
examples documentation 2025-09-08 16:32:54 +02:00
build.zig documentation 2025-09-08 16:32:54 +02:00
build.zig.zon documentation 2025-09-08 16:32:54 +02:00
main.zig slightly nicer encode api 2025-09-26 17:12:35 +02:00
README.md fixup! documentation 2025-09-08 16:36:01 +02:00
steamboat-willie-mickey_archive.torrent initial commit 2025-09-08 08:22:42 +02:00

bencode

Zig versions 0.15.x are supported

A robust, standards-compliant implementation of Bencode. It makes efficient use of Writergate.

Add to your project with Zig package manager

Add it to your build.zig.zon using: $ zig fetch --save https://codeberg.org/cancername/bencode

Make it available to your module in build.zig:

mod.addImport("bencode", b.dependency("bencode", .{ .target = target, .optimize = optimize }).module("bencode"));

Examples

Dump first file as JSON

The fundamental data type of this library is Value. To parse a whole value in one go use Value.decode:

Code
const std = @import("std");
const bencode = @import("bencode");

pub fn main() !void {
    var gpa_state = std.heap.DebugAllocator(.{}){};
    const gpa = gpa_state.allocator();
	
	// To decode all bencode streams supported by this library, the buffer must be at least 21 bytes.
	// You can also use std.Io.Reader.fixed(slice) to decode right from memory. In this case, you
	// can also set `allocate_strings` to `false`, as long as you keep the memory alive.
    var buf: [8 << 10]u8 = undefined;
    var reader = std.fs.File.stdin().reader(&buf);
	
	var value = try bencode.Value.decode(&reader.interface, .{.gpa = gpa});
	defer value.deinit(gpa); // Unlike with std.json, granular memory management is a thing
	
    // convenient accessor functions returning errors
	{
	    const info = try value.get("info");
	    const files = try info.get("files");
		const first_file = try files.at(0);
		std.debug.print("{f}\n", .{std.json.fmt(first_file, .{ .whitespace = .indent_2 })});
	}

}

$ zig build examples

Run it: $ ./zig-out/bin/dump_first_file <steamboat-willie-mickey_archive.torrent

{
  "crc32": "5a4fc499",
  "length": 1882130429,
  "md5": "6455314dd4c0878ecaba47eaf996b5c2",
  "mtime": "1672612537",
  "path": [
    "01 - Steamboat Willie.mkv"
  ],
  "sha1": "96ce88bac65ac5fe7aa5239fb508ff79cd24141b"
}

Configuration

This library can be configured in various ways.

The default limits are small to avoid running out of memory, adjust them in decoder.Config if you want to parse large files:

  • max_nesting_level
  • max_string_len
  • max_list_elements
  • max_dict_elements

Alternatively, you can set them to the maximum and use a limiting allocator.

Options that control spec compliance:

  • unsorted_dict_behavior, the default is to sort and ignore
  • duplicate_key_behavior, the default is to error
  • noncanonical_integer_behavior, the default is to allow

The encoder always gives canonical, compliant output.

Options that control logging:

  • log_unsorted_dict
  • log_duplicate_key
  • log_noncanonical_integer

Future work

  • There's some kind of canonicalization issue remaining where non-canonical encodings are erroneously accepted.
  • A builder-style API.