3 releases

0.1.2 Aug 20, 2023
0.1.1 Aug 20, 2023
0.1.0 Feb 22, 2023

#20 in #file-sharing

MIT license

64KB
1.5K SLoC

Bencode (Bee-encode)

Rust implementation for bencode encoding. Bencode is the encoding used by the peer-to-peer file sharing system BitTorrent for storing and transmitting loosely structured data. Bencoding is most commonly used in torrent files, and as such is part of the BitTorrent specification. These metadata files are simply bencoded dictionaries.

Bencoding is simple and (because numbers are encoded as text in decimal notation) is unaffected by endianness, which is important for a cross-platform application like BitTorrent. It is also fairly flexible, as long as applications ignore unexpected dictionary keys, so that new ones can be added without creating incompatibilities.

Installation

You can install bencode_encoder crate in your Rust project by using:

$ cargo add bencode-encoder

Data types

Bencode specification supports 4 data types:

  • byte strings (encoded as length:content, e.g. 4:rust)
  • integers (encoded as inumbere, e.g. i20e)
  • lists (encoded as l<contents>e, e.g. l4:rusti20ee)
  • dictionaries with sorted strings keys (encoded as d<contents>e, e.g. d1:ki2023ee)

Example usage (decoding from .torrent file and storing as .json file)

use bencode_encoder::Decoder;


fn main() {
    let input_file = std::env::args().nth(1)
            .expect("Usage: decode <input_file> <output_file>");
    let output_file = std::env::args().nth(2)
            .expect("Usage: decode <input_file> <output_file>");

    match Decoder::decode_from(input_file) {
        Err(err) => println!("{}", err.to_string()),
        Ok(t) => {
            match t.save_to_json(output_file) {
                Err(err) => println!("{}", err.to_string()),
                Ok(_) => println!("Decoded bencode saved to .json file"),
            }
        },
    }
}

Example usage (reading from .json file and encoding to .torrent file)

use bencode_encoder::{Encoder, Type};


fn main() {
    let input_file = std::env::args().nth(1)
            .expect("Usage: encode <input_file> <output_file>");
    let output_file = std::env::args().nth(2)
            .expect("Usage: encode <input_file> <output_file>");

    match Type::load_from_json(input_file) {
        Err(err) => println!("{}", err.to_string()),
        Ok(t) => {
            match Encoder::encode_to(&t, output_file) {
                Err(err) => println!("{}", err.to_string()),
                Ok(_) => println!("Encoded bencode saved to binary file"),
            }
        },
    }
}

BNF

BNF for parsing is shown below (click here to read more). This crate implements simple parser according to BNF shown below.

<BE>    ::= <DICT> | <LIST> | <INT> | <STR>

<DICT>  ::= "d" 1 * (<STR> <BE>) "e"
<LIST>  ::= "l" 1 * <BE>         "e"
<INT>   ::= "i"     <SNUM>       "e"
<STR>   ::= <NUM> ":" n * <CHAR>; where n equals the <NUM>

<SNUM>  ::= "-" <NUM> / <NUM>
<NUM>   ::= 1 * <DIGIT>
<CHAR>  ::= %
<DIGIT> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

Tests

To run tests use:

$ cargo test

Dependencies

~0.7–1.6MB
~35K SLoC