From cde44d689c3b8357f2e7a59ceb216687d293746f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 12:35:55 -0700 Subject: [PATCH] fix precision error by handling BNs correctly --- lib/bufferreader.js | 41 ++++++++++++++++++++++------------- test/test.bufferreader.js | 45 ++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index fc06b17..afee08f 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -1,3 +1,5 @@ +var BN = require('./bn'); + var BufferReader = function BufferReader(buf, pos) { if (!(this instanceof BufferReader)) return new BufferReader(buf); @@ -45,24 +47,19 @@ BufferReader.prototype.readUInt32LE = function() { return val; }; -//TODO: What if n is so large that it loses precision? -BufferReader.prototype.readUInt64BE = function() { - var val = 0; - for (var i = 0; i < 8; i++) { - val += Math.pow(256, i) * this.buf[this.pos + 8 - 1 - i]; - } +BufferReader.prototype.readUInt64BEBN = function() { + var buf = this.buf.slice(this.pos, this.pos + 8); + var bn = BN().fromBuffer(buf); this.pos = this.pos + 8; - return val; + return bn; }; -//TODO: What if n is so large that it loses precision? -BufferReader.prototype.readUInt64LE = function() { - var val = 0; - for (var i = 0; i < 8; i++) { - val += Math.pow(256, i) * this.buf[this.pos + i]; - } +BufferReader.prototype.readUInt64LEBN = function() { + var buf = this.buf.slice(this.pos, this.pos + 8); + var reversebuf = BufferReader(buf).reverse().read(); + var bn = BN().fromBuffer(reversebuf); this.pos = this.pos + 8; - return val; + return bn; }; BufferReader.prototype.readVarInt = function() { @@ -73,12 +70,26 @@ BufferReader.prototype.readVarInt = function() { case 0xFE: return this.readUInt32LE(); case 0xFF: - return this.readUInt64LE(); + return this.readUInt64LEBN().toNumber(); default: return first; } }; +BufferReader.prototype.readVarIntBN = function() { + var first = this.readUInt8(); + switch (first) { + case 0xFD: + return BN(this.readUInt16LE()); + case 0xFE: + return BN(this.readUInt32LE()); + case 0xFF: + return this.readUInt64LEBN(); + default: + return BN(first); + } +}; + BufferReader.prototype.reverse = function() { var buf = new Buffer(this.buf.length); for (var i = 0; i < buf.length; i++) diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js index bca7b63..f305ecd 100644 --- a/test/test.bufferreader.js +++ b/test/test.bufferreader.js @@ -82,33 +82,33 @@ describe('BufferReader', function() { }); - describe('#readUInt64BE', function() { + describe('#readUInt64BEBN', function() { it('should return 1', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32BE(1, 4); var br = new BufferReader(buf); - br.readUInt64BE().should.equal(1); + br.readUInt64BEBN().toNumber().should.equal(1); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); var br = new BufferReader(buf); - br.readUInt64BE().should.equal(Math.pow(2, 64)); + br.readUInt64BEBN().toNumber().should.equal(Math.pow(2, 64)); }); }); - describe('#readUInt64LE', function() { + describe('#readUInt64LEBN', function() { it('should return 1', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32LE(1, 0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(1); + br.readUInt64LEBN().toNumber().should.equal(1); }); it('should return 2^30', function() { @@ -116,21 +116,21 @@ describe('BufferReader', function() { buf.fill(0); buf.writeUInt32LE(Math.pow(2, 30), 0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(Math.pow(2, 30)); + br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 30)); }); it('should return 0', function() { var buf = new Buffer(8); buf.fill(0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(0); + br.readUInt64LEBN().toNumber().should.equal(0); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(Math.pow(2, 64)); + br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 64)); }); }); @@ -164,6 +164,35 @@ describe('BufferReader', function() { }); + describe('#readVarIntBN', function() { + + it('should read a 1 byte varint', function() { + var buf = new Buffer([50]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(50); + }); + + it('should read a 3 byte varint', function() { + var buf = new Buffer([253, 253, 0]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(253); + }); + + it('should read a 5 byte varint', function() { + var buf = new Buffer([254, 0, 0, 0, 0]); + buf.writeUInt32LE(50000, 1); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(50000); + }); + + it('should read a 9 byte varint', function() { + var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(Math.pow(2, 64)); + }); + + }); + describe('#reverse', function() { it('should reverse this [0, 1]', function() {