encoding: encoding improvements for base128 varints.

This commit is contained in:
Christopher Jeffrey 2017-08-17 10:47:20 -07:00
parent 51cdd6156b
commit 5415147d4e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 45 additions and 34 deletions

View File

@ -12,6 +12,7 @@
*/ */
const {U64, I64} = require('./int64'); const {U64, I64} = require('./int64');
const UINT128_MAX = U64.UINT64_MAX.shrn(7);
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER; const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
const encoding = exports; const encoding = exports;
@ -345,9 +346,9 @@ encoding.readI53BE = function readI53BE(data, off) {
*/ */
encoding._writeI64 = function _writeI64(dst, num, off, be) { encoding._writeI64 = function _writeI64(dst, num, off, be) {
const negative = num < 0; const neg = num < 0;
if (negative) { if (neg) {
num = -num; num = -num;
num -= 1; num -= 1;
} }
@ -357,7 +358,7 @@ encoding._writeI64 = function _writeI64(dst, num, off, be) {
let hi = (num * (1 / 0x100000000)) | 0; let hi = (num * (1 / 0x100000000)) | 0;
let lo = num | 0; let lo = num | 0;
if (negative) { if (neg) {
hi = ~hi; hi = ~hi;
lo = ~lo; lo = ~lo;
} }
@ -491,7 +492,7 @@ encoding.readI64BEN = function readI64BEN(data, off) {
*/ */
encoding.writeU64N = function writeU64N(dst, num, off) { encoding.writeU64N = function writeU64N(dst, num, off) {
enforce(!num.sign, off, 'Signed.'); enforce(!num.sign, off, 'Signed');
return num.writeLE(dst, off); return num.writeLE(dst, off);
}; };
@ -504,7 +505,7 @@ encoding.writeU64N = function writeU64N(dst, num, off) {
*/ */
encoding.writeU64BEN = function writeU64BEN(dst, num, off) { encoding.writeU64BEN = function writeU64BEN(dst, num, off) {
enforce(!num.sign, off, 'Signed.'); enforce(!num.sign, off, 'Signed');
return num.writeBE(dst, off); return num.writeBE(dst, off);
}; };
@ -517,7 +518,7 @@ encoding.writeU64BEN = function writeU64BEN(dst, num, off) {
*/ */
encoding.writeI64N = function writeI64N(dst, num, off) { encoding.writeI64N = function writeI64N(dst, num, off) {
enforce(num.sign, off, 'Not signed.'); enforce(num.sign, off, 'Not signed');
return num.writeLE(dst, off); return num.writeLE(dst, off);
}; };
@ -530,7 +531,7 @@ encoding.writeI64N = function writeI64N(dst, num, off) {
*/ */
encoding.writeI64BEN = function writeI64BEN(dst, num, off) { encoding.writeI64BEN = function writeI64BEN(dst, num, off) {
enforce(num.sign, off, 'Not signed.'); enforce(num.sign, off, 'Not signed');
return num.writeBE(dst, off); return num.writeBE(dst, off);
}; };
@ -682,9 +683,9 @@ encoding.readVarintN = function readVarintN(data, off) {
*/ */
encoding.writeVarintN = function writeVarintN(dst, num, off) { encoding.writeVarintN = function writeVarintN(dst, num, off) {
enforce(!num.sign, off, 'Signed.'); enforce(!num.sign, off, 'Signed');
if (num.gtn(0xffffffff)) { if (num.hi !== 0) {
dst[off++] = 0xff; dst[off++] = 0xff;
return encoding.writeU64N(dst, num, off); return encoding.writeU64N(dst, num, off);
} }
@ -699,9 +700,9 @@ encoding.writeVarintN = function writeVarintN(dst, num, off) {
*/ */
encoding.sizeVarintN = function sizeVarintN(num) { encoding.sizeVarintN = function sizeVarintN(num) {
enforce(!num.sign, 0, 'Signed.'); enforce(!num.sign, 0, 'Signed');
if (num.gtn(0xffffffff)) if (num.hi !== 0)
return 9; return 9;
return encoding.sizeVarint(num.toInt()); return encoding.sizeVarint(num.toInt());
@ -724,13 +725,16 @@ encoding.readVarint2 = function readVarint2(data, off) {
const ch = data[off++]; const ch = data[off++];
size++; size++;
enforce(num < 0x3fffffffffff, off, 'Number exceeds 2^53-1'); // Number.MAX_SAFE_INTEGER >>> 7
enforce(num <= 0x3fffffffffff - (ch & 0x7f), off, 'Number exceeds 2^53-1');
// num = (num << 7) | (ch & 0x7f);
num = (num * 0x80) + (ch & 0x7f); num = (num * 0x80) + (ch & 0x7f);
if ((ch & 0x80) === 0) if ((ch & 0x80) === 0)
break; break;
enforce(num !== MAX_SAFE_INTEGER, off, 'Number exceeds 2^53-1');
num++; num++;
} }
@ -747,17 +751,19 @@ encoding.readVarint2 = function readVarint2(data, off) {
encoding.writeVarint2 = function writeVarint2(dst, num, off) { encoding.writeVarint2 = function writeVarint2(dst, num, off) {
const tmp = []; const tmp = [];
let len = 0; let len = 0;
for (;;) { for (;;) {
tmp[len] = (num & 0x7f) | (len ? 0x80 : 0x00); tmp[len] = (num & 0x7f) | (len ? 0x80 : 0x00);
if (num <= 0x7f) if (num <= 0x7f)
break; break;
// num = (num >>> 7) - 1;
num = ((num - (num % 0x80)) / 0x80) - 1; num = ((num - (num % 0x80)) / 0x80) - 1;
len++; len++;
} }
assert(off + len <= dst.length, off); assert(off + len + 1 <= dst.length, off);
do { do {
dst[off++] = tmp[len]; dst[off++] = tmp[len];
@ -800,6 +806,7 @@ encoding.sizeVarint2 = function sizeVarint2(num) {
size++; size++;
if (num <= 0x7f) if (num <= 0x7f)
break; break;
// num = (num >>> 7) - 1;
num = ((num - (num % 0x80)) / 0x80) - 1; num = ((num - (num % 0x80)) / 0x80) - 1;
} }
@ -824,11 +831,14 @@ encoding.readVarint2N = function readVarint2N(data, off) {
const ch = data[off++]; const ch = data[off++];
size++; size++;
num.iushln(7).iaddn(ch & 0x7f); enforce(num.lte(UINT128_MAX), off, 'Number exceeds 2^64-1');
num.ishln(7).iorn(ch & 0x7f);
if ((ch & 0x80) === 0) if ((ch & 0x80) === 0)
break; break;
enforce(!num.eq(U64.UINT64_MAX), off, 'Number exceeds 2^64-1');
num.iaddn(1); num.iaddn(1);
} }
@ -844,25 +854,26 @@ encoding.readVarint2N = function readVarint2N(data, off) {
*/ */
encoding.writeVarint2N = function writeVarint2N(dst, num, off) { encoding.writeVarint2N = function writeVarint2N(dst, num, off) {
enforce(!num.sign, off, 'Signed.'); enforce(!num.sign, off, 'Signed');
if (num.hi === 0) if (num.hi === 0)
return encoding.writeVarint2(dst, num.toInt(), off); return encoding.writeVarint2(dst, num.toInt(), off);
const tmp = [];
let len = 0;
num = num.clone(); num = num.clone();
const tmp = [];
let len = 0;
for (;;) { for (;;) {
tmp[len] = num.andln(0x7f) | (len ? 0x80 : 0x00); tmp[len] = num.andln(0x7f) | (len ? 0x80 : 0x00);
if (num.lten(0x7f)) if (num.lten(0x7f))
break; break;
num.iushrn(7).isubn(1); num.ishrn(7).isubn(1);
len++; len++;
} }
enforce(off + len <= dst.length, off, 'Out of bounds write'); enforce(off + len + 1 <= dst.length, off, 'Out of bounds write');
do { do {
dst[off++] = tmp[len]; dst[off++] = tmp[len];
@ -878,7 +889,7 @@ encoding.writeVarint2N = function writeVarint2N(dst, num, off) {
*/ */
encoding.sizeVarint2N = function sizeVarint2N(num) { encoding.sizeVarint2N = function sizeVarint2N(num) {
enforce(!num.sign, 0, 'Signed.'); enforce(!num.sign, 0, 'Signed');
if (num.hi === 0) if (num.hi === 0)
return encoding.sizeVarint2(num.toInt()); return encoding.sizeVarint2(num.toInt());
@ -891,7 +902,7 @@ encoding.sizeVarint2N = function sizeVarint2N(num) {
size++; size++;
if (num.lten(0x7f)) if (num.lten(0x7f))
break; break;
num.iushrn(7).isubn(1); num.ishrn(7).isubn(1);
} }
return size; return size;

View File

@ -128,54 +128,54 @@ describe('Utils', function() {
* 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] * 2^32: [0x8E 0xFE 0xFE 0xFF 0x00]
*/ */
let b = Buffer.allocUnsafe(1); let b = Buffer.alloc(1, 0xff);
encoding.writeVarint2(b, 0, 0); encoding.writeVarint2(b, 0, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 0); assert.strictEqual(encoding.readVarint2(b, 0).value, 0);
assert.deepEqual(b, [0]); assert.deepEqual(b, [0]);
b = Buffer.allocUnsafe(1); b = Buffer.alloc(1, 0xff);
encoding.writeVarint2(b, 1, 0); encoding.writeVarint2(b, 1, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 1); assert.strictEqual(encoding.readVarint2(b, 0).value, 1);
assert.deepEqual(b, [1]); assert.deepEqual(b, [1]);
b = Buffer.allocUnsafe(1); b = Buffer.alloc(1, 0xff);
encoding.writeVarint2(b, 127, 0); encoding.writeVarint2(b, 127, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 127); assert.strictEqual(encoding.readVarint2(b, 0).value, 127);
assert.deepEqual(b, [0x7f]); assert.deepEqual(b, [0x7f]);
b = Buffer.allocUnsafe(2); b = Buffer.alloc(2, 0xff);
encoding.writeVarint2(b, 128, 0); encoding.writeVarint2(b, 128, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 128); assert.strictEqual(encoding.readVarint2(b, 0).value, 128);
assert.deepEqual(b, [0x80, 0x00]); assert.deepEqual(b, [0x80, 0x00]);
b = Buffer.allocUnsafe(2); b = Buffer.alloc(2, 0xff);
encoding.writeVarint2(b, 255, 0); encoding.writeVarint2(b, 255, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 255); assert.strictEqual(encoding.readVarint2(b, 0).value, 255);
assert.deepEqual(b, [0x80, 0x7f]); assert.deepEqual(b, [0x80, 0x7f]);
b = Buffer.allocUnsafe(2); b = Buffer.alloc(2, 0xff);
encoding.writeVarint2(b, 16383, 0); encoding.writeVarint2(b, 16383, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 16383); assert.strictEqual(encoding.readVarint2(b, 0).value, 16383);
assert.deepEqual(b, [0xfe, 0x7f]); assert.deepEqual(b, [0xfe, 0x7f]);
b = Buffer.allocUnsafe(2); b = Buffer.alloc(2, 0xff);
encoding.writeVarint2(b, 16384, 0); encoding.writeVarint2(b, 16384, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 16384); assert.strictEqual(encoding.readVarint2(b, 0).value, 16384);
assert.deepEqual(b, [0xff, 0x00]); assert.deepEqual(b, [0xff, 0x00]);
b = Buffer.allocUnsafe(3); b = Buffer.alloc(3, 0xff);
encoding.writeVarint2(b, 16511, 0); encoding.writeVarint2(b, 16511, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 16511); assert.strictEqual(encoding.readVarint2(b, 0).value, 16511);
assert.deepEqual(b.slice(0, 2), [0xff, 0x7f]);
// assert.deepEqual(b, [0x80, 0xff, 0x7f]); // assert.deepEqual(b, [0x80, 0xff, 0x7f]);
assert.deepEqual(b, [0xff, 0x7f, 0x00]);
b = Buffer.allocUnsafe(3); b = Buffer.alloc(3, 0xff);
encoding.writeVarint2(b, 65535, 0); encoding.writeVarint2(b, 65535, 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, 65535); assert.strictEqual(encoding.readVarint2(b, 0).value, 65535);
// assert.deepEqual(b, [0x82, 0xfd, 0x7f]);
assert.deepEqual(b, [0x82, 0xfe, 0x7f]); assert.deepEqual(b, [0x82, 0xfe, 0x7f]);
// assert.deepEqual(b, [0x82, 0xfd, 0x7f]);
b = Buffer.allocUnsafe(5); b = Buffer.alloc(5, 0xff);
encoding.writeVarint2(b, Math.pow(2, 32), 0); encoding.writeVarint2(b, Math.pow(2, 32), 0);
assert.strictEqual(encoding.readVarint2(b, 0).value, Math.pow(2, 32)); assert.strictEqual(encoding.readVarint2(b, 0).value, Math.pow(2, 32));
assert.deepEqual(b, [0x8e, 0xfe, 0xfe, 0xff, 0x00]); assert.deepEqual(b, [0x8e, 0xfe, 0xfe, 0xff, 0x00]);