babbel_bencode 0.1.0

Fast, binary-safe BitTorrent Bencode parser, serializer, and DOM in pure Rust
docs.rs failed to build babbel_bencode-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

babbel_bencode

A Rust library for parsing, constructing, and converting Bencode data. Designed for embedded systems, resource-constrained environments, and general-purpose use. Supports round-tripping Bencode and conversion to JSON, YAML, XML, and TOML.

Repository License: MIT Rust Edition Buy Me A Coffee

Features

  • Parse Bencode into a typed tree (Node)
  • Serialize Node back to canonical Bencode
  • Convert Node to JSON, YAML, XML, or TOML (optional Cargo features)
  • Zero-copy borrowed parsing via BorrowedNode — no heap allocation
  • no_std compatible (disable the default std feature)
  • Memory pool / arena allocation (Arena, StackBuffer, MemoryTracker)
  • Stack-based iterative parser — safe for deeply nested structures
  • Validation helpers for ergonomic field extraction (get_required, get_int_required, …)
  • Configurable parsing depth (ParserConfig) and canonicalisation enforcement (EncoderConfig)
  • Read/write from files or in-memory buffers

Installation

Add to your Cargo.toml:

[dependencies]

babbel_bencode = "0.1.0"

Or as a path dependency within this workspace:

[dependencies]

babbel_bencode = { path = "crates/bencode" }

All binary-safe streaming I/O (IByteStream, FileSource, FileDestination, Buffer) is unified with and powered by babbel_core::io.

To minimise binary size, disable unused format-conversion features:

[dependencies]

babbel_bencode = { version = "0.1.0", default-features = false, features = ["std", "json"] }

Available features: std (default), json, toml, xml, yaml.

Release Builds & LTO for Optimal Size

For the smallest and fastest binaries, enable Link Time Optimization (LTO):

[profile.release]

lto = true

Then build with:

cargo build --release

Quick Examples

Parse a .torrent file and convert to YAML

use babbel_bencode::{FileSource, FileDestination, parse, to_yaml};

let mut src = FileSource::new("example.torrent")?;
let node = parse(&mut src)?;
let mut dst = FileDestination::new("example.yaml")?;
to_yaml(&node, &mut dst);

Round-trip a Bencode buffer

use babbel_bencode::{parse_bytes, stringify_to_bytes};

let raw = b"d3:foo3:bar4:spamli1ei2ei3eee";
let node = parse_bytes(raw)?;
let encoded = stringify_to_bytes(&node);
assert_eq!(raw.as_slice(), encoded.as_slice());

Construct a Node and render as JSON

use babbel_bencode::{Node, make_node, to_json, BufferDestination};

// Using the From trait with an array of key-value pairs
let node = Node::from([
    ("name", Node::from("hello")),
    ("count", Node::from(42_i64)),
]);
let mut dst = BufferDestination::new();
to_json(&node, &mut dst);

Validate and extract fields ergonomically

use babbel_bencode::parse_bytes;

let node = parse_bytes(b"d4:name5:Alice3:agei30ee")?;
let name: &str = node.get_string_required("name")?;
let age:  i64  = node.get_int_required("age")?;

Data Model

Node::Integer(i64)                      — Bencode integer
Node::Str(String)                       — Bencode string (UTF-8)
Node::List(Vec<Node>)                   — Bencode list
Node::Dictionary(HashMap<String, Node>) — Bencode dictionary
Node::None                              — Empty / uninitialized node

Nodes implement Clone, Debug, PartialEq, and Display.

Creating Nodes

use babbel_bencode::{Node, make_node};
use std::collections::HashMap;

// Direct variants
let i = Node::Integer(42);
let s = Node::Str("hello".to_string());

// Via the generic helper (uses From conversions)
let n = make_node(99_i64);          // -> Node::Integer(99)
let n = make_node("world");         // -> Node::Str("world")
let n = make_node(vec![1_i64, 2]);  // -> Node::List([Integer(1), Integer(2)])

