BitTorrent encoding implementation for Clojure.
Clojure Java
Latest commit 3aae018 Jun 17, 2015 @danielfm Bumped version.

README.md

bencode

Build Status

Clojure implementation of Bencode, the encoding used by BitTorrent for storing and transmitting loosely structured data.

Features

  • Parsing bencode strings directly to Clojure data structures and vice-versa
  • Support for input and output streams
  • Read and write BitTorrent metainfo (.torrent) files
  • Can generate Magnet link from BitTorrent metainfo
  • Multi-threaded algorithm for fast piece hashing

Installation

Add the following dependency to your project.clj file:

[bencode "0.2.6"]

Usage

Encoding and Decoding

First, import the bencode.core namespace:

(use '[bencode.core])

At this point, you should be able to use bencode and bdecode functions for encoding and decoding, respectively:

(bencode {:cow "moo" :spam ["info" 32]})
;; -> "d3:cow3:moo4:spaml4:infoi32eee"

(bdecode "d3:cow3:moo4:spaml4:infoi32eee")
;; -> {:cow "moo", :spam ["info" 32]}
Supported Data Types

According to the Bencoding spec, only strings, integers, lists and dictionaries should be supported. Furthermore, only strings can be used as keys in a dictionary, and the keys must appear in sorted order (sorted as raw strings, not alphanumerics).

On the Clojure side, keywords are encoded as strings, sets and vectors are encoded as lists, and all integers - byte, short, int, long, big integers - are encoded as integers with arbitrary size, and decoded to the smallest type which can hold the number without losing data.

Encoding Options

The bencode function also accepts an optional map:

(bencode "moo" {:raw-str? true})
;; -> #<byte[] [B@53c059f6>

These are the supported encoding options:

  • :to - Instance of OutputStream where the encoding result should be written to. Default: nil
  • :raw-str? - Whether the string being encoded should be returned as a byte array. This option can only be used if the option :to is absent. Default: false

Decoding Options

The bdecode function also accepts an optional map:

(bdecode "d3:cow3:moo4:spaml4:infoi32eee", {:str-keys? true :raw-keys ["spam"]})
;; -> {"cow" "moo", "spam" [#<byte[] [B@74184b3b> 32]}

The input might be either a string, a byte array or an input stream.

These are the supported decoding options:

  • :str-keys? - Whether strings should be used as dictionary keys instead of keywords. Default: false
  • :raw-keys - List containing all dictionary keys whose values should be decoded as raw strings instead of UTF-8-encoded strings. Default: nil

BitTorrent Metainfo

Reading Metainfo Files

A collection of useful functions for BitTorrent metainfo parsing are available under the bencode.metainfo.reader namespace:

(use '[bencode.metainfo.reader])

;; parsing an input stream
(def metainfo (parse-metainfo input-stream))

;; parsing a file given its path
(def metainfo (parse-metainfo-file "/file/path"))

To extract bits of information from this metainfo:

(torrent-name metainfo)
;; -> "my.supercool.torrent"

(public-torrent? metainfo)
;; -> true

(torrent-info-hash-str metainfo)
;; -> "b174c9c090275f858853ba5ea1b01762eaa59f9d"

It's also possible to generate the Magnet link for a metainfo:

(torrent-magnet-link metainfo)
;; -> "magnet:?xt=urn:btih:..."

Please check out the source code for a complete list of the available functions.

Creating a Metainfo Dictionary

It's very easy to create a metainfo file:

(use '[bencode.metainfo.writer])

(def metainfo (create-metainfo :file               file-obj
                               :created-by         "You"
                               :announce-list      [["tracker-1"] ["tracker-2"]]
                               :private?           false
                               :name               "optional.torrent.name"
                               :piece-length-power 7 ;; piece length = 2^7KiB
                               :n-threads          4 ;; for fast parallel piece hashing
                               :comment            "Some torrent"))

This operation might take several minutes depending on the file size.

To be able to import this file in your favorite BitTorrent client, just bencode metainfo to a .torrent file and you're done:

(bencode metainfo {:to file-out-stream})

License

Copyright (C) Daniel Fernandes Martins

Distributed under the New BSD License. See COPYING for further details.