From c8bda6dde68a31202a00b357f0074aa33a085db6 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 1 May 2014 19:38:18 +1000 Subject: [PATCH] Adds Buffer extensions --- src/buffer.js | 94 +++++++++++++++++++++++++++++++++++++++++ src/index.js | 1 + test/buffer.js | 86 +++++++++++++++++++++++++++++++++++++ test/fixtures/buffer.js | 74 ++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+) create mode 100644 src/buffer.js create mode 100644 test/buffer.js create mode 100644 test/fixtures/buffer.js diff --git a/src/buffer.js b/src/buffer.js new file mode 100644 index 0000000..352a556 --- /dev/null +++ b/src/buffer.js @@ -0,0 +1,94 @@ +var assert = require('assert') + +function readUInt64LE(buffer, offset) { + var a = buffer.readUInt32LE(offset) + var b = buffer.readUInt32LE(offset + 4) + b *= 0x100000000 + + // Javascript Safe Integer limitation + // assert(Number.isSafeInteger(value), 'value must be < 2^53') + assert(b + a < 0x0020000000000000, 'value must be < 2^53') + + return b + a +} + +function readVarInt(buffer, offset) { + var t = buffer.readUInt8(offset) + var number, size + + // 8-bit + if (t < 253) { + number = t + size = 1 + + // 16-bit + } else if (t < 254) { + number = buffer.readUInt16LE(offset + 1) + size = 3 + + // 32-bit + } else if (t < 255) { + number = buffer.readUInt32LE(offset + 1) + size = 5 + + // 64 bit + } else { + number = readUInt64LE(buffer, offset + 1) + size = 9 + } + + return { + number: number, + size: size + } +} + +function writeUInt64LE(buffer, value, offset) { + // Javascript Safe Integer limitation + // assert(Number.isSafeInteger(value), 'value must be < 2^53') + assert(value < 0x0020000000000000, 'value must be < 2^53') + + buffer.writeInt32LE(value & -1, offset) + buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) +} + +function varIntSize(i) { + return i < 253 ? 1 + : i < 0x10000 ? 3 + : i < 0x100000000 ? 5 + : 9 +} + +function writeVarInt(buffer, number, offset) { + var size = varIntSize(number) + + // 8 bit + if (size === 1) { + buffer.writeUInt8(number, offset) + + // 16 bit + } else if (size === 3) { + buffer.writeUInt8(253, offset) + buffer.writeUInt16LE(number, offset + 1) + + // 32 bit + } else if (size === 5) { + buffer.writeUInt8(254, offset) + buffer.writeUInt32LE(number, offset + 1) + + // 64 bit + } else { + buffer.writeUInt8(255, offset) + writeUInt64LE(buffer, number, offset + 1) + } + + return size +} + +module.exports = { + readUInt64LE: readUInt64LE, + readVarInt: readVarInt, + varIntSize: varIntSize, + writeUInt64LE: writeUInt64LE, + writeVarInt: writeVarInt +} diff --git a/src/index.js b/src/index.js index 8d645d7..c236a02 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ module.exports = { Address: require('./address'), base58: require('./base58'), base58check: require('./base58check'), + BufferExt: require('./buffer'), convert: require('./convert'), crypto: require('./crypto'), ec: ec, diff --git a/test/buffer.js b/test/buffer.js new file mode 100644 index 0000000..67eb296 --- /dev/null +++ b/test/buffer.js @@ -0,0 +1,86 @@ +var assert = require('assert') +var BufferExt = require('../').BufferExt + +var fixtures = require('./fixtures/buffer.js') + +describe('Buffer Extensions', function() { + describe('readUInt64LE', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + var buffer = new Buffer(f.hex64, 'hex') + var number = BufferExt.readUInt64LE(buffer, 0) + + assert.equal(number, f.dec) + }) + }) + }) + + describe('readVarInt', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + var buffer = new Buffer(f.hexVI, 'hex') + var d = BufferExt.readVarInt(buffer, 0) + + assert.equal(d.number, f.dec) + assert.equal(d.size, buffer.length) + }) + }) + }) + + describe('varIntSize', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + var number = parseInt(f.dec) + var size = BufferExt.varIntSize(number) + + assert.equal(size, f.hexVI.length / 2) + }) + }) + }) + + describe('writeUInt64LE', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + var buffer = new Buffer(8) + buffer.fill(0) + + BufferExt.writeUInt64LE(buffer, f.dec, 0) + assert.equal(buffer.toString('hex'), f.hex64) + }) + }) + + fixtures.invalid.forEach(function(f) { + it('throws on ' + f.description, function() { + assert.throws(function() { + var buffer = new Buffer(8) + buffer.fill(0) + + BufferExt.writeUInt64LE(buffer, f.dec, 0) + }) + }) + }) + }) + + describe('writeVarInt', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + var buffer = new Buffer(9) + buffer.fill(0) + + var n = BufferExt.writeVarInt(buffer, f.dec, 0) + assert.equal(buffer.slice(0, n).toString('hex'), f.hexVI) + }) + }) + + fixtures.invalid.forEach(function(f) { + it('throws on ' + f.description, function() { + assert.throws(function() { + var buffer = new Buffer(9) + buffer.fill(0) + + BufferExt.writeVarInt(buffer, f.dec, 0) + }) + }) + }) + }) +}) diff --git a/test/fixtures/buffer.js b/test/fixtures/buffer.js new file mode 100644 index 0000000..7de5381 --- /dev/null +++ b/test/fixtures/buffer.js @@ -0,0 +1,74 @@ +module.exports = { + "valid": [ + { + "dec": 0, + "hex64": "0000000000000000", + "hexVI": "00" + }, + { + "dec": 1, + "hex64": "0100000000000000", + "hexVI": "01" + }, + { + "dec": 252, + "hex64": "fc00000000000000", + "hexVI": "fc" + }, + { + "dec": 253, + "hex64": "fd00000000000000", + "hexVI": "fdfd00" + }, + { + "dec": 254, + "hex64": "fe00000000000000", + "hexVI": "fdfe00" + }, + { + "dec": 65535, + "hex64": "ffff000000000000", + "hexVI": "fdffff" + }, + { + "dec": 65536, + "hex64": "0000010000000000", + "hexVI": "fe00000100" + }, + { + "dec": 65537, + "hex64": "0100010000000000", + "hexVI": "fe01000100" + }, + { + "dec": 4294967295, + "hex64": "ffffffff00000000", + "hexVI": "feffffffff" + }, + { + "dec": 4294967296, + "hex64": "0000000001000000", + "hexVI": "ff0000000001000000" + }, + { + "dec": 4294967297, + "hex64": "0100000001000000", + "hexVI": "ff0100000001000000" + }, + { + "dec": 9007199254740991, + "hex64": "ffffffffffff1f00", + "hexVI": "ffffffffffffff1f00" + } + ], + "invalid": [ + { + "description": "n === 2^53", + "value": 9007199254740992 + }, + { + "description": "n > 2^53", + "value": 18374686479671624000 + } + ] +}