diff --git a/bncode.js b/bncode.js index e1cdfb4..759f106 100644 --- a/bncode.js +++ b/bncode.js @@ -1,16 +1,16 @@ /*jshint es5:false, asi:true, quotmark:false, eqeqeq:false, forin: false */ + /* - * (c) 2011 Tim Becker, see file LICENSE for details + * (c) 2011-14 Tim Becker, see file LICENSE for details */ /* * Provides functionality for bencoding and decoding as use in - * bittorrent and described in: - * http://www.bittorrent.org/beps/bep_0003.html + * bittorrent and described in: http://www.bittorrent.org/beps/bep_0003.html * * Encoding is as follows: * - * var benc = require("bncode"), + * var benc = require('bncode'), * exmp = {} * * exmp.bla = "blup" @@ -63,15 +63,20 @@ * */ -var Transform = require('stream').Transform; +exports.encode = Bencode +exports.decoder = Bdecode +exports.decode = decode +exports.Stream = Stream + var inherits = require('util').inherits +var Transform = require('stream').Transform -var I = "i".charCodeAt(0) -var L = "l".charCodeAt(0) -var E = "e".charCodeAt(0) -var D = "d".charCodeAt(0) -var COLON = ":".charCodeAt(0) -var DASH = "-".charCodeAt(0) +var I = 'i'.charCodeAt(0) +var L = 'l'.charCodeAt(0) +var E = 'e'.charCodeAt(0) +var D = 'd'.charCodeAt(0) +var COLON = ':'.charCodeAt(0) +var DASH = '-'.charCodeAt(0) var STATE_INITIAL = 0 var STATE_STATE_STRING_LEN = STATE_INITIAL + 1 @@ -80,9 +85,6 @@ var STATE_COLON = STATE_STRING + 1 var STATE_STATE_INTEGER = STATE_COLON + 1 var STATE_INTEGER = STATE_STATE_INTEGER + 1 - -function log (m) {console.log(m); process.stdout.flush()} - /* * This is the internal state machine for taking apart bencoded strings, * it's not exposed in the eports. It's constructed with four callbacks @@ -104,34 +106,28 @@ function log (m) {console.log(m); process.stdout.flush()} * */ -var BdecodeSMachine = function (cb, cb_list, cb_dict, cb_end) { - var depth = 0 - var state = STATE_INITIAL - - var cb = cb - var cb_list = cb_list - var cb_dict = cb_dict - var cb_end = cb_end +function BdecodeSMachine (cb, cb_list, cb_dict, cb_end) { + var depth = 0 + var state = STATE_INITIAL - this.consistent = function() { - return (state === STATE_INITIAL && depth === 0) + this.consistent = function () { + return state === STATE_INITIAL && depth === 0 } var strLen = 0 - var str = "" + var str = '' var _int = 0 var neg = false this.parse = function (buffer, encoding) { - encoding = encoding ? encoding : "utf8" - if ("string" === typeof(buffer)) { - buffer = new Buffer(buffer, encoding) + if (typeof buffer === 'string') { + buffer = new Buffer(buffer, encoding || 'utf8') } - for (var pos = 0; pos != buffer.length; ++pos) { - switch(state) { + for (var pos = 0; pos !== buffer.length; ++pos) { + switch (state) { case STATE_INITIAL: - switch(buffer[pos]){ + switch (buffer[pos]) { case 0x30: case 0x31: case 0x32: @@ -165,13 +161,13 @@ var BdecodeSMachine = function (cb, cb_list, cb_dict, cb_end) { state = STATE_INITIAL depth -= 1 if (depth < 0) { - throw new Error("end with no beginning: "+pos) + throw new Error('end with no beginning: ' + pos) } else { cb_end() } break } - break; + break case STATE_STATE_STRING_LEN: if (integer(buffer[pos])) { strLen *= 10 @@ -183,8 +179,8 @@ var BdecodeSMachine = function (cb, cb_list, cb_dict, cb_end) { } break case STATE_COLON: - if (buffer[pos] != COLON) { - throw new Error("not a colon at:"+pos.toString(16)) + if (buffer[pos] !== COLON) { + throw new Error('not a colon at: ' + pos.toString(16)) } state = STATE_STRING // in case this is a zero length string, there's @@ -210,7 +206,7 @@ var BdecodeSMachine = function (cb, cb_list, cb_dict, cb_end) { break case STATE_STATE_INTEGER: state = STATE_INTEGER - if (buffer[pos] == DASH) { + if (buffer[pos] === DASH) { neg = true // handle neg and zero within value. break } // else fall through @@ -218,84 +214,82 @@ var BdecodeSMachine = function (cb, cb_list, cb_dict, cb_end) { if (integer(buffer[pos])) { _int *= 10 _int += buffer[pos] - 0x30 - } else if (buffer[pos] == E) { + } else if (buffer[pos] === E) { var ret = neg ? 0 - _int : _int cb(ret) state = STATE_INITIAL } else { - throw new Error("not part of int at:"+pos.toString(16)) + throw new Error('not part of int at:'+pos.toString(16)) } - break; + break } // switch state } // for buffer } // function parse - - var integer = function (value) { + function integer (value) { // check that value is a number and that // its value is ascii integer. - if (! (typeof(value)==='number') ) { + if (typeof value !== 'number') { return false } return between(value, 0x30, 0x39) } - var between = function(val, min, max) { + function between (val, min, max) { return (min <= val && val <= max) } - } // end BdecodeSMachine /* * The exported decode functionality. */ -var Bdecode = function () { +function Bdecode () { // markers var DICTIONARY_START = {} var LIST_START = {} - var Context = function() { + var Context = function () { var self = this var stack = [] - this.cb = function(o){ + this.cb = function (o) { stack.push(o) } - this.cb_list = function(){ + this.cb_list = function () { self.cb(LIST_START) } - this.cb_dict = function(){ + this.cb_dict = function () { self.cb(DICTIONARY_START) } - this.cb_end = function(){ + this.cb_end = function () { // unwind the stack until either a DICTIONARY_START or LIST_START is // found, create arr or hash, stick unwound stack on, push arr or hash // back onto stack - var obj = null, - tmp_stack = [] + var obj = null + var tmp_stack = [] - while ( undefined !== (obj = stack.pop()) ) { + while ((obj = stack.pop()) !== undefined) { if (LIST_START === obj) { var obj2 = null var list = [] - while( undefined !== (obj2 = tmp_stack.pop()) ) { + while((obj2 = tmp_stack.pop()) !== undefined) { list.push(obj2) } self.cb(list) break } else if (DICTIONARY_START === obj) { - var key = null, - val = null, - dic = {} - while ( (undefined !== (key = tmp_stack.pop())) && (undefined !== (val = tmp_stack.pop())) ) { + var key = null + var val = null + var dic = {} + while ((key = tmp_stack.pop()) !== undefined && (val = tmp_stack.pop()) !== undefined) { dic[key.toString()] = val } - if (undefined !== key && undefined === dic[key]) { - throw new Error("uneven number of keys and values A") + if (key !== undefined && dic[key] === undefined) { + throw new Error('uneven number of keys and values A') } self.cb(dic) break @@ -305,7 +299,7 @@ var Bdecode = function () { } if (tmp_stack.length > 0) { // could this case even occur? - throw new Error("uneven number of keys and values B") + throw new Error('uneven number of keys and values B') } } this.result = function () { @@ -313,90 +307,88 @@ var Bdecode = function () { } } - var self = this, - ctx = new Context(), - smachine = new BdecodeSMachine(ctx.cb, ctx.cb_list, ctx.cb_dict, ctx.cb_end) + var self = this + var ctx = new Context() + var smachine = new BdecodeSMachine(ctx.cb, ctx.cb_list, ctx.cb_dict, ctx.cb_end) - this.result = function () { + this.result = function () { if (!smachine.consistent()) { - throw new Error("not in consistent state. More bytes coming?") + throw new Error('not in consistent state. More bytes coming?') } return ctx.result() } - this.decode = function(buf, encoding) { + this.decode = function (buf, encoding) { smachine.parse(buf, encoding) } } -var Bencode = function(obj) { +function Bencode (obj) { var self = this var to_encode = obj var buffer = null - switch (typeof (obj) ) { - case "string": + switch (typeof obj) { + case 'string': return encodeString(obj) - case "number": + case 'number': return encodeNumber(obj) - break - case "object": + case 'object': if (obj instanceof Array) { return encodeList(obj) } else if (Buffer.isBuffer(obj)) { return encodeBuffer(obj) - } - - { + } else { // assume it's a hash return encodeDict(obj) } } - function encodeString(obj) { - var blen = Buffer.byteLength(obj), - len = blen.toString(10), - buf = new Buffer(len.length + 1 + blen) + function encodeString (obj) { + var blen = Buffer.byteLength(obj) + var len = blen.toString(10) + var buf = new Buffer(len.length + 1 + blen) - buf.write(len, 0, "ascii") - buf.write(":", len.length, "ascii") - buf.write(obj, len.length+1, "utf8") + buf.write(len, 0, 'ascii') + buf.write(':', len.length, 'ascii') + buf.write(obj, len.length + 1, 'utf8') return buf } - function encodeNumber(num) { - var n = num.toString(10), - buf = new Buffer(n.length+2) + function encodeNumber (num) { + var n = num.toString(10) + var buf = new Buffer(n.length + 2) - buf.write("i", 0) + buf.write('i', 0) buf.write(n, 1) - buf.write("e", n.length+1) + buf.write('e', n.length + 1) return buf } - function encodeDict(obj) { + function encodeDict (obj) { var func = function (obj, pos) { var keys = Object.keys(obj).sort() - for (var i in keys) { - var key = Bencode(keys[i]), - val = Bencode(obj[keys[i]]) + keys.forEach(function (key) { + var val = new Bencode(obj[key]) + key = new Bencode(key) + ensure(key.length + val.length, pos) key.copy(buffer, pos, 0) pos += key.length val.copy(buffer, pos, 0) pos += val.length - } + }) return pos } - return assemble(obj, "d", func) + return assemble(obj, 'd', func) } - function encodeList(obj) { + function encodeList (obj) { var func = function(obj, pos) { - obj.forEach (function(o){ - var elem = Bencode(o) + obj.forEach(function (o) { + var elem = new Bencode(o) ensure(elem.length, pos) elem.copy(buffer, pos, 0) @@ -404,16 +396,16 @@ var Bencode = function(obj) { }) return pos } - return assemble(obj, "l", func) + return assemble(obj, 'l', func) } - function encodeBuffer(obj) { - var len = obj.length.toString(10), - buf = new Buffer(len.length+1+obj.length); + function encodeBuffer (obj) { + var len = obj.length.toString(10) + var buf = new Buffer(len.length + 1 + obj.length) - buf.write(len, 0, "ascii") - buf.write(":", len.length, "ascii") - obj.copy(buf, len.length+1, 0) + buf.write(len, 0, 'ascii') + buf.write(':', len.length, 'ascii') + obj.copy(buf, len.length + 1, 0) return buf } @@ -427,7 +419,7 @@ var Bencode = function(obj) { pos = func(obj, pos) ensure(1, pos) - buffer.write("e", pos++) + buffer.write('e', pos++) return buffer.slice(0, pos) } @@ -435,7 +427,7 @@ var Bencode = function(obj) { if (!buffer) { buffer = new Buffer(num) } else { - if (buffer.length > num+pos+1) { + if (buffer.length > num + pos + 1) { return } else { var buf2 = new Buffer(buffer.length + num) @@ -454,30 +446,24 @@ function decode (buffer, encoding) { } function Stream (options) { - options = options || {}; - options.objectMode = true; - Transform.call(this, options); + options = options || {} + options.objectMode = true + Transform.call(this, options) this._decoder = new Bdecode() } -inherits(Stream, Transform); +inherits(Stream, Transform) Stream.prototype._transform = function (chunk, encoding, callback) { try { this._decoder.decode(chunk, encoding) - callback(null); + callback(null) } catch(err) { - callback(err); + callback(err) } } Stream.prototype._flush = function (callback) { - this.push(this._decoder.result()[0]); - callback(null); + this.push(this._decoder.result()[0]) + callback(null) } - -exports.encode = Bencode -exports.decoder = Bdecode -exports.decode = decode -exports.Stream = Stream -