Skip to content

Commit 31d3890

Browse files
author
Daniel Wirtz
committed
varint64 support
1 parent 1e3c535 commit 31d3890

File tree

11 files changed

+2978
-870
lines changed

11 files changed

+2978
-870
lines changed

ByteBuffer.js

Lines changed: 255 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@
10401040
* Maximum number of bytes required to store a 32bit base 128 variable-length integer.
10411041
* @type {number}
10421042
* @const
1043+
* @expose
10431044
*/
10441045
ByteBuffer.MAX_VARINT32_BYTES = 5;
10451046

@@ -1102,7 +1103,7 @@
11021103
ByteBuffer.prototype.readVarint32 = function(offset) {
11031104
var advance = typeof offset == 'undefined';
11041105
offset = typeof offset != 'undefined' ? offset : this.offset;
1105-
// ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc
1106+
// ref: src/google/protobuf/io/coded_stream.cc
11061107

11071108
var src = new Uint8Array(this.array),
11081109
count = 0,
@@ -1154,6 +1155,154 @@
11541155
}
11551156
return ByteBuffer.zigZagDecode32(dec);
11561157
};
1158+
1159+
/**
1160+
* Maximum number of bytes required to store a 64bit base 128 variable-length integer.
1161+
* @type {number}
1162+
* @const
1163+
* @expose
1164+
*/
1165+
ByteBuffer.MAX_VARINT64_BYTES = 10;
1166+
1167+
/**
1168+
* @type {number}
1169+
* @const
1170+
* @private
1171+
*/
1172+
var TWO_PWR_7_DBL = 1 << 7;
1173+
1174+
/**
1175+
* @type {number}
1176+
* @const
1177+
* @private
1178+
*/
1179+
var TWO_PWR_14_DBL = TWO_PWR_7_DBL * TWO_PWR_7_DBL;
1180+
1181+
/**
1182+
* @type {number}
1183+
* @const
1184+
* @private
1185+
*/
1186+
var TWO_PWR_21_DBL = TWO_PWR_7_DBL * TWO_PWR_14_DBL;
1187+
1188+
/**
1189+
* @type {number}
1190+
* @const
1191+
* @private
1192+
*/
1193+
var TWO_PWR_28_DBL = TWO_PWR_14_DBL * TWO_PWR_14_DBL;
1194+
1195+
/**
1196+
* Writes a 64bit base 128 variable-length integer as used in protobuf.
1197+
* @param {number|Long} value Value to write
1198+
* @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1199+
* @return {ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
1200+
* @throws {Error} If long support is not available
1201+
* @expose
1202+
*/
1203+
ByteBuffer.prototype.writeVarint64 = function(value, offset) {
1204+
if (!Long) {
1205+
throw(new Error("Long support is not available: See https://github.com/dcodeIO/ByteBuffer.js#on-long-int64-support for details"))
1206+
}
1207+
var advance = typeof offset == 'undefined';
1208+
offset = typeof offset != 'undefined' ? offset : this.offset;
1209+
if (!(typeof value == 'object' && value instanceof Long)) value = Long.fromNumber(value, false);
1210+
1211+
var part0 = value.toInt() >>> 0,
1212+
part1 = value.shiftRightUnsigned(28).toInt() >>> 0,
1213+
part2 = value.shiftRightUnsigned(56).toInt() >>> 0,
1214+
size = ByteBuffer.calculateVarint64(value),
1215+
dst = new Uint8Array(this.array);
1216+
1217+
this.ensureCapacity(offset+size);
1218+
switch (size) {
1219+
case 10: dst[offset+9] = ((part2 >>> 7) | 0x80);
1220+
case 9 : dst[offset+8] = ((part2 ) | 0x80);
1221+
case 8 : dst[offset+7] = ((part1 >>> 21) | 0x80);
1222+
case 7 : dst[offset+6] = ((part1 >>> 14) | 0x80);
1223+
case 6 : dst[offset+5] = ((part1 >>> 7) | 0x80);
1224+
case 5 : dst[offset+4] = ((part1 ) | 0x80);
1225+
case 4 : dst[offset+3] = ((part0 >>> 21) | 0x80);
1226+
case 3 : dst[offset+2] = ((part0 >>> 14) | 0x80);
1227+
case 2 : dst[offset+1] = ((part0 >>> 7) | 0x80);
1228+
case 1 : dst[offset+0] = ((part0 ) | 0x80);
1229+
}
1230+
dst[offset+size-1] &= 0x7F;
1231+
if (advance) {
1232+
this.offset += size;
1233+
return this;
1234+
} else {
1235+
return size;
1236+
}
1237+
};
1238+
1239+
/**
1240+
* Reads a 32bit base 128 variable-length integer as used in protobuf.
1241+
* @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1242+
* @return {Long|{value: Long, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read.
1243+
* @throws {Error} If it's not a valid varint or long support is not available
1244+
* @expose
1245+
*/
1246+
ByteBuffer.prototype.readVarint64 = function(offset) {
1247+
if (!Long) {
1248+
throw(new Error("Long support is not available: See https://github.com/dcodeIO/ByteBuffer.js#on-long-int64-support for details"))
1249+
}
1250+
var advance = typeof offset == 'undefined';
1251+
offset = typeof offset != 'undefined' ? offset : this.offset;
1252+
var start = offset;
1253+
// ref: src/google/protobuf/io/coded_stream.cc
1254+
1255+
var src = new Uint8Array(this.array);
1256+
var part0, part1 = 0, part2 = 0, b;
1257+
b = src[offset++]; part0 = (b & 0x7F) ; if (b & 0x80) {
1258+
b = src[offset++]; part0 |= (b & 0x7F) << 7; if (b & 0x80) {
1259+
b = src[offset++]; part0 |= (b & 0x7F) << 14; if (b & 0x80) {
1260+
b = src[offset++]; part0 |= (b & 0x7F) << 21; if (b & 0x80) {
1261+
b = src[offset++]; part1 = (b & 0x7F) ; if (b & 0x80) {
1262+
b = src[offset++]; part1 |= (b & 0x7F) << 7; if (b & 0x80) {
1263+
b = src[offset++]; part1 |= (b & 0x7F) << 14; if (b & 0x80) {
1264+
b = src[offset++]; part1 |= (b & 0x7F) << 21; if (b & 0x80) {
1265+
b = src[offset++]; part2 = (b & 0x7F) ; if (b & 0x80) {
1266+
b = src[offset++]; part2 |= (b & 0x7F) << 7; if (b & 0x80) {
1267+
throw(new Error("Data must be corrupt: Buffer overrun")); }}}}}}}}}}
1268+
var value = Long.from28Bits(part0, part1, part2, false);
1269+
if (advance) {
1270+
this.offset = offset;
1271+
return value;
1272+
} else {
1273+
return {
1274+
"value": value,
1275+
"length": offset-start
1276+
};
1277+
}
1278+
};
1279+
1280+
/**
1281+
* Writes a zigzag encoded 64bit base 128 encoded variable-length integer as used in protobuf.
1282+
* @param {number} value Value to write
1283+
* @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1284+
* @return {ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
1285+
* @expose
1286+
*/
1287+
ByteBuffer.prototype.writeZigZagVarint64 = function(value, offset) {
1288+
return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset);
1289+
};
1290+
1291+
/**
1292+
* Reads a zigzag encoded 64bit base 128 variable-length integer as used in protobuf.
1293+
* @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1294+
* @return {number|{value: number, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read.
1295+
* @throws {Error} If it's not a valid varint
1296+
* @expose
1297+
*/
1298+
ByteBuffer.prototype.readZigZagVarint64 = function(offset) {
1299+
var dec = this.readVarint64(offset);
1300+
if (typeof dec == 'object') {
1301+
dec['value'] = ByteBuffer.zigZagDecode64(dec['value']);
1302+
return dec;
1303+
}
1304+
return ByteBuffer.zigZagDecode64(dec);
1305+
};
11571306

