Bencode

BitTorrent encoding implementation for Clojure.

View the Project on GitHub danielfm/bencode

bencode

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

Features

Installation

Add the following dependency to your project.clj file:

[bencode "0.2.5"]

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:

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:

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.