Skip to content
Browse files

First revision. Everything ought to work.

  • Loading branch information...
1 parent 7ea65ef commit 3eaf5a96d2539a0c4d484b82c6ea0f13bd258217 @clarkf committed Feb 7, 2012
View
4 Makefile
@@ -1,5 +1,5 @@
test:
- @echo "populate me"
+ @./node_modules/.bin/mocha --growl $(TESTFLAGS) --reporter spec test/*.js
-.PHONY: test
+.PHONY: test
View
164 lib/bencoding.js
@@ -1,4 +1,3 @@
-
/*!
* bencoding
* Copyright(c) 2011 Clark Fischer <clark.fischer@gmail.com>
@@ -9,4 +8,165 @@
* Library version.
*/
-exports.version = '0.0.1';
+exports.version = '0.0.1';
+
+function BDict() {
+ this.keys = [];
+ this.vals = [];
+ this.length = 0;
+}
+BDict.prototype.add = function (key, val) {
+ this.keys.push(key);
+ this.vals.push(val);
+ this.length++;
+};
+BDict.prototype.vget = function (i) {
+ return this.vals[i];
+};
+BDict.prototype.kget = function (i) {
+ return this.keys[i];
+}
+BDict.prototype.get = function (i) {
+ return [this.keys[i], this.vals[i]];
+};
+BDict.prototype.toJSON = function () {
+ var ret = {};
+ for (var i = 0; i < this.keys.length; i++) {
+ ret[this.keys[i]] = this.vals[i] instanceof BDict ? this.vals[i].toJSON() : this.vals[i];
+ }
+ return ret;
+};
+
+function decode(buf) {
+ if (!(this instanceof decode)) {
+ return new decode(buf);
+ }
+ this.raw = buf;
+ this.remainder = buf;
+ return this.getNext();
+
+};
+
+decode.prototype.nextToken = function () {
+ return String.fromCharCode(this.remainder[0]);
+}
+
+decode.prototype.findNext = function (x) {
+ if (typeof x == 'string') {
+ x = x.charCodeAt(0);
+ }
+ for (var i = 0; i < this.remainder.length; i++) {
+ if (this.remainder[i] === x) return i;
+ }
+ return -1;
+};
+
+decode.prototype.getNext = function () {
+ var tok = this.nextToken();
+ switch (this.nextToken()) {
+ case 'd':
+ return this.consumeDictionary();
+ case 'l':
+ return this.consumeList();
+ case 'i':
+ return this.consumeInteger();
+ default:
+ return this.consumeByteString();
+ }
+};
+
+decode.prototype.consumeDictionary = function () {
+ var i = 0;
+ this.ff(1);
+ var dict = new BDict();
+ while (this.nextToken() !== 'e') {
+ dict.add(this.getNext(), this.getNext());
+ }
+ this.ff(1);
+ return dict;
+};
+
+decode.prototype.consumeInteger = function () {
+ var end = this.findNext('e'),
+ intBuf = this.remainder.slice(1, end);
+ this.ff(end + 1);
+ return +intBuf.toString();
+};
+
+decode.prototype.consumeByteString = function () {
+ var sep = this.findNext(':'),
+ length = +this.remainder.slice(0, sep).toString('ascii'),
+ buf;
+ buf = this.remainder.slice(sep + 1, sep + 1 + length);
+ this.ff(sep + 1 + length);
+ return buf;
+};
+
+decode.prototype.consumeList = function () {
+ this.ff(1);
+ var res = [];
+ while(this.nextToken() !== 'e') {
+ res.push(this.getNext());
+ }
+ this.ff(1);
+ return res;
+};
+
+decode.prototype.ff = function (o) {
+ this.remainder = this.remainder.slice(o, this.remainder.length);
+};
+
+function combineBuffers(list) {
+ var length = list.reduce(function (pV, tV) {
+ return pV + tV.length;
+ }, 0),
+ res = new Buffer(length),
+ i = 0;
+ list.forEach(function (item) {
+ item.copy(res, i);
+ i += item.length;
+ });
+ return res;
+};
+
+function encode(obj) {
+ if (typeof obj === 'string') {
+ return new Buffer(obj.length + ":" + obj);
+ } else if (obj instanceof Buffer) {
+ var res = new Buffer(obj.length + 1 + (obj.length.toString()).length);
+ res.write(obj.length.toString() + ':', 0)
+ obj.copy(res, obj.length.toString().length + 1);
+ return res;
+ } else if (typeof obj === 'number') {
+ return new Buffer('i' + (+obj) + 'e');
+ } else if (Array.isArray(obj)) {
+ obj = obj.map(encode);
+ obj.splice(0, 0, new Buffer('l'));
+ obj.push(new Buffer('e'));
+ return combineBuffers(obj);
+ } else if (obj instanceof BDict) {
+ var parts = [];
+ for (var i = 0; i < obj.length; i++) {
+ parts.push(encode(obj.kget(i)));
+ parts.push(encode(obj.vget(i)));
+ }
+ parts.splice(0, 0, new Buffer('d'));
+ parts.push(new Buffer('e'));
+ return combineBuffers(parts);
+ } else {
+ //object
+ var parts = [];
+ Object.keys(obj).forEach(function (k) {
+ parts.push(encode(k));
+ parts.push(encode(obj[k]));
+ });
+ parts.splice(0, 0, new Buffer('d'));
+ parts.push(new Buffer('e'));
+ return combineBuffers(parts);
+ }
+}
+
+
+exports = module.exports;
+exports.decode = decode;
+exports.encode = encode;
View
4 package.json
@@ -8,6 +8,10 @@
, "devDependencies": {
"mocha": ""
, "expect.js": ""
+ , "benchmark": "~0.3.0"
+ , "dht-bencode": ""
+ , "bncode": ""
+ , "bencode": ""
}
, "main": "index"
, "engines": { "node": "0.6.x" }
View
31 performance/decoding.js
@@ -0,0 +1,31 @@
+var Benchmark = require('benchmark'),
+ bencoding = require('../lib/bencoding'),
+ bencode = require('bencode'),
+ bncode = require('bncode'),
+ dht = require('dht-bencode'),
+
+ data = require('fs').readFileSync(__dirname + '/../test/support/ubuntu.torrent');
+
+var suite = new Benchmark.Suite;
+
+suite.add("bencoding#decode", function () {
+ bencoding.decode(data);
+});
+suite.add("bencode#decode", function () {
+ bencode.decode(data.toString());
+});
+suite.add("bncode#decode", function () {
+ bncode.decode(data);
+});
+suite.add("dht-bencode#decode", function () {
+ dht.bdecode(data.toString());
+});
+
+suite.on('cycle', function (event, bench) {
+ console.log(String(bench));
+});
+suite.on('complete', function (event, bench) {
+ console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+});
+
+suite.run();
View
35 performance/encoding.js
@@ -0,0 +1,35 @@
+var Benchmark = require('benchmark'),
+ bencoding = require('../lib/bencoding'),
+ bencode = require('bencode'),
+ bncode = require('bncode'),
+ dhtBencode = require('dht-bencode'),
+
+ data = {
+ int: 12345678,
+ str: "hello world",
+ list: [1, 2, 3, 4, 5]
+ };
+
+var suite = new Benchmark.Suite;
+
+suite.add("bencoding#encode", function () {
+ bencoding.encode(data);
+});
+suite.add("bencode#encode", function () {
+ bencode.encode(data.toString());
+});
+suite.add("bncode#encode", function () {
+ bncode.encode(data);
+});
+suite.add("dht-bencode#encode", function () {
+ dhtBencode.bencode(data.toString());
+});
+
+suite.on('cycle', function (event, bench) {
+ console.log(String(bench));
+});
+suite.on('complete', function (event, bench) {
+ console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+});
+
+suite.run();
View
68 test/bdecode.test.js
@@ -0,0 +1,68 @@
+var expect = require('expect.js'),
+ assert = require('assert'),
+ crypto = require('crypto'),
+ fs = require('fs'),
+ bencoding = require('../lib/bencoding');
+
+function fixture(name) {
+ return fs.readFileSync(__dirname + "/support/" + name);
+};
+
+function shasum(val) {
+ var hasher = crypto.createHash('sha1');
+ hasher.update(val);
+ return new Buffer(hasher.digest('binary'), 'binary');
+};
+
+describe("Decoding", function () {
+ it("can decode simple.txt", function (done) {
+ var data = fixture('simple.txt'),
+ res = bencoding.decode(data).toJSON();
+ expect(res).to.be.a('object');
+ expect(res).to.have.keys('int', 'str', 'list');
+ expect(res.int).to.be.a('number');
+ expect(+res.int.toString()).to.be(1024768);
+ expect(res.str).to.be.a(Buffer);
+ assert.deepEqual(res.str, new Buffer('abcde'));
+ expect(res.list).to.be.an(Array);
+ expect(res.list).to.have.length(3);
+ expect(+res.list[0].toString()).to.be(1);
+ expect(+res.list[1].toString()).to.be(2);
+ expect(+res.list[2].toString()).to.be(3);
+ done();
+ });
+ it("can decode a simple torrent", function (done) {
+ var data = fixture('ubuntu.torrent'),
+ res = bencoding.decode(data).toJSON();
+ done();
+ });
+ it("Can encode simply", function (done) {
+ var data = {
+ int: 1024768,
+ str: 'abcde',
+ list: [1, 2, 3]
+ }, res = bencoding.encode(data);
+
+ assert.deepEqual(res.toString(), fixture('simple.txt').toString().trim());
+ done();
+ });
+ it("Can decode and re-encode", function (done) {
+ var data = fixture('ubuntu.torrent'),
+ res = bencoding.decode(data),
+ n = bencoding.encode(res);
+
+ assert.deepEqual(n, data);
+ done();
+ });
+ it("Will yell if the data is malformed", function (done) {
+ var data = fixture('bad.txt');
+
+ expect(function () {
+ bencoding.decode(data);
+ }).to.throwError(function (e) {
+ console.log(e);
+ });
+ done();
+ });
+ it("can decode a http tracker response");
+});
View
1 test/support/bad.txt
@@ -0,0 +1 @@
+d5:hi
View
1 test/support/simple.txt
@@ -0,0 +1 @@
+d3:inti1024768e3:str5:abcde4:listli1ei2ei3eee
View
BIN test/support/ubuntu.torrent
Binary file not shown.

0 comments on commit 3eaf5a9

Please sign in to comment.
Something went wrong with that request. Please try again.