go-torrent

module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: MIT

README

go-torrent

Bencode and torrent metainfo parsing for Go. No dependencies outside the standard library.

Written as a replacement for github.com/anacrolix/torrent/metainfo and its bencode package in autobrr, where pulling in a full torrent client module was too heavy and its strict parsing rejected torrents that trackers actually serve. The packages keep the same names and layout, so migrating is usually just replacing the module prefix github.com/anacrolix/torrent with github.com/autobrr/go-torrent.

The scope is read-only on purpose: parse torrents, get the infohash, sizes, files and trackers. There is no torrent creation, no piece hashing and no magnet link support. BitTorrent v2 and hybrid torrents are supported for parsing, including correct sizes and piece-aligned file offsets from the v2 file tree.

Install

go get github.com/autobrr/go-torrent

Example

package main

import (
	"fmt"
	"log"

	"github.com/autobrr/go-torrent/metainfo"
)

func main() {
	mi, err := metainfo.LoadFromFile("debian-12.5.0-amd64-netinst.iso.torrent")
	if err != nil {
		log.Fatal(err)
	}

	info, err := mi.UnmarshalInfo()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(info.BestName())
	fmt.Println(mi.HashInfoBytes()) // v1 infohash as hex
	fmt.Println(info.TotalLength()) // size in bytes

	for _, tier := range mi.UpvertedAnnounceList() {
		for _, tracker := range tier {
			fmt.Println(tracker)
		}
	}

	for _, f := range info.UpvertedFiles() {
		fmt.Println(f.DisplayPath(&info), f.Length)
	}
}

Plain bencode lives in github.com/autobrr/go-torrent/bencode with the usual Marshal, Unmarshal, Decoder and Encoder API and bencode:"..." struct tags, including omitempty and ignore_unmarshal_type_error:

var v struct {
	Name  string `bencode:"name"`
	Total int64  `bencode:"total,omitempty"`
}
err := bencode.Unmarshal(data, &v)

Differences from anacrolix/torrent

The parser is lenient about the malformed optional fields trackers produce in the wild. Only the info dict is treated as canonical.

  • announce-list may be a bencode string, a flat list of strings, or contain junk entries; all of it decodes without error. The motivating case was a private tracker encoding an empty announce-list as the bencode string 0:, which fails the entire parse in anacrolix.
  • Junk in optional metadata such as creation date, comment or created by never fails a parse, whatever type or value it holds.
  • A private value with a type that is not an integer, a string, or a one-element list leaves Info.Private nil. In anacrolix, this fails the parse of the whole info dict.
  • Trailing whitespace after the torrent payload is accepted. Some tracker webservers append a newline.
  • Unsorted and duplicate dict keys are accepted (the last value wins), as are leading zeros in integer literals and string length prefixes.

It is stricter where safety matters, since torrent files are usually untrusted input:

  • nesting is capped at 512 levels
  • integers must fit the target type; there is no big.Int fallback
  • a lying length prefix cannot force allocation beyond the actual input
  • the v2 file tree decodes in a single pass with memory linear in input size

Error types keep the anacrolix names and shapes (bencode.SyntaxError, bencode.UnmarshalTypeError, bencode.ErrUnusedTrailingBytes, ...), so existing errors.As code ports unchanged.

Performance

Measured with benchstat (n=6, go 1.26, Ryzen 3700X) against github.com/anacrolix/torrent v1.61.0, decoding real torrent files. "Pipeline" is the full consumer path: load, unmarshal the info dict, compute the infohash, sum the file lengths.

Benchmark anacrolix this package delta
Load, 287 B torrent 8.3 us 2.5 us -70%
Load, 2.8 KB 26.3 us 6.9 us -74%
Load, 31 KB 28.0 us 21.3 us -24%
Load, 123 KB 104.9 us 91.7 us -13%
UnmarshalInfo, v2 hybrid 118.3 us 36.8 us -69%
Pipeline, 123 KB single file 212.1 us 196.9 us -7%
Pipeline, v2 hybrid 240.8 us 153.4 us -36%
Decode into any, 335 KB 227.5 us 78.0 us -66%

Geomean across the suite: 44% less time, 47% fewer allocations, 28% fewer bytes allocated. Unmarshal decodes directly from the input slice, so the info dict is captured as a subslice instead of being copied through an intermediate buffer.

One caveat: loading v2 hybrid torrents allocates more bytes here (+68%) than anacrolix. That is the cost of the single-pass file tree decoding, which trades a bounded constant factor for the unbounded amplification the recursive approach allows. It is still faster in wall time.

Testing

The test suite asserts byte-level parity with anacrolix on a corpus of real torrents: identical infohashes, names, sizes, file lists, offsets and byte-identical re-encoded info dicts. Behavior was additionally checked with a differential fuzzer against anacrolix and with adversarial tests for hostile input (deep nesting, lying length prefixes, memory amplification). The bencode fuzzer also verifies that the slice fast path and the streaming decoder always agree.

License

MIT

Directories

Path Synopsis
Package bencode implements encoding and decoding of bencode data as used in the BitTorrent metainfo format (BEP 3).
Package bencode implements encoding and decoding of bencode data as used in the BitTorrent metainfo format (BEP 3).
Package metainfo parses .torrent files (BEP 3, BEP 12, BEP 19, BEP 52), tolerating the malformed optional fields that real-world trackers serve.
Package metainfo parses .torrent files (BEP 3, BEP 12, BEP 19, BEP 52), tolerating the malformed optional fields that real-world trackers serve.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL