Sorry, the diff of this file is not supported yet
| var Benchmark = require('benchmark') | ||
| var bencode = require('../') | ||
| var buf = require('fs').readFileSync(__dirname + '/test.torrent'); | ||
| var str = buf.toString(); | ||
| var decoding = new Benchmark.Suite({ | ||
| maxTime: 10 | ||
| }) | ||
| .add('bencode buffer', function() { | ||
| bencode.decode(buf); | ||
| }) | ||
| .add('bencode string', function() { | ||
| bencode.decode(str); | ||
| }) | ||
| .on( 'cycle', function ( event ) { | ||
| console.log( event.target.toString() ) | ||
| }) | ||
| .on( 'complete', function ( event, bench ) { | ||
| console.log( | ||
| '\nFastest is ' + this.filter( 'fastest' ).pluck( 'name' ) + '\n\n' | ||
| ) | ||
| }) | ||
| .on('error', function() { | ||
| console.log(arguments) | ||
| }) | ||
| .run() |
+29
| ## 0.4.3 | ||
| * improved performance a lot | ||
| * dropped support for de- and encoding floats to respect the spec | ||
| *note:* node-bencode will still decodes stuff like "i42.23e" but will cast the | ||
| result to an interger | ||
| ## 0.4.2 | ||
| * bugfix: sort dictionary keys to follow the spec | ||
| ## 0.4.1 | ||
| * bugfix: number decoding was kinda broken | ||
| ## 0.4.0 | ||
| * fixed problems with multibyte strings | ||
| * some performance improvements | ||
| * improved code quality | ||
| ## 0.3.0 | ||
| * #decode() accepts a encoding as its second paramtere | ||
| ## 0.2.0 | ||
| * complete rewrite, @jhermsmeier joins the team | ||
| ## 0.1.0 | ||
| * added encoding | ||
| ## 0.0.1 | ||
| First version, decoding only |
| "use strict"; | ||
| var util = require('util') | ||
| , events = require('events') | ||
| , microtime = require('microtime') | ||
| var Benchmark = { | ||
| Test: require('./test.js') | ||
| , Suite: require('./suite.js') | ||
| } | ||
| module.exports = Benchmark; |
| "use strict"; | ||
| var util = require('util') | ||
| , events = require('events') | ||
| , Test = require('./test.js') | ||
| var Suite = function(options) | ||
| { | ||
| if(!(this instanceof Suite)) { | ||
| return new Suite(options) | ||
| } | ||
| events.EventEmitter.call(this) | ||
| this.options = options || {} | ||
| this.tests = [] | ||
| } | ||
| util.inherits(Suite, events.EventEmitter) | ||
| Suite.prototype.add = function(name, test, options) | ||
| { | ||
| this.tests.push( new Test(name, test, options, this) ) | ||
| return this | ||
| } | ||
| Suite.prototype.run = function(options) | ||
| { | ||
| var tl = this.tests.length | ||
| var results = {} | ||
| for(var a=0;a<5;a++) { | ||
| for(var i=0;i<tl;i++) { | ||
| var test = this.tests[i] | ||
| var samples = test.run(options) | ||
| if(!results[test.name]) { | ||
| results[test.name] = samples | ||
| } | ||
| else { | ||
| for(var j=0;j<samples.length;j++) { | ||
| results[test.name].push(samples[j]) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| var stats = {} | ||
| for(var i=0;i<tl;i++) { | ||
| var test = this.tests[i] | ||
| var s = results[test.name] | ||
| stats[test.name] = this.doMathStuff(s) | ||
| } | ||
| console.log(stats) | ||
| } | ||
| Suite.prototype.doMathStuff = function(samples) { | ||
| var l = samples.length | ||
| var time = 0, runs = 0 | ||
| var maxOPS = 0 | ||
| , minOPS = Infinity | ||
| for(var i=0; i < l ; i++ ) { | ||
| time += samples[i][0] | ||
| runs += samples[i][1] | ||
| maxOPS = Math.max(samples[i][2], maxOPS) | ||
| minOPS = Math.min(samples[i][2], minOPS) | ||
| } | ||
| return { | ||
| avg: runs/time * 1e9, | ||
| best: maxOPS, | ||
| worst: minOPS | ||
| } | ||
| } | ||
| module.exports = Suite | ||
| exports.merge_objs = function() { | ||
| var result = {} | ||
| for( var i = 0 ; i < arguments.length ; i++ ) { | ||
| var obj = arguments[i] | ||
| if(typeof obj !== 'object') { | ||
| continue | ||
| } | ||
| var keys = Object.getOwnPropertyNames(obj) | ||
| var keyl = keys.length | ||
| for( var j = 0 ; j < keyl ; j++ ) { | ||
| var key = keys[j] | ||
| result[key] = obj[key] | ||
| } | ||
| } | ||
| return result | ||
| } | ||
| exports.time = function() { | ||
| var t = process.hrtime() | ||
| return t[0]*1e9 + t[1] | ||
| } |
+110
| /** | ||
| * Decodes bencoded data. | ||
| * | ||
| * @param {Buffer} data | ||
| * @param {String} encoding | ||
| * @return {Object|Array|Buffer|String|Number} | ||
| */ | ||
| function decode( data, encoding ) { | ||
| decode.position = 0 | ||
| decode.encoding = encoding || null | ||
| decode.data = !( data instanceof Buffer ) | ||
| ? new Buffer( data ) | ||
| : data | ||
| return decode.next() | ||
| } | ||
| decode.position = 0 | ||
| decode.data = null | ||
| decode.encoding = null | ||
| decode.next = function() { | ||
| switch( decode.data[decode.position] ) { | ||
| case 0x64: return decode.dictionary(); break | ||
| case 0x6C: return decode.list(); break | ||
| case 0x69: return decode.integer(); break | ||
| default: return decode.bytes(); break | ||
| } | ||
| } | ||
| decode.find = function( chr ) { | ||
| var i = decode.position | ||
| var c = decode.data.length | ||
| var d = decode.data | ||
| while( i < c ) { | ||
| if( d[i] === chr ) | ||
| return i | ||
| i++ | ||
| } | ||
| return -1 | ||
| } | ||
| decode.dictionary = function() { | ||
| decode.position++ | ||
| var dict = {} | ||
| while( decode.data[decode.position] !== 0x65 ) { | ||
| dict[ decode.bytes() ] = decode.next() | ||
| } | ||
| decode.position++ | ||
| return dict | ||
| } | ||
| decode.list = function() { | ||
| decode.position++ | ||
| var lst = [] | ||
| while( decode.data[decode.position] !== 0x65 ) { | ||
| lst.push( decode.next() ) | ||
| } | ||
| decode.position++ | ||
| return lst | ||
| } | ||
| decode.integer = function() { | ||
| var end = decode.find( 0x65 ) | ||
| var number = decode.data.toString('ascii', decode.position+1, end ) | ||
| decode.position += end + 1 - decode.position | ||
| return parseInt( number ) | ||
| } | ||
| decode.bytes = function() { | ||
| var sep = decode.find( 0x3A ) | ||
| var length = parseInt(decode.data.toString('ascii', decode.position, sep ), 10) | ||
| var end = ++sep + length | ||
| decode.position += end - decode.position | ||
| return decode.encoding | ||
| ? decode.data.toString(decode.encoding, sep, end ) | ||
| : decode.data.slice( sep, end ) | ||
| } | ||
| // Expose | ||
| module.exports = decode |
| /** | ||
| * Encodes data in bencode. | ||
| * | ||
| * @param {Buffer|Array|String|Object|Number} data | ||
| * @return {Buffer} | ||
| */ | ||
| function encode( data ) { | ||
| var buffers = [] | ||
| encode._encode( buffers, data) | ||
| return Buffer.concat(buffers) | ||
| } | ||
| encode._encode = function( buffers, data ) { | ||
| if( data instanceof Buffer ) { | ||
| buffers.push(new Buffer(data.length + ':')) | ||
| buffers.push(data) | ||
| return; | ||
| } | ||
| switch( typeof data ) { | ||
| case 'string': | ||
| encode.bytes( buffers, data ) | ||
| break | ||
| case 'number': | ||
| encode.number( buffers, data ) | ||
| break | ||
| case 'object': | ||
| data.constructor === Array | ||
| ? encode.list( buffers, data ) | ||
| : encode.dict( buffers, data ) | ||
| break | ||
| } | ||
| } | ||
| var buff_e = new Buffer('e') | ||
| , buff_d = new Buffer('d') | ||
| , buff_l = new Buffer('l') | ||
| encode.bytes = function( buffers, data ) { | ||
| buffers.push( new Buffer(Buffer.byteLength( data ) + ':' + data) ) | ||
| } | ||
| encode.number = function( buffers, data ) { | ||
| buffers.push( new Buffer( 'i' + ( data << 0 ) + 'e' ) ) | ||
| } | ||
| encode.dict = function( buffers, data ) { | ||
| buffers.push( buff_d ) | ||
| var j = 0 | ||
| var k | ||
| // fix for issue #13 - sorted dicts | ||
| var keys = Object.keys(data).sort() | ||
| var kl = keys.length | ||
| for( ; j < kl ; j++) { | ||
| k=keys[j] | ||
| encode.bytes( buffers, k ) | ||
| encode._encode( buffers, data[k] ) | ||
| } | ||
| buffers.push( buff_e ) | ||
| } | ||
| encode.list = function( buffers, data ) { | ||
| var i = 0, j = 1 | ||
| var c = data.length | ||
| buffers.push( buff_l ) | ||
| for( ; i < c; i++ ) { | ||
| encode._encode( buffers, data[i] ) | ||
| } | ||
| buffers.push( buff_e ) | ||
| } | ||
| // Expose | ||
| module.exports = encode |
| module.exports = { | ||
| binKeyData: new Buffer("ZDU6ZmlsZXNkMjA6N7VVuuCjmp5LoM+n15a5iM/XJHdkODpjb21wbGV0ZWkwZTEwOmRvd25sb2FkZWRpMTBlMTA6aW5jb21wbGV0ZWkwZWVlZQ==", 'base64') | ||
| , binKeyName: (new Buffer("N++/vVXvv73go5rvv71L77+9z6fXlu+/ve+/ve+/ve+/vSR3", 'base64')).toString() | ||
| } |
| var assert = require('assert') | ||
| var bencode = require('./lib.js') | ||
| var data = require('./data.js') | ||
| describe("bencode", function() { | ||
| describe("#decode(x)", function() { | ||
| it('should be able to decode an integer', function() { | ||
| assert.deepEqual(bencode.decode('i123e'), 123); | ||
| assert.deepEqual(bencode.decode('i-123e', 'utf8'), -123); | ||
| }); | ||
| it('should be able to decode a float (as int)', function() { | ||
| assert.deepEqual(bencode.decode('i12.3e'), 12); | ||
| assert.deepEqual(bencode.decode('i-12.3e'), -12); | ||
| }); | ||
| it('should be able to decode a string', function() { | ||
| assert.deepEqual(bencode.decode('5:asdfe'), new Buffer('asdfe')); | ||
| assert.deepEqual(bencode.decode('4:öö'), new Buffer('öö')); | ||
| }); | ||
| it('should be able to decode "binary keys"', function() { | ||
| assert.ok(bencode.decode(data.binKeyData).files.hasOwnProperty(data.binKeyName)); | ||
| }); | ||
| it('should be able to decode a dictionary', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'd3:cow3:moo4:spam4:eggse' ), | ||
| { | ||
| cow: new Buffer('moo'), | ||
| spam: new Buffer('eggs') | ||
| } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd4:spaml1:a1:bee' ), | ||
| { spam: [ | ||
| new Buffer('a'), | ||
| new Buffer('b') | ||
| ] } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee'), | ||
| { | ||
| 'publisher': new Buffer('bob'), | ||
| 'publisher-webpage': new Buffer('www.example.com'), | ||
| 'publisher.location': new Buffer('home') | ||
| } | ||
| ) | ||
| }); | ||
| it('should be able to decode a list', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'l4:spam4:eggse'), | ||
| [ new Buffer('spam'), | ||
| new Buffer('eggs') ] | ||
| ) | ||
| }); | ||
| it('should return the correct type', function() { | ||
| assert.ok(bencode.decode('4:öö') instanceof Buffer); | ||
| }); | ||
| it('should be able to decode stuff in dicts (issue #12)', function() { | ||
| var someData = { | ||
| string: 'Hello World', | ||
| integer: 12345, | ||
| dict: { | ||
| key: 'This is a string within a dictionary' | ||
| }, | ||
| list: [ 1, 2, 3, 4, 'string', 5, {} ] | ||
| } | ||
| var result = bencode.encode( someData ) | ||
| var dat = bencode.decode ( result ) | ||
| assert.equal(dat.integer, 12345) | ||
| assert.deepEqual(dat.string, new Buffer("Hello World")) | ||
| assert.deepEqual(dat.dict.key, new Buffer("This is a string within a dictionary")) | ||
| assert.deepEqual(dat.list, [1, 2, 3, 4, new Buffer('string'), 5, {}]) | ||
| }); | ||
| }); | ||
| }); |
| var assert = require('assert') | ||
| var bencode = require('./lib.js') | ||
| var data = require('./data.js') | ||
| describe("bencode", function() { | ||
| describe("#decode(x, 'uft8')", function() { | ||
| it('should be able to decode an integer', function() { | ||
| assert.deepEqual(bencode.decode('i123e', 'utf8'), 123); | ||
| assert.deepEqual(bencode.decode('i-123e', 'utf8'), -123); | ||
| }); | ||
| it('should be able to decode a float (as int)', function() { | ||
| assert.deepEqual(bencode.decode('i12.3e', 'utf8'), 12); | ||
| assert.deepEqual(bencode.decode('i-12.3e', 'utf8'), -12); | ||
| }); | ||
| it('should be able to decode a string', function() { | ||
| assert.deepEqual(bencode.decode('5:asdfe', 'utf8'), 'asdfe'); | ||
| assert.deepEqual(bencode.decode('4:öö', 'utf8'), 'öö'); | ||
| }); | ||
| it('should be able to decode "binary keys"', function() { | ||
| var decoded = bencode.decode(data.binKeyData, 'utf8') | ||
| assert.ok(decoded.files.hasOwnProperty(data.binKeyName)); | ||
| }); | ||
| it('should be able to decode a dictionary', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'd3:cow3:moo4:spam4:eggse', 'utf8' ), | ||
| { | ||
| cow: 'moo', | ||
| spam: 'eggs' | ||
| } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd4:spaml1:a1:bee', 'utf8' ), | ||
| { spam: [ 'a', 'b' ] } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee', 'utf8' ), | ||
| { | ||
| 'publisher': 'bob', | ||
| 'publisher-webpage': 'www.example.com', | ||
| 'publisher.location': 'home' | ||
| } | ||
| ) | ||
| }); | ||
| it('should be able to decode a list', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'l4:spam4:eggse', 'utf8' ), | ||
| [ 'spam', 'eggs' ] | ||
| ) | ||
| }); | ||
| it('should return the correct type', function() { | ||
| assert.ok(typeof(bencode.decode('4:öö', 'utf8')) === 'string'); | ||
| }); | ||
| it('should be able to decode stuff in dicts (issue #12)', function() { | ||
| var someData = { | ||
| string: 'Hello World', | ||
| integer: 12345, | ||
| dict: { | ||
| key: 'This is a string within a dictionary' | ||
| }, | ||
| list: [ 1, 2, 3, 4, 'string', 5, {} ] | ||
| } | ||
| var result = bencode.encode( someData ) | ||
| var dat = bencode.decode ( result, 'utf8' ) | ||
| assert.equal(dat.integer, 12345) | ||
| assert.deepEqual(dat.string, "Hello World") | ||
| assert.deepEqual(dat.dict.key, "This is a string within a dictionary") | ||
| assert.deepEqual(dat.list, [1, 2, 3, 4, 'string', 5, {}]) | ||
| }); | ||
| }); | ||
| }); |
| var assert = require("assert"); | ||
| var bencode = require('./lib.js'); | ||
| describe("bencode", function() { | ||
| describe("#encode()", function() { | ||
| it('should always return a Buffer', function() { | ||
| assert.ok(Buffer.isBuffer(bencode.encode({})), "its not a buffer for empty dicts"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode("test")), "its not a buffer for strings"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode([3, 2])), "its not a buffer for lists"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode({"a": "b", 3: 6})), "its not a buffer for big dicts"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode(123)), "its not a buffer for numbers"); | ||
| }); | ||
| it('should sort dictionories', function() { | ||
| var data = { | ||
| string: 'Hello World', | ||
| integer: 12345, | ||
| }; | ||
| assert.equal(bencode.encode(data).toString(), "d7:integeri12345e6:string11:Hello Worlde"); | ||
| }) | ||
| it('should force keys to be strings', function() { | ||
| var data = { | ||
| 12: 'Hello World', | ||
| 34: 12345, | ||
| }; | ||
| assert.equal(bencode.encode(data).toString(), "d2:1211:Hello World2:34i12345ee") | ||
| }) | ||
| it('should be able to encode a positive integer', function() { | ||
| assert.equal(bencode.encode(123), 'i123e'); | ||
| }) | ||
| it('should be able to encode a negative integer', function() { | ||
| assert.equal(bencode.encode(-123), 'i-123e'); | ||
| }) | ||
| it('should be able to encode a positive float (as int)', function() { | ||
| assert.equal(bencode.encode(123.5), 'i123e'); | ||
| }) | ||
| it('should be able to encode a negative float (as int)', function() { | ||
| assert.equal(bencode.encode(-123.5), 'i-123e'); | ||
| }) | ||
| it('should be able to encode a string', function() { | ||
| assert.equal(bencode.encode("asdf"), '4:asdf'); | ||
| assert.equal(bencode.encode(":asdf:"), '6::asdf:'); | ||
| }) | ||
| it('should be able to encode a unicode string', function() { | ||
| assert.equal(bencode.encode("ö±sdf"), '7:ö±sdf'); | ||
| assert.equal(bencode.encode(new Buffer("ö±sdf")), '7:ö±sdf'); | ||
| }) | ||
| it('should be able to encode a buffer', function() { | ||
| assert.equal(bencode.encode(new Buffer("asdf")), '4:asdf'); | ||
| assert.equal(bencode.encode(new Buffer(":asdf:")), '6::asdf:'); | ||
| }) | ||
| it('should be able to encode an array', function() { | ||
| assert.equal(bencode.encode([32, 12]), 'li32ei12ee'); | ||
| assert.equal(bencode.encode([":asdf:"]), 'l6::asdf:e'); | ||
| }) | ||
| it('should be able to encode an object', function() { | ||
| assert.equal(bencode.encode({"a": "bc"}), 'd1:a2:bce') | ||
| assert.equal(bencode.encode({"a": "45", "b": 45}), 'd1:a2:451:bi45ee') | ||
| assert.equal(bencode.encode({"a": new Buffer("bc")}), 'd1:a2:bce') | ||
| }) | ||
| }) | ||
| }); |
| module.exports = require('../bencode.js'); | ||
| //module.exports.decode = module.exports.bdecode | ||
| //module.exports.encode = module.exports.bencode |
Sorry, the diff of this file is not supported yet
| _Strings_ are length-prefixed base ten followed by a colon and the string. For example 4:spam corresponds to 'spam'. | ||
| _Integers_ are represented by an 'i' followed by the number in base 10 followed by an 'e'. For example i3e corresponds to 3 and i-3e corresponds to -3. Integers have no size limitation. i-0e is invalid. All encodings with a leading zero, such as i03e, are invalid, other than i0e, which of course corresponds to 0. | ||
| _Lists_ are encoded as an 'l' followed by their elements (also bencoded) followed by an 'e'. For example l4:spam4:eggse corresponds to ['spam', 'eggs']. | ||
| _Dictionaries_ are encoded as a 'd' followed by a list of alternating keys and their corresponding values followed by an 'e'. For example, d3:cow3:moo4:spam4:eggse corresponds to {'cow': 'moo', 'spam': 'eggs'} and d4:spaml1:a1:bee corresponds to {'spam': ['a', 'b']}. | ||
| Keys must be strings and appear in sorted order (sorted as raw strings, not alphanumerics). |
+15
-8
| var Benchmark = require( 'benchmark' ) | ||
| var Benchmark = require( '../lib/benchmark' ) | ||
| var fs = require( 'fs' ) | ||
@@ -13,8 +13,13 @@ | ||
| var object = bencode.decode( buffer, 'ascii' ) | ||
| var doEncode = true, | ||
| doDecode = true; | ||
| if(process.argv.length > 2) { | ||
| doEncode = (process.argv.indexOf('encode') !== -1); | ||
| doDecode = (process.argv.indexOf('decode') !== -1); | ||
| } | ||
| // //////////////////////////////////////////////////////////////////////////// | ||
| if(doEncode) { | ||
| console.log( 'ENCODING\n' ) | ||
| var encoding = new Benchmark.Suite() | ||
| .add( 'bencode', function () { | ||
@@ -35,3 +40,3 @@ bencode.encode( object ) | ||
| }) | ||
| .on( 'cycle', function ( event ) { | ||
@@ -46,8 +51,9 @@ console.log( event.target.toString() ) | ||
| .run() | ||
| } | ||
| // //////////////////////////////////////////////////////////////////////////// | ||
| if(doDecode) { | ||
| console.log( 'DECODING\n' ) | ||
| var decoding = new Benchmark.Suite() | ||
| .add( 'bencode', function () { | ||
@@ -68,3 +74,3 @@ bencode.decode( buffer ) | ||
| }) | ||
| .on( 'cycle', function ( event, bench ) { | ||
@@ -79,1 +85,2 @@ console.log( event.target.toString() ) | ||
| .run() | ||
| } |
+3
-194
@@ -1,195 +0,4 @@ | ||
| /** | ||
| * Encodes data in bencode. | ||
| * | ||
| * @param {Buffer|Array|String|Object|Number} data | ||
| * @return {Buffer} | ||
| */ | ||
| function encode( data ) { | ||
| if( data instanceof Buffer ) { | ||
| return Buffer.concat([ | ||
| new Buffer(data.length+':'), | ||
| data | ||
| ]) | ||
| } | ||
| switch( typeof data ) { | ||
| case 'string': | ||
| return encode.bytes( data ) | ||
| break | ||
| case 'number': | ||
| return encode.number( data ) | ||
| break | ||
| case 'object': | ||
| return data.constructor === Array | ||
| ? encode.list( data ) | ||
| : encode.dict( data ) | ||
| break | ||
| } | ||
| module.exports = { | ||
| encode: require( './lib/encode' ), | ||
| decode: require( './lib/decode' ) | ||
| } | ||
| var buff_e = new Buffer('e') | ||
| , buff_d = new Buffer('d') | ||
| , buff_l = new Buffer('l') | ||
| encode.bytes = function( data ) { | ||
| return Buffer.concat([ | ||
| new Buffer(Buffer.byteLength( data )+":"), | ||
| new Buffer(data) | ||
| ]) | ||
| } | ||
| encode.number = function( data ) { | ||
| return new Buffer('i' + data + 'e') | ||
| } | ||
| encode.dict = function( data ) { | ||
| var dict = [ buff_d ] | ||
| var i = 1; | ||
| // fix for issue #13 - sorted dicts | ||
| var keys = [] | ||
| for( var k in data) { | ||
| keys.push(k) | ||
| } | ||
| keys = keys.sort() | ||
| for(var j=0;j<keys.length;j++) { | ||
| dict[i++] = encode( keys[j] ) | ||
| dict[i++] = encode( data[keys[j]] ) | ||
| } | ||
| dict[i++] = buff_e | ||
| return Buffer.concat(dict) | ||
| } | ||
| encode.list = function( data ) { | ||
| var i = 0, j = 1 | ||
| var c = data.length | ||
| var lst = [ buff_l ] | ||
| for( ; i < c; i++ ) { | ||
| lst[j++] = encode( data[i] ) | ||
| } | ||
| lst[j++] = buff_e | ||
| return Buffer.concat(lst) | ||
| } | ||
| /** | ||
| * Decodes bencoded data. | ||
| * | ||
| * @param {Buffer} data | ||
| * @param {String} encoding | ||
| * @return {Object|Array|Buffer|String|Number} | ||
| */ | ||
| function decode( data, encoding ) { | ||
| decode.position = 0 | ||
| decode.encoding = encoding || null | ||
| decode.data = !( data instanceof Buffer ) | ||
| ? new Buffer( data ) | ||
| : data | ||
| return decode.next() | ||
| } | ||
| decode.position = 0 | ||
| decode.data = null | ||
| decode.encoding = null | ||
| decode.next = function() { | ||
| switch( decode.data[decode.position] ) { | ||
| case 0x64: return decode.dictionary(); break | ||
| case 0x6C: return decode.list(); break | ||
| case 0x69: return decode.integer(); break | ||
| default: return decode.bytes(); break | ||
| } | ||
| } | ||
| decode.find = function( chr ) { | ||
| var i = decode.position | ||
| var c = decode.data.length | ||
| var d = decode.data | ||
| while( i < c ) { | ||
| if( d[i] === chr ) | ||
| return i | ||
| i++ | ||
| } | ||
| return -1 | ||
| } | ||
| decode.dictionary = function() { | ||
| decode.position++ | ||
| var dict = {} | ||
| while( decode.data[decode.position] !== 0x65 ) { | ||
| dict[ decode.next() ] = decode.next() | ||
| } | ||
| decode.position++ | ||
| return dict | ||
| } | ||
| decode.list = function() { | ||
| decode.position++ | ||
| var lst = [] | ||
| while( decode.data[decode.position] !== 0x65 ) { | ||
| lst.push( decode.next() ) | ||
| } | ||
| decode.position++ | ||
| return lst | ||
| } | ||
| decode.integer = function() { | ||
| var end = decode.find( 0x65 ) | ||
| var number = decode.data.slice( decode.position+1, end ) | ||
| decode.position += end + 1 - decode.position | ||
| return +number | ||
| } | ||
| decode.bytes = function() { | ||
| var sep = decode.find( 0x3A ) | ||
| var length = +decode.data.slice( decode.position, sep ).toString() | ||
| var sepl = ++sep + length | ||
| var bytes = decode.data.slice( sep, sepl ) | ||
| decode.position += sepl - decode.position | ||
| return decode.encoding | ||
| ? bytes.toString( decode.encoding ) | ||
| : bytes | ||
| } | ||
| // Expose methods | ||
| exports.encode = encode | ||
| exports.decode = decode |
+14
-7
| { | ||
| "name": "bencode", | ||
| "version": "0.4.2", | ||
| "version": "0.4.3", | ||
| "license": "MIT", | ||
@@ -8,8 +8,15 @@ "description": "Bencode de/encoder", | ||
| "author": { | ||
| "name": "Mark Schmale", | ||
| "email": "ma.schmale@googlemail.com", | ||
| "url": "http://masch.it/" | ||
| }, | ||
| "contributors": [ | ||
| { | ||
| "name": "Mark Schmale", | ||
| "email": "masch@masch.it", | ||
| "url": "http://masch.it/" | ||
| }, | ||
| { | ||
| "name": "Jonas Hermsmeier", | ||
| "email": "jonas@hojoki.com" | ||
| } | ||
| ], | ||
| "main": "bencode.js", | ||
@@ -26,3 +33,3 @@ | ||
| "scripts": { | ||
| "test": "./node_modules/.bin/mocha tests/*.test.js" | ||
| "test": "./node_modules/.bin/mocha" | ||
| }, | ||
@@ -29,0 +36,0 @@ |
+18
-14
| # node-bencode [](http://travis-ci.org/themasch/node-bencode) | ||
| A node library for encoding and decoding bencoded data, | ||
| A node library for encoding and decoding bencoded data, | ||
| according to the [BitTorrent specification](http://www.bittorrent.org/beps/bep_0003.html). | ||
@@ -39,18 +39,22 @@ | ||
| ### encode | ||
| ``` | ||
| bencode x 1,143 ops/sec ±2.15% (94 runs sampled) | ||
| bencoding x 1,362 ops/sec ±1.07% (92 runs sampled) | ||
| dht-bencode x 1,502 ops/sec ±1.48% (97 runs sampled) | ||
| bncode x 1,424 ops/sec ±1.89% (95 runs sampled) | ||
| dht.js x 1,277 ops/sec ±1.37% (96 runs sampled) | ||
| bencode x 7,939 ops/sec ±2.22% (74 runs sampled) | ||
| bencoding x 5,632 ops/sec ±1.47% (86 runs sampled) | ||
| dht-bencode x 6,403 ops/sec ±3.45% (87 runs sampled) | ||
| bncode x 5,106 ops/sec ±3.63% (82 runs sampled) | ||
| dht.js x 7,972 ops/sec ±2.22% (84 runs sampled) | ||
| ``` | ||
| ### decode | ||
| ``` | ||
| bencode x 29,588 ops/sec ±0.75% (99 runs sampled) | ||
| bencoding x 27,844 ops/sec ±1.97% (95 runs sampled) | ||
| dht-bencode x 22,975 ops/sec ±1.84% (90 runs sampled) | ||
| bncode x 990 ops/sec ±2.71% (88 runs sampled) | ||
| dht.js x 19,103 ops/sec ±2.14% (89 runs sampled) | ||
| ``` | ||
| bencode x 23,162 ops/sec ±0.86% (100 runs sampled) | ||
| bencoding x 30,022 ops/sec ±0.53% (98 runs sampled) | ||
| dht-bencode x 26,607 ops/sec ±0.15% (102 runs sampled) | ||
| bncode x 883 ops/sec ±1.26% (97 runs sampled) | ||
| dht.js x 20,978 ops/sec ±1.05% (99 runs sampled) | ||
| ``` | ||
| *all benchmarks from an Intel Core2Duo@2.6GHz with node v0.10.3.* | ||
| ## Usage | ||
@@ -82,3 +86,3 @@ | ||
| ``` | ||
| d6:string11:Hello World7:integeri12345e4:dictd3:key36:This is a string within a dictionarye4:litli1ei2ei3ei4e6:stringi5edeee | ||
| d4:dictd3:key36:This is a string within a dictionarye7:integeri12345e4:listli1ei2ei3ei4e6:stringi5edee6:string11:Hello Worlde | ||
| ``` | ||
@@ -135,3 +139,3 @@ | ||
| > `Buffer` __data__ | ||
| > `Buffer` __data__ | ||
| > `String` __encoding__ | ||
@@ -138,0 +142,0 @@ |
| var assert = require("assert"); | ||
| var binary_key = new Buffer("ZDU6ZmlsZXNkMjA6N7VVuuCjmp5LoM+n15a5iM/XJHdkODpjb21wbGV0ZWkwZTEwOmRvd25sb2FkZWRpMTBlMTA6aW5jb21wbGV0ZWkwZWVlZQ==", 'base64') | ||
| var keyName = (new Buffer("N++/vVXvv73go5rvv71L77+9z6fXlu+/ve+/ve+/ve+/vSR3", 'base64')).toString(); | ||
| var bencode = require('../bencode.js'); | ||
| describe("bencode", function() { | ||
| describe("#decode()", function() { | ||
| it('should be able to decode a number', function() { | ||
| assert.equal(bencode.decode('i123e'), 123); | ||
| assert.equal(bencode.decode('i123.5e'), 123.5); | ||
| }); | ||
| it('should be able to decode a string', function() { | ||
| assert.equal(bencode.decode('5:asdfe'), 'asdfe'); | ||
| assert.equal(bencode.decode('4:öö'), 'öö'); | ||
| assert.equal(bencode.decode('4:öö', 'utf8'), 'öö'); | ||
| }); | ||
| it('should be able to decode "binary keys"', function() { | ||
| assert.equal(true, bencode.decode(binary_key).files.hasOwnProperty(keyName)); | ||
| }); | ||
| it('should be able to decode a dictionary', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'd3:cow3:moo4:spam4:eggse', 'utf8' ), | ||
| { cow: 'moo', spam: 'eggs' } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd4:spaml1:a1:bee', 'utf8' ), | ||
| { spam: [ 'a', 'b' ] } | ||
| ) | ||
| assert.deepEqual( | ||
| bencode.decode( 'd9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee', 'utf8' ), | ||
| { | ||
| 'publisher': 'bob', | ||
| 'publisher-webpage': 'www.example.com', | ||
| 'publisher.location': 'home' | ||
| } | ||
| ) | ||
| }); | ||
| it('should be able to decode a list', function() { | ||
| assert.deepEqual( | ||
| bencode.decode( 'l4:spam4:eggse', 'utf8' ), | ||
| [ 'spam', 'eggs' ] | ||
| ) | ||
| }); | ||
| it('should return the correct type', function() { | ||
| assert.ok(bencode.decode('4:öö') instanceof Buffer); | ||
| assert.ok(typeof(bencode.decode('4:öö', 'utf8')) === 'string'); | ||
| }); | ||
| it('should be able to decode integers (issue #12)', function() { | ||
| var data = { | ||
| string: 'Hello World', | ||
| integer: 12345, | ||
| dict: { | ||
| key: 'This is a string within a dictionary' | ||
| }, | ||
| list: [ 1, 2, 3, 4, 'string', 5, {} ] | ||
| } | ||
| var result = bencode.encode( data ) | ||
| var dat = bencode.decode ( result , 'utf8') | ||
| assert.equal(dat.integer, 12345) | ||
| assert.deepEqual(dat.list, [1, 2, 3, 4, 'string', 5, {}]) | ||
| }); | ||
| }); | ||
| }); |
| var assert = require("assert"); | ||
| var bencode = require('../bencode.js'); | ||
| describe("bencode", function() { | ||
| describe("#encode()", function() { | ||
| it('should always return a Buffer', function() { | ||
| assert.ok(Buffer.isBuffer(bencode.encode({})), "its not a buffer for empty dicts"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode("test")), "its not a buffer for strings"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode([3, 2])), "its not a buffer for lists"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode({"a": "b", 3: 6})), "its not a buffer for big dicts"); | ||
| assert.ok(Buffer.isBuffer(bencode.encode(123)), "its not a buffer for numbers"); | ||
| }); | ||
| it('should sort dictionories', function() { | ||
| var data = { | ||
| string: 'Hello World', | ||
| integer: 12345, | ||
| }; | ||
| assert.equal(bencode.encode(data).toString(), "d7:integeri12345e6:string11:Hello Worlde"); | ||
| }) | ||
| it('should be able to encode a number', function() { | ||
| assert.equal(bencode.encode(123), 'i123e'); | ||
| assert.equal(bencode.encode(123.5), 'i123.5e'); | ||
| }) | ||
| it('should be able to encode a negative number', function() { | ||
| assert.equal(bencode.encode(-123), 'i-123e'); | ||
| assert.equal(bencode.encode(-123.5), 'i-123.5e'); | ||
| }) | ||
| it('should be able to encode a string', function() { | ||
| assert.equal(bencode.encode("asdf"), '4:asdf'); | ||
| assert.equal(bencode.encode(":asdf:"), '6::asdf:'); | ||
| }) | ||
| it('should be able to encode a unicode string', function() { | ||
| assert.equal(bencode.encode("ö±sdf"), '7:ö±sdf'); | ||
| assert.equal(bencode.encode(new Buffer("ö±sdf")), '7:ö±sdf'); | ||
| }) | ||
| it('should be able to encode a buffer', function() { | ||
| assert.equal(bencode.encode(new Buffer("asdf")), '4:asdf'); | ||
| assert.equal(bencode.encode(new Buffer(":asdf:")), '6::asdf:'); | ||
| }) | ||
| it('should be able to encode an array', function() { | ||
| assert.equal(bencode.encode([32, 12]), 'li32ei12ee'); | ||
| assert.equal(bencode.encode([":asdf:"]), 'l6::asdf:e'); | ||
| }) | ||
| it('should be able to encode an object', function() { | ||
| assert.equal(bencode.encode({"a": "bc"}), 'd1:a2:bce') | ||
| assert.equal(bencode.encode({"a": "45", "b": 45}), 'd1:a2:451:bi45ee') | ||
| assert.equal(bencode.encode({"a": new Buffer("bc")}), 'd1:a2:bce') | ||
| }) | ||
| }) | ||
| }); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
51485
21.06%23
130%539
72.2%144
2.86%3
50%