encoding: start using int64 objects instead of BNs.

This commit is contained in:
Christopher Jeffrey 2017-08-17 09:56:57 -07:00
parent 411db44a76
commit 668202b2f9
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 199 additions and 218 deletions

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);
};
/**

View File

@ -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));
};
/**

View File

@ -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));