diff --git a/bignum/index.js b/bignum/index.js deleted file mode 100644 index db1002d..0000000 --- a/bignum/index.js +++ /dev/null @@ -1,18 +0,0 @@ -var BigNumber = require('bignumber.js') -var bencode = module.exports - -bencode.encode = require('../lib/encode') -bencode.decode = require('../lib/decode') - -bencode.encode.number = function (buffers, data) { - buffers.push(new Buffer('i' + data.toString() + 'e')) -} - -bencode.decode.integer = function () { - var end = bencode.decode.find(0x65) - var number = bencode.decode.data.toString('ascii', bencode.decode.position + 1, end) - - bencode.decode.position += end + 1 - bencode.decode.position - - return new BigNumber(number, 10) -} diff --git a/lib/decode.js b/lib/decode.js new file mode 100644 index 0000000..e51289b --- /dev/null +++ b/lib/decode.js @@ -0,0 +1,117 @@ +/** + * Decodes bencoded data. + * + * @param {Buffer} data + * @param {Number} start (optional) + * @param {Number} end (optional) + * @param {String} encoding (optional) + * @return {Object|Array|Buffer|String|Number} + */ +function decode (data, start, end, encoding) { + if (typeof start !== 'number' && encoding == null) { + encoding = start + start = undefined + } + + if (typeof end !== 'number' && encoding == null) { + encoding = end + end = undefined + } + + decode.position = 0 + decode.encoding = encoding || null + + decode.data = !(Buffer.isBuffer(data)) + ? new Buffer(data) + : data.slice(start, end) + + decode.bytes = decode.data.length + + return decode.next() +} + +decode.bytes = 0 +decode.position = 0 +decode.data = null +decode.encoding = null + +decode.next = function () { + switch (decode.data[decode.position]) { + case 0x64: + return decode.dictionary() + case 0x6C: + return decode.list() + case 0x69: + return decode.integer() + default: + return decode.buffer() + } +} + +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++ + } + + throw new Error( + 'Invalid data: Missing delimiter "' + + String.fromCharCode(chr) + '" [0x' + + chr.toString(16) + ']' + ) +} + +decode.dictionary = function () { + decode.position++ + + var dict = {} + + while (decode.data[decode.position] !== 0x65) { + dict[decode.buffer()] = 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, 10) +} + +decode.buffer = function () { + var sep = decode.find(0x3A) + var length = parseInt(decode.data.toString('ascii', decode.position, sep), 10) + var end = ++sep + length + + decode.position = end + + return decode.encoding + ? decode.data.toString(decode.encoding, sep, end) + : decode.data.slice(sep, end) +} + +module.exports = decode diff --git a/lib/decoder.js b/lib/decoder.js deleted file mode 100644 index 7e45ce3..0000000 --- a/lib/decoder.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Decoder - * @return {Decoder} - */ -function Decoder () { - if (!(this instanceof Decoder)) { - return new Decoder() - } - - this.bytes = 0 - this.position = 0 - this.data = null - this.encoding = null -} - -/** - * Decoder prototype - * @type {Object} - */ -Decoder.prototype = { - - constructor: Decoder, - - use: function (type, fn) { - this[type] = fn - }, - - decode: function (data, start, end, encoding) { - if (typeof start !== 'number' && encoding == null) { - encoding = start - start = undefined - } - - if (typeof end !== 'number' && encoding == null) { - encoding = end - end = undefined - } - - this.position = 0 - this.encoding = encoding || null - - this.data = !(Buffer.isBuffer(data)) - ? new Buffer(data) - : data.slice(start, end) - - this.bytes = this.data.length - - return this.next() - }, - - next: function () { - switch (this.data[this.position]) { - case 0x64: return this.object() - case 0x6C: return this.array() - case 0x69: return this.number() - default: return this.buffer() - } - }, - - find: function (chr) { - var i = this.position - var c = this.data.length - var d = this.data - - while (i < c) { - if (d[i] === chr) return i - i++ - } - - throw new Error( - 'Invalid data: Missing delimiter "' + String.fromCharCode(chr) + - '" [0x' + chr.toString(16) + ']' - ) - }, - - object: function () { - this.position++ - - var dict = {} - - while (this.data[this.position] !== 0x65) { - dict[this.buffer()] = this.next() - } - - this.position++ - - return dict - }, - - array: function () { - this.position++ - - var lst = [] - - while (this.data[this.position] !== 0x65) { - lst.push(this.next()) - } - - this.position++ - - return lst - }, - - number: function () { - var end = this.find(0x65) - var number = this.data.toString('ascii', this.position + 1, end) - - this.position += end + 1 - this.position - - return parseInt(number, 10) - }, - - buffer: function () { - var sep = this.find(0x3A) - var length = parseInt(this.data.toString('ascii', this.position, sep), 10) - var end = ++sep + length - - this.position = end - - return this.encoding - ? this.data.toString(this.encoding, sep, end) - : this.data.slice(sep, end) - } - -} - -// Exports -module.exports = Decoder diff --git a/lib/encode.js b/lib/encode.js new file mode 100644 index 0000000..c3311cb --- /dev/null +++ b/lib/encode.js @@ -0,0 +1,107 @@ +/** + * Encodes data in bencode. + * + * @param {Buffer|Array|String|Object|Number|Boolean} data + * @return {Buffer} + */ +function encode (data, buffer, offset) { + var buffers = [] + var result = null + + encode._encode(buffers, data) + result = Buffer.concat(buffers) + encode.bytes = result.length + + if (Buffer.isBuffer(buffer)) { + result.copy(buffer, offset) + return buffer + } + + return result +} + +encode.bytes = -1 +encode._floatConversionDetected = false + +encode._encode = function (buffers, data) { + if (Buffer.isBuffer(data)) { + buffers.push(new Buffer(data.length + ':')) + buffers.push(data) + return + } + + switch (typeof data) { + case 'string': + encode.buffer(buffers, data) + break + case 'number': + encode.number(buffers, data) + break + case 'object': + data.constructor === Array + ? encode.list(buffers, data) + : encode.dict(buffers, data) + break + case 'boolean': + encode.number(buffers, data ? 1 : 0) + break + } +} + +var buffE = new Buffer('e') +var buffD = new Buffer('d') +var buffL = new Buffer('l') + +encode.buffer = function (buffers, data) { + buffers.push(new Buffer(Buffer.byteLength(data) + ':' + data)) +} + +encode.number = function (buffers, data) { + var maxLo = 0x80000000 + var hi = (data / maxLo) << 0 + var lo = (data % maxLo) << 0 + var val = hi * maxLo + lo + + buffers.push(new Buffer('i' + val + 'e')) + + if (val !== data && !encode._floatConversionDetected) { + encode._floatConversionDetected = true + console.warn( + 'WARNING: Possible data corruption detected with value "' + data + '":', + 'Bencoding only defines support for integers, value was converted to "' + val + '"' + ) + console.trace() + } +} + +encode.dict = function (buffers, data) { + buffers.push(buffD) + + 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.buffer(buffers, k) + encode._encode(buffers, data[k]) + } + + buffers.push(buffE) +} + +encode.list = function (buffers, data) { + var i = 0 + var c = data.length + buffers.push(buffL) + + for (; i < c; i++) { + encode._encode(buffers, data[i]) + } + + buffers.push(buffE) +} + +module.exports = encode diff --git a/lib/encoder.js b/lib/encoder.js deleted file mode 100644 index 837ad55..0000000 --- a/lib/encoder.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Encoder - * @return {Encoder} - */ -function Encoder () { - if (!(this instanceof Encoder)) { - return new Encoder() - } - - this.bytes = -1 - this._types = Encoder.types.slice() - this._floatConversionDetected = false -} - -Encoder.types = [ - function isString (value) { return typeof value === 'string' ? 'string' : void 0 }, - function isNumber (value) { return typeof value === 'number' ? 'number' : void 0 }, - function isBoolean (value) { return typeof value === 'boolean' ? 'boolean' : void 0 }, - function isBuffer (value) { return Buffer.isBuffer(value) ? 'buffer' : void 0 }, - function isArray (value) { return Array.isArray(value) ? 'array' : void 0 }, - function isObject (value) { return typeof value === 'object' ? 'object' : void 0 } -] - -var buffE = new Buffer('e') -var buffD = new Buffer('d') -var buffL = new Buffer('l') - -/** - * Encoder prototype - * @type {Object} - */ -Encoder.prototype = { - - constructor: Encoder, - - use: function (type, fn, check) { - this[type] = fn - this._types.unshift(function (value) { - return check(value) ? type : void 0 - }) - }, - - encode: function (data, buffer, offset) { - var buffers = [] - var result = null - - this._encode(buffers, data) - result = Buffer.concat(buffers) - this.bytes = result.length - - if (Buffer.isBuffer(buffer)) { - result.copy(buffer, offset) - return buffer - } - - return result - }, - - _typeof: function (value) { - var type - var len = this._types.length - - for (var i = 0; i < len; i++) { - type = this._types[i](value) - if (type) return type - } - }, - - _encode: function (buffers, data) { - var type = this._typeof(data) - this[type](buffers, data) - }, - - buffer: function (buffers, data) { - buffers.push(new Buffer(data.length + ':')) - buffers.push(data) - }, - - string: function (buffers, data) { - buffers.push(new Buffer(Buffer.byteLength(data) + ':' + data)) - }, - - boolean: function (buffers, data) { - this.number(buffers, data ? 1 : 0) - }, - - number: function (buffers, data) { - var maxLo = 0x80000000 - var hi = (data / maxLo) << 0 - var lo = (data % maxLo) << 0 - var val = hi * maxLo + lo - - buffers.push(new Buffer('i' + val + 'e')) - - if (val !== data && !this._floatConversionDetected) { - this._floatConversionDetected = true - console.warn( - 'WARNING: Possible data corruption detected with value "' + data + '":', - 'Bencoding only defines support for integers, value was converted to "' + val + '"' - ) - console.trace() - } - }, - - object: function (buffers, data) { - buffers.push(buffD) - - 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] - this.string(buffers, k) - this._encode(buffers, data[k]) - } - - buffers.push(buffE) - }, - - array: function (buffers, data) { - var i = 0 - var c = data.length - buffers.push(buffL) - - for (; i < c; i++) { - this._encode(buffers, data[i]) - } - - buffers.push(buffE) - } - -} - -// Exports -module.exports = Encoder diff --git a/lib/index.js b/lib/index.js index 99ea94c..1a0f428 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,33 +1,7 @@ -/** - * @constructor - * @return {Bencode} - */ -function Bencode () { - if (!(this instanceof Bencode)) { - return new Bencode() - } - - this.encoder = new Bencode.Encoder() - this.decoder = new Bencode.Decoder() -} - -Bencode.Encoder = require('./encoder') -Bencode.Decoder = require('./decoder') +var bencode = module.exports -var encoder = new Bencode.Encoder() -var decoder = new Bencode.Decoder() - -Bencode.encode = function (data, buffer, offset) { - var result = encoder.encode(data, buffer, offset) - Bencode.encode.bytes = encoder.bytes - return result -} - -Bencode.decode = function (data, start, end, encoding) { - var result = decoder.decode(data, start, end, encoding) - Bencode.decode.bytes = decoder.bytes - return result -} +bencode.encode = require('./encode') +bencode.decode = require('./decode') /** * Determines the amount of bytes @@ -35,45 +9,6 @@ Bencode.decode = function (data, start, end, encoding) { * @param {Object|Array|Buffer|String|Number|Boolean} value * @return {Number} byteCount */ -Bencode.byteLength = Bencode.encodingLength = function (value) { - return Bencode.encode(value).length +bencode.byteLength = bencode.encodingLength = function (value) { + return bencode.encode(value).length } - -/** - * Bencode prototype - * @type {Object} - */ -Bencode.prototype = { - - constructor: Bencode, - - encode: function (data, buffer, offset) { - var result = this.encoder.encode(data, buffer, offset) - this.encode.bytes = this.encoder.bytes - return result - }, - - decode: function (data, start, end, encoding) { - var result = this.decoder.decode(data, start, end, encoding) - this.decode.bytes = this.decoder.bytes - return result - }, - - use: function (customType, type, fns) { - this.encoder.use(customType, fns.encode, fns.check) - this.decoder.use(type, fns.decode) - return this - }, - - byteLength: function (value) { - return this.encode(value).length - }, - - encodingLength: function (value) { - return this.byteLength(value) - } - -} - -// Exports -module.exports = Bencode diff --git a/package.json b/package.json index bf499d7..720fe50 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,8 @@ "url": "https://jhermsmeier.de/" } ], - "dependencies": {}, "devDependencies": { "bencoding": "latest", - "bignumber.js": "latest", - "bn.js": "latest", "bncode": "latest", "dht-bencode": "latest", "dht.js": "latest",