11581307
/**
11591308
* Writes a base 128 variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#writeVarint32}.
@@ -1173,49 +1322,136 @@
11731322
* @expose
11741323
*/
11751324
ByteBuffer.prototype.readVarint = ByteBuffer.prototype.readVarint32;
1325+
1326+
/**
1327+
* Writes a zigzag encoded base 128 encoded variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#writeZigZagVarint32}.
1328+
* @param {number} value Value to write
1329+
* @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1330+
* @return {ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
1331+
* @expose
1332+
*/
1333+
ByteBuffer.prototype.writeZigZagVarint = ByteBuffer.prototype.writeZigZagVarint32;
1334+
1335+
/**
1336+
* Reads a zigzag encoded base 128 variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#readZigZagVarint32}.
1337+
* @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted.
1338+
* @return {number|{value: number, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read.
1339+
* @throws {Error} If it's not a valid varint
1340+
* @expose
1341+
*/
1342+
ByteBuffer.prototype.readZigZagVarint = ByteBuffer.prototype.readZigZagVarint32;
11761343

11771344
/**
11781345
* Calculates the actual number of bytes required to encode a 32bit base 128 variable-length integer.
11791346
* @param {number} value Value to encode
1180-
* @return {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} (35bit), no overflow errors.
1347+
* @return {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} (35bit). No overflow error.
11811348
* @expose
11821349
*/
11831350
ByteBuffer.calculateVarint32 = function(value) {
1184-
// ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc
1185-
ByteBuffer.UINT32[0] = value;
1186-
if (ByteBuffer.UINT32[0] < 0x80) {
1351+
// ref: src/google/protobuf/io/coded_stream.cc
1352+
value = value >>> 0;
1353+
if (value < TWO_PWR_7_DBL) {
11871354
return 1;
1188-
} else if (ByteBuffer.UINT32[0] < 0x4000) {
1355+
} else if (value < TWO_PWR_14_DBL) {
11891356
return 2;
1190-
} else if (ByteBuffer.UINT32[0] < 0x200000) {
1357+
} else if (value < TWO_PWR_21_DBL) {
11911358
return 3;
1192-
} else if (ByteBuffer.UINT32[0] < 0x10000000) {
1359+
} else if (value < TWO_PWR_28_DBL) {
11931360
return 4;
11941361
} else {
11951362
return 5;
1196-
} // As this is casted, we can't throw an overflow error.
1363+
}
1364+
};
1365+
1366+
/**
1367+
* Calculates the actual number of bytes required to encode a 64bit base 128 variable-length integer.
1368+
* @param {number|Long} value Value to encode
1369+
* @return {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES}. No overflow error.
1370+
* @throws {Error} If long support is not available
1371+
* @expose
1372+
*/
1373+
ByteBuffer.calculateVarint64 = function(value) {
1374+
if (!Long) {
1375+
throw(new Error("Long support is not available: See https://github.com/dcodeIO/ByteBuffer.js#on-long-int64-support for details"))
1376+
}
1377+
// ref: src/google/protobuf/io/coded_stream.cc
1378+
if (!(typeof value == 'object' && value instanceof Long)) value = Long.fromNumber(value, false);
1379+
1380+
var part0 = value.toInt() >>> 0,
1381+
part1 = value.shiftRightUnsigned(28).toInt() >>> 0,
1382+
part2 = value.shiftRightUnsigned(56).toInt() >>> 0;
1383+
1384+
if (part2 == 0) {
1385+
if (part1 == 0) {
1386+
if (part0 < TWO_PWR_14_DBL) {
1387+
return part0 < TWO_PWR_7_DBL ? 1 : 2;
1388+
} else {
1389+
return part0 < TWO_PWR_21_DBL ? 3 : 4;
1390+
}
1391+
} else {
1392+
if (part1 < TWO_PWR_14_DBL) {
1393+
return part1 < TWO_PWR_7_DBL ? 5 : 6;
1394+
} else {
1395+
return part1 < TWO_PWR_21_DBL ? 7 : 8;
1396+
}
1397+
}
1398+
} else {
1399+
return part2 < TWO_PWR_7_DBL ? 9 : 10;
1400+
}
11971401
};
11981402

