diff --git a/lib/utils/encoding.js b/lib/utils/encoding.js index c5e56e06..ef477fb1 100644 --- a/lib/utils/encoding.js +++ b/lib/utils/encoding.js @@ -11,7 +11,7 @@ * @module utils/encoding */ -const BN = require('bn.js'); +const {U64, I64} = require('./int64'); const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER; const encoding = exports; @@ -443,133 +443,95 @@ encoding.writeI64BE = function writeI64BE(dst, num, off) { * Read uint64le. * @param {Buffer} data * @param {Number} off - * @returns {BN} + * @returns {U64} */ -encoding.readU64BN = function readU64BN(data, off) { - const num = data.slice(off, off + 8); - return new BN(num, 'le'); +encoding.readU64N = function readU64N(data, off) { + return U64.readLE(data, off); }; /** * Read uint64be. * @param {Buffer} data * @param {Number} off - * @returns {BN} + * @returns {U64} */ -encoding.readU64BEBN = function readU64BEBN(data, off) { - const num = data.slice(off, off + 8); - return new BN(num, 'be'); +encoding.readU64BEN = function readU64BEN(data, off) { + return U64.readBE(data, off); }; /** * Read int64le. * @param {Buffer} data * @param {Number} off - * @returns {BN} + * @returns {I64} */ -encoding.readI64BN = function readI64BN(data, off) { - const num = data.slice(off, off + 8); - - if (num[num.length - 1] & 0x80) - return new BN(num, 'le').notn(64).addn(1).neg(); - - return new BN(num, 'le'); +encoding.readI64N = function readI64N(data, off) { + return I64.readLE(data, off); }; /** * Read int64be. * @param {Buffer} data * @param {Number} off - * @returns {BN} + * @returns {I64} */ -encoding.readI64BEBN = function readI64BEBN(data, off) { - const num = data.slice(off, off + 8); - - if (num[0] & 0x80) - return new BN(num, 'be').notn(64).addn(1).neg(); - - return new BN(num, 'be'); -}; - -/** - * Write int64le. - * @private - * @param {Buffer} dst - * @param {BN} num - * @param {Number} off - * @param {Boolean} be - * @returns {Number} Buffer offset. - */ - -encoding._writeI64BN = function _writeI64BN(dst, num, off, be) { - const bits = num.bitLength(); - - if (bits <= 53) - return encoding._writeI64(dst, num.toNumber(), off, be); - - if (bits > 64) - num = num.maskn(64); - - if (num.isNeg()) - num = num.neg().inotn(64).iaddn(1); - - num = num.toArray(be ? 'be' : 'le', 8); - - for (let i = 0; i < num.length; i++) - dst[off++] = num[i]; - - return off; +encoding.readI64BEN = function readI64BEN(data, off) { + return I64.readBE(data, off); }; /** * Write uint64le. * @param {Buffer} dst - * @param {BN} num + * @param {U64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeU64BN = function writeU64BN(dst, num, off) { - return encoding._writeI64BN(dst, num, off, false); +encoding.writeU64N = function writeU64N(dst, num, off) { + enforce(!num.sign, off, 'Signed.'); + return num.writeLE(dst, off); }; /** * Write uint64be. * @param {Buffer} dst - * @param {BN} num + * @param {U64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeU64BEBN = function writeU64BEBN(dst, num, off) { - return encoding._writeI64BN(dst, num, off, true); +encoding.writeU64BEN = function writeU64BEN(dst, num, off) { + enforce(!num.sign, off, 'Signed.'); + return num.writeBE(dst, off); }; /** * Write int64le. * @param {Buffer} dst - * @param {BN} num + * @param {U64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeI64BN = function writeI64BN(dst, num, off) { - return encoding._writeI64BN(dst, num, off, false); +encoding.writeI64N = function writeI64N(dst, num, off) { + enforce(num.sign, off, 'Not signed.'); + return num.writeLE(dst, off); }; /** * Write int64be. * @param {Buffer} dst - * @param {BN} num + * @param {I64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeI64BEBN = function writeI64BEBN(dst, num, off) { - return encoding._writeI64BN(dst, num, off, true); +encoding.writeI64BEN = function writeI64BEN(dst, num, off) { + enforce(num.sign, off, 'Not signed.'); + return num.writeBE(dst, off); }; /** @@ -695,54 +657,54 @@ encoding.sizeVarint = function sizeVarint(num) { * @returns {Object} */ -encoding.readVarintBN = function readVarintBN(data, off) { +encoding.readVarintN = function readVarintN(data, off) { assert(off < data.length, off); - switch (data[off]) { - case 0xff: { - const size = 9; - assert(off + size <= data.length, off); - const value = encoding.readU64BN(data, off + 1); - enforce(value.bitLength() > 32, off, 'Non-canonical varint'); - return new Varint(size, value); - } - default: { - const result = encoding.readVarint(data, off); - result.value = new BN(result.value); - return result; - } + if (data[off] === 0xff) { + const size = 9; + assert(off + size <= data.length, off); + const value = encoding.readU64N(data, off + 1); + enforce(value.gtn(0xffffffff), off, 'Non-canonical varint'); + return new Varint(size, value); } + + const result = encoding.readVarint(data, off); + result.value = U64.fromInt(result.value); + return result; }; /** * Write a varint. * @param {Buffer} dst - * @param {BN} num + * @param {U64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeVarintBN = function writeVarintBN(dst, num, off) { - if (num.bitLength() > 32) { +encoding.writeVarintN = function writeVarintN(dst, num, off) { + enforce(!num.sign, off, 'Signed.'); + + if (num.gtn(0xffffffff)) { dst[off++] = 0xff; - off = encoding.writeU64BN(dst, num, off); - return off; + return encoding.writeU64N(dst, num, off); } - return encoding.writeVarint(dst, num.toNumber(), off); + return encoding.writeVarint(dst, num.toInt(), off); }; /** * Calculate size of varint. - * @param {BN} num + * @param {U64} num * @returns {Number} size */ -encoding.sizeVarintBN = function sizeVarintBN(num) { - if (num.bitLength() > 32) +encoding.sizeVarintN = function sizeVarintN(num) { + enforce(!num.sign, off, 'Signed.'); + + if (num.gtn(0xffffffff)) return 9; - return encoding.sizeVarint(num.toNumber()); + return encoding.sizeVarint(num.toInt()); }; /** @@ -851,34 +813,16 @@ encoding.sizeVarint2 = function sizeVarint2(num) { * @returns {Object} */ -encoding.readVarint2BN = function readVarint2BN(data, off) { - let num = 0; +encoding.readVarint2N = function readVarint2N(data, off) { + let num = new U64(); let size = 0; - while (num < 0x3fffffffffff) { - assert(off < data.length, off); - - const ch = data[off++]; - size++; - - num = (num * 0x80) + (ch & 0x7f); - - if ((ch & 0x80) === 0) - return new Varint(size, new BN(num)); - - num++; - } - - num = new BN(num); - for (;;) { assert(off < data.length, off); const ch = data[off++]; size++; - enforce(num.bitLength() <= 64, off, 'Number exceeds 64 bits'); - num.iushln(7).iaddn(ch & 0x7f); if ((ch & 0x80) === 0) @@ -893,21 +837,25 @@ encoding.readVarint2BN = function readVarint2BN(data, off) { /** * Write a varint (type 2). * @param {Buffer} dst - * @param {BN} num + * @param {U64} num * @param {Number} off * @returns {Number} Buffer offset. */ -encoding.writeVarint2BN = function writeVarint2BN(dst, num, off) { - if (num.bitLength() <= 53) - return encoding.writeVarint2(dst, num.toNumber()); +encoding.writeVarint2N = function writeVarint2N(dst, num, off) { + enforce(!num.sign, off, 'Signed.'); + + if (num.hi === 0) + return encoding.writeVarint2(dst, num.toInt(), off); const tmp = []; let len = 0; + num = num.clone(); + for (;;) { - tmp[len] = (num.words[0] & 0x7f) | (len ? 0x80 : 0x00); - if (num.cmpn(0x7f) <= 0) + tmp[len] = num.andln(0x7f) | (len ? 0x80 : 0x00); + if (num.lten(0x7f)) break; num.iushrn(7).isubn(1); len++; @@ -924,13 +872,15 @@ encoding.writeVarint2BN = function writeVarint2BN(dst, num, off) { /** * Calculate size of varint (type 2). - * @param {BN} num + * @param {U64} num * @returns {Number} size */ -encoding.sizeVarint2BN = function sizeVarint2BN(num) { - if (num.bitLength() <= 53) - return encoding.sizeVarint(num.toNumber()); +encoding.sizeVarint2N = function sizeVarint2N(num) { + enforce(!num.sign, off, 'Signed.'); + + if (num.hi === 0) + return encoding.sizeVarint2(num.toInt()); num = num.clone(); @@ -938,7 +888,7 @@ encoding.sizeVarint2BN = function sizeVarint2BN(num) { for (;;) { size++; - if (num.cmpn(0x7f) <= 0) + if (num.lten(0x7f)) break; num.iushrn(7).isubn(1); } diff --git a/lib/utils/reader.js b/lib/utils/reader.js index ef142738..ee8a7d5e 100644 --- a/lib/utils/reader.js +++ b/lib/utils/reader.js @@ -375,48 +375,48 @@ BufferReader.prototype.readI53BE = function readI53BE() { /** * Read uint64le. - * @returns {BN} + * @returns {U64} */ -BufferReader.prototype.readU64BN = function readU64BN() { +BufferReader.prototype.readU64N = function readU64N() { this.assert(this.offset + 8 <= this.data.length); - const ret = encoding.readU64BN(this.data, this.offset); + const ret = encoding.readU64N(this.data, this.offset); this.offset += 8; return ret; }; /** * Read uint64be. - * @returns {BN} + * @returns {U64} */ -BufferReader.prototype.readU64BEBN = function readU64BEBN() { +BufferReader.prototype.readU64BEN = function readU64BEN() { this.assert(this.offset + 8 <= this.data.length); - const ret = encoding.readU64BEBN(this.data, this.offset); + const ret = encoding.readU64BEN(this.data, this.offset); this.offset += 8; return ret; }; /** * Read int64le. - * @returns {BN} + * @returns {I64} */ -BufferReader.prototype.readI64BN = function readI64BN() { +BufferReader.prototype.readI64N = function readI64N() { this.assert(this.offset + 8 <= this.data.length); - const ret = encoding.readI64BN(this.data, this.offset); + const ret = encoding.readI64N(this.data, this.offset); this.offset += 8; return ret; }; /** * Read int64be. - * @returns {BN} + * @returns {I64} */ -BufferReader.prototype.readI64BEBN = function readI64BEBN() { +BufferReader.prototype.readI64BEN = function readI64BEN() { this.assert(this.offset + 8 <= this.data.length); - const ret = encoding.readI64BEBN(this.data, this.offset); + const ret = encoding.readI64BEN(this.data, this.offset); this.offset += 8; return ret; }; @@ -489,15 +489,16 @@ BufferReader.prototype.skipVarint = function skipVarint() { const size = encoding.skipVarint(this.data, this.offset); this.assert(this.offset + size <= this.data.length); this.offset += size; + return size; }; /** * Read a varint. - * @returns {BN} + * @returns {U64} */ -BufferReader.prototype.readVarintBN = function readVarintBN() { - const {size, value} = encoding.readVarintBN(this.data, this.offset); +BufferReader.prototype.readVarintN = function readVarintN() { + const {size, value} = encoding.readVarintN(this.data, this.offset); this.offset += size; return value; }; @@ -526,11 +527,11 @@ BufferReader.prototype.skipVarint2 = function skipVarint2() { /** * Read a varint (type 2). - * @returns {BN} + * @returns {U64} */ -BufferReader.prototype.readVarint2BN = function readVarint2BN() { - const {size, value} = encoding.readVarint2BN(this.data, this.offset); +BufferReader.prototype.readVarint2N = function readVarint2N() { + const {size, value} = encoding.readVarint2N(this.data, this.offset); this.offset += size; return value; }; diff --git a/lib/utils/staticwriter.js b/lib/utils/staticwriter.js index fdd76a13..1c0c53d0 100644 --- a/lib/utils/staticwriter.js +++ b/lib/utils/staticwriter.js @@ -134,20 +134,20 @@ StaticWriter.prototype.writeU64BE = function writeU64BE(value) { /** * Write uint64le. - * @param {BN} value + * @param {U64} value */ -StaticWriter.prototype.writeU64BN = function writeU64BN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeU64N = function writeU64N(value) { + this.written = encoding.writeU64N(this.data, value, this.written); }; /** * Write uint64be. - * @param {BN} value + * @param {U64} value */ -StaticWriter.prototype.writeU64BEBN = function writeU64BEBN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeU64BEN = function writeU64BEN(value) { + this.written = encoding.writeU64BEN(this.data, value, this.written); }; /** @@ -215,20 +215,20 @@ StaticWriter.prototype.writeI64BE = function writeI64BE(value) { /** * Write int64le. - * @param {BN} value + * @param {I64} value */ -StaticWriter.prototype.writeI64BN = function writeI64BN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeI64N = function writeI64N(value) { + this.written = encoding.writeI64N(this.data, value, this.written); }; /** * Write int64be. - * @param {BN} value + * @param {I64} value */ -StaticWriter.prototype.writeI64BEBN = function writeI64BEBN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeI64BEN = function writeI64BEN(value) { + this.written = encoding.writeI64BEN(this.data, value, this.written); }; /** @@ -278,11 +278,11 @@ StaticWriter.prototype.writeVarint = function writeVarint(value) { /** * Write a varint. - * @param {BN} value + * @param {U64} value */ -StaticWriter.prototype.writeVarintBN = function writeVarintBN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeVarintN = function writeVarintN(value) { + this.written = encoding.writeVarintN(this.data, value, this.written); }; /** @@ -296,11 +296,11 @@ StaticWriter.prototype.writeVarint2 = function writeVarint2(value) { /** * Write a varint (type 2). - * @param {BN} value + * @param {U64} value */ -StaticWriter.prototype.writeVarint2BN = function writeVarint2BN(value) { - assert(false, 'Not implemented.'); +StaticWriter.prototype.writeVarint2N = function writeVarint2N(value) { + this.written = encoding.writeVarint2N(this.data, value, this.written); }; /** diff --git a/lib/utils/writer.js b/lib/utils/writer.js index 7a101bd4..3047a53e 100644 --- a/lib/utils/writer.js +++ b/lib/utils/writer.js @@ -23,23 +23,29 @@ const UI32 = 4; const UI32BE = 5; const UI64 = 6; const UI64BE = 7; -const I8 = 8; -const I16 = 9; -const I16BE = 10; -const I32 = 11; -const I32BE = 12; -const I64 = 13; -const I64BE = 14; -const FL = 15; -const FLBE = 16; -const DBL = 17; -const DBLBE = 18; -const VARINT = 19; -const VARINT2 = 20; -const BYTES = 21; -const STR = 22; -const CHECKSUM = 23; -const FILL = 24; +const UI64N = 8; +const UI64BEN = 9; +const I8 = 10; +const I16 = 11; +const I16BE = 12; +const I32 = 13; +const I32BE = 14; +const I64 = 15; +const I64BE = 16; +const I64N = 17; +const I64BEN = 18; +const FL = 19; +const FLBE = 20; +const DBL = 21; +const DBLBE = 22; +const VARINT = 23; +const VARINTN = 24; +const VARINT2 = 25; +const VARINT2N = 26; +const BYTES = 27; +const STR = 28; +const CHECKSUM = 29; +const FILL = 30; /** * An object that allows writing of buffers in a @@ -99,6 +105,12 @@ BufferWriter.prototype.render = function render(keep) { case UI64BE: off = encoding.writeU64BE(data, op.value, off); break; + case UI64N: + off = encoding.writeU64N(data, op.value, off); + break; + case UI64BEN: + off = encoding.writeU64BEN(data, op.value, off); + break; case I8: off = data.writeInt8(op.value, off, true); break; @@ -120,6 +132,12 @@ BufferWriter.prototype.render = function render(keep) { case I64BE: off = encoding.writeI64BE(data, op.value, off); break; + case I64N: + off = encoding.writeI64N(data, op.value, off); + break; + case I64BEN: + off = encoding.writeI64BEN(data, op.value, off); + break; case FL: off = data.writeFloatLE(op.value, off, true); break; @@ -135,9 +153,15 @@ BufferWriter.prototype.render = function render(keep) { case VARINT: off = encoding.writeVarint(data, op.value, off); break; + case VARINTN: + off = encoding.writeVarintN(data, op.value, off); + break; case VARINT2: off = encoding.writeVarint2(data, op.value, off); break; + case VARINT2N: + off = encoding.writeVarint2N(data, op.value, off); + break; case BYTES: off += op.value.copy(data, off); break; @@ -266,20 +290,22 @@ BufferWriter.prototype.writeU64BE = function writeU64BE(value) { /** * Write uint64le. - * @param {BN} value + * @param {U64} value */ -BufferWriter.prototype.writeU64BN = function writeU64BN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeU64N = function writeU64N(value) { + this.written += 8; + this.ops.push(new WriteOp(UI64N, value)); }; /** * Write uint64be. - * @param {BN} value + * @param {U64} value */ -BufferWriter.prototype.writeU64BEBN = function writeU64BEBN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeU64BEN = function writeU64BEN(value) { + this.written += 8; + this.ops.push(new WriteOp(UI64BEN, value)); }; /** @@ -354,20 +380,22 @@ BufferWriter.prototype.writeI64BE = function writeI64BE(value) { /** * Write int64le. - * @param {BN} value + * @param {I64} value */ -BufferWriter.prototype.writeI64BN = function writeI64BN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeI64N = function writeI64N(value) { + this.written += 8; + this.ops.push(new WriteOp(I64N, value)); }; /** * Write int64be. - * @param {BN} value + * @param {I64} value */ -BufferWriter.prototype.writeI64BEBN = function writeI64BEBN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeI64BEN = function writeI64BEN(value) { + this.written += 8; + this.ops.push(new WriteOp(I64BEN, value)); }; /** @@ -422,11 +450,12 @@ BufferWriter.prototype.writeVarint = function writeVarint(value) { /** * Write a varint. - * @param {BN} value + * @param {U64} value */ -BufferWriter.prototype.writeVarintBN = function writeVarintBN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeVarintN = function writeVarintN(value) { + this.written += encoding.sizeVarintN(value); + this.ops.push(new WriteOp(VARINTN, value)); }; /** @@ -441,11 +470,12 @@ BufferWriter.prototype.writeVarint2 = function writeVarint2(value) { /** * Write a varint (type 2). - * @param {BN} value + * @param {U64} value */ -BufferWriter.prototype.writeVarint2BN = function writeVarint2BN(value) { - assert(false, 'Not implemented.'); +BufferWriter.prototype.writeVarint2N = function writeVarint2N(value) { + this.written += encoding.sizeVarint2N(value); + this.ops.push(new WriteOp(VARINT2N, value)); }; /** diff --git a/test/utils-test.js b/test/utils-test.js index b87fbd5e..959cceb1 100644 --- a/test/utils-test.js +++ b/test/utils-test.js @@ -4,7 +4,7 @@ 'use strict'; const assert = require('./util/assert'); -const BN = require('../lib/crypto/bn'); +const {U64, I64} = require('../lib/utils/int64'); const base58 = require('../lib/utils/base58'); const encoding = require('../lib/utils/encoding'); const Amount = require('../lib/btc/amount'); @@ -33,27 +33,27 @@ const base58Tests = [ ]; const unsigned = [ - new BN('ffeeffee'), - new BN('001fffeeffeeffee'), - new BN('eeffeeff'), - new BN('001feeffeeffeeff'), - new BN(0), - new BN(1) + new U64('ffeeffee', 16), + new U64('001fffeeffeeffee', 16), + new U64('eeffeeff', 16), + new U64('001feeffeeffeeff', 16), + new U64(0), + new U64(1) ]; const signed = [ - new BN('ffeeffee'), - new BN('001fffeeffeeffee'), - new BN('eeffeeff'), - new BN('001feeffeeffeeff'), - new BN(0), - new BN(1), - new BN('ffeeffee').ineg(), - new BN('001fffeeffeeffee').ineg(), - new BN('eeffeeff').ineg(), - new BN('001feeffeeffeeff').ineg(), - new BN(0).ineg(), - new BN(1).ineg() + new I64('ffeeffee', 16), + new I64('001fffeeffeeffee', 16), + new I64('eeffeeff', 16), + new I64('001feeffeeffeeff', 16), + new I64(0), + new I64(1), + new I64('ffeeffee', 16).ineg(), + new I64('001fffeeffeeffee', 16).ineg(), + new I64('eeffeeff', 16).ineg(), + new I64('001feeffeeffeeff', 16).ineg(), + new I64(0).ineg(), + new I64(1).ineg() ]; describe('Utils', function() { @@ -188,11 +188,11 @@ describe('Utils', function() { const buf1 = Buffer.allocUnsafe(8); const buf2 = Buffer.allocUnsafe(8); - encoding.writeU64BN(buf1, num, 0); + encoding.writeU64N(buf1, num, 0); encoding.writeU64(buf2, num.toNumber(), 0); assert.bufferEqual(buf1, buf2); - const n1 = encoding.readU64BN(buf1, 0); + const n1 = encoding.readU64N(buf1, 0); const n2 = encoding.readU64(buf2, 0); assert.strictEqual(n1.toNumber(), n2); @@ -207,11 +207,11 @@ describe('Utils', function() { const buf1 = Buffer.allocUnsafe(8); const buf2 = Buffer.allocUnsafe(8); - encoding.writeI64BN(buf1, num, 0); + encoding.writeI64N(buf1, num, 0); encoding.writeI64(buf2, num.toNumber(), 0); assert.bufferEqual(buf1, buf2); - const n1 = encoding.readI64BN(buf1, 0); + const n1 = encoding.readI64N(buf1, 0); const n2 = encoding.readI64(buf2, 0); assert.strictEqual(n1.toNumber(), n2); @@ -221,11 +221,11 @@ describe('Utils', function() { const buf1 = Buffer.allocUnsafe(8); const buf2 = Buffer.allocUnsafe(8); - encoding.writeU64BN(buf1, num, 0); + encoding.writeU64N(buf1, num.toU64(), 0); encoding.writeU64(buf2, num.toNumber(), 0); assert.bufferEqual(buf1, buf2); - const n1 = encoding.readU64BN(buf1, 0); + const n1 = encoding.readU64N(buf1, 0); if (num.isNeg()) { assert.throws(() => encoding.readU64(buf2, 0));