// Array literal short-hand
let list = Node::from([1_i64, 2, 3]);
let dict = Node::from([("key", Node::Integer(1))]);

API Overview

Parsing

Function Description
parse(&mut src) Parse from any Source (file or buffer)
parse_bytes(data: &[u8]) Parse directly from a byte slice
parse_str(data: &str) Parse directly from a string slice
parse_iterative(&mut src) Stack-based iterative parse (deep nesting safe)
parse_bytes_iterative(data) Iterative parse from byte slice
parse_str_iterative(data) Iterative parse from string slice
parse_borrowed(data: &[u8]) Zero-copy parse returning BorrowedNode
validate_bencode(data: &[u8]) Validate without building a node tree

Stringifying

Function Description
stringify(&node, &mut dst) Write canonical Bencode to any Destination
stringify_to_bytes(&node) Return Bencode as Vec<u8>
stringify_to_string(&node) Return Bencode as String
to_json(&node, &mut dst) Convert to JSON (json feature)
to_toml(&node, &mut dst) Convert to TOML (toml feature)
to_xml(&node, &mut dst) Convert to XML (xml feature)
to_yaml(&node, &mut dst) Convert to YAML (yaml feature)

I/O

Type Description
BufferSource Read Bencode from an in-memory buffer
FileSource Read Bencode from a file (std feature)
BufferDestination Write output to an in-memory buffer
FileDestination Write output to a file (std feature)

Node Methods

Type checking: is_integer(), is_string(), is_list(), is_dictionary(), is_none()

Value access: as_integer(), as_string(), as_list(), as_list_mut(), as_dictionary(), as_dictionary_mut()

Dictionary access: get(key), get_mut(key)

Validation helpers:

Method Returns
get_required(key) Result<&Node, String>
get_int_required(key) Result<i64, String>
get_string_required(key) Result<&str, String>
get_list_required(key) Result<&Vec<Node>, String>
get_dict_required(key) Result<&HashMap<String, Node>, String>
get_int_optional(key) Option<i64>
get_string_optional(key) Option<&str>
get_list_optional(key) Option<&Vec<Node>>
get_dict_optional(key) Option<&HashMap<String, Node>>

Utility: len(), is_empty(), type_name()

Embedded / no_std API

Type / Function Description
Arena Bump allocator from a fixed buffer
StackBuffer<N> Stack-allocated byte buffer
MemoryTracker Allocation accounting for embedded systems
FixedSizeBuffer<N> Stack-allocated fixed-size buffer (const generic)
MemoryBounds Const-generic memory bounds calculator
BorrowedNode Zero-copy borrowed node (no allocation)

Configuration

use babbel_bencode::{ParserConfig, EncoderConfig};

let parser = ParserConfig::new().with_max_depth(50);   // default: 100

let encoder = EncoderConfig::new()
    .with_canonical(true)               // enforce sorted dict keys, no leading zeros
    .with_dict_order_verification(true);

Utilities

Function Description
version() Returns the library version string
read_file(path) Read a file to String (std feature) → Result<String, io::Error>
write_file(path, content) Write a string to a file (std feature) → Result<(), io::Error>

Error Handling

BencodeError is a lightweight, allocation-free enum suitable for no_std environments:

pub enum BencodeError {
    EmptyInput,
    InvalidInteger,
    UnterminatedInteger,
    InvalidStringLength,
    StringTooShort,
    UnterminatedList,
    UnterminatedDictionary,
    DictKeysOutOfOrder,
    DictKeyMustBeString,
    UnexpectedCharacter(char),
    FileNotFound,
    IoError,
}

Each variant exposes .code() -> u8 for compact logging and .as_str() -> &'static str for human-readable messages. In std environments, BencodeError implements std::error::Error.

Note: read_file / write_file return std::io::Error, not BencodeError.

Minimum Supported Rust Version

Rust 1.85.0 (edition 2024).

Documentation

Support

If you find this project useful, you can support its development by buying me a coffee:

Buy Me A Coffee

Or visit buymeacoffee.com/roberttizz1.

License

MIT License. See LICENSE for details.