11991403
/**
1200-
* Encodes a signed integer so that it can be effectively used with varint encoding.
1201-
* @param {number} n Signed integer
1202-
* @return {number} Unsigned, zigzag encoded integer
1404+
* Encodes a signed 32bit integer so that it can be effectively used with varint encoding.
1405+
* @param {number} n Signed 32bit integer
1406+
* @return {number} Unsigned zigzag encoded 32bit integer
12031407
* @expose
12041408
*/
12051409
ByteBuffer.zigZagEncode32 = function(n) {
1206-
ByteBuffer.INT32[0]=n;
1207-
return ((n=ByteBuffer.INT32[0])>=0) ? n*2 : -n*2-1; // If we'd have real 32bit arithmetic: (n << 1) ^ (n >> 31);
1410+
// ref: src/google/protobuf/wire_format_lite.h
1411+
return (((n |= 0) << 1) ^ (n >> 31)) >>> 0;
12081412
};
12091413

12101414
/**
1211-
* Decodes a zigzag encoded signed integer.
1212-
* @param {number} n Unsigned zigzag encoded integer
1213-
* @return {number} Signed integer
1415+
* Decodes a zigzag encoded signed 32bit integer.
1416+
* @param {number} n Unsigned zigzag encoded 32bit integer
1417+
* @return {number} Signed 32bit integer
12141418
* @expose
12151419
*/
12161420
ByteBuffer.zigZagDecode32 = function(n) {
1217-
ByteBuffer.UINT32[0]=n;
1218-
return (((n=ByteBuffer.UINT32[0])&1)==0) ? n/2 : -(n+1)/2; // If we'd have real 32bit arithmetic: (n >> 1) ^ -(n & 1);
1421+
// ref: src/google/protobuf/wire_format_lite.h
1422+
return ((n >>> 1) ^ -(n & 1)) | 0;
1423+
};
1424+
1425+
/**
1426+
* Encodes a signed 64bit integer so that it can be effectively used with varint encoding.
1427+
* @param {number|Long} n Signed long
1428+
* @return {Long} Unsigned zigzag encoded long
1429+
* @expose
1430+
*/
1431+
ByteBuffer.zigZagEncode64 = function(n) {
1432+
// ref: src/google/protobuf/wire_format_lite.h
1433+
if (typeof n == 'object' && n instanceof Long) {
1434+
if (n.unsigned) n = n.toSigned();
1435+
} else {
1436+
n = Long.fromNumber(n, false);
1437+
}
1438+
return n.shiftLeft(1).xor(n.shiftRight(63)).toUnsigned();
1439+
};
1440+
1441+
/**
1442+
* Decodes a zigzag encoded signed 64bit integer.
1443+
* @param {Long} n Unsigned zigzag encoded long
1444+
* @return {Long} Signed long
1445+
* @expose
1446+
*/
1447+
ByteBuffer.zigZagDecode64 = function(n) {
1448+
// ref: src/google/protobuf/wire_format_lite.h
1449+
if (typeof n == 'object' && n instanceof Long) {
1450+
if (!n.unsigned) n = n.toUnsigned();
1451+
} else {
1452+
n = Long.fromNumber(n, true);
1453+
}
1454+
return n.shiftRightUnsigned(1).xor(n.and(Long.ONE).toSigned().negate()).toSigned();
12191455
};
12201456

12211457
/**

0 commit comments

Comments
 (0)