A Rust crate for parsing and creating BitTorrent metainfo files (.torrent).
bitors supports BitTorrent v1 (BEP 3),
v2 (BEP 52), and hybrid torrents. It can read
existing torrents without copying their data, build new ones from files on disk, and turn any
torrent into a magnet link.
- Zero-copy parsing.
Parserborrows from the input buffer, and the parsed types (Bencode,Torrent, and friends) holdCow<'a, ...>values tied to it. Callinto_ownedto detach a torrent from its buffer. - v1, v2, and hybrid torrents. The metainfo is modeled by
TorrentMeta, so every version-specific field is only reachable in the variants where it exists. - A typestate builder.
TorrentBuilderwill not let you build a torrent until at least one path has been supplied, so an empty torrent is a compile-time error. - Fast hashing. Pieces are hashed in parallel with
rayon, files are memory-mapped, and v2 Merkle trees are computed alongside the v1 piece hashes when building a hybrid torrent. SHA-1 and SHA-256 come from the RustCrypto crates, which use hardware acceleration where available. - Info hashes and magnet links. Compute the v1 (SHA-1) and v2 (SHA-256) info hashes and
generate magnet links with
MagnetLink. - Strict bencode. The parser rejects leading zeros, negative zero, plus signs, unsorted or duplicate dictionary keys, non-string keys, trailing data, and nesting deeper than a configurable limit.
use std::fs;
use bitors::parse_torrent;
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = fs::read("my_torrent.torrent")?;
let torrent = parse_torrent(&data)?;
println!("name: {}", torrent.name());
println!("files: {}", torrent.file_count());
println!("size: {} bytes", torrent.total_size());
if let Some(v1) = torrent.info_hash_v1() {
let hex: String = v1.iter().map(|b| format!("{b:02x}")).collect();
println!("v1 info hash: {hex}");
}
# Ok(())
# }
parse_torrent returns a Torrent that borrows from data. If you need to keep
the torrent after data is dropped, convert it with
IntoOwned::into_owned.
use std::{fs::File, io::BufWriter};
use bitors::Torrent;
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let torrent = Torrent::builder()
.private(true)
.comment("Created with bitors")
.add_tracker("https://tracker.example.com/announce".parse()?)
.add_path("my_folder")
.build()?; // hybrid (v1 + v2) by default
let mut writer = BufWriter::new(File::create("my_folder.torrent")?);
torrent.to_bencode().encode_to_writer(&mut writer)?;
# Ok(())
# }
build produces a hybrid torrent. Use
build_v1 or
build_v2 when you need a single version. Fields you do not set
get sensible defaults; see the TorrentBuilder documentation for the full
list.
use bitors::Torrent;
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let torrent = Torrent::builder().add_path("my_file").build()?;
println!("{}", torrent.magnet_link());
// magnet:?xt=urn:btih:<v1 hash>&xt=urn:btmh:1220<v2 hash>&dn=my_file&xl=<size>...
// Some clients prefer the v1 hash in Base32.
println!("{}", torrent.magnet_link().v1_base32());
# Ok(())
# }
The bencode module is usable on its own:
use bitors::Parser;
let bencode = Parser::new(b"d3:bar4:spam3:fooi42ee").parse().unwrap();
let dict = bencode.as_dict().unwrap();
assert_eq!(dict[&b"foo"[..]].as_int().unwrap(), 42);
assert_eq!(bencode.encode(), b"d3:bar4:spam3:fooi42ee");
| Module | Contents |
|---|---|
bencode |
The Bencode value type, the zero-copy Parser, and encoding. |
torrent |
Torrent and the types that model the info dictionary, file lists, and file trees. |
torrent::builder |
TorrentBuilder, which hashes files and assembles new torrents. |
magnet |
MagnetLink and InfoHashes. |
error |
The crate-wide Error, which wraps the per-module error types. |
- The only supported torrent
encodingis UTF-8. - Bencode integers are signed 64-bit, so a single file larger than
i64::MAXbytes (≈ 8 EiB) cannot be represented. - Padding files are handled according to BEP 47 when building hybrid torrents, and are ignored when checking that the v1 and v2 file lists of a hybrid torrent agree.
This README and the module-level docs, as well as a few others, were written by an LLM and manually reviewed. The rest of the documentation and the entire codebase were written manually.
Rust 1.88 or newer (edition 2024).
Licensed under either of the MIT license or the Apache License, Version 2.0 at your option.