encoding: refactor int64 handling.

This commit is contained in:
Christopher Jeffrey 2017-09-05 21:17:58 -07:00
parent 79d1bbd823
commit b81643473e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
8 changed files with 82 additions and 321 deletions

View File

@ -2105,9 +2105,9 @@ ChainState.fromRaw = function fromRaw(data) {
const state = new ChainState();
const br = new BufferReader(data);
state.tip = br.readHash();
state.tx = br.readU53();
state.coin = br.readU53();
state.value = br.readU53();
state.tx = br.readU64();
state.coin = br.readU64();
state.value = br.readU64();
return state;
};

View File

@ -291,7 +291,7 @@ VersionPacket.prototype.fromReader = function fromReader(br) {
// are currently unused.
br.readU32();
this.time = br.readI53();
this.time = br.readI64();
this.remote.fromReader(br, false);
if (br.left() > 0) {
@ -2209,7 +2209,7 @@ SendCmpctPacket.prototype.toRaw = function toRaw() {
SendCmpctPacket.prototype.fromReader = function fromReader(br) {
this.mode = br.readU8();
this.version = br.readU53();
this.version = br.readU64();
return this;
};

View File

@ -166,36 +166,6 @@ encoding.ZERO_U32 = Buffer.from('00000000', 'hex');
encoding.ZERO_U64 = Buffer.from('0000000000000000', 'hex');
/**
* Read uint64 as a js number.
* @private
* @param {Buffer} data
* @param {Number} off
* @param {Boolean} force53 - Read only 53 bits, but maintain the sign.
* @param {Boolean} be
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding._readU64 = function _readU64(data, off, force53, be) {
let hi, lo;
if (be) {
hi = data.readUInt32BE(off, true);
lo = data.readUInt32BE(off + 4, true);
} else {
hi = data.readUInt32LE(off + 4, true);
lo = data.readUInt32LE(off, true);
}
if (force53)
hi &= 0x1fffff;
enforce((hi & 0xffe00000) === 0, off, 'Number exceeds 2^53-1');
return (hi * 0x100000000) + lo;
};
/**
* Read uint64le as a js number.
* @param {Buffer} data
@ -205,7 +175,10 @@ encoding._readU64 = function _readU64(data, off, force53, be) {
*/
encoding.readU64 = function readU64(data, off) {
return encoding._readU64(data, off, false, false);
const hi = data.readUInt32LE(off + 4, true);
const lo = data.readUInt32LE(off, true);
enforce((hi & 0xffe00000) === 0, off, 'Number exceeds 2^53-1');
return hi * 0x100000000 + lo;
};
/**
@ -217,72 +190,9 @@ encoding.readU64 = function readU64(data, off) {
*/
encoding.readU64BE = function readU64BE(data, off) {
return encoding._readU64(data, off, false, true);
};
/**
* Read uint64le as a js number (limit at 53 bits).
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding.readU53 = function readU53(data, off) {
return encoding._readU64(data, off, true, false);
};
/**
* Read uint64be as a js number (limit at 53 bits).
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding.readU53BE = function readU53BE(data, off) {
return encoding._readU64(data, off, true, true);
};
/**
* Read int64 as a js number.
* @private
* @param {Buffer} data
* @param {Number} off
* @param {Boolean} force53 - Read only 53 bits, but maintain the sign.
* @param {Boolean} be
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding._readI64 = function _readI64(data, off, force53, be) {
let hi, lo;
if (be) {
hi = data.readUInt32BE(off, true);
lo = data.readUInt32BE(off + 4, true);
} else {
hi = data.readUInt32LE(off + 4, true);
lo = data.readUInt32LE(off, true);
}
if (hi & 0x80000000) {
hi = ~hi >>> 0;
lo = ~lo >>> 0;
if (force53)
hi &= 0x1fffff;
enforce((hi & 0xffe00000) === 0, off, 'Number exceeds 2^53-1');
return -(hi * 0x100000000 + lo + 1);
}
if (force53)
hi &= 0x1fffff;
const hi = data.readUInt32BE(off, true);
const lo = data.readUInt32BE(off + 4, true);
enforce((hi & 0xffe00000) === 0, off, 'Number exceeds 2^53-1');
return hi * 0x100000000 + lo;
};
@ -295,7 +205,10 @@ encoding._readI64 = function _readI64(data, off, force53, be) {
*/
encoding.readI64 = function readI64(data, off) {
return encoding._readI64(data, off, false, false);
const hi = data.readInt32LE(off + 4, true);
const lo = data.readUInt32LE(off, true);
enforce(isSafe(hi, lo), 'Number exceeds 2^53-1');
return hi * 0x100000000 + lo;
};
/**
@ -307,85 +220,10 @@ encoding.readI64 = function readI64(data, off) {
*/
encoding.readI64BE = function readI64BE(data, off) {
return encoding._readI64(data, off, false, true);
};
/**
* Read int64be as a js number (limit at 53 bits).
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding.readI53 = function readI53(data, off) {
return encoding._readI64(data, off, true, false);
};
/**
* Read int64be as a js number (limit at 53 bits).
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
* @throws on num > MAX_SAFE_INTEGER
*/
encoding.readI53BE = function readI53BE(data, off) {
return encoding._readI64(data, off, true, true);
};
/**
* Write a javascript number as an int64.
* @private
* @param {Buffer} dst
* @param {Number} num
* @param {Number} off
* @param {Boolean} be
* @returns {Number} Buffer offset.
* @throws on num > MAX_SAFE_INTEGER
*/
encoding._writeI64 = function _writeI64(dst, num, off, be) {
const neg = num < 0;
if (neg) {
num = -num;
num -= 1;
}
enforce(num <= MAX_SAFE_INTEGER, off, 'Number exceeds 2^53-1');
let hi = (num * (1 / 0x100000000)) | 0;
let lo = num | 0;
if (neg) {
hi = ~hi;
lo = ~lo;
}
if (be) {
dst[off++] = hi >>> 24;
dst[off++] = (hi >> 16) & 0xff;
dst[off++] = (hi >> 8) & 0xff;
dst[off++] = hi & 0xff;
dst[off++] = lo >>> 24;
dst[off++] = (lo >> 16) & 0xff;
dst[off++] = (lo >> 8) & 0xff;
dst[off++] = lo & 0xff;
} else {
dst[off++] = lo & 0xff;
dst[off++] = (lo >> 8) & 0xff;
dst[off++] = (lo >> 16) & 0xff;
dst[off++] = lo >>> 24;
dst[off++] = hi & 0xff;
dst[off++] = (hi >> 8) & 0xff;
dst[off++] = (hi >> 16) & 0xff;
dst[off++] = hi >>> 24;
}
return off;
const hi = data.readInt32BE(off, true);
const lo = data.readUInt32BE(off + 4, true);
enforce(isSafe(hi, lo), 'Number exceeds 2^53-1');
return hi * 0x100000000 + lo;
};
/**
@ -398,7 +236,7 @@ encoding._writeI64 = function _writeI64(dst, num, off, be) {
*/
encoding.writeU64 = function writeU64(dst, num, off) {
return encoding._writeI64(dst, num, off, false);
return write64(dst, num, off, false);
};
/**
@ -411,7 +249,7 @@ encoding.writeU64 = function writeU64(dst, num, off) {
*/
encoding.writeU64BE = function writeU64BE(dst, num, off) {
return encoding._writeI64(dst, num, off, true);
return write64(dst, num, off, true);
};
/**
@ -424,7 +262,7 @@ encoding.writeU64BE = function writeU64BE(dst, num, off) {
*/
encoding.writeI64 = function writeI64(dst, num, off) {
return encoding._writeI64(dst, num, off, false);
return write64(dst, num, off, false);
};
/**
@ -437,7 +275,7 @@ encoding.writeI64 = function writeI64(dst, num, off) {
*/
encoding.writeI64BE = function writeI64BE(dst, num, off) {
return encoding._writeI64(dst, num, off, true);
return write64(dst, num, off, true);
};
/**
@ -610,28 +448,6 @@ encoding.writeVarint = function writeVarint(dst, num, off) {
return off;
};
/**
* Read a varint size.
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
*/
encoding.skipVarint = function skipVarint(data, off) {
assert(off < data.length, off);
switch (data[off]) {
case 0xff:
return 9;
case 0xfe:
return 5;
case 0xfd:
return 3;
default:
return 1;
}
};
/**
* Calculate size of varint.
* @param {Number} num
@ -772,27 +588,6 @@ encoding.writeVarint2 = function writeVarint2(dst, num, off) {
return off;
};
/**
* Read a varint size.
* @param {Buffer} data
* @param {Number} off
* @returns {Number}
*/
encoding.skipVarint2 = function skipVarint2(data, off) {
let size = 0;
for (;;) {
assert(off < data.length, off);
const ch = data[off++];
size++;
if ((ch & 0x80) === 0)
break;
}
return size;
};
/**
* Calculate size of varint (type 2).
* @param {Number} num
@ -986,9 +781,9 @@ encoding.sizeVarString = function sizeVarString(str, enc) {
* @param {String} reason
*/
encoding.EncodingError = function EncodingError(offset, reason) {
encoding.EncodingError = function EncodingError(offset, reason, start) {
if (!(this instanceof EncodingError))
return new EncodingError(offset, reason);
return new EncodingError(offset, reason, start);
Error.call(this);
@ -996,7 +791,7 @@ encoding.EncodingError = function EncodingError(offset, reason) {
this.message = `${reason} (offset=${offset}).`;
if (Error.captureStackTrace)
Error.captureStackTrace(this, EncodingError);
Error.captureStackTrace(this, start || EncodingError);
};
Object.setPrototypeOf(encoding.EncodingError.prototype, Error.prototype);
@ -1005,17 +800,58 @@ Object.setPrototypeOf(encoding.EncodingError.prototype, Error.prototype);
* Helpers
*/
function isSafe(hi, lo) {
if (hi < 0) {
hi = ~hi;
if (lo === 0)
hi += 1;
}
return (hi & 0xffe00000) === 0;
}
function write64(dst, num, off, be) {
let neg = false;
if (num < 0) {
num = -num;
neg = true;
}
let hi = (num * (1 / 0x100000000)) | 0;
let lo = num | 0;
if (neg) {
if (lo === 0) {
hi = (~hi + 1) | 0;
} else {
hi = ~hi;
lo = ~lo + 1;
}
}
if (be) {
off = dst.writeInt32BE(hi, off, true);
off = dst.writeInt32BE(lo, off, true);
} else {
off = dst.writeInt32LE(lo, off, true);
off = dst.writeInt32LE(hi, off, true);
}
return off;
}
function Varint(size, value) {
this.size = size;
this.value = value;
}
function enforce(value, offset, reason) {
if (!value)
throw new encoding.EncodingError(offset, reason);
}
function assert(value, offset) {
if (!value)
throw new encoding.EncodingError(offset, 'Out of bounds read');
throw new encoding.EncodingError(offset, 'Out of bounds read', assert);
}
function enforce(value, offset, reason) {
if (!value)
throw new encoding.EncodingError(offset, reason, enforce);
}

View File

@ -42,7 +42,7 @@ function BufferReader(data, zeroCopy) {
BufferReader.prototype.assert = function assert(value) {
if (!value)
throw new encoding.EncodingError(this.offset, 'Out of bounds read');
throw new encoding.EncodingError(this.offset, 'Out of bounds read', assert);
};
/**
@ -53,7 +53,7 @@ BufferReader.prototype.assert = function assert(value) {
BufferReader.prototype.enforce = function enforce(value, reason) {
if (!value)
throw new encoding.EncodingError(this.offset, reason);
throw new encoding.EncodingError(this.offset, reason, enforce);
};
/**
@ -237,32 +237,6 @@ BufferReader.prototype.readU64BE = function readU64BE() {
return ret;
};
/**
* Read first least significant 53 bits of
* a uint64le as a js number. Maintain the sign.
* @returns {Number}
*/
BufferReader.prototype.readU53 = function readU53() {
this.assert(this.offset + 8 <= this.data.length);
const ret = encoding.readU53(this.data, this.offset);
this.offset += 8;
return ret;
};
/**
* Read first least significant 53 bits of
* a uint64be as a js number. Maintain the sign.
* @returns {Number}
*/
BufferReader.prototype.readU53BE = function readU53BE() {
this.assert(this.offset + 8 <= this.data.length);
const ret = encoding.readU53BE(this.data, this.offset);
this.offset += 8;
return ret;
};
/**
* Read int8.
* @returns {Number}
@ -349,32 +323,6 @@ BufferReader.prototype.readI64BE = function readI64BE() {
return ret;
};
/**
* Read first least significant 53 bits of
* a int64le as a js number. Maintain the sign.
* @returns {Number}
*/
BufferReader.prototype.readI53 = function readI53() {
this.assert(this.offset + 8 <= this.data.length);
const ret = encoding.readI53(this.data, this.offset);
this.offset += 8;
return ret;
};
/**
* Read first least significant 53 bits of
* a int64be as a js number. Maintain the sign.
* @returns {Number}
*/
BufferReader.prototype.readI53BE = function readI53BE() {
this.assert(this.offset + 8 <= this.data.length);
const ret = encoding.readI53BE(this.data, this.offset);
this.offset += 8;
return ret;
};
/**
* Read uint64le.
* @returns {U64}
@ -482,18 +430,6 @@ BufferReader.prototype.readVarint = function readVarint() {
return value;
};
/**
* Skip past a varint.
* @returns {Number}
*/
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 {U64}
@ -516,17 +452,6 @@ BufferReader.prototype.readVarint2 = function readVarint2() {
return value;
};
/**
* Skip past a varint (type 2).
* @returns {Number}
*/
BufferReader.prototype.skipVarint2 = function skipVarint2() {
const size = encoding.skipVarint2(this.data, this.offset);
this.assert(this.offset + size <= this.data.length);
this.offset += size;
};
/**
* Read a varint (type 2).
* @returns {U64}

View File

@ -594,7 +594,7 @@ MasterKey.prototype.fromRaw = function fromRaw(raw) {
}
// NOTE: useless varint
br.skipVarint();
br.readVarint();
this.key = HD.PrivateKey.fromRaw(br.readBytes(82));

View File

@ -2669,10 +2669,10 @@ TXDBState.prototype.toRaw = function toRaw() {
TXDBState.prototype.fromRaw = function fromRaw(data) {
const br = new BufferReader(data);
this.tx = br.readU53();
this.coin = br.readU53();
this.unconfirmed = br.readU53();
this.confirmed = br.readU53();
this.tx = br.readU64();
this.coin = br.readU64();
this.unconfirmed = br.readU64();
this.confirmed = br.readU64();
return this;
};

View File

@ -600,7 +600,7 @@ function skipCoin(br) {
}
// Skip past the value.
br.skipVarint();
br.readVarint();
return br.offset - start;
}

View File

@ -218,7 +218,7 @@ function skipOutput(br) {
const start = br.offset;
// Skip past the value.
br.skipVarint();
br.readVarint();
// Skip past the compressed scripts.
switch (br.readU8()) {