parent
4cf5bfee95
commit
cc938ea39a
70 changed files with 25118 additions and 0 deletions
@ -0,0 +1,314 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of apps JSPB for proto2 all types |
||||
*/ |
||||
goog.module('protobuf.benchmark.code_size.apps_jspb.AllTypesProto2'); |
||||
|
||||
// const ForeignEnum = goog.require('proto.proto2_unittest.ForeignEnum');
|
||||
const ForeignMessage = goog.require('proto.proto2_unittest.ForeignMessage'); |
||||
const TestAllTypes = goog.require('proto.proto2_unittest.TestAllTypes'); |
||||
const TestPackedTypes = goog.require('proto.proto2_unittest.TestPackedTypes'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
/** |
||||
* The testing scenario is the same as kernel one. |
||||
* We have |
||||
* 1) add element to repeated fields |
||||
* 2) add element list to repeated fields |
||||
* 3) set fields |
||||
* 4) set repeated fields element |
||||
* 5) get fields |
||||
* 6) get repeated fields element |
||||
* 7) get repeated fields length |
||||
* @return {string} |
||||
*/ |
||||
function accessAllTypes() { |
||||
const msgAllTypes = TestAllTypes.deserialize(''); |
||||
const msgPackedTypes = TestPackedTypes.deserialize(''); |
||||
|
||||
msgPackedTypes.addPackedBool(true); |
||||
[true].forEach((e) => msgPackedTypes.addPackedBool(e)); |
||||
msgAllTypes.addRepeatedBool(true, 1); |
||||
[true].forEach((e) => msgAllTypes.addRepeatedBool(e)); |
||||
msgAllTypes.addRepeatedBytes('1', 1); |
||||
['1'].forEach((e) => msgAllTypes.addRepeatedBytes(e)); |
||||
msgPackedTypes.addPackedDouble(1.0); |
||||
[1.0].forEach((e) => msgPackedTypes.addPackedDouble(e)); |
||||
msgAllTypes.addRepeatedDouble(1.0, 1); |
||||
[1.0].forEach((e) => msgAllTypes.addRepeatedDouble(e)); |
||||
msgPackedTypes.addPackedFixed32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedFixed32(e)); |
||||
msgAllTypes.addRepeatedFixed32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedFixed32(e)); |
||||
msgPackedTypes.addPackedFixed64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedFixed64(e)); |
||||
msgAllTypes.addRepeatedFixed64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedFixed64(e)); |
||||
msgPackedTypes.addPackedFloat(1.0, 1); |
||||
[1.0].forEach((e) => msgPackedTypes.addPackedFloat(e)); |
||||
msgAllTypes.addRepeatedFloat(1.0, 1); |
||||
[1.0].forEach((e) => msgAllTypes.addRepeatedFloat(e)); |
||||
msgPackedTypes.addPackedInt32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedInt32(e)); |
||||
msgAllTypes.addRepeatedInt32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedInt32(e)); |
||||
msgPackedTypes.addPackedInt64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedInt64(e)); |
||||
msgAllTypes.addRepeatedInt64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedInt64(e)); |
||||
// msgPackedTypes.addPackedEnum(ForeignEnum.FOREIGN_BAR);
|
||||
// [ForeignEnum.FOREIGN_BAR].forEach((e) => msgPackedTypes.addPackedEnum(e));
|
||||
// msgAllTypes.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
// [ForeignEnum.FOREIGN_BAR].forEach(
|
||||
// (e) => msgAllTypes.addRepeatedForeignEnum(e));
|
||||
msgAllTypes.addRepeatedForeignMessage(ForeignMessage.deserialize(''), 1); |
||||
[ForeignMessage.deserialize('')].forEach( |
||||
(e) => msgAllTypes.addRepeatedForeignMessage(e)); |
||||
msgPackedTypes.addPackedSfixed32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSfixed32(e)); |
||||
msgAllTypes.addRepeatedSfixed32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSfixed32(e)); |
||||
msgPackedTypes.addPackedSfixed64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSfixed64(e)); |
||||
msgAllTypes.addRepeatedSfixed64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSfixed64(e)); |
||||
msgPackedTypes.addPackedSint32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSint32(e)); |
||||
msgAllTypes.addRepeatedSint32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSint32(e)); |
||||
msgPackedTypes.addPackedSint64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSint64(e)); |
||||
msgAllTypes.addRepeatedSint64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSint64(e)); |
||||
msgAllTypes.addRepeatedString('', 1); |
||||
[''].forEach((e) => msgAllTypes.addRepeatedString(e)); |
||||
msgPackedTypes.addPackedUint32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedUint32(e)); |
||||
msgAllTypes.addRepeatedUint32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedUint32(e)); |
||||
msgPackedTypes.addPackedUint64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedUint64(e)); |
||||
msgAllTypes.addRepeatedUint64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedUint64(e)); |
||||
|
||||
msgAllTypes.setOptionalBool(true); |
||||
msgAllTypes.setOptionalBytes(''); |
||||
msgAllTypes.setOptionalDouble(1.0); |
||||
msgAllTypes.setOptionalFixed32(1); |
||||
msgAllTypes.setOptionalFixed64(1); |
||||
msgAllTypes.setOptionalFloat(1.0); |
||||
msgAllTypes.setOptionalInt32(1); |
||||
msgAllTypes.setOptionalInt64(1); |
||||
// msgAllTypes.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
msgAllTypes.setOptionalForeignMessage(ForeignMessage.deserialize('')); |
||||
msgAllTypes.setOptionalSfixed32(1); |
||||
msgAllTypes.setOptionalSfixed64(1); |
||||
msgAllTypes.setOptionalSint32(1); |
||||
msgAllTypes.setOptionalSint64(1); |
||||
msgAllTypes.setOptionalString(''); |
||||
msgAllTypes.setOptionalUint32(1); |
||||
msgAllTypes.setOptionalUint64(1); |
||||
msgPackedTypes.setPackedBoolList([true]); |
||||
let arrayVal; |
||||
arrayVal = msgPackedTypes.getPackedBoolList(); |
||||
arrayVal[0] = true; |
||||
msgPackedTypes.setPackedBoolList(arrayVal); |
||||
msgAllTypes.setRepeatedBoolList([true]); |
||||
arrayVal = msgAllTypes.getRepeatedBoolList(); |
||||
arrayVal[0] = true; |
||||
msgAllTypes.setRepeatedBoolList(arrayVal); |
||||
msgAllTypes.setRepeatedBytesList(['']); |
||||
arrayVal = msgAllTypes.getRepeatedBytesList(); |
||||
arrayVal[0] = ''; |
||||
msgAllTypes.setRepeatedBytesList(arrayVal); |
||||
msgPackedTypes.setPackedDoubleList([1.0]); |
||||
arrayVal = msgPackedTypes.getPackedDoubleList(); |
||||
arrayVal[0] = 1.0; |
||||
msgPackedTypes.setPackedDoubleList(arrayVal); |
||||
msgAllTypes.setRepeatedDoubleList([1.0]); |
||||
arrayVal = msgAllTypes.getRepeatedDoubleList(); |
||||
arrayVal[0] = 1.0; |
||||
msgAllTypes.setRepeatedDoubleList(arrayVal); |
||||
msgPackedTypes.setPackedFixed32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedFixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedFixed32List(arrayVal); |
||||
msgAllTypes.setRepeatedFixed32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedFixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedFixed32List(arrayVal); |
||||
msgPackedTypes.setPackedFixed64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedFixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedFixed64List(arrayVal); |
||||
msgAllTypes.setRepeatedFixed64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedFixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedFixed64List(arrayVal); |
||||
msgPackedTypes.setPackedFloatList([1.0]); |
||||
arrayVal = msgPackedTypes.getPackedFloatList(); |
||||
arrayVal[0] = 1.0; |
||||
msgPackedTypes.setPackedFloatList(arrayVal); |
||||
msgAllTypes.setRepeatedFloatList([1.0]); |
||||
arrayVal = msgAllTypes.getRepeatedFloatList(); |
||||
arrayVal[0] = 1.0; |
||||
msgAllTypes.setRepeatedFloatList(arrayVal); |
||||
msgPackedTypes.setPackedInt32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedInt32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedInt32List(arrayVal); |
||||
msgAllTypes.setRepeatedInt32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedInt32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedInt32List(arrayVal); |
||||
msgPackedTypes.setPackedInt64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedInt64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedInt64List(arrayVal); |
||||
msgAllTypes.setRepeatedInt64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedInt64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedInt64List(arrayVal); |
||||
// msgPackedTypes.setPackedEnumList([ForeignEnum.FOREIGN_BAR]);
|
||||
// arrayVal = msgPackedTypes.getPackedEnumList();
|
||||
// arrayVal[0] = ForeignEnum.FOREIGN_BAR;
|
||||
// msgPackedTypes.setPackedEnumList(arrayVal);
|
||||
// msgAllTypes.setRepeatedForeignEnumList([ForeignEnum.FOREIGN_BAR]);
|
||||
// arrayVal = msgAllTypes.getRepeatedForeignEnumList();
|
||||
// arrayVal[0] = ForeignEnum.FOREIGN_BAR;
|
||||
// msgAllTypes.setRepeatedForeignEnumList(arrayVal);
|
||||
msgAllTypes.setRepeatedForeignMessageList([ForeignMessage.deserialize('')]); |
||||
arrayVal = msgAllTypes.getRepeatedForeignMessageList(); |
||||
arrayVal[0] = ForeignMessage.deserialize(''); |
||||
msgAllTypes.setRepeatedForeignMessageList(arrayVal); |
||||
msgPackedTypes.setPackedSfixed32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSfixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSfixed32List(arrayVal); |
||||
msgAllTypes.setRepeatedSfixed32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSfixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSfixed32List(arrayVal); |
||||
msgPackedTypes.setPackedSfixed64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSfixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSfixed64List(arrayVal); |
||||
msgAllTypes.setRepeatedSfixed64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSfixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSfixed64List(arrayVal); |
||||
msgPackedTypes.setPackedSint32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSint32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSint32List(arrayVal); |
||||
msgAllTypes.setRepeatedSint32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSint32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSint32List(arrayVal); |
||||
msgPackedTypes.setPackedSint64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSint64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSint64List(arrayVal); |
||||
msgAllTypes.setRepeatedSint64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSint64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSint64List(arrayVal); |
||||
msgPackedTypes.setPackedUint32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedUint32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedUint32List(arrayVal); |
||||
msgAllTypes.setRepeatedUint32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedUint32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedUint32List(arrayVal); |
||||
msgPackedTypes.setPackedUint64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedUint64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedUint64List(arrayVal); |
||||
msgAllTypes.setRepeatedUint64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedUint64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedUint64List(arrayVal); |
||||
|
||||
let s = ''; |
||||
s += msgAllTypes.getOptionalBool() || false; |
||||
s += msgAllTypes.getOptionalBytes() || ''; |
||||
// s += msgAllTypes.getOptionalBytes_asB64() || "";
|
||||
// s += msgAllTypes.getOptionalBytes_asU8() || new Uint8Array([]);
|
||||
s += msgAllTypes.getOptionalDouble() || 0.0; |
||||
s += msgAllTypes.getOptionalFixed32() || 0; |
||||
s += msgAllTypes.getOptionalFixed64() || 0; |
||||
s += msgAllTypes.getOptionalFloat() || 0.0; |
||||
s += msgAllTypes.getOptionalInt32() || 0; |
||||
s += msgAllTypes.getOptionalInt64() || 0; |
||||
// s += msgAllTypes.getOptionalForeignEnum() || ForeignEnum.FOREIGN_BAR;
|
||||
s += msgAllTypes.getOptionalForeignMessage(); |
||||
s += msgAllTypes.getOptionalSfixed32() || 0; |
||||
s += msgAllTypes.getOptionalSfixed64() || 0; |
||||
s += msgAllTypes.getOptionalSint32() || 0; |
||||
s += msgAllTypes.getOptionalSint64() || 0; |
||||
s += msgAllTypes.getOptionalString() || ''; |
||||
s += msgAllTypes.getOptionalUint32() || 0; |
||||
s += msgAllTypes.getOptionalUint64() || 0; |
||||
s += msgAllTypes.getRepeatedBoolList(); |
||||
s += msgAllTypes.getRepeatedBoolList()[0]; |
||||
s += msgAllTypes.getRepeatedBoolList().length; |
||||
s += msgAllTypes.getRepeatedBytesList(); |
||||
s += msgAllTypes.getRepeatedBytesList()[0]; |
||||
s += msgAllTypes.getRepeatedBytesList().length; |
||||
s += msgAllTypes.getRepeatedBytesList_asB64(); |
||||
s += msgAllTypes.getRepeatedBytesList_asU8(); |
||||
s += msgAllTypes.getRepeatedDoubleList(); |
||||
s += msgAllTypes.getRepeatedDoubleList()[0]; |
||||
s += msgAllTypes.getRepeatedDoubleList().length; |
||||
s += msgAllTypes.getRepeatedFixed32List(); |
||||
s += msgAllTypes.getRepeatedFixed32List()[0]; |
||||
s += msgAllTypes.getRepeatedFixed32List().length; |
||||
s += msgAllTypes.getRepeatedFixed64List(); |
||||
s += msgAllTypes.getRepeatedFixed64List()[0]; |
||||
s += msgAllTypes.getRepeatedFixed64List().length; |
||||
s += msgAllTypes.getRepeatedFloatList(); |
||||
s += msgAllTypes.getRepeatedFloatList()[0]; |
||||
s += msgAllTypes.getRepeatedFloatList().length; |
||||
s += msgAllTypes.getRepeatedInt32List(); |
||||
s += msgAllTypes.getRepeatedInt32List()[0]; |
||||
s += msgAllTypes.getRepeatedInt32List().length; |
||||
s += msgAllTypes.getRepeatedInt64List(); |
||||
s += msgAllTypes.getRepeatedInt64List()[0]; |
||||
s += msgAllTypes.getRepeatedInt64List().length; |
||||
// s += msgAllTypes.getRepeatedForeignEnumList();
|
||||
// s += msgAllTypes.getRepeatedForeignEnumList()[0];
|
||||
// s += msgAllTypes.getRepeatedForeignEnumList().length;
|
||||
s += msgAllTypes.getRepeatedForeignMessageList(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList()[0]; |
||||
s += msgAllTypes.getRepeatedForeignMessageList().length; |
||||
s += msgAllTypes.getRepeatedSfixed32List(); |
||||
s += msgAllTypes.getRepeatedSfixed32List()[0]; |
||||
s += msgAllTypes.getRepeatedSfixed32List().length; |
||||
s += msgAllTypes.getRepeatedSfixed64List(); |
||||
s += msgAllTypes.getRepeatedSfixed64List()[0]; |
||||
s += msgAllTypes.getRepeatedSfixed64List().length; |
||||
s += msgAllTypes.getRepeatedSint32List(); |
||||
s += msgAllTypes.getRepeatedSint32List()[0]; |
||||
s += msgAllTypes.getRepeatedSint32List().length; |
||||
s += msgAllTypes.getRepeatedSint64List(); |
||||
s += msgAllTypes.getRepeatedSint64List()[0]; |
||||
s += msgAllTypes.getRepeatedSint64List().length; |
||||
s += msgAllTypes.getRepeatedStringList(); |
||||
s += msgAllTypes.getRepeatedStringList()[0]; |
||||
s += msgAllTypes.getRepeatedStringList().length; |
||||
s += msgAllTypes.getRepeatedUint32List(); |
||||
s += msgAllTypes.getRepeatedUint32List()[0]; |
||||
s += msgAllTypes.getRepeatedUint32List().length; |
||||
s += msgAllTypes.getRepeatedUint64List(); |
||||
s += msgAllTypes.getRepeatedUint64List()[0]; |
||||
s += msgAllTypes.getRepeatedUint64List().length; |
||||
|
||||
s += msgAllTypes.serialize(); |
||||
s += msgPackedTypes.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessAllTypes(); |
@ -0,0 +1,314 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of apps JSPB for proto3 all types |
||||
*/ |
||||
goog.module('protobuf.benchmark.code_size.apps_jspb.AllTypesProto3'); |
||||
|
||||
// const ForeignEnum = goog.require('proto.proto3_unittest.ForeignEnum');
|
||||
const ForeignMessage = goog.require('proto.proto3_unittest.ForeignMessage'); |
||||
const TestAllTypes = goog.require('proto.proto3_unittest.TestAllTypes'); |
||||
const TestPackedTypes = goog.require('proto.proto3_unittest.TestPackedTypes'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
/** |
||||
* The testing scenario is the same as kernel one. |
||||
* We have |
||||
* 1) add element to repeated fields |
||||
* 2) add element list to repeated fields |
||||
* 3) set fields |
||||
* 4) set repeated fields element |
||||
* 5) get fields |
||||
* 6) get repeated fields element |
||||
* 7) get repeated fields length |
||||
* @return {string} |
||||
*/ |
||||
function accessAllTypes() { |
||||
const msgAllTypes = TestAllTypes.deserialize(''); |
||||
const msgPackedTypes = TestPackedTypes.deserialize(''); |
||||
|
||||
msgPackedTypes.addPackedBool(true); |
||||
[true].forEach((e) => msgPackedTypes.addPackedBool(e)); |
||||
msgAllTypes.addRepeatedBool(true, 1); |
||||
[true].forEach((e) => msgAllTypes.addRepeatedBool(e)); |
||||
msgAllTypes.addRepeatedBytes('1', 1); |
||||
['1'].forEach((e) => msgAllTypes.addRepeatedBytes(e)); |
||||
msgPackedTypes.addPackedDouble(1.0); |
||||
[1.0].forEach((e) => msgPackedTypes.addPackedDouble(e)); |
||||
msgAllTypes.addRepeatedDouble(1.0, 1); |
||||
[1.0].forEach((e) => msgAllTypes.addRepeatedDouble(e)); |
||||
msgPackedTypes.addPackedFixed32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedFixed32(e)); |
||||
msgAllTypes.addRepeatedFixed32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedFixed32(e)); |
||||
msgPackedTypes.addPackedFixed64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedFixed64(e)); |
||||
msgAllTypes.addRepeatedFixed64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedFixed64(e)); |
||||
msgPackedTypes.addPackedFloat(1.0, 1); |
||||
[1.0].forEach((e) => msgPackedTypes.addPackedFloat(e)); |
||||
msgAllTypes.addRepeatedFloat(1.0, 1); |
||||
[1.0].forEach((e) => msgAllTypes.addRepeatedFloat(e)); |
||||
msgPackedTypes.addPackedInt32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedInt32(e)); |
||||
msgAllTypes.addRepeatedInt32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedInt32(e)); |
||||
msgPackedTypes.addPackedInt64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedInt64(e)); |
||||
msgAllTypes.addRepeatedInt64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedInt64(e)); |
||||
// msgPackedTypes.addPackedEnum(ForeignEnum.FOREIGN_BAR);
|
||||
// [ForeignEnum.FOREIGN_BAR].forEach((e) => msgPackedTypes.addPackedEnum(e));
|
||||
// msgAllTypes.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
// [ForeignEnum.FOREIGN_BAR].forEach(
|
||||
// (e) => msgAllTypes.addRepeatedForeignEnum(e));
|
||||
msgAllTypes.addRepeatedForeignMessage(ForeignMessage.deserialize(''), 1); |
||||
[ForeignMessage.deserialize('')].forEach( |
||||
(e) => msgAllTypes.addRepeatedForeignMessage(e)); |
||||
msgPackedTypes.addPackedSfixed32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSfixed32(e)); |
||||
msgAllTypes.addRepeatedSfixed32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSfixed32(e)); |
||||
msgPackedTypes.addPackedSfixed64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSfixed64(e)); |
||||
msgAllTypes.addRepeatedSfixed64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSfixed64(e)); |
||||
msgPackedTypes.addPackedSint32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSint32(e)); |
||||
msgAllTypes.addRepeatedSint32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSint32(e)); |
||||
msgPackedTypes.addPackedSint64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedSint64(e)); |
||||
msgAllTypes.addRepeatedSint64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedSint64(e)); |
||||
msgAllTypes.addRepeatedString('', 1); |
||||
[''].forEach((e) => msgAllTypes.addRepeatedString(e)); |
||||
msgPackedTypes.addPackedUint32(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedUint32(e)); |
||||
msgAllTypes.addRepeatedUint32(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedUint32(e)); |
||||
msgPackedTypes.addPackedUint64(1, 1); |
||||
[1].forEach((e) => msgPackedTypes.addPackedUint64(e)); |
||||
msgAllTypes.addRepeatedUint64(1, 1); |
||||
[1].forEach((e) => msgAllTypes.addRepeatedUint64(e)); |
||||
|
||||
msgAllTypes.setOptionalBool(true); |
||||
msgAllTypes.setOptionalBytes(''); |
||||
msgAllTypes.setOptionalDouble(1.0); |
||||
msgAllTypes.setOptionalFixed32(1); |
||||
msgAllTypes.setOptionalFixed64(1); |
||||
msgAllTypes.setOptionalFloat(1.0); |
||||
msgAllTypes.setOptionalInt32(1); |
||||
msgAllTypes.setOptionalInt64(1); |
||||
// msgAllTypes.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
msgAllTypes.setOptionalForeignMessage(ForeignMessage.deserialize('')); |
||||
msgAllTypes.setOptionalSfixed32(1); |
||||
msgAllTypes.setOptionalSfixed64(1); |
||||
msgAllTypes.setOptionalSint32(1); |
||||
msgAllTypes.setOptionalSint64(1); |
||||
msgAllTypes.setOptionalString(''); |
||||
msgAllTypes.setOptionalUint32(1); |
||||
msgAllTypes.setOptionalUint64(1); |
||||
msgPackedTypes.setPackedBoolList([true]); |
||||
let arrayVal; |
||||
arrayVal = msgPackedTypes.getPackedBoolList(); |
||||
arrayVal[0] = true; |
||||
msgPackedTypes.setPackedBoolList(arrayVal); |
||||
msgAllTypes.setRepeatedBoolList([true]); |
||||
arrayVal = msgAllTypes.getRepeatedBoolList(); |
||||
arrayVal[0] = true; |
||||
msgAllTypes.setRepeatedBoolList(arrayVal); |
||||
msgAllTypes.setRepeatedBytesList(['']); |
||||
arrayVal = msgAllTypes.getRepeatedBytesList(); |
||||
arrayVal[0] = ''; |
||||
msgAllTypes.setRepeatedBytesList(arrayVal); |
||||
msgPackedTypes.setPackedDoubleList([1.0]); |
||||
arrayVal = msgPackedTypes.getPackedDoubleList(); |
||||
arrayVal[0] = 1.0; |
||||
msgPackedTypes.setPackedDoubleList(arrayVal); |
||||
msgAllTypes.setRepeatedDoubleList([1.0]); |
||||
arrayVal = msgAllTypes.getRepeatedDoubleList(); |
||||
arrayVal[0] = 1.0; |
||||
msgAllTypes.setRepeatedDoubleList(arrayVal); |
||||
msgPackedTypes.setPackedFixed32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedFixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedFixed32List(arrayVal); |
||||
msgAllTypes.setRepeatedFixed32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedFixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedFixed32List(arrayVal); |
||||
msgPackedTypes.setPackedFixed64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedFixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedFixed64List(arrayVal); |
||||
msgAllTypes.setRepeatedFixed64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedFixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedFixed64List(arrayVal); |
||||
msgPackedTypes.setPackedFloatList([1.0]); |
||||
arrayVal = msgPackedTypes.getPackedFloatList(); |
||||
arrayVal[0] = 1.0; |
||||
msgPackedTypes.setPackedFloatList(arrayVal); |
||||
msgAllTypes.setRepeatedFloatList([1.0]); |
||||
arrayVal = msgAllTypes.getRepeatedFloatList(); |
||||
arrayVal[0] = 1.0; |
||||
msgAllTypes.setRepeatedFloatList(arrayVal); |
||||
msgPackedTypes.setPackedInt32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedInt32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedInt32List(arrayVal); |
||||
msgAllTypes.setRepeatedInt32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedInt32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedInt32List(arrayVal); |
||||
msgPackedTypes.setPackedInt64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedInt64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedInt64List(arrayVal); |
||||
msgAllTypes.setRepeatedInt64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedInt64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedInt64List(arrayVal); |
||||
// msgPackedTypes.setPackedEnumList([ForeignEnum.FOREIGN_BAR]);
|
||||
// arrayVal = msgPackedTypes.getPackedEnumList();
|
||||
// arrayVal[0] = ForeignEnum.FOREIGN_BAR;
|
||||
// msgPackedTypes.setPackedEnumList(arrayVal);
|
||||
// msgAllTypes.setRepeatedForeignEnumList([ForeignEnum.FOREIGN_BAR]);
|
||||
// arrayVal = msgAllTypes.getRepeatedForeignEnumList();
|
||||
// arrayVal[0] = ForeignEnum.FOREIGN_BAR;
|
||||
// msgAllTypes.setRepeatedForeignEnumList(arrayVal);
|
||||
msgAllTypes.setRepeatedForeignMessageList([ForeignMessage.deserialize('')]); |
||||
arrayVal = msgAllTypes.getRepeatedForeignMessageList(); |
||||
arrayVal[0] = ForeignMessage.deserialize(''); |
||||
msgAllTypes.setRepeatedForeignMessageList(arrayVal); |
||||
msgPackedTypes.setPackedSfixed32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSfixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSfixed32List(arrayVal); |
||||
msgAllTypes.setRepeatedSfixed32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSfixed32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSfixed32List(arrayVal); |
||||
msgPackedTypes.setPackedSfixed64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSfixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSfixed64List(arrayVal); |
||||
msgAllTypes.setRepeatedSfixed64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSfixed64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSfixed64List(arrayVal); |
||||
msgPackedTypes.setPackedSint32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSint32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSint32List(arrayVal); |
||||
msgAllTypes.setRepeatedSint32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSint32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSint32List(arrayVal); |
||||
msgPackedTypes.setPackedSint64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedSint64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedSint64List(arrayVal); |
||||
msgAllTypes.setRepeatedSint64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedSint64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedSint64List(arrayVal); |
||||
msgPackedTypes.setPackedUint32List([1]); |
||||
arrayVal = msgPackedTypes.getPackedUint32List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedUint32List(arrayVal); |
||||
msgAllTypes.setRepeatedUint32List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedUint32List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedUint32List(arrayVal); |
||||
msgPackedTypes.setPackedUint64List([1]); |
||||
arrayVal = msgPackedTypes.getPackedUint64List(); |
||||
arrayVal[0] = 1; |
||||
msgPackedTypes.setPackedUint64List(arrayVal); |
||||
msgAllTypes.setRepeatedUint64List([1]); |
||||
arrayVal = msgAllTypes.getRepeatedUint64List(); |
||||
arrayVal[0] = 1; |
||||
msgAllTypes.setRepeatedUint64List(arrayVal); |
||||
|
||||
let s = ''; |
||||
s += msgAllTypes.getOptionalBool() || false; |
||||
s += msgAllTypes.getOptionalBytes() || ''; |
||||
// s += msgAllTypes.getOptionalBytes_asB64() || "";
|
||||
// s += msgAllTypes.getOptionalBytes_asU8() || new Uint8Array([]);
|
||||
s += msgAllTypes.getOptionalDouble() || 0.0; |
||||
s += msgAllTypes.getOptionalFixed32() || 0; |
||||
s += msgAllTypes.getOptionalFixed64() || 0; |
||||
s += msgAllTypes.getOptionalFloat() || 0.0; |
||||
s += msgAllTypes.getOptionalInt32() || 0; |
||||
s += msgAllTypes.getOptionalInt64() || 0; |
||||
// s += msgAllTypes.getOptionalForeignEnum() || ForeignEnum.FOREIGN_BAR;
|
||||
s += msgAllTypes.getOptionalForeignMessage(); |
||||
s += msgAllTypes.getOptionalSfixed32() || 0; |
||||
s += msgAllTypes.getOptionalSfixed64() || 0; |
||||
s += msgAllTypes.getOptionalSint32() || 0; |
||||
s += msgAllTypes.getOptionalSint64() || 0; |
||||
s += msgAllTypes.getOptionalString() || ''; |
||||
s += msgAllTypes.getOptionalUint32() || 0; |
||||
s += msgAllTypes.getOptionalUint64() || 0; |
||||
s += msgAllTypes.getRepeatedBoolList(); |
||||
s += msgAllTypes.getRepeatedBoolList()[0]; |
||||
s += msgAllTypes.getRepeatedBoolList().length; |
||||
s += msgAllTypes.getRepeatedBytesList(); |
||||
s += msgAllTypes.getRepeatedBytesList()[0]; |
||||
s += msgAllTypes.getRepeatedBytesList().length; |
||||
s += msgAllTypes.getRepeatedBytesList_asB64(); |
||||
s += msgAllTypes.getRepeatedBytesList_asU8(); |
||||
s += msgAllTypes.getRepeatedDoubleList(); |
||||
s += msgAllTypes.getRepeatedDoubleList()[0]; |
||||
s += msgAllTypes.getRepeatedDoubleList().length; |
||||
s += msgAllTypes.getRepeatedFixed32List(); |
||||
s += msgAllTypes.getRepeatedFixed32List()[0]; |
||||
s += msgAllTypes.getRepeatedFixed32List().length; |
||||
s += msgAllTypes.getRepeatedFixed64List(); |
||||
s += msgAllTypes.getRepeatedFixed64List()[0]; |
||||
s += msgAllTypes.getRepeatedFixed64List().length; |
||||
s += msgAllTypes.getRepeatedFloatList(); |
||||
s += msgAllTypes.getRepeatedFloatList()[0]; |
||||
s += msgAllTypes.getRepeatedFloatList().length; |
||||
s += msgAllTypes.getRepeatedInt32List(); |
||||
s += msgAllTypes.getRepeatedInt32List()[0]; |
||||
s += msgAllTypes.getRepeatedInt32List().length; |
||||
s += msgAllTypes.getRepeatedInt64List(); |
||||
s += msgAllTypes.getRepeatedInt64List()[0]; |
||||
s += msgAllTypes.getRepeatedInt64List().length; |
||||
// s += msgAllTypes.getRepeatedForeignEnumList();
|
||||
// s += msgAllTypes.getRepeatedForeignEnumList()[0];
|
||||
// s += msgAllTypes.getRepeatedForeignEnumList().length;
|
||||
s += msgAllTypes.getRepeatedForeignMessageList(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList()[0]; |
||||
s += msgAllTypes.getRepeatedForeignMessageList().length; |
||||
s += msgAllTypes.getRepeatedSfixed32List(); |
||||
s += msgAllTypes.getRepeatedSfixed32List()[0]; |
||||
s += msgAllTypes.getRepeatedSfixed32List().length; |
||||
s += msgAllTypes.getRepeatedSfixed64List(); |
||||
s += msgAllTypes.getRepeatedSfixed64List()[0]; |
||||
s += msgAllTypes.getRepeatedSfixed64List().length; |
||||
s += msgAllTypes.getRepeatedSint32List(); |
||||
s += msgAllTypes.getRepeatedSint32List()[0]; |
||||
s += msgAllTypes.getRepeatedSint32List().length; |
||||
s += msgAllTypes.getRepeatedSint64List(); |
||||
s += msgAllTypes.getRepeatedSint64List()[0]; |
||||
s += msgAllTypes.getRepeatedSint64List().length; |
||||
s += msgAllTypes.getRepeatedStringList(); |
||||
s += msgAllTypes.getRepeatedStringList()[0]; |
||||
s += msgAllTypes.getRepeatedStringList().length; |
||||
s += msgAllTypes.getRepeatedUint32List(); |
||||
s += msgAllTypes.getRepeatedUint32List()[0]; |
||||
s += msgAllTypes.getRepeatedUint32List().length; |
||||
s += msgAllTypes.getRepeatedUint64List(); |
||||
s += msgAllTypes.getRepeatedUint64List()[0]; |
||||
s += msgAllTypes.getRepeatedUint64List().length; |
||||
|
||||
s += msgAllTypes.serialize(); |
||||
s += msgPackedTypes.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessAllTypes(); |
@ -0,0 +1,53 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of apps JSPB for proto2 popular types. |
||||
*/ |
||||
goog.module('protobuf.benchmark.code_size.apps_jspb.PopularTypesProto2'); |
||||
|
||||
// const ForeignEnum = goog.require('proto.proto2_unittest.ForeignEnum');
|
||||
const ForeignMessage = goog.require('proto.proto2_unittest.ForeignMessage'); |
||||
const TestAllTypes = goog.require('proto.proto2_unittest.TestAllTypes'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
/** |
||||
* @return {string} |
||||
*/ |
||||
function accessPopularTypes() { |
||||
const msgAllTypes = TestAllTypes.deserialize(''); |
||||
msgAllTypes.addRepeatedForeignMessage(ForeignMessage.deserialize(''), 1); |
||||
[ForeignMessage.deserialize('')].forEach( |
||||
(e) => msgAllTypes.addRepeatedForeignMessage(e)); |
||||
|
||||
msgAllTypes.setOptionalString(''); |
||||
msgAllTypes.setOptionalInt32(1); |
||||
msgAllTypes.setOptionalForeignMessage(ForeignMessage.deserialize('')); |
||||
msgAllTypes.setOptionalBool(true); |
||||
// msgAllTypes.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
msgAllTypes.setOptionalInt64(1); |
||||
msgAllTypes.setOptionalDouble(1.0); |
||||
msgAllTypes.setRepeatedForeignMessageList([ForeignMessage.deserialize('')]); |
||||
let arrayVal = msgAllTypes.getRepeatedForeignMessageList(); |
||||
arrayVal[0] = ForeignMessage.deserialize(''); |
||||
msgAllTypes.setRepeatedForeignMessageList(arrayVal); |
||||
msgAllTypes.setOptionalUint64(1); |
||||
|
||||
let s = ''; |
||||
s += msgAllTypes.getOptionalString(); |
||||
s += msgAllTypes.getOptionalInt32(); |
||||
s += msgAllTypes.getOptionalForeignMessage(); |
||||
s += msgAllTypes.getOptionalBool(); |
||||
// s += msgAllTypes.getOptionalForeignEnum();
|
||||
s += msgAllTypes.getOptionalInt64(); |
||||
s += msgAllTypes.getOptionalDouble(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList()[0]; |
||||
s += msgAllTypes.getRepeatedForeignMessageList().length; |
||||
s += msgAllTypes.getOptionalUint64(); |
||||
|
||||
s += msgAllTypes.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessPopularTypes(); |
@ -0,0 +1,53 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of apps JSPB for proto3 popular types. |
||||
*/ |
||||
goog.module('protobuf.benchmark.code_size.apps_jspb.PopularTypesProto3'); |
||||
|
||||
// const ForeignEnum = goog.require('proto.proto3_unittest.ForeignEnum');
|
||||
const ForeignMessage = goog.require('proto.proto3_unittest.ForeignMessage'); |
||||
const TestAllTypes = goog.require('proto.proto3_unittest.TestAllTypes'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
/** |
||||
* @return {string} |
||||
*/ |
||||
function accessPopularTypes() { |
||||
const msgAllTypes = TestAllTypes.deserialize(''); |
||||
msgAllTypes.addRepeatedForeignMessage(ForeignMessage.deserialize(''), 1); |
||||
[ForeignMessage.deserialize('')].forEach( |
||||
(e) => msgAllTypes.addRepeatedForeignMessage(e)); |
||||
|
||||
msgAllTypes.setOptionalString(''); |
||||
msgAllTypes.setOptionalInt32(1); |
||||
msgAllTypes.setOptionalForeignMessage(ForeignMessage.deserialize('')); |
||||
msgAllTypes.setOptionalBool(true); |
||||
// msgAllTypes.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAR);
|
||||
msgAllTypes.setOptionalInt64(1); |
||||
msgAllTypes.setOptionalDouble(1.0); |
||||
msgAllTypes.setRepeatedForeignMessageList([ForeignMessage.deserialize('')]); |
||||
let arrayVal = msgAllTypes.getRepeatedForeignMessageList(); |
||||
arrayVal[0] = ForeignMessage.deserialize(''); |
||||
msgAllTypes.setRepeatedForeignMessageList(arrayVal); |
||||
msgAllTypes.setOptionalUint64(1); |
||||
|
||||
let s = ''; |
||||
s += msgAllTypes.getOptionalString(); |
||||
s += msgAllTypes.getOptionalInt32(); |
||||
s += msgAllTypes.getOptionalForeignMessage(); |
||||
s += msgAllTypes.getOptionalBool(); |
||||
// s += msgAllTypes.getOptionalForeignEnum();
|
||||
s += msgAllTypes.getOptionalInt64(); |
||||
s += msgAllTypes.getOptionalDouble(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList(); |
||||
s += msgAllTypes.getRepeatedForeignMessageList()[0]; |
||||
s += msgAllTypes.getRepeatedForeignMessageList().length; |
||||
s += msgAllTypes.getOptionalUint64(); |
||||
|
||||
s += msgAllTypes.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessPopularTypes(); |
@ -0,0 +1,57 @@ |
||||
/** |
||||
* @fileoverview Ensures types are live that would be live in a typical g3 |
||||
* JS program. |
||||
* |
||||
* Making certain constructs live ensures that we compare against the same |
||||
* baseline for all code size benchmarks. This increases the size |
||||
* of our benchmarks, but note that this size in a regular app would be |
||||
* attributes to other places. |
||||
*/ |
||||
goog.module('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
|
||||
/** |
||||
* Ensures that the array iterator polyfill is live. |
||||
* @return {string} |
||||
*/ |
||||
function useArrayIterator() { |
||||
let a = []; |
||||
let s = ''; |
||||
for (let value of a) { |
||||
s += value; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
/** |
||||
* Ensures that the symbol iterator polyfill is live. |
||||
* @return {string} |
||||
*/ |
||||
function useSymbolIterator() { |
||||
/** |
||||
* @implements {Iterable} |
||||
*/ |
||||
class Foo { |
||||
/** @return {!Iterator} */ |
||||
[Symbol.iterator]() {} |
||||
} |
||||
|
||||
let foo = new Foo(); |
||||
let s = ''; |
||||
for (let value of foo) { |
||||
s += value; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
/** |
||||
* Ensures certain base libs are live so we can have an apples to apples |
||||
* comparison for code size of different implementations |
||||
*/ |
||||
function ensureCommonBaseLine() { |
||||
goog.global['__hiddenTest'] += useArrayIterator(); |
||||
goog.global['__hiddenTest'] += useSymbolIterator(); |
||||
} |
||||
|
||||
|
||||
exports = {ensureCommonBaseLine}; |
@ -0,0 +1,227 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of binary kernel for accessing all |
||||
* types setter and getter. |
||||
*/ |
||||
goog.module('protobuf.benchmark.KernelCodeSizeBenchmarkAllTypes'); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
|
||||
/** |
||||
* @return {string} |
||||
*/ |
||||
function accessAllTypes() { |
||||
const message = new TestMessage(LazyAccessor.createEmpty()); |
||||
|
||||
message.addPackedBoolElement(1, true); |
||||
message.addPackedBoolIterable(1, [true]); |
||||
message.addUnpackedBoolElement(1, true); |
||||
message.addUnpackedBoolIterable(1, [true]); |
||||
message.addRepeatedBytesElement(1, ByteString.EMPTY); |
||||
message.addRepeatedBytesIterable(1, [ByteString.EMPTY]); |
||||
message.addPackedDoubleElement(1, 1.0); |
||||
message.addPackedDoubleIterable(1, [1.0]); |
||||
message.addUnpackedDoubleElement(1, 1.0); |
||||
message.addUnpackedDoubleIterable(1, [1.0]); |
||||
message.addPackedFixed32Element(1, 1); |
||||
message.addPackedFixed32Iterable(1, [1]); |
||||
message.addUnpackedFixed32Element(1, 1); |
||||
message.addUnpackedFixed32Iterable(1, [1]); |
||||
message.addPackedFixed64Element(1, Int64.fromBits(0, 1)); |
||||
message.addPackedFixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addUnpackedFixed64Element(1, Int64.fromBits(0, 1)); |
||||
message.addUnpackedFixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addPackedFloatElement(1, 1.0); |
||||
message.addPackedFloatIterable(1, [1.0]); |
||||
message.addUnpackedFloatElement(1, 1.0); |
||||
message.addUnpackedFloatIterable(1, [1.0]); |
||||
message.addPackedInt32Element(1, 1); |
||||
message.addPackedInt32Iterable(1, [1]); |
||||
message.addUnpackedInt32Element(1, 1); |
||||
message.addUnpackedInt32Iterable(1, [1]); |
||||
message.addPackedInt64Element(1, Int64.fromBits(0, 1)); |
||||
message.addPackedInt64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addUnpackedInt64Element(1, Int64.fromBits(0, 1)); |
||||
message.addUnpackedInt64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addRepeatedMessageElement(1, message, TestMessage.instanceCreator); |
||||
message.addRepeatedMessageIterable(1, [message], TestMessage.instanceCreator); |
||||
message.addPackedSfixed32Element(1, 1); |
||||
message.addPackedSfixed32Iterable(1, [1]); |
||||
message.addUnpackedSfixed32Element(1, 1); |
||||
message.addUnpackedSfixed32Iterable(1, [1]); |
||||
message.addPackedSfixed64Element(1, Int64.fromBits(0, 1)); |
||||
message.addPackedSfixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addUnpackedSfixed64Element(1, Int64.fromBits(0, 1)); |
||||
message.addUnpackedSfixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addPackedSint32Element(1, 1); |
||||
message.addPackedSint32Iterable(1, [1]); |
||||
message.addUnpackedSint32Element(1, 1); |
||||
message.addUnpackedSint32Iterable(1, [1]); |
||||
message.addPackedSint64Element(1, Int64.fromBits(0, 1)); |
||||
message.addPackedSint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addUnpackedSint64Element(1, Int64.fromBits(0, 1)); |
||||
message.addUnpackedSint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addRepeatedStringElement(1, ''); |
||||
message.addRepeatedStringIterable(1, ['']); |
||||
message.addPackedUint32Element(1, 1); |
||||
message.addPackedUint32Iterable(1, [1]); |
||||
message.addUnpackedUint32Element(1, 1); |
||||
message.addUnpackedUint32Iterable(1, [1]); |
||||
message.addPackedUint64Element(1, Int64.fromBits(0, 1)); |
||||
message.addPackedUint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.addUnpackedUint64Element(1, Int64.fromBits(0, 1)); |
||||
message.addUnpackedUint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
|
||||
message.setBool(1, true); |
||||
message.setBytes(1, ByteString.EMPTY); |
||||
message.setDouble(1, 1.0); |
||||
message.setFixed32(1, 1); |
||||
message.setFixed64(1, Int64.fromBits(0, 1)); |
||||
message.setFloat(1, 1.0); |
||||
message.setInt32(1, 1); |
||||
message.setInt64(1, Int64.fromBits(0, 1)); |
||||
message.setMessage(1, message); |
||||
message.setSfixed32(1, 1); |
||||
message.setSfixed64(1, Int64.fromBits(0, 1)); |
||||
message.setSint32(1, 1); |
||||
message.setSint64(1, Int64.fromBits(0, 1)); |
||||
message.setString(1, 'abc'); |
||||
message.setUint32(1, 1); |
||||
message.setUint64(1, Int64.fromBits(0, 1)); |
||||
message.setPackedBoolElement(1, 0, true); |
||||
message.setPackedBoolIterable(1, [true]); |
||||
message.setUnpackedBoolElement(1, 0, true); |
||||
message.setUnpackedBoolIterable(1, [true]); |
||||
message.setRepeatedBytesElement(1, 0, ByteString.EMPTY); |
||||
message.setRepeatedBytesIterable(1, [ByteString.EMPTY]); |
||||
message.setPackedDoubleElement(1, 0, 1.0); |
||||
message.setPackedDoubleIterable(1, [1.0]); |
||||
message.setUnpackedDoubleElement(1, 0, 1.0); |
||||
message.setUnpackedDoubleIterable(1, [1.0]); |
||||
message.setPackedFixed32Element(1, 0, 1); |
||||
message.setPackedFixed32Iterable(1, [1]); |
||||
message.setUnpackedFixed32Element(1, 0, 1); |
||||
message.setUnpackedFixed32Iterable(1, [1]); |
||||
message.setPackedFixed64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setPackedFixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setUnpackedFixed64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setUnpackedFixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setPackedFloatElement(1, 0, 1.0); |
||||
message.setPackedFloatIterable(1, [1.0]); |
||||
message.setUnpackedFloatElement(1, 0, 1.0); |
||||
message.setUnpackedFloatIterable(1, [1.0]); |
||||
message.setPackedInt32Element(1, 0, 1); |
||||
message.setPackedInt32Iterable(1, [1]); |
||||
message.setUnpackedInt32Element(1, 0, 1); |
||||
message.setUnpackedInt32Iterable(1, [1]); |
||||
message.setPackedInt64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setPackedInt64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setUnpackedInt64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setUnpackedInt64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setRepeatedMessageElement(1, message, TestMessage.instanceCreator, 0); |
||||
message.setRepeatedMessageIterable(1, [message]); |
||||
message.setPackedSfixed32Element(1, 0, 1); |
||||
message.setPackedSfixed32Iterable(1, [1]); |
||||
message.setUnpackedSfixed32Element(1, 0, 1); |
||||
message.setUnpackedSfixed32Iterable(1, [1]); |
||||
message.setPackedSfixed64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setPackedSfixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setUnpackedSfixed64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setUnpackedSfixed64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setRepeatedStringElement(1, 0, ''); |
||||
message.setRepeatedStringIterable(1, ['']); |
||||
message.setPackedSint32Element(1, 0, 1); |
||||
message.setPackedSint32Iterable(1, [1]); |
||||
message.setUnpackedSint32Element(1, 0, 1); |
||||
message.setUnpackedSint32Iterable(1, [1]); |
||||
message.setPackedSint64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setPackedSint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setUnpackedSint64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setUnpackedSint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setPackedUint32Element(1, 0, 1); |
||||
message.setPackedUint32Iterable(1, [1]); |
||||
message.setUnpackedUint32Element(1, 0, 1); |
||||
message.setUnpackedUint32Iterable(1, [1]); |
||||
message.setPackedUint64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setPackedUint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
message.setUnpackedUint64Element(1, 0, Int64.fromBits(0, 1)); |
||||
message.setUnpackedUint64Iterable(1, [Int64.fromBits(0, 1)]); |
||||
|
||||
let s = ''; |
||||
s += message.getBoolWithDefault(1); |
||||
s += message.getBytesWithDefault(1); |
||||
s += message.getDoubleWithDefault(1); |
||||
s += message.getFixed32WithDefault(1); |
||||
s += message.getFixed64WithDefault(1); |
||||
s += message.getFloatWithDefault(1); |
||||
s += message.getInt32WithDefault(1); |
||||
s += message.getInt64WithDefault(1); |
||||
s += message.getMessage(1, TestMessage.instanceCreator); |
||||
s += message.getSfixed32WithDefault(1); |
||||
s += message.getSfixed64WithDefault(1); |
||||
s += message.getSint32WithDefault(1); |
||||
s += message.getSint64WithDefault(1); |
||||
s += message.getStringWithDefault(1); |
||||
s += message.getUint32WithDefault(1); |
||||
s += message.getUint64WithDefault(1); |
||||
s += message.getRepeatedBoolElement(1, 0); |
||||
s += message.getRepeatedBoolIterable(1); |
||||
s += message.getRepeatedBoolSize(1); |
||||
s += message.getRepeatedBytesElement(1, 0); |
||||
s += message.getRepeatedBytesIterable(1); |
||||
s += message.getRepeatedBytesSize(1); |
||||
s += message.getRepeatedDoubleElement(1, 0); |
||||
s += message.getRepeatedDoubleIterable(1); |
||||
s += message.getRepeatedDoubleSize(1); |
||||
s += message.getRepeatedFixed32Element(1, 0); |
||||
s += message.getRepeatedFixed32Iterable(1); |
||||
s += message.getRepeatedFixed32Size(1); |
||||
s += message.getRepeatedFixed64Element(1, 0); |
||||
s += message.getRepeatedFixed64Iterable(1); |
||||
s += message.getRepeatedFixed64Size(1); |
||||
s += message.getRepeatedFloatElement(1, 0); |
||||
s += message.getRepeatedFloatIterable(1); |
||||
s += message.getRepeatedFloatSize(1); |
||||
s += message.getRepeatedInt32Element(1, 0); |
||||
s += message.getRepeatedInt32Iterable(1); |
||||
s += message.getRepeatedInt32Size(1); |
||||
s += message.getRepeatedInt64Element(1, 0); |
||||
s += message.getRepeatedInt64Iterable(1); |
||||
s += message.getRepeatedInt64Size(1); |
||||
s += message.getRepeatedMessageElement(1, TestMessage.instanceCreator, 0); |
||||
s += message.getRepeatedMessageIterable(1, TestMessage.instanceCreator); |
||||
s += message.getRepeatedMessageSize(1, TestMessage.instanceCreator); |
||||
s += message.getRepeatedSfixed32Element(1, 0); |
||||
s += message.getRepeatedSfixed32Iterable(1); |
||||
s += message.getRepeatedSfixed32Size(1); |
||||
s += message.getRepeatedSfixed64Element(1, 0); |
||||
s += message.getRepeatedSfixed64Iterable(1); |
||||
s += message.getRepeatedSfixed64Size(1); |
||||
s += message.getRepeatedSint32Element(1, 0); |
||||
s += message.getRepeatedSint32Iterable(1); |
||||
s += message.getRepeatedSint32Size(1); |
||||
s += message.getRepeatedSint64Element(1, 0); |
||||
s += message.getRepeatedSint64Iterable(1); |
||||
s += message.getRepeatedSint64Size(1); |
||||
s += message.getRepeatedStringElement(1, 0); |
||||
s += message.getRepeatedStringIterable(1); |
||||
s += message.getRepeatedStringSize(1); |
||||
s += message.getRepeatedUint32Element(1, 0); |
||||
s += message.getRepeatedUint32Iterable(1); |
||||
s += message.getRepeatedUint32Size(1); |
||||
s += message.getRepeatedUint64Element(1, 0); |
||||
s += message.getRepeatedUint64Iterable(1); |
||||
s += message.getRepeatedUint64Size(1); |
||||
|
||||
s += message.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessAllTypes(); |
@ -0,0 +1,68 @@ |
||||
/** |
||||
* @fileoverview The code size benchmark of binary kernel for accessing all |
||||
* popular types setter and getter. |
||||
* |
||||
* The types are those whose usage are more than 1%: |
||||
* |
||||
* ('STRING__LABEL_OPTIONAL', '29.7214%') |
||||
* ('INT32__LABEL_OPTIONAL', '17.7277%') |
||||
* ('MESSAGE__LABEL_OPTIONAL', '15.6462%') |
||||
* ('BOOL__LABEL_OPTIONAL', '13.0038%') |
||||
* ('ENUM__LABEL_OPTIONAL', '11.4466%') |
||||
* ('INT64__LABEL_OPTIONAL', '3.2198%') |
||||
* ('DOUBLE__LABEL_OPTIONAL', '1.357%') |
||||
* ('MESSAGE__LABEL_REPEATED', '1.2775%') |
||||
* ('FIXED32__LABEL_REQUIRED', '1.2%') |
||||
* ('UINT64__LABEL_OPTIONAL', '1.1771%') |
||||
* ('STRING__LABEL_REQUIRED', '1.0785%') |
||||
* |
||||
*/ |
||||
goog.module('protobuf.benchmark.KernelCodeSizeBenchmarkPopularTypes'); |
||||
|
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); |
||||
const {ensureCommonBaseLine} = goog.require('protobuf.benchmark.codeSize.codeSizeBase'); |
||||
|
||||
ensureCommonBaseLine(); |
||||
|
||||
|
||||
/** |
||||
* @return {string} |
||||
*/ |
||||
function accessAllTypes() { |
||||
const message = new TestMessage(LazyAccessor.createEmpty()); |
||||
|
||||
message.addRepeatedMessageElement(1, message, TestMessage.instanceCreator); |
||||
message.addRepeatedMessageIterable(1, [message], TestMessage.instanceCreator); |
||||
|
||||
message.setString(1, 'abc'); |
||||
message.setInt32(1, 1); |
||||
message.setMessage(1, message); |
||||
message.setBool(1, true); |
||||
message.setInt64(1, Int64.fromBits(0, 1)); |
||||
message.setDouble(1, 1.0); |
||||
message.setRepeatedMessageElement(1, message, TestMessage.instanceCreator, 0); |
||||
message.setRepeatedMessageIterable(1, [message]); |
||||
message.setUint64(1, Int64.fromBits(0, 1)); |
||||
|
||||
|
||||
let s = ''; |
||||
s += message.getStringWithDefault(1); |
||||
s += message.getInt32WithDefault(1); |
||||
s += message.getMessage(1, TestMessage.instanceCreator); |
||||
s += message.getMessageOrNull(1, TestMessage.instanceCreator); |
||||
s += message.getBoolWithDefault(1); |
||||
s += message.getInt64WithDefault(1); |
||||
s += message.getDoubleWithDefault(1); |
||||
s += message.getRepeatedMessageElement(1, TestMessage.instanceCreator, 0); |
||||
s += message.getRepeatedMessageIterable(1, TestMessage.instanceCreator); |
||||
s += message.getRepeatedMessageSize(1, TestMessage.instanceCreator); |
||||
s += message.getUint64WithDefault(1); |
||||
|
||||
s += message.serialize(); |
||||
|
||||
return s; |
||||
} |
||||
|
||||
goog.global['__hiddenTest'] += accessAllTypes(); |
@ -0,0 +1,183 @@ |
||||
/** |
||||
* @fileoverview Provides ByteString as a basic data type for protos. |
||||
*/ |
||||
goog.module('protobuf.ByteString'); |
||||
|
||||
const base64 = goog.require('goog.crypt.base64'); |
||||
const {arrayBufferSlice, cloneArrayBufferView, hashUint8Array, uint8ArrayEqual} = goog.require('protobuf.binary.typedArrays'); |
||||
|
||||
/** |
||||
* Immutable sequence of bytes. |
||||
* |
||||
* Bytes can be obtained as an ArrayBuffer or a base64 encoded string. |
||||
* @final |
||||
*/ |
||||
class ByteString { |
||||
/** |
||||
* @param {?Uint8Array} bytes |
||||
* @param {?string} base64 |
||||
* @private |
||||
*/ |
||||
constructor(bytes, base64) { |
||||
/** @private {?Uint8Array}*/ |
||||
this.bytes_ = bytes; |
||||
/** @private {?string} */ |
||||
this.base64_ = base64; |
||||
/** @private {number} */ |
||||
this.hashCode_ = 0; |
||||
} |
||||
|
||||
/** |
||||
* Constructs a ByteString instance from a base64 string. |
||||
* @param {string} value |
||||
* @return {!ByteString} |
||||
*/ |
||||
static fromBase64String(value) { |
||||
if (value == null) { |
||||
throw new Error('value must not be null'); |
||||
} |
||||
return new ByteString(/* bytes */ null, value); |
||||
} |
||||
|
||||
/** |
||||
* Constructs a ByteString from an array buffer. |
||||
* @param {!ArrayBuffer} bytes |
||||
* @param {number=} start |
||||
* @param {number=} end |
||||
* @return {!ByteString} |
||||
*/ |
||||
static fromArrayBuffer(bytes, start = 0, end = undefined) { |
||||
return new ByteString( |
||||
new Uint8Array(arrayBufferSlice(bytes, start, end)), /* base64 */ null); |
||||
} |
||||
|
||||
/** |
||||
* Constructs a ByteString from any ArrayBufferView (e.g. DataView, |
||||
* TypedArray, Uint8Array, etc.). |
||||
* @param {!ArrayBufferView} bytes |
||||
* @return {!ByteString} |
||||
*/ |
||||
static fromArrayBufferView(bytes) { |
||||
return new ByteString(cloneArrayBufferView(bytes), /* base64 */ null); |
||||
} |
||||
|
||||
/** |
||||
* Constructs a ByteString from an Uint8Array. DON'T MODIFY the underlying |
||||
* ArrayBuffer, since the ByteString directly uses it without making a copy. |
||||
* |
||||
* This method exists so that internal APIs can construct a ByteString without |
||||
* paying the penalty of copying an ArrayBuffer when that ArrayBuffer is not |
||||
* supposed to change. It is exposed to a limited number of internal classes |
||||
* through bytestring_internal.js. |
||||
* |
||||
* @param {!Uint8Array} bytes |
||||
* @return {!ByteString} |
||||
* @package |
||||
*/ |
||||
static fromUint8ArrayUnsafe(bytes) { |
||||
return new ByteString(bytes, /* base64 */ null); |
||||
} |
||||
|
||||
/** |
||||
* Returns this ByteString as an ArrayBuffer. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
toArrayBuffer() { |
||||
const bytes = this.ensureBytes_(); |
||||
return arrayBufferSlice( |
||||
bytes.buffer, bytes.byteOffset, bytes.byteOffset + bytes.byteLength); |
||||
} |
||||
|
||||
/** |
||||
* Returns this ByteString as an Uint8Array. DON'T MODIFY the returned array, |
||||
* since the ByteString holds the reference to the same array. |
||||
* |
||||
* This method exists so that internal APIs can get contents of a ByteString |
||||
* without paying the penalty of copying an ArrayBuffer. It is exposed to a |
||||
* limited number of internal classes through bytestring_internal.js. |
||||
* @return {!Uint8Array} |
||||
* @package |
||||
*/ |
||||
toUint8ArrayUnsafe() { |
||||
return this.ensureBytes_(); |
||||
} |
||||
|
||||
/** |
||||
* Returns this ByteString as a base64 encoded string. |
||||
* @return {string} |
||||
*/ |
||||
toBase64String() { |
||||
return this.ensureBase64String_(); |
||||
} |
||||
|
||||
/** |
||||
* Returns true for Bytestrings that contain identical values. |
||||
* @param {*} other |
||||
* @return {boolean} |
||||
*/ |
||||
equals(other) { |
||||
if (this === other) { |
||||
return true; |
||||
} |
||||
|
||||
if (!(other instanceof ByteString)) { |
||||
return false; |
||||
} |
||||
|
||||
const otherByteString = /** @type {!ByteString} */ (other); |
||||
return uint8ArrayEqual(this.ensureBytes_(), otherByteString.ensureBytes_()); |
||||
} |
||||
|
||||
/** |
||||
* Returns a number (int32) that is suitable for using in hashed structures. |
||||
* @return {number} |
||||
*/ |
||||
hashCode() { |
||||
if (this.hashCode_ == 0) { |
||||
this.hashCode_ = hashUint8Array(this.ensureBytes_()); |
||||
} |
||||
return this.hashCode_; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the bytestring is empty. |
||||
* @return {boolean} |
||||
*/ |
||||
isEmpty() { |
||||
if (this.bytes_ != null && this.bytes_.byteLength == 0) { |
||||
return true; |
||||
} |
||||
if (this.base64_ != null && this.base64_.length == 0) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* @return {!Uint8Array} |
||||
* @private |
||||
*/ |
||||
ensureBytes_() { |
||||
if (this.bytes_) { |
||||
return this.bytes_; |
||||
} |
||||
return this.bytes_ = base64.decodeStringToUint8Array( |
||||
/** @type {string} */ (this.base64_)); |
||||
} |
||||
|
||||
/** |
||||
* @return {string} |
||||
* @private |
||||
*/ |
||||
ensureBase64String_() { |
||||
if (this.base64_ == null) { |
||||
this.base64_ = base64.encodeByteArray(this.bytes_); |
||||
} |
||||
return this.base64_; |
||||
} |
||||
} |
||||
|
||||
/** @const {!ByteString} */ |
||||
ByteString.EMPTY = new ByteString(new Uint8Array(0), null); |
||||
|
||||
exports = ByteString; |
@ -0,0 +1,33 @@ |
||||
/** |
||||
* @fileoverview Exposes internal only functions for ByteString. The |
||||
* corresponding BUILD rule restricts access to this file to only the binary |
||||
* kernel and APIs directly using the binary kernel. |
||||
*/ |
||||
goog.module('protobuf.byteStringInternal'); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
|
||||
/** |
||||
* Constructs a ByteString from an Uint8Array. DON'T MODIFY the underlying |
||||
* ArrayBuffer, since the ByteString directly uses it without making a copy. |
||||
* @param {!Uint8Array} bytes |
||||
* @return {!ByteString} |
||||
*/ |
||||
function byteStringFromUint8ArrayUnsafe(bytes) { |
||||
return ByteString.fromUint8ArrayUnsafe(bytes); |
||||
} |
||||
|
||||
/** |
||||
* Returns this ByteString as an Uint8Array. DON'T MODIFY the returned array, |
||||
* since the ByteString holds the reference to the same array. |
||||
* @param {!ByteString} bytes |
||||
* @return {!Uint8Array} |
||||
*/ |
||||
function byteStringToUint8ArrayUnsafe(bytes) { |
||||
return bytes.toUint8ArrayUnsafe(); |
||||
} |
||||
|
||||
exports = { |
||||
byteStringFromUint8ArrayUnsafe, |
||||
byteStringToUint8ArrayUnsafe, |
||||
}; |
@ -0,0 +1,277 @@ |
||||
goog.module('proto.im.integration.ByteStringFieldsTest'); |
||||
goog.setTestOnly(); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const {arrayBufferSlice} = goog.require('protobuf.binary.typedArrays'); |
||||
|
||||
const /** !ArrayBuffer */ TEST_BYTES = new Uint8Array([1, 2, 3, 4]).buffer; |
||||
const /** !ByteString */ TEST_STRING = ByteString.fromArrayBuffer(TEST_BYTES); |
||||
const /** !ArrayBuffer */ PREFIXED_TEST_BYTES = |
||||
new Uint8Array([0, 1, 2, 3, 4]).buffer; |
||||
const /** string */ HALLO_IN_BASE64 = 'aGFsbG8='; |
||||
const /** string */ HALLO_IN_BASE64_WITH_SPACES = 'a G F s b G 8='; |
||||
const /** !ArrayBufferView */ BYTES_WITH_HALLO = new Uint8Array([ |
||||
'h'.charCodeAt(0), |
||||
'a'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'o'.charCodeAt(0), |
||||
]); |
||||
|
||||
describe('ByteString does', () => { |
||||
it('create bytestring from buffer', () => { |
||||
const byteString = |
||||
ByteString.fromArrayBuffer(arrayBufferSlice(TEST_BYTES, 0)); |
||||
expect(byteString.toArrayBuffer()).toEqual(TEST_BYTES); |
||||
expect(byteString.toUint8ArrayUnsafe()).toEqual(new Uint8Array(TEST_BYTES)); |
||||
}); |
||||
|
||||
it('create bytestring from ArrayBufferView', () => { |
||||
const byteString = |
||||
ByteString.fromArrayBufferView(new Uint8Array(TEST_BYTES)); |
||||
expect(byteString.toArrayBuffer()).toEqual(TEST_BYTES); |
||||
expect(byteString.toUint8ArrayUnsafe()).toEqual(new Uint8Array(TEST_BYTES)); |
||||
}); |
||||
|
||||
it('create bytestring from subarray', () => { |
||||
const byteString = ByteString.fromArrayBufferView( |
||||
new Uint8Array(TEST_BYTES, /* offset */ 1, /* length */ 2)); |
||||
const expected = new Uint8Array([2, 3]); |
||||
expect(new Uint8Array(byteString.toArrayBuffer())).toEqual(expected); |
||||
expect(byteString.toUint8ArrayUnsafe()).toEqual(expected); |
||||
}); |
||||
|
||||
it('create bytestring from Uint8Array (unsafe)', () => { |
||||
const array = new Uint8Array(TEST_BYTES); |
||||
const byteString = ByteString.fromUint8ArrayUnsafe(array); |
||||
expect(byteString.toArrayBuffer()).toEqual(array.buffer); |
||||
expect(byteString.toUint8ArrayUnsafe()).toBe(array); |
||||
}); |
||||
|
||||
it('create bytestring from base64 string', () => { |
||||
const byteString = ByteString.fromBase64String(HALLO_IN_BASE64); |
||||
expect(byteString.toBase64String()).toEqual(HALLO_IN_BASE64); |
||||
expect(byteString.toArrayBuffer()).toEqual(BYTES_WITH_HALLO.buffer); |
||||
expect(byteString.toUint8ArrayUnsafe()).toEqual(BYTES_WITH_HALLO); |
||||
}); |
||||
|
||||
it('preserve immutability if underlying buffer changes: from buffer', () => { |
||||
const buffer = new ArrayBuffer(4); |
||||
const array = new Uint8Array(buffer); |
||||
array[0] = 1; |
||||
array[1] = 2; |
||||
array[2] = 3; |
||||
array[3] = 4; |
||||
|
||||
const byteString = ByteString.fromArrayBuffer(buffer); |
||||
const otherBuffer = byteString.toArrayBuffer(); |
||||
|
||||
expect(otherBuffer).not.toBe(buffer); |
||||
expect(new Uint8Array(otherBuffer)).toEqual(array); |
||||
|
||||
// modify the original buffer
|
||||
array[0] = 5; |
||||
// Are we still returning the original bytes?
|
||||
expect(new Uint8Array(byteString.toArrayBuffer())).toEqual(new Uint8Array([ |
||||
1, 2, 3, 4 |
||||
])); |
||||
}); |
||||
|
||||
it('preserve immutability if underlying buffer changes: from ArrayBufferView', |
||||
() => { |
||||
const buffer = new ArrayBuffer(4); |
||||
const array = new Uint8Array(buffer); |
||||
array[0] = 1; |
||||
array[1] = 2; |
||||
array[2] = 3; |
||||
array[3] = 4; |
||||
|
||||
const byteString = ByteString.fromArrayBufferView(array); |
||||
const otherBuffer = byteString.toArrayBuffer(); |
||||
|
||||
expect(otherBuffer).not.toBe(buffer); |
||||
expect(new Uint8Array(otherBuffer)).toEqual(array); |
||||
|
||||
// modify the original buffer
|
||||
array[0] = 5; |
||||
// Are we still returning the original bytes?
|
||||
expect(new Uint8Array(byteString.toArrayBuffer())) |
||||
.toEqual(new Uint8Array([1, 2, 3, 4])); |
||||
}); |
||||
|
||||
it('mutate if underlying buffer changes: from Uint8Array (unsafe)', () => { |
||||
const buffer = new ArrayBuffer(4); |
||||
const array = new Uint8Array(buffer); |
||||
array[0] = 1; |
||||
array[1] = 2; |
||||
array[2] = 3; |
||||
array[3] = 4; |
||||
|
||||
const byteString = ByteString.fromUint8ArrayUnsafe(array); |
||||
const otherBuffer = byteString.toArrayBuffer(); |
||||
|
||||
expect(otherBuffer).not.toBe(buffer); |
||||
expect(new Uint8Array(otherBuffer)).toEqual(array); |
||||
|
||||
// modify the original buffer
|
||||
array[0] = 5; |
||||
// We are no longer returning the original bytes
|
||||
expect(new Uint8Array(byteString.toArrayBuffer())).toEqual(new Uint8Array([ |
||||
5, 2, 3, 4 |
||||
])); |
||||
}); |
||||
|
||||
it('preserve immutability for returned buffers: toArrayBuffer', () => { |
||||
const byteString = ByteString.fromArrayBufferView(new Uint8Array(4)); |
||||
const buffer1 = byteString.toArrayBuffer(); |
||||
const buffer2 = byteString.toArrayBuffer(); |
||||
|
||||
expect(buffer1).toEqual(buffer2); |
||||
|
||||
const array1 = new Uint8Array(buffer1); |
||||
array1[0] = 1; |
||||
|
||||
expect(buffer1).not.toEqual(buffer2); |
||||
}); |
||||
|
||||
it('does not preserve immutability for returned buffers: toUint8ArrayUnsafe', |
||||
() => { |
||||
const byteString = ByteString.fromUint8ArrayUnsafe(new Uint8Array(4)); |
||||
const array1 = byteString.toUint8ArrayUnsafe(); |
||||
const array2 = byteString.toUint8ArrayUnsafe(); |
||||
|
||||
expect(array1).toEqual(array2); |
||||
array1[0] = 1; |
||||
|
||||
expect(array1).toEqual(array2); |
||||
}); |
||||
|
||||
it('throws when created with null ArrayBufferView', () => { |
||||
expect( |
||||
() => ByteString.fromArrayBufferView( |
||||
/** @type {!ArrayBufferView} */ (/** @type{*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('throws when created with null buffer', () => { |
||||
expect( |
||||
() => ByteString.fromBase64String( |
||||
/** @type {string} */ (/** @type{*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('convert base64 to ArrayBuffer', () => { |
||||
const other = ByteString.fromBase64String(HALLO_IN_BASE64); |
||||
expect(BYTES_WITH_HALLO).toEqual(new Uint8Array(other.toArrayBuffer())); |
||||
}); |
||||
|
||||
it('convert base64 with spaces to ArrayBuffer', () => { |
||||
const other = ByteString.fromBase64String(HALLO_IN_BASE64_WITH_SPACES); |
||||
expect(new Uint8Array(other.toArrayBuffer())).toEqual(BYTES_WITH_HALLO); |
||||
}); |
||||
|
||||
it('convert bytes to base64', () => { |
||||
const other = ByteString.fromArrayBufferView(BYTES_WITH_HALLO); |
||||
expect(HALLO_IN_BASE64).toEqual(other.toBase64String()); |
||||
}); |
||||
|
||||
it('equal empty bytetring', () => { |
||||
const empty = ByteString.fromArrayBuffer(new ArrayBuffer(0)); |
||||
expect(ByteString.EMPTY.equals(empty)).toEqual(true); |
||||
}); |
||||
|
||||
it('equal empty bytestring constructed from ArrayBufferView', () => { |
||||
const empty = ByteString.fromArrayBufferView(new Uint8Array(0)); |
||||
expect(ByteString.EMPTY.equals(empty)).toEqual(true); |
||||
}); |
||||
|
||||
it('equal empty bytestring constructed from Uint8Array (unsafe)', () => { |
||||
const empty = ByteString.fromUint8ArrayUnsafe(new Uint8Array(0)); |
||||
expect(ByteString.EMPTY.equals(empty)).toEqual(true); |
||||
}); |
||||
|
||||
it('equal empty bytestring constructed from base64', () => { |
||||
const empty = ByteString.fromBase64String(''); |
||||
expect(ByteString.EMPTY.equals(empty)).toEqual(true); |
||||
}); |
||||
|
||||
it('equal other instance', () => { |
||||
const other = ByteString.fromArrayBuffer(arrayBufferSlice(TEST_BYTES, 0)); |
||||
expect(TEST_STRING.equals(other)).toEqual(true); |
||||
}); |
||||
|
||||
it('not equal different instance', () => { |
||||
const other = |
||||
ByteString.fromArrayBuffer(new Uint8Array([1, 2, 3, 4, 5]).buffer); |
||||
expect(TEST_STRING.equals(other)).toEqual(false); |
||||
}); |
||||
|
||||
it('equal other instance constructed from ArrayBufferView', () => { |
||||
const other = |
||||
ByteString.fromArrayBufferView(new Uint8Array(PREFIXED_TEST_BYTES, 1)); |
||||
expect(TEST_STRING.equals(other)).toEqual(true); |
||||
}); |
||||
|
||||
it('not equal different instance constructed from ArrayBufferView', () => { |
||||
const other = |
||||
ByteString.fromArrayBufferView(new Uint8Array([1, 2, 3, 4, 5])); |
||||
expect(TEST_STRING.equals(other)).toEqual(false); |
||||
}); |
||||
|
||||
it('equal other instance constructed from Uint8Array (unsafe)', () => { |
||||
const other = |
||||
ByteString.fromUint8ArrayUnsafe(new Uint8Array(PREFIXED_TEST_BYTES, 1)); |
||||
expect(TEST_STRING.equals(other)).toEqual(true); |
||||
}); |
||||
|
||||
it('not equal different instance constructed from Uint8Array (unsafe)', |
||||
() => { |
||||
const other = |
||||
ByteString.fromUint8ArrayUnsafe(new Uint8Array([1, 2, 3, 4, 5])); |
||||
expect(TEST_STRING.equals(other)).toEqual(false); |
||||
}); |
||||
|
||||
it('have same hashcode for empty bytes', () => { |
||||
const empty = ByteString.fromArrayBuffer(new ArrayBuffer(0)); |
||||
expect(ByteString.EMPTY.hashCode()).toEqual(empty.hashCode()); |
||||
}); |
||||
|
||||
it('have same hashcode for test bytes', () => { |
||||
const other = ByteString.fromArrayBuffer(arrayBufferSlice(TEST_BYTES, 0)); |
||||
expect(TEST_STRING.hashCode()).toEqual(other.hashCode()); |
||||
}); |
||||
|
||||
it('have same hashcode for test bytes', () => { |
||||
const other = ByteString.fromArrayBufferView( |
||||
new Uint8Array(arrayBufferSlice(TEST_BYTES, 0))); |
||||
expect(TEST_STRING.hashCode()).toEqual(other.hashCode()); |
||||
}); |
||||
|
||||
it('have same hashcode for different instance constructed with base64', |
||||
() => { |
||||
const other = ByteString.fromBase64String(HALLO_IN_BASE64); |
||||
expect(ByteString.fromArrayBufferView(BYTES_WITH_HALLO).hashCode()) |
||||
.toEqual(other.hashCode()); |
||||
}); |
||||
|
||||
it('preserves the length of a Uint8Array', () => { |
||||
const original = new Uint8Array([105, 183, 51, 251, 253, 118, 247]); |
||||
const afterByteString = new Uint8Array( |
||||
ByteString.fromArrayBufferView(original).toArrayBuffer()); |
||||
expect(afterByteString).toEqual(original); |
||||
}); |
||||
|
||||
it('preserves the length of a base64 value', () => { |
||||
const expected = new Uint8Array([105, 183, 51, 251, 253, 118, 247]); |
||||
const afterByteString = new Uint8Array( |
||||
ByteString.fromBase64String('abcz+/129w').toArrayBuffer()); |
||||
expect(afterByteString).toEqual(expected); |
||||
}); |
||||
|
||||
it('preserves the length of a base64 value with padding', () => { |
||||
const expected = new Uint8Array([105, 183, 51, 251, 253, 118, 247]); |
||||
const afterByteString = new Uint8Array( |
||||
ByteString.fromBase64String('abcz+/129w==').toArrayBuffer()); |
||||
expect(afterByteString).toEqual(expected); |
||||
}); |
||||
}); |
@ -0,0 +1,403 @@ |
||||
/** |
||||
* @fileoverview Protobufs Int64 representation. |
||||
*/ |
||||
goog.module('protobuf.Int64'); |
||||
|
||||
const Long = goog.require('goog.math.Long'); |
||||
const {assert} = goog.require('goog.asserts'); |
||||
|
||||
/** |
||||
* A container for protobufs Int64/Uint64 data type. |
||||
* @final |
||||
*/ |
||||
class Int64 { |
||||
/** @return {!Int64} */ |
||||
static getZero() { |
||||
return ZERO; |
||||
} |
||||
|
||||
/** @return {!Int64} */ |
||||
static getMinValue() { |
||||
return MIN_VALUE; |
||||
} |
||||
|
||||
/** @return {!Int64} */ |
||||
static getMaxValue() { |
||||
return MAX_VALUE; |
||||
} |
||||
|
||||
/** |
||||
* Constructs a Int64 given two 32 bit numbers |
||||
* @param {number} lowBits |
||||
* @param {number} highBits |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromBits(lowBits, highBits) { |
||||
return new Int64(lowBits, highBits); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an Int64 from a signed 32 bit number. |
||||
* @param {number} value |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromInt(value) { |
||||
// TODO: Use our own checking system here.
|
||||
assert(value === (value | 0), 'value should be a 32-bit integer'); |
||||
// Right shift 31 bits so all high bits are equal to the sign bit.
|
||||
// Note: cannot use >> 32, because (1 >> 32) = 1 (!).
|
||||
const signExtendedHighBits = value >> 31; |
||||
return new Int64(value, signExtendedHighBits); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an Int64 from a number (over 32 bits). |
||||
* @param {number} value |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromNumber(value) { |
||||
if (value > 0) { |
||||
return new Int64(value, value / TWO_PWR_32_DBL); |
||||
} else if (value < 0) { |
||||
return negate(-value, -value / TWO_PWR_32_DBL); |
||||
} |
||||
return ZERO; |
||||
} |
||||
|
||||
/** |
||||
* Construct an Int64 from a signed decimal string. |
||||
* @param {string} value |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromDecimalString(value) { |
||||
// TODO: Use our own checking system here.
|
||||
assert(value.length > 0); |
||||
// The basic Number conversion loses precision, but we can use it for
|
||||
// a quick validation that the format is correct and it is an integer.
|
||||
assert(Math.floor(Number(value)).toString().length == value.length); |
||||
return decimalStringToInt64(value); |
||||
} |
||||
|
||||
/** |
||||
* Construct an Int64 from a signed hexadecimal string. |
||||
* @param {string} value |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromHexString(value) { |
||||
// TODO: Use our own checking system here.
|
||||
assert(value.length > 0); |
||||
assert(value.slice(0, 2) == '0x' || value.slice(0, 3) == '-0x'); |
||||
const minus = value[0] === '-'; |
||||
// Strip the 0x or -0x prefix.
|
||||
value = value.slice(minus ? 3 : 2); |
||||
const lowBits = parseInt(value.slice(-8), 16); |
||||
const highBits = parseInt(value.slice(-16, -8) || '', 16); |
||||
return (minus ? negate : Int64.fromBits)(lowBits, highBits); |
||||
} |
||||
|
||||
// Note to the reader:
|
||||
// goog.math.Long suffers from a code size issue. JsCompiler almost always
|
||||
// considers toString methods to be alive in a program. So if you are
|
||||
// constructing a Long instance the toString method is assumed to be live.
|
||||
// Unfortunately Long's toString method makes a large chunk of code alive
|
||||
// of the entire class adding 1.3kB (gzip) of extra code size.
|
||||
// Callers that are sensitive to code size and are not using Long already
|
||||
// should avoid calling this method.
|
||||
/** |
||||
* Creates an Int64 instance from a Long value. |
||||
* @param {!Long} value |
||||
* @return {!Int64} |
||||
*/ |
||||
static fromLong(value) { |
||||
return new Int64(value.getLowBits(), value.getHighBits()); |
||||
} |
||||
|
||||
/** |
||||
* @param {number} lowBits |
||||
* @param {number} highBits |
||||
* @private |
||||
*/ |
||||
constructor(lowBits, highBits) { |
||||
/** @const @private {number} */ |
||||
this.lowBits_ = lowBits | 0; |
||||
/** @const @private {number} */ |
||||
this.highBits_ = highBits | 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns the int64 value as a JavaScript number. This will lose precision |
||||
* if the number is outside of the safe range for JavaScript of 53 bits |
||||
* precision. |
||||
* @return {number} |
||||
*/ |
||||
asNumber() { |
||||
const result = this.highBits_ * TWO_PWR_32_DBL + this.getLowBitsUnsigned(); |
||||
// TODO: Use our own checking system here.
|
||||
assert( |
||||
Number.isSafeInteger(result), 'conversion to number loses precision.'); |
||||
return result; |
||||
} |
||||
|
||||
// Note to the reader:
|
||||
// goog.math.Long suffers from a code size issue. JsCompiler almost always
|
||||
// considers toString methods to be alive in a program. So if you are
|
||||
// constructing a Long instance the toString method is assumed to be live.
|
||||
// Unfortunately Long's toString method makes a large chunk of code alive
|
||||
// of the entire class adding 1.3kB (gzip) of extra code size.
|
||||
// Callers that are sensitive to code size and are not using Long already
|
||||
// should avoid calling this method.
|
||||
/** @return {!Long} */ |
||||
asLong() { |
||||
return Long.fromBits(this.lowBits_, this.highBits_); |
||||
} |
||||
|
||||
/** @return {number} Signed 32-bit integer value. */ |
||||
getLowBits() { |
||||
return this.lowBits_; |
||||
} |
||||
|
||||
/** @return {number} Signed 32-bit integer value. */ |
||||
getHighBits() { |
||||
return this.highBits_; |
||||
} |
||||
|
||||
/** @return {number} Unsigned 32-bit integer. */ |
||||
getLowBitsUnsigned() { |
||||
return this.lowBits_ >>> 0; |
||||
} |
||||
|
||||
/** @return {number} Unsigned 32-bit integer. */ |
||||
getHighBitsUnsigned() { |
||||
return this.highBits_ >>> 0; |
||||
} |
||||
|
||||
/** @return {string} */ |
||||
toSignedDecimalString() { |
||||
return joinSignedDecimalString(this); |
||||
} |
||||
|
||||
/** @return {string} */ |
||||
toUnsignedDecimalString() { |
||||
return joinUnsignedDecimalString(this); |
||||
} |
||||
|
||||
/** |
||||
* Returns an unsigned hexadecimal string representation of the Int64. |
||||
* @return {string} |
||||
*/ |
||||
toHexString() { |
||||
let nibbles = new Array(16); |
||||
let lowBits = this.lowBits_; |
||||
let highBits = this.highBits_; |
||||
for (let highIndex = 7, lowIndex = 15; lowIndex > 7; |
||||
highIndex--, lowIndex--) { |
||||
nibbles[highIndex] = HEX_DIGITS[highBits & 0xF]; |
||||
nibbles[lowIndex] = HEX_DIGITS[lowBits & 0xF]; |
||||
highBits = highBits >>> 4; |
||||
lowBits = lowBits >>> 4; |
||||
} |
||||
// Always leave the least significant hex digit.
|
||||
while (nibbles.length > 1 && nibbles[0] == '0') { |
||||
nibbles.shift(); |
||||
} |
||||
return `0x${nibbles.join('')}`; |
||||
} |
||||
|
||||
/** |
||||
* @param {*} other object to compare against. |
||||
* @return {boolean} Whether this Int64 equals the other. |
||||
*/ |
||||
equals(other) { |
||||
if (this === other) { |
||||
return true; |
||||
} |
||||
if (!(other instanceof Int64)) { |
||||
return false; |
||||
} |
||||
// Compare low parts first as there is higher chance they are different.
|
||||
const otherInt64 = /** @type{!Int64} */ (other); |
||||
return (this.lowBits_ === otherInt64.lowBits_) && |
||||
(this.highBits_ === otherInt64.highBits_); |
||||
} |
||||
|
||||
/** |
||||
* Returns a number (int32) that is suitable for using in hashed structures. |
||||
* @return {number} |
||||
*/ |
||||
hashCode() { |
||||
return (31 * this.lowBits_ + 17 * this.highBits_) | 0; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Losslessly converts a 64-bit unsigned integer in 32:32 split representation |
||||
* into a decimal string. |
||||
* @param {!Int64} int64 |
||||
* @return {string} The binary number represented as a string. |
||||
*/ |
||||
const joinUnsignedDecimalString = (int64) => { |
||||
const lowBits = int64.getLowBitsUnsigned(); |
||||
const highBits = int64.getHighBitsUnsigned(); |
||||
// Skip the expensive conversion if the number is small enough to use the
|
||||
// built-in conversions.
|
||||
// Number.MAX_SAFE_INTEGER = 0x001FFFFF FFFFFFFF, thus any number with
|
||||
// highBits <= 0x1FFFFF can be safely expressed with a double and retain
|
||||
// integer precision.
|
||||
// Proven by: Number.isSafeInteger(0x1FFFFF * 2**32 + 0xFFFFFFFF) == true.
|
||||
if (highBits <= 0x1FFFFF) { |
||||
return String(TWO_PWR_32_DBL * highBits + lowBits); |
||||
} |
||||
|
||||
// What this code is doing is essentially converting the input number from
|
||||
// base-2 to base-1e7, which allows us to represent the 64-bit range with
|
||||
// only 3 (very large) digits. Those digits are then trivial to convert to
|
||||
// a base-10 string.
|
||||
|
||||
// The magic numbers used here are -
|
||||
// 2^24 = 16777216 = (1,6777216) in base-1e7.
|
||||
// 2^48 = 281474976710656 = (2,8147497,6710656) in base-1e7.
|
||||
|
||||
// Split 32:32 representation into 16:24:24 representation so our
|
||||
// intermediate digits don't overflow.
|
||||
const low = lowBits & LOW_24_BITS; |
||||
const mid = ((lowBits >>> 24) | (highBits << 8)) & LOW_24_BITS; |
||||
const high = (highBits >> 16) & LOW_16_BITS; |
||||
|
||||
// Assemble our three base-1e7 digits, ignoring carries. The maximum
|
||||
// value in a digit at this step is representable as a 48-bit integer, which
|
||||
// can be stored in a 64-bit floating point number.
|
||||
let digitA = low + (mid * 6777216) + (high * 6710656); |
||||
let digitB = mid + (high * 8147497); |
||||
let digitC = (high * 2); |
||||
|
||||
// Apply carries from A to B and from B to C.
|
||||
const base = 10000000; |
||||
if (digitA >= base) { |
||||
digitB += Math.floor(digitA / base); |
||||
digitA %= base; |
||||
} |
||||
|
||||
if (digitB >= base) { |
||||
digitC += Math.floor(digitB / base); |
||||
digitB %= base; |
||||
} |
||||
|
||||
// If digitC is 0, then we should have returned in the trivial code path
|
||||
// at the top for non-safe integers. Given this, we can assume both digitB
|
||||
// and digitA need leading zeros.
|
||||
// TODO: Use our own checking system here.
|
||||
assert(digitC); |
||||
return digitC + decimalFrom1e7WithLeadingZeros(digitB) + |
||||
decimalFrom1e7WithLeadingZeros(digitA); |
||||
}; |
||||
|
||||
/** |
||||
* @param {number} digit1e7 Number < 1e7 |
||||
* @return {string} Decimal representation of digit1e7 with leading zeros. |
||||
*/ |
||||
const decimalFrom1e7WithLeadingZeros = (digit1e7) => { |
||||
const partial = String(digit1e7); |
||||
return '0000000'.slice(partial.length) + partial; |
||||
}; |
||||
|
||||
/** |
||||
* Losslessly converts a 64-bit signed integer in 32:32 split representation |
||||
* into a decimal string. |
||||
* @param {!Int64} int64 |
||||
* @return {string} The binary number represented as a string. |
||||
*/ |
||||
const joinSignedDecimalString = (int64) => { |
||||
// If we're treating the input as a signed value and the high bit is set, do
|
||||
// a manual two's complement conversion before the decimal conversion.
|
||||
const negative = (int64.getHighBits() & 0x80000000); |
||||
if (negative) { |
||||
int64 = negate(int64.getLowBits(), int64.getHighBits()); |
||||
} |
||||
|
||||
const result = joinUnsignedDecimalString(int64); |
||||
return negative ? '-' + result : result; |
||||
}; |
||||
|
||||
/** |
||||
* @param {string} dec |
||||
* @return {!Int64} |
||||
*/ |
||||
const decimalStringToInt64 = (dec) => { |
||||
// Check for minus sign.
|
||||
const minus = dec[0] === '-'; |
||||
if (minus) { |
||||
dec = dec.slice(1); |
||||
} |
||||
|
||||
// Work 6 decimal digits at a time, acting like we're converting base 1e6
|
||||
// digits to binary. This is safe to do with floating point math because
|
||||
// Number.isSafeInteger(ALL_32_BITS * 1e6) == true.
|
||||
const base = 1e6; |
||||
let lowBits = 0; |
||||
let highBits = 0; |
||||
function add1e6digit(begin, end = undefined) { |
||||
// Note: Number('') is 0.
|
||||
const digit1e6 = Number(dec.slice(begin, end)); |
||||
highBits *= base; |
||||
lowBits = lowBits * base + digit1e6; |
||||
// Carry bits from lowBits to
|
||||
if (lowBits >= TWO_PWR_32_DBL) { |
||||
highBits = highBits + ((lowBits / TWO_PWR_32_DBL) | 0); |
||||
lowBits = lowBits % TWO_PWR_32_DBL; |
||||
} |
||||
} |
||||
add1e6digit(-24, -18); |
||||
add1e6digit(-18, -12); |
||||
add1e6digit(-12, -6); |
||||
add1e6digit(-6); |
||||
|
||||
return (minus ? negate : Int64.fromBits)(lowBits, highBits); |
||||
}; |
||||
|
||||
/** |
||||
* @param {number} lowBits |
||||
* @param {number} highBits |
||||
* @return {!Int64} Two's compliment negation of input. |
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Signed_32-bit_integers
|
||||
*/ |
||||
const negate = (lowBits, highBits) => { |
||||
highBits = ~highBits; |
||||
if (lowBits) { |
||||
lowBits = ~lowBits + 1; |
||||
} else { |
||||
// If lowBits is 0, then bitwise-not is 0xFFFFFFFF,
|
||||
// adding 1 to that, results in 0x100000000, which leaves
|
||||
// the low bits 0x0 and simply adds one to the high bits.
|
||||
highBits += 1; |
||||
} |
||||
return Int64.fromBits(lowBits, highBits); |
||||
}; |
||||
|
||||
/** @const {!Int64} */ |
||||
const ZERO = new Int64(0, 0); |
||||
|
||||
/** @const @private {number} */ |
||||
const LOW_16_BITS = 0xFFFF; |
||||
|
||||
/** @const @private {number} */ |
||||
const LOW_24_BITS = 0xFFFFFF; |
||||
|
||||
/** @const @private {number} */ |
||||
const LOW_31_BITS = 0x7FFFFFFF; |
||||
|
||||
/** @const @private {number} */ |
||||
const ALL_32_BITS = 0xFFFFFFFF; |
||||
|
||||
/** @const {!Int64} */ |
||||
const MAX_VALUE = Int64.fromBits(ALL_32_BITS, LOW_31_BITS); |
||||
|
||||
/** @const {!Int64} */ |
||||
const MIN_VALUE = Int64.fromBits(0, 0x80000000); |
||||
|
||||
/** @const {number} */ |
||||
const TWO_PWR_32_DBL = 0x100000000; |
||||
|
||||
/** @const {string} */ |
||||
const HEX_DIGITS = '0123456789abcdef'; |
||||
|
||||
exports = Int64; |
@ -0,0 +1,213 @@ |
||||
/** |
||||
* @fileoverview Tests for Int64. |
||||
*/ |
||||
goog.module('protobuf.Int64Test'); |
||||
goog.setTestOnly(); |
||||
|
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const Long = goog.require('goog.math.Long'); |
||||
|
||||
describe('Int64', () => { |
||||
it('can be constructed from bits', () => { |
||||
const int64 = Int64.fromBits(0, 1); |
||||
expect(int64.getLowBits()).toEqual(0); |
||||
expect(int64.getHighBits()).toEqual(1); |
||||
}); |
||||
|
||||
it('zero is defined', () => { |
||||
const int64 = Int64.getZero(); |
||||
expect(int64.getLowBits()).toEqual(0); |
||||
expect(int64.getHighBits()).toEqual(0); |
||||
}); |
||||
|
||||
it('max value is defined', () => { |
||||
const int64 = Int64.getMaxValue(); |
||||
expect(int64).toEqual(Int64.fromBits(0xFFFFFFFF, 0x7FFFFFFF)); |
||||
expect(int64.asLong()).toEqual(Long.getMaxValue()); |
||||
}); |
||||
|
||||
it('min value is defined', () => { |
||||
const int64 = Int64.getMinValue(); |
||||
expect(int64).toEqual(Int64.fromBits(0, 0x80000000)); |
||||
expect(int64.asLong()).toEqual(Long.getMinValue()); |
||||
}); |
||||
|
||||
it('Can be converted to long', () => { |
||||
const int64 = Int64.fromInt(1); |
||||
expect(int64.asLong()).toEqual(Long.fromInt(1)); |
||||
}); |
||||
|
||||
it('Negative value can be converted to long', () => { |
||||
const int64 = Int64.fromInt(-1); |
||||
expect(int64.getLowBits()).toEqual(0xFFFFFFFF | 0); |
||||
expect(int64.getHighBits()).toEqual(0xFFFFFFFF | 0); |
||||
expect(int64.asLong()).toEqual(Long.fromInt(-1)); |
||||
}); |
||||
|
||||
it('Can be converted to number', () => { |
||||
const int64 = Int64.fromInt(1); |
||||
expect(int64.asNumber()).toEqual(1); |
||||
}); |
||||
|
||||
it('Can convert negative value to number', () => { |
||||
const int64 = Int64.fromInt(-1); |
||||
expect(int64.asNumber()).toEqual(-1); |
||||
}); |
||||
|
||||
it('MAX_SAFE_INTEGER can be used.', () => { |
||||
const int64 = Int64.fromNumber(Number.MAX_SAFE_INTEGER); |
||||
expect(int64.getLowBitsUnsigned()).toEqual(0xFFFFFFFF); |
||||
expect(int64.getHighBits()).toEqual(0x1FFFFF); |
||||
expect(int64.asNumber()).toEqual(Number.MAX_SAFE_INTEGER); |
||||
}); |
||||
|
||||
it('MIN_SAFE_INTEGER can be used.', () => { |
||||
const int64 = Int64.fromNumber(Number.MIN_SAFE_INTEGER); |
||||
expect(int64.asNumber()).toEqual(Number.MIN_SAFE_INTEGER); |
||||
}); |
||||
|
||||
it('constructs fromInt', () => { |
||||
const int64 = Int64.fromInt(1); |
||||
expect(int64.getLowBits()).toEqual(1); |
||||
expect(int64.getHighBits()).toEqual(0); |
||||
}); |
||||
|
||||
it('constructs fromLong', () => { |
||||
const int64 = Int64.fromLong(Long.fromInt(1)); |
||||
expect(int64.getLowBits()).toEqual(1); |
||||
expect(int64.getHighBits()).toEqual(0); |
||||
}); |
||||
|
||||
// TODO: Use our own checking system here.
|
||||
if (goog.DEBUG) { |
||||
it('asNumber throws for MAX_SAFE_INTEGER + 1', () => { |
||||
expect(() => Int64.fromNumber(Number.MAX_SAFE_INTEGER + 1).asNumber()) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('fromInt(MAX_SAFE_INTEGER) throws', () => { |
||||
expect(() => Int64.fromInt(Number.MAX_SAFE_INTEGER)).toThrow(); |
||||
}); |
||||
|
||||
it('fromInt(1.5) throws', () => { |
||||
expect(() => Int64.fromInt(1.5)).toThrow(); |
||||
}); |
||||
} |
||||
|
||||
const decimalHexPairs = { |
||||
'0x0000000000000000': {signed: '0'}, |
||||
'0x0000000000000001': {signed: '1'}, |
||||
'0x00000000ffffffff': {signed: '4294967295'}, |
||||
'0x0000000100000000': {signed: '4294967296'}, |
||||
'0xffffffffffffffff': {signed: '-1', unsigned: '18446744073709551615'}, |
||||
'0x8000000000000000': |
||||
{signed: '-9223372036854775808', unsigned: '9223372036854775808'}, |
||||
'0x8000000080000000': |
||||
{signed: '-9223372034707292160', unsigned: '9223372039002259456'}, |
||||
'0x01b69b4bacd05f15': {signed: '123456789123456789'}, |
||||
'0xfe4964b4532fa0eb': |
||||
{signed: '-123456789123456789', unsigned: '18323287284586094827'}, |
||||
'0xa5a5a5a5a5a5a5a5': |
||||
{signed: '-6510615555426900571', unsigned: '11936128518282651045'}, |
||||
'0x5a5a5a5a5a5a5a5a': {signed: '6510615555426900570'}, |
||||
'0xffffffff00000000': |
||||
{signed: '-4294967296', unsigned: '18446744069414584320'}, |
||||
}; |
||||
|
||||
it('serializes to signed decimal strings', () => { |
||||
for (const [hex, decimals] of Object.entries(decimalHexPairs)) { |
||||
const int64 = hexToInt64(hex); |
||||
expect(int64.toSignedDecimalString()).toEqual(decimals.signed); |
||||
} |
||||
}); |
||||
|
||||
it('serializes to unsigned decimal strings', () => { |
||||
for (const [hex, decimals] of Object.entries(decimalHexPairs)) { |
||||
const int64 = hexToInt64(hex); |
||||
expect(int64.toUnsignedDecimalString()) |
||||
.toEqual(decimals.unsigned || decimals.signed); |
||||
} |
||||
}); |
||||
|
||||
it('serializes to unsigned hex strings', () => { |
||||
for (const [hex, decimals] of Object.entries(decimalHexPairs)) { |
||||
const int64 = hexToInt64(hex); |
||||
let shortHex = hex.replace(/0x0*/, '0x'); |
||||
if (shortHex == '0x') { |
||||
shortHex = '0x0'; |
||||
} |
||||
expect(int64.toHexString()).toEqual(shortHex); |
||||
} |
||||
}); |
||||
|
||||
it('parses decimal strings', () => { |
||||
for (const [hex, decimals] of Object.entries(decimalHexPairs)) { |
||||
const signed = Int64.fromDecimalString(decimals.signed); |
||||
expect(int64ToHex(signed)).toEqual(hex); |
||||
if (decimals.unsigned) { |
||||
const unsigned = Int64.fromDecimalString(decimals.unsigned); |
||||
expect(int64ToHex(unsigned)).toEqual(hex); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
it('parses hex strings', () => { |
||||
for (const [hex, decimals] of Object.entries(decimalHexPairs)) { |
||||
expect(int64ToHex(Int64.fromHexString(hex))).toEqual(hex); |
||||
} |
||||
expect(int64ToHex(Int64.fromHexString('-0x1'))) |
||||
.toEqual('0xffffffffffffffff'); |
||||
}); |
||||
|
||||
// TODO: Use our own checking system here.
|
||||
if (goog.DEBUG) { |
||||
it('throws when parsing empty string', () => { |
||||
expect(() => Int64.fromDecimalString('')).toThrow(); |
||||
}); |
||||
|
||||
it('throws when parsing float string', () => { |
||||
expect(() => Int64.fromDecimalString('1.5')).toThrow(); |
||||
}); |
||||
|
||||
it('throws when parsing non-numeric string', () => { |
||||
expect(() => Int64.fromDecimalString('0xa')).toThrow(); |
||||
}); |
||||
} |
||||
|
||||
it('checks if equal', () => { |
||||
const low = Int64.fromInt(1); |
||||
const high = Int64.getMaxValue(); |
||||
expect(low.equals(Int64.fromInt(1))).toEqual(true); |
||||
expect(low.equals(high)).toEqual(false); |
||||
expect(high.equals(Int64.getMaxValue())).toEqual(true); |
||||
}); |
||||
|
||||
it('returns unique hashcode', () => { |
||||
expect(Int64.fromInt(1).hashCode()).toEqual(Int64.fromInt(1).hashCode()); |
||||
expect(Int64.fromInt(1).hashCode()) |
||||
.not.toEqual(Int64.fromInt(2).hashCode()); |
||||
}); |
||||
}); |
||||
|
||||
/** |
||||
* @param {string} hexString |
||||
* @return {!Int64} |
||||
*/ |
||||
function hexToInt64(hexString) { |
||||
const high = hexString.slice(2, 10); |
||||
const low = hexString.slice(10); |
||||
return Int64.fromBits(parseInt(low, 16), parseInt(high, 16)); |
||||
} |
||||
|
||||
/** |
||||
* @param {!Int64} int64 |
||||
* @return {string} |
||||
*/ |
||||
function int64ToHex(int64) { |
||||
const ZEROS_32_BIT = '00000000'; |
||||
const highPartialHex = int64.getHighBitsUnsigned().toString(16); |
||||
const lowPartialHex = int64.getLowBitsUnsigned().toString(16); |
||||
const highHex = ZEROS_32_BIT.slice(highPartialHex.length) + highPartialHex; |
||||
const lowHex = ZEROS_32_BIT.slice(lowPartialHex.length) + lowPartialHex; |
||||
return `0x${highHex}${lowHex}`; |
||||
} |
@ -0,0 +1,708 @@ |
||||
/** |
||||
* @fileoverview Proto internal runtime checks. |
||||
* |
||||
* Checks are grouped into different severity, see: |
||||
* http://g3doc/javascript/protobuf/README.md#configurable-check-support-in-protocol-buffers
|
||||
* |
||||
* Checks are also grouped into different sections: |
||||
* - CHECK_BOUNDS: |
||||
* Checks that ensure that indexed access is within bounds |
||||
* (e.g. an array being accessed past its size). |
||||
* - CHECK_STATE |
||||
* Checks related to the state of an object |
||||
* (e.g. a parser hitting an invalid case). |
||||
* - CHECK_TYPE: |
||||
* Checks that relate to type errors (e.g. code receives a number instead |
||||
* of a string). |
||||
*/ |
||||
goog.module('protobuf.internal.checks'); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const WireType = goog.require('protobuf.binary.WireType'); |
||||
|
||||
//
|
||||
// See
|
||||
// http://g3doc/javascript/protobuf/README.md#configurable-check-support-in-protocol-buffers
|
||||
//
|
||||
/** @define{string} */ |
||||
const CHECK_LEVEL_DEFINE = goog.define('protobuf.defines.CHECK_LEVEL', ''); |
||||
|
||||
/** @define{boolean} */ |
||||
const POLYFILL_TEXT_ENCODING = |
||||
goog.define('protobuf.defines.POLYFILL_TEXT_ENCODING', true); |
||||
|
||||
/** |
||||
* @const {number} |
||||
*/ |
||||
const MAX_FIELD_NUMBER = Math.pow(2, 29) - 1; |
||||
|
||||
/** |
||||
* The largest finite float32 value. |
||||
* @const {number} |
||||
*/ |
||||
const FLOAT32_MAX = 3.4028234663852886e+38; |
||||
|
||||
/** @enum {number} */ |
||||
const CheckLevel = { |
||||
DEBUG: 0, |
||||
CRITICAL: 1, |
||||
OFF: 2 |
||||
}; |
||||
|
||||
|
||||
/** @return {!CheckLevel} */ |
||||
function calculateCheckLevel() { |
||||
const definedLevel = CHECK_LEVEL_DEFINE.toUpperCase(); |
||||
if (definedLevel === '') { |
||||
// user did not set a value, value now just depends on goog.DEBUG
|
||||
return goog.DEBUG ? CheckLevel.DEBUG : CheckLevel.CRITICAL; |
||||
} |
||||
|
||||
if (definedLevel === 'CRITICAL') { |
||||
return CheckLevel.CRITICAL; |
||||
} |
||||
|
||||
if (definedLevel === 'OFF') { |
||||
return CheckLevel.OFF; |
||||
} |
||||
|
||||
if (definedLevel === 'DEBUG') { |
||||
return CheckLevel.DEBUG; |
||||
} |
||||
|
||||
throw new Error(`Unknown value for CHECK_LEVEL: ${CHECK_LEVEL_DEFINE}`); |
||||
} |
||||
|
||||
const /** !CheckLevel */ CHECK_LEVEL = calculateCheckLevel(); |
||||
|
||||
const /** boolean */ CHECK_STATE = CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
const /** boolean */ CHECK_CRITICAL_STATE = |
||||
CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
const /** boolean */ CHECK_BOUNDS = CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
const /** boolean */ CHECK_CRITICAL_BOUNDS = |
||||
CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
const /** boolean */ CHECK_TYPE = CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
const /** boolean */ CHECK_CRITICAL_TYPE = |
||||
CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG; |
||||
|
||||
/** |
||||
* Ensures the truth of an expression involving the state of the calling |
||||
* instance, but not involving any parameters to the calling method. |
||||
* |
||||
* For cases where failing fast is pretty important and not failing early could |
||||
* cause bugs that are much harder to debug. |
||||
* @param {boolean} state |
||||
* @param {string=} message |
||||
* @throws {!Error} If the state is false and the check state is critical. |
||||
*/ |
||||
function checkCriticalState(state, message = '') { |
||||
if (!CHECK_CRITICAL_STATE) { |
||||
return; |
||||
} |
||||
if (!state) { |
||||
throw new Error(message); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures the truth of an expression involving the state of the calling |
||||
* instance, but not involving any parameters to the calling method. |
||||
* |
||||
* @param {boolean} state |
||||
* @param {string=} message |
||||
* @throws {!Error} If the state is false and the check state is debug. |
||||
*/ |
||||
function checkState(state, message = '') { |
||||
if (!CHECK_STATE) { |
||||
return; |
||||
} |
||||
checkCriticalState(state, message); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that `index` specifies a valid position in an indexable object of |
||||
* size `size`. A position index may range from zero to size, inclusive. |
||||
* @param {number} index |
||||
* @param {number} size |
||||
* @throws {!Error} If the index is out of range and the check state is debug. |
||||
*/ |
||||
function checkPositionIndex(index, size) { |
||||
if (!CHECK_BOUNDS) { |
||||
return; |
||||
} |
||||
checkCriticalPositionIndex(index, size); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that `index` specifies a valid position in an indexable object of |
||||
* size `size`. A position index may range from zero to size, inclusive. |
||||
* @param {number} index |
||||
* @param {number} size |
||||
* @throws {!Error} If the index is out of range and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalPositionIndex(index, size) { |
||||
if (!CHECK_CRITICAL_BOUNDS) { |
||||
return; |
||||
} |
||||
if (index < 0 || index > size) { |
||||
throw new Error(`Index out of bounds: index: ${index} size: ${size}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that `index` specifies a valid element in an indexable object of |
||||
* size `size`. A element index may range from zero to size, exclusive. |
||||
* @param {number} index |
||||
* @param {number} size |
||||
* @throws {!Error} If the index is out of range and the check state is |
||||
* debug. |
||||
*/ |
||||
function checkElementIndex(index, size) { |
||||
if (!CHECK_BOUNDS) { |
||||
return; |
||||
} |
||||
checkCriticalElementIndex(index, size); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that `index` specifies a valid element in an indexable object of |
||||
* size `size`. A element index may range from zero to size, exclusive. |
||||
* @param {number} index |
||||
* @param {number} size |
||||
* @throws {!Error} If the index is out of range and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalElementIndex(index, size) { |
||||
if (!CHECK_CRITICAL_BOUNDS) { |
||||
return; |
||||
} |
||||
if (index < 0 || index >= size) { |
||||
throw new Error(`Index out of bounds: index: ${index} size: ${size}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures the range of [start, end) is with the range of [0, size). |
||||
* @param {number} start |
||||
* @param {number} end |
||||
* @param {number} size |
||||
* @throws {!Error} If start and end are out of range and the check state is |
||||
* debug. |
||||
*/ |
||||
function checkRange(start, end, size) { |
||||
if (!CHECK_BOUNDS) { |
||||
return; |
||||
} |
||||
checkCriticalRange(start, end, size); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the range of [start, end) is with the range of [0, size). |
||||
* @param {number} start |
||||
* @param {number} end |
||||
* @param {number} size |
||||
* @throws {!Error} If start and end are out of range and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalRange(start, end, size) { |
||||
if (!CHECK_CRITICAL_BOUNDS) { |
||||
return; |
||||
} |
||||
if (start < 0 || end < 0 || start > size || end > size) { |
||||
throw new Error(`Range error: start: ${start} end: ${end} size: ${size}`); |
||||
} |
||||
if (start > end) { |
||||
throw new Error(`Start > end: ${start} > ${end}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that field number is an integer and within the range of |
||||
* [1, MAX_FIELD_NUMBER]. |
||||
* @param {number} fieldNumber |
||||
* @throws {!Error} If the field number is out of range and the check state is |
||||
* debug. |
||||
*/ |
||||
function checkFieldNumber(fieldNumber) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalFieldNumber(fieldNumber); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that the value is neither null nor undefined. |
||||
* |
||||
* @param {T} value |
||||
* @return {R} |
||||
* |
||||
* @template T |
||||
* @template R := |
||||
* mapunion(T, (V) => |
||||
* cond(eq(V, 'null'), |
||||
* none(), |
||||
* cond(eq(V, 'undefined'), |
||||
* none(), |
||||
* V))) |
||||
* =: |
||||
*/ |
||||
function checkDefAndNotNull(value) { |
||||
if (CHECK_TYPE) { |
||||
// Note that undefined == null.
|
||||
if (value == null) { |
||||
throw new Error(`Value can't be null`); |
||||
} |
||||
} |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Ensures that the value exists and is a function. |
||||
* |
||||
* @param {function(?): ?} func |
||||
*/ |
||||
function checkFunctionExists(func) { |
||||
if (CHECK_TYPE) { |
||||
if (typeof func !== 'function') { |
||||
throw new Error(`${func} is not a function`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that field number is an integer and within the range of |
||||
* [1, MAX_FIELD_NUMBER]. |
||||
* @param {number} fieldNumber |
||||
* @throws {!Error} If the field number is out of range and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalFieldNumber(fieldNumber) { |
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
if (fieldNumber <= 0 || fieldNumber > MAX_FIELD_NUMBER) { |
||||
throw new Error(`Field number is out of range: ${fieldNumber}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that wire type is valid. |
||||
* @param {!WireType} wireType |
||||
* @throws {!Error} If the wire type is invalid and the check state is debug. |
||||
*/ |
||||
function checkWireType(wireType) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalWireType(wireType); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that wire type is valid. |
||||
* @param {!WireType} wireType |
||||
* @throws {!Error} If the wire type is invalid and the check state is critical. |
||||
*/ |
||||
function checkCriticalWireType(wireType) { |
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
if (wireType < WireType.VARINT || wireType > WireType.FIXED32) { |
||||
throw new Error(`Invalid wire type: ${wireType}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures the given value has the correct type. |
||||
* @param {boolean} expression |
||||
* @param {string} errorMsg |
||||
* @throws {!Error} If the value has the wrong type and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalType(expression, errorMsg) { |
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
if (!expression) { |
||||
throw new Error(errorMsg); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array. |
||||
* @param {*} value |
||||
* @return {!Array<*>} |
||||
*/ |
||||
function checkCriticalTypeArray(value) { |
||||
checkCriticalType( |
||||
Array.isArray(value), `Must be an array, but got: ${value}`); |
||||
return /** @type {!Array<*>} */ (value); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an iterable. |
||||
* @param {*} value |
||||
* @return {!Iterable<*>} |
||||
*/ |
||||
function checkCriticalTypeIterable(value) { |
||||
checkCriticalType( |
||||
!!value[Symbol.iterator], `Must be an iterable, but got: ${value}`); |
||||
return /** @type {!Iterable<*>} */ (value); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a boolean. |
||||
* @param {*} value |
||||
*/ |
||||
function checkCriticalTypeBool(value) { |
||||
checkCriticalType( |
||||
typeof value === 'boolean', `Must be a boolean, but got: ${value}`); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of boolean. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeBoolArray(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeBool(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a ByteString. |
||||
* @param {*} value |
||||
*/ |
||||
function checkCriticalTypeByteString(value) { |
||||
checkCriticalType( |
||||
value instanceof ByteString, `Must be a ByteString, but got: ${value}`); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of ByteString. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeByteStringArray(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeByteString(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not float and the check state is debug. |
||||
*/ |
||||
function checkTypeDouble(value) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeDouble(value); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not float and the check state is critical. |
||||
*/ |
||||
function checkCriticalTypeDouble(value) { |
||||
checkCriticalType( |
||||
typeof value === 'number', `Must be a number, but got: ${value}`); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of double. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeDoubleArray(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeDouble(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not signed int32 and the check state is |
||||
* debug. |
||||
*/ |
||||
function checkTypeSignedInt32(value) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeSignedInt32(value); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not signed int32 and the check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalTypeSignedInt32(value) { |
||||
checkCriticalTypeDouble(value); |
||||
const valueAsNumber = /** @type {number} */ (value); |
||||
if (CHECK_CRITICAL_TYPE) { |
||||
if (valueAsNumber < -Math.pow(2, 31) || valueAsNumber > Math.pow(2, 31) || |
||||
!Number.isInteger(valueAsNumber)) { |
||||
throw new Error(`Must be int32, but got: ${valueAsNumber}`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of numbers. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeSignedInt32Array(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeSignedInt32(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that value is a long instance. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not a long instance and check state is |
||||
* debug. |
||||
*/ |
||||
function checkTypeSignedInt64(value) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeSignedInt64(value); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that value is a long instance. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not a long instance and check state is |
||||
* critical. |
||||
*/ |
||||
function checkCriticalTypeSignedInt64(value) { |
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
if (!(value instanceof Int64)) { |
||||
throw new Error(`Must be Int64 instance, but got: ${value}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of long instances. |
||||
* @param {*} values |
||||
* @throws {!Error} If values is not an array of long instances. |
||||
*/ |
||||
function checkCriticalTypeSignedInt64Array(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeSignedInt64(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number and within float32 precision. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not float and the check state is debug. |
||||
*/ |
||||
function checkTypeFloat(value) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeFloat(value); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a number and within float32 precision. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is not float and the check state is critical. |
||||
*/ |
||||
function checkCriticalTypeFloat(value) { |
||||
checkCriticalTypeDouble(value); |
||||
if (CHECK_CRITICAL_TYPE) { |
||||
const valueAsNumber = /** @type {number} */ (value); |
||||
if (Number.isFinite(valueAsNumber) && |
||||
(valueAsNumber > FLOAT32_MAX || valueAsNumber < -FLOAT32_MAX)) { |
||||
throw new Error( |
||||
`Given number does not fit into float precision: ${value}`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an iterable of floats. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeFloatIterable(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const iterable = checkCriticalTypeIterable(values); |
||||
for (const value of iterable) { |
||||
checkCriticalTypeFloat(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is a string. |
||||
* @param {*} value |
||||
*/ |
||||
function checkCriticalTypeString(value) { |
||||
checkCriticalType( |
||||
typeof value === 'string', `Must be string, but got: ${value}`); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of string. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeStringArray(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeString(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ensures that value is a valid unsigned int32. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is out of range and the check state is debug. |
||||
*/ |
||||
function checkTypeUnsignedInt32(value) { |
||||
if (!CHECK_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeUnsignedInt32(value); |
||||
} |
||||
|
||||
/** |
||||
* Ensures that value is a valid unsigned int32. |
||||
* @param {*} value |
||||
* @throws {!Error} If the value is out of range and the check state |
||||
* is critical. |
||||
*/ |
||||
function checkCriticalTypeUnsignedInt32(value) { |
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
checkCriticalTypeDouble(value); |
||||
const valueAsNumber = /** @type {number} */ (value); |
||||
if (valueAsNumber < 0 || valueAsNumber > Math.pow(2, 32) - 1 || |
||||
!Number.isInteger(valueAsNumber)) { |
||||
throw new Error(`Must be uint32, but got: ${value}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of unsigned int32. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeUnsignedInt32Array(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalTypeUnsignedInt32(value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a given object is an array of message. |
||||
* @param {*} values |
||||
*/ |
||||
function checkCriticalTypeMessageArray(values) { |
||||
// TODO(b/134765672)
|
||||
if (!CHECK_CRITICAL_TYPE) { |
||||
return; |
||||
} |
||||
const array = checkCriticalTypeArray(values); |
||||
for (const value of array) { |
||||
checkCriticalType( |
||||
value !== null, 'Given value is not a message instance: null'); |
||||
} |
||||
} |
||||
|
||||
exports = { |
||||
checkDefAndNotNull, |
||||
checkCriticalElementIndex, |
||||
checkCriticalFieldNumber, |
||||
checkCriticalPositionIndex, |
||||
checkCriticalRange, |
||||
checkCriticalState, |
||||
checkCriticalTypeBool, |
||||
checkCriticalTypeBoolArray, |
||||
checkCriticalTypeByteString, |
||||
checkCriticalTypeByteStringArray, |
||||
checkCriticalTypeDouble, |
||||
checkTypeDouble, |
||||
checkCriticalTypeDoubleArray, |
||||
checkTypeFloat, |
||||
checkCriticalTypeFloat, |
||||
checkCriticalTypeFloatIterable, |
||||
checkCriticalTypeMessageArray, |
||||
checkCriticalTypeSignedInt32, |
||||
checkCriticalTypeSignedInt32Array, |
||||
checkCriticalTypeSignedInt64, |
||||
checkTypeSignedInt64, |
||||
checkCriticalTypeSignedInt64Array, |
||||
checkCriticalTypeString, |
||||
checkCriticalTypeStringArray, |
||||
checkCriticalTypeUnsignedInt32, |
||||
checkCriticalTypeUnsignedInt32Array, |
||||
checkCriticalType, |
||||
checkCriticalWireType, |
||||
checkElementIndex, |
||||
checkFieldNumber, |
||||
checkFunctionExists, |
||||
checkPositionIndex, |
||||
checkRange, |
||||
checkState, |
||||
checkTypeUnsignedInt32, |
||||
checkTypeSignedInt32, |
||||
checkWireType, |
||||
CHECK_BOUNDS, |
||||
CHECK_CRITICAL_BOUNDS, |
||||
CHECK_STATE, |
||||
CHECK_CRITICAL_STATE, |
||||
CHECK_TYPE, |
||||
CHECK_CRITICAL_TYPE, |
||||
MAX_FIELD_NUMBER, |
||||
POLYFILL_TEXT_ENCODING, |
||||
}; |
@ -0,0 +1,58 @@ |
||||
/** |
||||
* @fileoverview Tests for checks.js. |
||||
*/ |
||||
goog.module('protobuf.internal.checksTest'); |
||||
|
||||
const {CHECK_TYPE, checkDefAndNotNull, checkFunctionExists} = goog.require('protobuf.internal.checks'); |
||||
|
||||
describe('checkDefAndNotNull', () => { |
||||
it('throws if undefined', () => { |
||||
let value; |
||||
if (CHECK_TYPE) { |
||||
expect(() => checkDefAndNotNull(value)).toThrow(); |
||||
} else { |
||||
expect(checkDefAndNotNull(value)).toBeUndefined(); |
||||
} |
||||
}); |
||||
|
||||
it('throws if null', () => { |
||||
const value = null; |
||||
if (CHECK_TYPE) { |
||||
expect(() => checkDefAndNotNull(value)).toThrow(); |
||||
} else { |
||||
expect(checkDefAndNotNull(value)).toBeNull(); |
||||
} |
||||
}); |
||||
|
||||
it('does not throw if empty string', () => { |
||||
const value = ''; |
||||
expect(checkDefAndNotNull(value)).toEqual(''); |
||||
}); |
||||
}); |
||||
|
||||
describe('checkFunctionExists', () => { |
||||
it('throws if the function is undefined', () => { |
||||
let foo = /** @type {function()} */ (/** @type {*} */ (undefined)); |
||||
if (CHECK_TYPE) { |
||||
expect(() => checkFunctionExists(foo)).toThrow(); |
||||
} else { |
||||
checkFunctionExists(foo); |
||||
} |
||||
}); |
||||
|
||||
it('throws if the property is defined but not a function', () => { |
||||
let foo = /** @type {function()} */ (/** @type {*} */ (1)); |
||||
if (CHECK_TYPE) { |
||||
expect(() => checkFunctionExists(foo)).toThrow(); |
||||
} else { |
||||
checkFunctionExists(foo); |
||||
} |
||||
}); |
||||
|
||||
it('does not throw if the function is defined', () => { |
||||
function foo(x) { |
||||
return x; |
||||
} |
||||
checkFunctionExists(foo); |
||||
}); |
||||
}); |
@ -0,0 +1,79 @@ |
||||
/** |
||||
* @fileoverview Test data for bool encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.boolTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of boolean values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, boolValue: boolean, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getBoolPairs() { |
||||
const boolPairs = [ |
||||
{ |
||||
name: 'true', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'false', |
||||
boolValue: false, |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'two-byte true', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder(0x80, 0x01), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'two-byte false', |
||||
boolValue: false, |
||||
bufferDecoder: createBufferDecoder(0x80, 0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'max signed int 2^63 - 1', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'min signed int -2^63', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder( |
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'overflowed but valid varint', |
||||
boolValue: false, |
||||
bufferDecoder: createBufferDecoder( |
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'errors out for 11 bytes', |
||||
boolValue: true, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), |
||||
error: true, |
||||
skip_writer: true, |
||||
}, |
||||
]; |
||||
return [...boolPairs]; |
||||
} |
||||
|
||||
exports = {getBoolPairs}; |
@ -0,0 +1,256 @@ |
||||
/** |
||||
* @fileoverview A buffer implementation that can decode data for protobufs. |
||||
*/ |
||||
|
||||
goog.module('protobuf.binary.BufferDecoder'); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const functions = goog.require('goog.functions'); |
||||
const {POLYFILL_TEXT_ENCODING, checkCriticalElementIndex, checkCriticalPositionIndex, checkState} = goog.require('protobuf.internal.checks'); |
||||
const {byteStringFromUint8ArrayUnsafe} = goog.require('protobuf.byteStringInternal'); |
||||
const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays'); |
||||
const {decode} = goog.require('protobuf.binary.textencoding'); |
||||
|
||||
/** |
||||
* Returns a valid utf-8 decoder function based on TextDecoder if available or |
||||
* a polyfill. |
||||
* Some of the environments we run in do not have TextDecoder defined. |
||||
* TextDecoder is faster than our polyfill so we prefer it over the polyfill. |
||||
* @return {function(!DataView): string} |
||||
*/ |
||||
function getStringDecoderFunction() { |
||||
if (goog.global['TextDecoder']) { |
||||
const textDecoder = new goog.global['TextDecoder']('utf-8', {fatal: true}); |
||||
return bytes => textDecoder.decode(bytes); |
||||
} |
||||
if (POLYFILL_TEXT_ENCODING) { |
||||
return decode; |
||||
} else { |
||||
throw new Error( |
||||
'TextDecoder is missing. ' + |
||||
'Enable protobuf.defines.POLYFILL_TEXT_ENCODING.'); |
||||
} |
||||
} |
||||
|
||||
/** @type {function(): function(!DataView): string} */ |
||||
const stringDecoderFunction = |
||||
functions.cacheReturnValue(() => getStringDecoderFunction()); |
||||
|
||||
/** @type {function(): !DataView} */ |
||||
const emptyDataView = |
||||
functions.cacheReturnValue(() => new DataView(new ArrayBuffer(0))); |
||||
|
||||
class BufferDecoder { |
||||
/** |
||||
* @param {!Array<!BufferDecoder>} bufferDecoders |
||||
* @return {!BufferDecoder} |
||||
*/ |
||||
static merge(bufferDecoders) { |
||||
const uint8Arrays = bufferDecoders.map(b => b.asUint8Array()); |
||||
const bytesArray = concatenateByteArrays(uint8Arrays); |
||||
return BufferDecoder.fromArrayBuffer(bytesArray.buffer); |
||||
} |
||||
|
||||
/** |
||||
* @param {!ArrayBuffer} arrayBuffer |
||||
* @return {!BufferDecoder} |
||||
*/ |
||||
static fromArrayBuffer(arrayBuffer) { |
||||
return new BufferDecoder( |
||||
new DataView(arrayBuffer), 0, arrayBuffer.byteLength); |
||||
} |
||||
|
||||
/** |
||||
* @param {!DataView} dataView |
||||
* @param {number} startIndex |
||||
* @param {number} length |
||||
* @private |
||||
*/ |
||||
constructor(dataView, startIndex, length) { |
||||
/** @private @const {!DataView} */ |
||||
this.dataView_ = dataView; |
||||
/** @private @const {number} */ |
||||
this.startIndex_ = startIndex; |
||||
/** @private @const {number} */ |
||||
this.endIndex_ = this.startIndex_ + length; |
||||
} |
||||
|
||||
/** |
||||
* Returns the start index of the underlying buffer. |
||||
* @return {number} |
||||
*/ |
||||
startIndex() { |
||||
return this.startIndex_; |
||||
} |
||||
|
||||
/** |
||||
* Returns the end index of the underlying buffer. |
||||
* @return {number} |
||||
*/ |
||||
endIndex() { |
||||
return this.endIndex_; |
||||
} |
||||
|
||||
/** |
||||
* Returns the length of the underlying buffer. |
||||
* @return {number} |
||||
*/ |
||||
length() { |
||||
return this.endIndex_ - this.startIndex_; |
||||
} |
||||
|
||||
/** |
||||
* Returns a float32 from a given index |
||||
* @param {number} index |
||||
* @return {number} |
||||
*/ |
||||
getFloat32(index) { |
||||
return this.dataView_.getFloat32(index, true); |
||||
} |
||||
|
||||
/** |
||||
* Returns a float64 from a given index |
||||
* @param {number} index |
||||
* @return {number} |
||||
*/ |
||||
getFloat64(index) { |
||||
return this.dataView_.getFloat64(index, true); |
||||
} |
||||
|
||||
/** |
||||
* Returns an int32 from a given index |
||||
* @param {number} index |
||||
* @return {number} |
||||
*/ |
||||
getInt32(index) { |
||||
return this.dataView_.getInt32(index, true); |
||||
} |
||||
|
||||
/** |
||||
* @param {number} index |
||||
* @return {number} |
||||
*/ |
||||
getUint8(index) { |
||||
return this.dataView_.getUint8(index); |
||||
} |
||||
|
||||
/** |
||||
* Returns a uint32 from a given index |
||||
* @param {number} index |
||||
* @return {number} |
||||
*/ |
||||
getUint32(index) { |
||||
return this.dataView_.getUint32(index, true); |
||||
} |
||||
|
||||
/** |
||||
* Returns two JS numbers each representing 32 bits of a 64 bit number. |
||||
* @param {number} index |
||||
* @return {{lowBits: number, highBits: number, dataStart: number}} |
||||
*/ |
||||
getVarint(index) { |
||||
let start = index; |
||||
let lowBits = 0; |
||||
let highBits = 0; |
||||
|
||||
for (let shift = 0; shift < 28; shift += 7) { |
||||
const b = this.dataView_.getUint8(start++); |
||||
lowBits |= (b & 0x7F) << shift; |
||||
if ((b & 0x80) === 0) { |
||||
return {lowBits, highBits, dataStart: start}; |
||||
} |
||||
} |
||||
|
||||
const middleByte = this.dataView_.getUint8(start++); |
||||
|
||||
// last four bits of the first 32 bit number
|
||||
lowBits |= (middleByte & 0x0F) << 28; |
||||
|
||||
// 3 upper bits are part of the next 32 bit number
|
||||
highBits = (middleByte & 0x70) >> 4; |
||||
|
||||
if ((middleByte & 0x80) === 0) { |
||||
return {lowBits, highBits, dataStart: start}; |
||||
} |
||||
|
||||
|
||||
for (let shift = 3; shift <= 31; shift += 7) { |
||||
const b = this.dataView_.getUint8(start++); |
||||
highBits |= (b & 0x7F) << shift; |
||||
if ((b & 0x80) === 0) { |
||||
return {lowBits, highBits, dataStart: start}; |
||||
} |
||||
} |
||||
|
||||
checkState(false, 'Data is longer than 10 bytes'); |
||||
return {lowBits, highBits, dataStart: start}; |
||||
} |
||||
|
||||
/** |
||||
* Skips over a varint at a given index and returns the next position. |
||||
* @param {number} index Start of the data. |
||||
* @return {number} Position of the first byte after the varint. |
||||
* @package |
||||
*/ |
||||
skipVarint(index) { |
||||
let cursor = index; |
||||
checkCriticalElementIndex(cursor, this.endIndex()); |
||||
while (this.dataView_.getUint8(cursor++) & 0x80) { |
||||
checkCriticalElementIndex(cursor, this.endIndex()); |
||||
} |
||||
checkCriticalPositionIndex(cursor, index + 10); |
||||
return cursor; |
||||
} |
||||
|
||||
/** |
||||
* @param {number} startIndex |
||||
* @param {number} length |
||||
* @return {!BufferDecoder} |
||||
*/ |
||||
subBufferDecoder(startIndex, length) { |
||||
checkState( |
||||
startIndex >= this.startIndex(), |
||||
`Current start: ${this.startIndex()}, subBufferDecoder start: ${ |
||||
startIndex}`);
|
||||
checkState(length >= 0, `Length: ${length}`); |
||||
checkState( |
||||
startIndex + length <= this.endIndex(), |
||||
`Current end: ${this.endIndex()}, subBufferDecoder start: ${ |
||||
startIndex}, subBufferDecoder length: ${length}`);
|
||||
return new BufferDecoder(this.dataView_, startIndex, length); |
||||
} |
||||
|
||||
/** |
||||
* Returns the buffer as a string. |
||||
* @return {string} |
||||
*/ |
||||
asString() { |
||||
// TODO: Remove this check when we no longer need to support IE
|
||||
const stringDataView = this.length() === 0 ? |
||||
emptyDataView() : |
||||
new DataView(this.dataView_.buffer, this.startIndex_, this.length()); |
||||
return stringDecoderFunction()(stringDataView); |
||||
} |
||||
|
||||
/** |
||||
* Returns the buffer as a ByteString. |
||||
* @return {!ByteString} |
||||
*/ |
||||
asByteString() { |
||||
return byteStringFromUint8ArrayUnsafe(this.asUint8Array()); |
||||
} |
||||
|
||||
/** |
||||
* Returns the DataView as an Uint8Array. DO NOT MODIFY or expose the |
||||
* underlying buffer. |
||||
* |
||||
* @package |
||||
* @return {!Uint8Array} |
||||
*/ |
||||
asUint8Array() { |
||||
return new Uint8Array( |
||||
this.dataView_.buffer, this.startIndex_, this.length()); |
||||
} |
||||
} |
||||
|
||||
exports = BufferDecoder; |
@ -0,0 +1,18 @@ |
||||
/** |
||||
* @fileoverview Helper methods to create BufferDecoders. |
||||
*/ |
||||
goog.module('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
|
||||
/** |
||||
* @param {...number} bytes |
||||
* @return {!BufferDecoder} |
||||
*/ |
||||
function createBufferDecoder(...bytes) { |
||||
return BufferDecoder.fromArrayBuffer(new Uint8Array(bytes).buffer); |
||||
} |
||||
|
||||
exports = { |
||||
createBufferDecoder, |
||||
}; |
@ -0,0 +1,104 @@ |
||||
/** |
||||
* @fileoverview Tests for BufferDecoder. |
||||
*/ |
||||
|
||||
goog.module('protobuf.binary.varintsTest'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {CHECK_CRITICAL_BOUNDS, CHECK_STATE} = goog.require('protobuf.internal.checks'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
/** |
||||
* @param {...number} bytes |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
function createArrayBuffer(...bytes) { |
||||
return new Uint8Array(bytes).buffer; |
||||
} |
||||
|
||||
describe('Skip varint does', () => { |
||||
it('skip a varint', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01)); |
||||
expect(bufferDecoder.skipVarint(0)).toBe(1); |
||||
}); |
||||
|
||||
it('fail when varint is larger than 10 bytes', () => { |
||||
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer( |
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); |
||||
|
||||
if (CHECK_CRITICAL_BOUNDS) { |
||||
expect(() => bufferDecoder.skipVarint(0)).toThrow(); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
expect(bufferDecoder.skipVarint(0)).toBe(11); |
||||
} |
||||
}); |
||||
|
||||
it('fail when varint is beyond end of underlying array', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x80, 0x80)); |
||||
expect(() => bufferDecoder.skipVarint(0)).toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
describe('readVarint64 does', () => { |
||||
it('read zero', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00)); |
||||
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0); |
||||
expect(dataStart).toBe(1); |
||||
expect(lowBits).toBe(0); |
||||
expect(highBits).toBe(0); |
||||
}); |
||||
|
||||
it('read one', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01)); |
||||
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0); |
||||
expect(dataStart).toBe(1); |
||||
expect(lowBits).toBe(1); |
||||
expect(highBits).toBe(0); |
||||
}); |
||||
|
||||
it('read max value', () => { |
||||
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01)); |
||||
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0); |
||||
expect(dataStart).toBe(10); |
||||
expect(lowBits).toBe(-1); |
||||
expect(highBits).toBe(-1); |
||||
}); |
||||
}); |
||||
|
||||
describe('subBufferDecoder does', () => { |
||||
it('can create valid sub buffers', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00, 0x01, 0x02)); |
||||
|
||||
expect(bufferDecoder.subBufferDecoder(0, 0)) |
||||
.toEqual(BufferDecoder.fromArrayBuffer(createArrayBuffer())); |
||||
expect(bufferDecoder.subBufferDecoder(0, 1)) |
||||
.toEqual(BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00))); |
||||
expect(bufferDecoder.subBufferDecoder(1, 0)) |
||||
.toEqual(BufferDecoder.fromArrayBuffer(createArrayBuffer())); |
||||
expect(bufferDecoder.subBufferDecoder(1, 1)) |
||||
.toEqual(BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01))); |
||||
expect(bufferDecoder.subBufferDecoder(1, 2)) |
||||
.toEqual(BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01, 0x02))); |
||||
}); |
||||
|
||||
it('can not create invalid', () => { |
||||
const bufferDecoder = |
||||
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00, 0x01, 0x02)); |
||||
if (CHECK_STATE) { |
||||
expect(() => bufferDecoder.subBufferDecoder(-1, 1)).toThrow(); |
||||
expect(() => bufferDecoder.subBufferDecoder(0, -4)).toThrow(); |
||||
expect(() => bufferDecoder.subBufferDecoder(0, 4)).toThrow(); |
||||
} |
||||
}); |
||||
}); |
@ -0,0 +1,91 @@ |
||||
/** |
||||
* @fileoverview Handwritten code of ConformanceRequest. |
||||
*/ |
||||
goog.module('proto.conformance.ConformanceRequest'); |
||||
|
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
const WireFormat = goog.require('proto.conformance.WireFormat'); |
||||
|
||||
/** |
||||
* Handwritten code of conformance.ConformanceRequest. |
||||
* This is used to send request from the conformance test runner to the testee. |
||||
* Check //third_party/protobuf/testing/protobuf/conformance/conformance.proto
|
||||
* for more details. |
||||
* @final |
||||
*/ |
||||
class ConformanceRequest { |
||||
/** |
||||
* @param {!ArrayBuffer} bytes |
||||
* @private |
||||
*/ |
||||
constructor(bytes) { |
||||
/** @private @const {!LazyAccessor} */ |
||||
this.accessor_ = LazyAccessor.fromArrayBuffer(bytes); |
||||
} |
||||
|
||||
/** |
||||
* Create a request instance with the given bytes data. |
||||
* @param {!ArrayBuffer} bytes |
||||
* @return {!ConformanceRequest} |
||||
*/ |
||||
static deserialize(bytes) { |
||||
return new ConformanceRequest(bytes); |
||||
} |
||||
|
||||
/** |
||||
* Gets the protobuf_payload. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
getProtobufPayload() { |
||||
return this.accessor_.getBytesWithDefault(1).toArrayBuffer(); |
||||
} |
||||
|
||||
/** |
||||
* Gets the requested_output_format. |
||||
* @return {!WireFormat} |
||||
*/ |
||||
getRequestedOutputFormat() { |
||||
return /** @type {!WireFormat} */ (this.accessor_.getInt32WithDefault(3)); |
||||
} |
||||
|
||||
/** |
||||
* Gets the message_type. |
||||
* @return {string} |
||||
*/ |
||||
getMessageType() { |
||||
return this.accessor_.getStringWithDefault(4); |
||||
} |
||||
|
||||
/** |
||||
* Gets the oneof case for payload field. |
||||
* This implementation assumes only one field in a oneof group is set. |
||||
* @return {!ConformanceRequest.PayloadCase} |
||||
*/ |
||||
getPayloadCase() { |
||||
if (this.accessor_.hasFieldNumber(1)) { |
||||
return /** @type {!ConformanceRequest.PayloadCase} */ ( |
||||
ConformanceRequest.PayloadCase.PROTOBUF_PAYLOAD); |
||||
} else if (this.accessor_.hasFieldNumber(2)) { |
||||
return /** @type {!ConformanceRequest.PayloadCase} */ ( |
||||
ConformanceRequest.PayloadCase.JSON_PAYLOAD); |
||||
} else if (this.accessor_.hasFieldNumber(8)) { |
||||
return /** @type {!ConformanceRequest.PayloadCase} */ ( |
||||
ConformanceRequest.PayloadCase.TEXT_PAYLOAD); |
||||
} else { |
||||
return /** @type {!ConformanceRequest.PayloadCase} */ ( |
||||
ConformanceRequest.PayloadCase.PAYLOAD_NOT_SET); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @enum {number} |
||||
*/ |
||||
ConformanceRequest.PayloadCase = { |
||||
PAYLOAD_NOT_SET: 0, |
||||
PROTOBUF_PAYLOAD: 1, |
||||
JSON_PAYLOAD: 2, |
||||
TEXT_PAYLOAD: 8, |
||||
}; |
||||
|
||||
exports = ConformanceRequest; |
@ -0,0 +1,76 @@ |
||||
/** |
||||
* @fileoverview Handwritten code of ConformanceResponse. |
||||
*/ |
||||
goog.module('proto.conformance.ConformanceResponse'); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
|
||||
/** |
||||
* Handwritten code of conformance.ConformanceResponse. |
||||
* This is used to send response from the conformance testee to the test runner. |
||||
* Check //third_party/protobuf/testing/protobuf/conformance/conformance.proto
|
||||
* for more details. |
||||
* @final |
||||
*/ |
||||
class ConformanceResponse { |
||||
/** |
||||
* @param {!ArrayBuffer} bytes |
||||
* @private |
||||
*/ |
||||
constructor(bytes) { |
||||
/** @private @const {!LazyAccessor} */ |
||||
this.accessor_ = LazyAccessor.fromArrayBuffer(bytes); |
||||
} |
||||
|
||||
/** |
||||
* Create an empty response instance. |
||||
* @return {!ConformanceResponse} |
||||
*/ |
||||
static createEmpty() { |
||||
return new ConformanceResponse(new ArrayBuffer(0)); |
||||
} |
||||
|
||||
/** |
||||
* Sets parse_error field. |
||||
* @param {string} value |
||||
*/ |
||||
setParseError(value) { |
||||
this.accessor_.setString(1, value); |
||||
} |
||||
|
||||
/** |
||||
* Sets runtime_error field. |
||||
* @param {string} value |
||||
*/ |
||||
setRuntimeError(value) { |
||||
this.accessor_.setString(2, value); |
||||
} |
||||
|
||||
/** |
||||
* Sets protobuf_payload field. |
||||
* @param {!ArrayBuffer} value |
||||
*/ |
||||
setProtobufPayload(value) { |
||||
const bytesString = ByteString.fromArrayBuffer(value); |
||||
this.accessor_.setBytes(3, bytesString); |
||||
} |
||||
|
||||
/** |
||||
* Sets skipped field. |
||||
* @param {string} value |
||||
*/ |
||||
setSkipped(value) { |
||||
this.accessor_.setString(5, value); |
||||
} |
||||
|
||||
/** |
||||
* Serializes into binary data. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
serialize() { |
||||
return this.accessor_.serialize(); |
||||
} |
||||
} |
||||
|
||||
exports = ConformanceResponse; |
@ -0,0 +1,103 @@ |
||||
goog.module('javascript.protobuf.conformance'); |
||||
|
||||
const ConformanceRequest = goog.require('proto.conformance.ConformanceRequest'); |
||||
const ConformanceResponse = goog.require('proto.conformance.ConformanceResponse'); |
||||
const TestAllTypesProto2 = goog.require('proto.conformance.TestAllTypesProto2'); |
||||
const TestAllTypesProto3 = goog.require('proto.conformance.TestAllTypesProto3'); |
||||
const WireFormat = goog.require('proto.conformance.WireFormat'); |
||||
const base64 = goog.require('goog.crypt.base64'); |
||||
|
||||
/** |
||||
* Creates a `proto.conformance.ConformanceResponse` response according to the |
||||
* `proto.conformance.ConformanceRequest` request. |
||||
* @param {!ConformanceRequest} request |
||||
* @return {!ConformanceResponse} response |
||||
*/ |
||||
function doTest(request) { |
||||
const response = ConformanceResponse.createEmpty(); |
||||
|
||||
if(request.getPayloadCase() === ConformanceRequest.PayloadCase.JSON_PAYLOAD) { |
||||
response.setSkipped('Json is not supported as input format.'); |
||||
return response; |
||||
} |
||||
|
||||
if(request.getPayloadCase() === ConformanceRequest.PayloadCase.TEXT_PAYLOAD) { |
||||
response.setSkipped('Text format is not supported as input format.'); |
||||
return response; |
||||
} |
||||
|
||||
if(request.getPayloadCase() === ConformanceRequest.PayloadCase.PAYLOAD_NOT_SET) { |
||||
response.setRuntimeError('Request didn\'t have payload.'); |
||||
return response; |
||||
} |
||||
|
||||
if(request.getPayloadCase() !== ConformanceRequest.PayloadCase.PROTOBUF_PAYLOAD) { |
||||
throw new Error('Request didn\'t have accepted input format.'); |
||||
} |
||||
|
||||
if (request.getRequestedOutputFormat() === WireFormat.JSON) { |
||||
response.setSkipped('Json is not supported as output format.'); |
||||
return response; |
||||
} |
||||
|
||||
if (request.getRequestedOutputFormat() === WireFormat.TEXT_FORMAT) { |
||||
response.setSkipped('Text format is not supported as output format.'); |
||||
return response; |
||||
} |
||||
|
||||
if (request.getRequestedOutputFormat() === WireFormat.TEXT_FORMAT) { |
||||
response.setRuntimeError('Unspecified output format'); |
||||
return response; |
||||
} |
||||
|
||||
if (request.getRequestedOutputFormat() !== WireFormat.PROTOBUF) { |
||||
throw new Error('Request didn\'t have accepted output format.'); |
||||
} |
||||
|
||||
if (request.getMessageType() === 'conformance.FailureSet') { |
||||
response.setProtobufPayload(new ArrayBuffer(0)); |
||||
} else if ( |
||||
request.getMessageType() === |
||||
'protobuf_test_messages.proto2.TestAllTypesProto2') { |
||||
try { |
||||
const testMessage = |
||||
TestAllTypesProto2.deserialize(request.getProtobufPayload()); |
||||
response.setProtobufPayload(testMessage.serialize()); |
||||
} catch (err) { |
||||
response.setParseError(err.toString()); |
||||
} |
||||
} else if ( |
||||
request.getMessageType() === |
||||
'protobuf_test_messages.proto3.TestAllTypesProto3') { |
||||
try { |
||||
const testMessage = |
||||
TestAllTypesProto3.deserialize(request.getProtobufPayload()); |
||||
response.setProtobufPayload(testMessage.serialize()); |
||||
} catch (err) { |
||||
response.setParseError(err.toString()); |
||||
} |
||||
} else { |
||||
throw new Error( |
||||
`Payload message not supported: ${request.getMessageType()}.`); |
||||
} |
||||
|
||||
return response; |
||||
} |
||||
|
||||
/** |
||||
* Same as doTest, but both request and response are in base64. |
||||
* @param {string} base64Request |
||||
* @return {string} response |
||||
*/ |
||||
function runConformanceTest(base64Request) { |
||||
const request = |
||||
ConformanceRequest.deserialize( |
||||
base64.decodeStringToUint8Array(base64Request).buffer); |
||||
const response = doTest(request); |
||||
return base64.encodeByteArray(new Uint8Array(response.serialize())); |
||||
} |
||||
|
||||
// Needed for node test
|
||||
exports.doTest = doTest; |
||||
// Needed for browser test
|
||||
goog.exportSymbol('runConformanceTest', runConformanceTest); |
@ -0,0 +1,62 @@ |
||||
const ConformanceRequest = goog.require('proto.conformance.ConformanceRequest'); |
||||
const {doTest} = goog.require('javascript.protobuf.conformance'); |
||||
const fs = require('fs'); |
||||
|
||||
|
||||
/** |
||||
* Reads a buffer of N bytes. |
||||
* @param {number} bytes Number of bytes to read. |
||||
* @return {!Buffer} Buffer which contains data. |
||||
*/ |
||||
function readBuffer(bytes) { |
||||
// Linux cannot use process.stdin.fd (which isn't set up as sync)
|
||||
const buf = new Buffer.alloc(bytes); |
||||
const fd = fs.openSync('/dev/stdin', 'r'); |
||||
fs.readSync(fd, buf, 0, bytes); |
||||
fs.closeSync(fd); |
||||
return buf; |
||||
} |
||||
|
||||
/** |
||||
* Writes all data in buffer. |
||||
* @param {!Buffer} buffer Buffer which contains data. |
||||
*/ |
||||
function writeBuffer(buffer) { |
||||
// Under linux, process.stdout.fd is async. Needs to open stdout in a synced
|
||||
// way for sync write.
|
||||
const fd = fs.openSync('/dev/stdout', 'w'); |
||||
fs.writeSync(fd, buffer, 0, buffer.length); |
||||
fs.closeSync(fd); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the test ran successfully, false on legitimate EOF. |
||||
* @return {boolean} Whether to continue test. |
||||
*/ |
||||
function runConformanceTest() { |
||||
const requestLengthBuf = readBuffer(4); |
||||
const requestLength = requestLengthBuf.readInt32LE(0); |
||||
if (!requestLength) { |
||||
return false; |
||||
} |
||||
|
||||
const serializedRequest = readBuffer(requestLength); |
||||
const array = new Uint8Array(serializedRequest); |
||||
const request = ConformanceRequest.deserialize(array.buffer); |
||||
const response = doTest(request); |
||||
|
||||
const serializedResponse = response.serialize(); |
||||
|
||||
const responseLengthBuf = new Buffer.alloc(4); |
||||
responseLengthBuf.writeInt32LE(serializedResponse.byteLength, 0); |
||||
writeBuffer(responseLengthBuf); |
||||
writeBuffer(new Buffer.from(serializedResponse)); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
while (true) { |
||||
if (!runConformanceTest()) { |
||||
break; |
||||
} |
||||
} |
@ -0,0 +1,309 @@ |
||||
/** |
||||
* @fileoverview Handwritten code of TestAllTypesProto2. |
||||
*/ |
||||
goog.module('proto.conformance.TestAllTypesProto2'); |
||||
|
||||
const InternalMessage = goog.require('protobuf.binary.InternalMessage'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
|
||||
/** |
||||
* Handwritten code of conformance.TestAllTypesProto2. |
||||
* Check google/protobuf/test_messages_proto3.proto for more details. |
||||
* @implements {InternalMessage} |
||||
* @final |
||||
*/ |
||||
class TestAllTypesProto2 { |
||||
/** |
||||
* @param {!LazyAccessor=} accessor |
||||
* @private |
||||
*/ |
||||
constructor(accessor = LazyAccessor.createEmpty()) { |
||||
/** @private @const {!LazyAccessor} */ |
||||
this.accessor_ = accessor; |
||||
} |
||||
|
||||
/** |
||||
* @override |
||||
* @package |
||||
* @return {!LazyAccessor} |
||||
*/ |
||||
internalGetKernel() { |
||||
return this.accessor_; |
||||
} |
||||
|
||||
/** |
||||
* Create a request instance with the given bytes data. |
||||
* If we directly use the accessor created by the binary decoding, the |
||||
* LazyAccessor instance will only copy the same data over for encoding. By |
||||
* explicitly fetching data from the previous accessor and setting all fields |
||||
* into a new accessor, we will actually test encoding/decoding for the binary |
||||
* format. |
||||
* @param {!ArrayBuffer} bytes |
||||
* @return {!TestAllTypesProto2} |
||||
*/ |
||||
static deserialize(bytes) { |
||||
const msg = new TestAllTypesProto2(); |
||||
const requestAccessor = LazyAccessor.fromArrayBuffer(bytes); |
||||
|
||||
if (requestAccessor.hasFieldNumber(1)) { |
||||
const value = requestAccessor.getInt32WithDefault(1); |
||||
msg.accessor_.setInt32(1, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(2)) { |
||||
const value = requestAccessor.getInt64WithDefault(2); |
||||
msg.accessor_.setInt64(2, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(3)) { |
||||
const value = requestAccessor.getUint32WithDefault(3); |
||||
msg.accessor_.setUint32(3, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(4)) { |
||||
const value = requestAccessor.getUint64WithDefault(4); |
||||
msg.accessor_.setUint64(4, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(5)) { |
||||
const value = requestAccessor.getSint32WithDefault(5); |
||||
msg.accessor_.setSint32(5, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(6)) { |
||||
const value = requestAccessor.getSint64WithDefault(6); |
||||
msg.accessor_.setSint64(6, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(7)) { |
||||
const value = requestAccessor.getFixed32WithDefault(7); |
||||
msg.accessor_.setFixed32(7, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(8)) { |
||||
const value = requestAccessor.getFixed64WithDefault(8); |
||||
msg.accessor_.setFixed64(8, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(9)) { |
||||
const value = requestAccessor.getSfixed32WithDefault(9); |
||||
msg.accessor_.setSfixed32(9, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(10)) { |
||||
const value = requestAccessor.getSfixed64WithDefault(10); |
||||
msg.accessor_.setSfixed64(10, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(11)) { |
||||
const value = requestAccessor.getFloatWithDefault(11); |
||||
msg.accessor_.setFloat(11, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(12)) { |
||||
const value = requestAccessor.getDoubleWithDefault(12); |
||||
msg.accessor_.setDouble(12, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(13)) { |
||||
const value = requestAccessor.getBoolWithDefault(13); |
||||
msg.accessor_.setBool(13, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(14)) { |
||||
const value = requestAccessor.getStringWithDefault(14); |
||||
msg.accessor_.setString(14, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(15)) { |
||||
const value = requestAccessor.getBytesWithDefault(15); |
||||
msg.accessor_.setBytes(15, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(18)) { |
||||
const value = requestAccessor.getMessage( |
||||
18, (accessor) => new TestAllTypesProto2(accessor)); |
||||
msg.accessor_.setMessage(18, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(21)) { |
||||
// Unknown enum is not checked here, because even if an enum is unknown,
|
||||
// it should be kept during encoding. For the purpose of wire format test,
|
||||
// we can simplify the implementation by treating it as an int32 field,
|
||||
// which has the same semantic except for the unknown value checking.
|
||||
const value = requestAccessor.getInt32WithDefault(21); |
||||
msg.accessor_.setInt32(21, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(31)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(31); |
||||
msg.accessor_.setUnpackedInt32Iterable(31, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(32)) { |
||||
const value = requestAccessor.getRepeatedInt64Iterable(32); |
||||
msg.accessor_.setUnpackedInt64Iterable(32, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(33)) { |
||||
const value = requestAccessor.getRepeatedUint32Iterable(33); |
||||
msg.accessor_.setUnpackedUint32Iterable(33, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(34)) { |
||||
const value = requestAccessor.getRepeatedUint64Iterable(34); |
||||
msg.accessor_.setUnpackedUint64Iterable(34, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(35)) { |
||||
const value = requestAccessor.getRepeatedSint32Iterable(35); |
||||
msg.accessor_.setUnpackedSint32Iterable(35, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(36)) { |
||||
const value = requestAccessor.getRepeatedSint64Iterable(36); |
||||
msg.accessor_.setUnpackedSint64Iterable(36, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(37)) { |
||||
const value = requestAccessor.getRepeatedFixed32Iterable(37); |
||||
msg.accessor_.setUnpackedFixed32Iterable(37, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(38)) { |
||||
const value = requestAccessor.getRepeatedFixed64Iterable(38); |
||||
msg.accessor_.setUnpackedFixed64Iterable(38, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(39)) { |
||||
const value = requestAccessor.getRepeatedSfixed32Iterable(39); |
||||
msg.accessor_.setUnpackedSfixed32Iterable(39, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(40)) { |
||||
const value = requestAccessor.getRepeatedSfixed64Iterable(40); |
||||
msg.accessor_.setUnpackedSfixed64Iterable(40, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(41)) { |
||||
const value = requestAccessor.getRepeatedFloatIterable(41); |
||||
msg.accessor_.setUnpackedFloatIterable(41, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(42)) { |
||||
const value = requestAccessor.getRepeatedDoubleIterable(42); |
||||
msg.accessor_.setUnpackedDoubleIterable(42, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(43)) { |
||||
const value = requestAccessor.getRepeatedBoolIterable(43); |
||||
msg.accessor_.setUnpackedBoolIterable(43, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(44)) { |
||||
const value = requestAccessor.getRepeatedStringIterable(44); |
||||
msg.accessor_.setRepeatedStringIterable(44, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(45)) { |
||||
const value = requestAccessor.getRepeatedBytesIterable(45); |
||||
msg.accessor_.setRepeatedBytesIterable(45, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(48)) { |
||||
const value = requestAccessor.getRepeatedMessageIterable( |
||||
48, (accessor) => new TestAllTypesProto2(accessor)); |
||||
msg.accessor_.setRepeatedMessageIterable(48, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(51)) { |
||||
// Unknown enum is not checked here, because even if an enum is unknown,
|
||||
// it should be kept during encoding. For the purpose of wire format test,
|
||||
// we can simplify the implementation by treating it as an int32 field,
|
||||
// which has the same semantic except for the unknown value checking.
|
||||
const value = requestAccessor.getRepeatedInt32Iterable(51); |
||||
msg.accessor_.setUnpackedInt32Iterable(51, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(75)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(75); |
||||
msg.accessor_.setPackedInt32Iterable(75, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(76)) { |
||||
const value = requestAccessor.getRepeatedInt64Iterable(76); |
||||
msg.accessor_.setPackedInt64Iterable(76, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(77)) { |
||||
const value = requestAccessor.getRepeatedUint32Iterable(77); |
||||
msg.accessor_.setPackedUint32Iterable(77, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(78)) { |
||||
const value = requestAccessor.getRepeatedUint64Iterable(78); |
||||
msg.accessor_.setPackedUint64Iterable(78, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(79)) { |
||||
const value = requestAccessor.getRepeatedSint32Iterable(79); |
||||
msg.accessor_.setPackedSint32Iterable(79, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(80)) { |
||||
const value = requestAccessor.getRepeatedSint64Iterable(80); |
||||
msg.accessor_.setPackedSint64Iterable(80, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(81)) { |
||||
const value = requestAccessor.getRepeatedFixed32Iterable(81); |
||||
msg.accessor_.setPackedFixed32Iterable(81, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(82)) { |
||||
const value = requestAccessor.getRepeatedFixed64Iterable(82); |
||||
msg.accessor_.setPackedFixed64Iterable(82, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(83)) { |
||||
const value = requestAccessor.getRepeatedSfixed32Iterable(83); |
||||
msg.accessor_.setPackedSfixed32Iterable(83, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(84)) { |
||||
const value = requestAccessor.getRepeatedSfixed64Iterable(84); |
||||
msg.accessor_.setPackedSfixed64Iterable(84, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(85)) { |
||||
const value = requestAccessor.getRepeatedFloatIterable(85); |
||||
msg.accessor_.setPackedFloatIterable(85, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(86)) { |
||||
const value = requestAccessor.getRepeatedDoubleIterable(86); |
||||
msg.accessor_.setPackedDoubleIterable(86, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(87)) { |
||||
const value = requestAccessor.getRepeatedBoolIterable(87); |
||||
msg.accessor_.setPackedBoolIterable(87, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(88)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(88); |
||||
msg.accessor_.setPackedInt32Iterable(88, value); |
||||
} |
||||
return msg; |
||||
} |
||||
|
||||
/** |
||||
* Serializes into binary data. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
serialize() { |
||||
return this.accessor_.serialize(); |
||||
} |
||||
} |
||||
|
||||
exports = TestAllTypesProto2; |
@ -0,0 +1,310 @@ |
||||
/** |
||||
* @fileoverview Handwritten code of TestAllTypesProto3. |
||||
*/ |
||||
goog.module('proto.conformance.TestAllTypesProto3'); |
||||
|
||||
const InternalMessage = goog.require('protobuf.binary.InternalMessage'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
|
||||
/** |
||||
* Handwritten code of conformance.TestAllTypesProto3. |
||||
* Check google/protobuf/test_messages_proto3.proto for more details. |
||||
* @implements {InternalMessage} |
||||
* @final |
||||
*/ |
||||
class TestAllTypesProto3 { |
||||
/** |
||||
* @param {!LazyAccessor=} accessor |
||||
* @private |
||||
*/ |
||||
constructor(accessor = LazyAccessor.createEmpty()) { |
||||
/** @private @const {!LazyAccessor} */ |
||||
this.accessor_ = accessor; |
||||
} |
||||
|
||||
/** |
||||
* @override |
||||
* @package |
||||
* @return {!LazyAccessor} |
||||
*/ |
||||
internalGetKernel() { |
||||
return this.accessor_; |
||||
} |
||||
|
||||
/** |
||||
* Create a request instance with the given bytes data. |
||||
* If we directly use the accessor created by the binary decoding, the |
||||
* LazyAccessor instance will only copy the same data over for encoding. By |
||||
* explicitly fetching data from the previous accessor and setting all fields |
||||
* into a new accessor, we will actually test encoding/decoding for the binary |
||||
* format. |
||||
* @param {!ArrayBuffer} bytes |
||||
* @return {!TestAllTypesProto3} |
||||
*/ |
||||
static deserialize(bytes) { |
||||
const msg = new TestAllTypesProto3(); |
||||
const requestAccessor = LazyAccessor.fromArrayBuffer(bytes); |
||||
|
||||
if (requestAccessor.hasFieldNumber(1)) { |
||||
const value = requestAccessor.getInt32WithDefault(1); |
||||
msg.accessor_.setInt32(1, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(2)) { |
||||
const value = requestAccessor.getInt64WithDefault(2); |
||||
msg.accessor_.setInt64(2, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(3)) { |
||||
const value = requestAccessor.getUint32WithDefault(3); |
||||
msg.accessor_.setUint32(3, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(4)) { |
||||
const value = requestAccessor.getUint64WithDefault(4); |
||||
msg.accessor_.setUint64(4, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(5)) { |
||||
const value = requestAccessor.getSint32WithDefault(5); |
||||
msg.accessor_.setSint32(5, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(6)) { |
||||
const value = requestAccessor.getSint64WithDefault(6); |
||||
msg.accessor_.setSint64(6, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(7)) { |
||||
const value = requestAccessor.getFixed32WithDefault(7); |
||||
msg.accessor_.setFixed32(7, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(8)) { |
||||
const value = requestAccessor.getFixed64WithDefault(8); |
||||
msg.accessor_.setFixed64(8, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(9)) { |
||||
const value = requestAccessor.getSfixed32WithDefault(9); |
||||
msg.accessor_.setSfixed32(9, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(10)) { |
||||
const value = requestAccessor.getSfixed64WithDefault(10); |
||||
msg.accessor_.setSfixed64(10, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(11)) { |
||||
const value = requestAccessor.getFloatWithDefault(11); |
||||
msg.accessor_.setFloat(11, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(12)) { |
||||
const value = requestAccessor.getDoubleWithDefault(12); |
||||
msg.accessor_.setDouble(12, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(13)) { |
||||
const value = requestAccessor.getBoolWithDefault(13); |
||||
msg.accessor_.setBool(13, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(14)) { |
||||
const value = requestAccessor.getStringWithDefault(14); |
||||
msg.accessor_.setString(14, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(15)) { |
||||
const value = requestAccessor.getBytesWithDefault(15); |
||||
msg.accessor_.setBytes(15, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(18)) { |
||||
const value = requestAccessor.getMessage( |
||||
18, (accessor) => new TestAllTypesProto3(accessor)); |
||||
msg.accessor_.setMessage(18, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(21)) { |
||||
// Unknown enum is not checked here, because even if an enum is unknown,
|
||||
// it should be kept during encoding. For the purpose of wire format test,
|
||||
// we can simplify the implementation by treating it as an int32 field,
|
||||
// which has the same semantic except for the unknown value checking.
|
||||
const value = requestAccessor.getInt32WithDefault(21); |
||||
msg.accessor_.setInt32(21, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(31)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(31); |
||||
msg.accessor_.setPackedInt32Iterable(31, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(32)) { |
||||
const value = requestAccessor.getRepeatedInt64Iterable(32); |
||||
msg.accessor_.setPackedInt64Iterable(32, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(33)) { |
||||
const value = requestAccessor.getRepeatedUint32Iterable(33); |
||||
msg.accessor_.setPackedUint32Iterable(33, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(34)) { |
||||
const value = requestAccessor.getRepeatedUint64Iterable(34); |
||||
msg.accessor_.setPackedUint64Iterable(34, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(35)) { |
||||
const value = requestAccessor.getRepeatedSint32Iterable(35); |
||||
msg.accessor_.setPackedSint32Iterable(35, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(36)) { |
||||
const value = requestAccessor.getRepeatedSint64Iterable(36); |
||||
msg.accessor_.setPackedSint64Iterable(36, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(37)) { |
||||
const value = requestAccessor.getRepeatedFixed32Iterable(37); |
||||
msg.accessor_.setPackedFixed32Iterable(37, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(38)) { |
||||
const value = requestAccessor.getRepeatedFixed64Iterable(38); |
||||
msg.accessor_.setPackedFixed64Iterable(38, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(39)) { |
||||
const value = requestAccessor.getRepeatedSfixed32Iterable(39); |
||||
msg.accessor_.setPackedSfixed32Iterable(39, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(40)) { |
||||
const value = requestAccessor.getRepeatedSfixed64Iterable(40); |
||||
msg.accessor_.setPackedSfixed64Iterable(40, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(41)) { |
||||
const value = requestAccessor.getRepeatedFloatIterable(41); |
||||
msg.accessor_.setPackedFloatIterable(41, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(42)) { |
||||
const value = requestAccessor.getRepeatedDoubleIterable(42); |
||||
msg.accessor_.setPackedDoubleIterable(42, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(43)) { |
||||
const value = requestAccessor.getRepeatedBoolIterable(43); |
||||
msg.accessor_.setPackedBoolIterable(43, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(44)) { |
||||
const value = requestAccessor.getRepeatedStringIterable(44); |
||||
msg.accessor_.setRepeatedStringIterable(44, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(45)) { |
||||
const value = requestAccessor.getRepeatedBytesIterable(45); |
||||
msg.accessor_.setRepeatedBytesIterable(45, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(48)) { |
||||
const value = requestAccessor.getRepeatedMessageIterable( |
||||
48, (accessor) => new TestAllTypesProto3(accessor)); |
||||
msg.accessor_.setRepeatedMessageIterable(48, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(51)) { |
||||
// Unknown enum is not checked here, because even if an enum is unknown,
|
||||
// it should be kept during encoding. For the purpose of wire format test,
|
||||
// we can simplify the implementation by treating it as an int32 field,
|
||||
// which has the same semantic except for the unknown value checking.
|
||||
const value = requestAccessor.getRepeatedInt32Iterable(51); |
||||
msg.accessor_.setPackedInt32Iterable(51, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(89)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(89); |
||||
msg.accessor_.setUnpackedInt32Iterable(89, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(90)) { |
||||
const value = requestAccessor.getRepeatedInt64Iterable(90); |
||||
msg.accessor_.setUnpackedInt64Iterable(90, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(91)) { |
||||
const value = requestAccessor.getRepeatedUint32Iterable(91); |
||||
msg.accessor_.setUnpackedUint32Iterable(91, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(92)) { |
||||
const value = requestAccessor.getRepeatedUint64Iterable(92); |
||||
msg.accessor_.setUnpackedUint64Iterable(92, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(93)) { |
||||
const value = requestAccessor.getRepeatedSint32Iterable(93); |
||||
msg.accessor_.setUnpackedSint32Iterable(93, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(94)) { |
||||
const value = requestAccessor.getRepeatedSint64Iterable(94); |
||||
msg.accessor_.setUnpackedSint64Iterable(94, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(95)) { |
||||
const value = requestAccessor.getRepeatedFixed32Iterable(95); |
||||
msg.accessor_.setUnpackedFixed32Iterable(95, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(96)) { |
||||
const value = requestAccessor.getRepeatedFixed64Iterable(96); |
||||
msg.accessor_.setUnpackedFixed64Iterable(96, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(97)) { |
||||
const value = requestAccessor.getRepeatedSfixed32Iterable(97); |
||||
msg.accessor_.setUnpackedSfixed32Iterable(97, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(98)) { |
||||
const value = requestAccessor.getRepeatedSfixed64Iterable(98); |
||||
msg.accessor_.setUnpackedSfixed64Iterable(98, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(99)) { |
||||
const value = requestAccessor.getRepeatedFloatIterable(99); |
||||
msg.accessor_.setUnpackedFloatIterable(99, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(100)) { |
||||
const value = requestAccessor.getRepeatedDoubleIterable(100); |
||||
msg.accessor_.setUnpackedDoubleIterable(100, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(101)) { |
||||
const value = requestAccessor.getRepeatedBoolIterable(101); |
||||
msg.accessor_.setUnpackedBoolIterable(101, value); |
||||
} |
||||
|
||||
if (requestAccessor.hasFieldNumber(102)) { |
||||
const value = requestAccessor.getRepeatedInt32Iterable(102); |
||||
msg.accessor_.setUnpackedInt32Iterable(102, value); |
||||
} |
||||
|
||||
return msg; |
||||
} |
||||
|
||||
/** |
||||
* Serializes into binary data. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
serialize() { |
||||
return this.accessor_.serialize(); |
||||
} |
||||
} |
||||
|
||||
exports = TestAllTypesProto3; |
@ -0,0 +1,16 @@ |
||||
/** |
||||
* @fileoverview Handwritten code of WireFormat. |
||||
*/ |
||||
goog.module('proto.conformance.WireFormat'); |
||||
|
||||
/** |
||||
* @enum {number} |
||||
*/ |
||||
const WireFormat = { |
||||
UNSPECIFIED: 0, |
||||
PROTOBUF: 1, |
||||
JSON: 2, |
||||
TEXT_FORMAT: 4, |
||||
}; |
||||
|
||||
exports = WireFormat; |
@ -0,0 +1,89 @@ |
||||
/** |
||||
* @fileoverview Test data for double encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.doubleTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of double values and their bit representation. |
||||
* This is used to test encoding and decoding from the protobuf wire format. |
||||
* @return {!Array<{name: string, doubleValue:number, bufferDecoder: |
||||
* !BufferDecoder}>} |
||||
*/ |
||||
function getDoublePairs() { |
||||
const doublePairs = [ |
||||
{ |
||||
name: 'zero', |
||||
doubleValue: 0, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) |
||||
}, |
||||
{ |
||||
name: 'minus zero', |
||||
doubleValue: -0, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80) |
||||
}, |
||||
{ |
||||
name: 'one', |
||||
doubleValue: 1, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F) |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
doubleValue: -1, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF) |
||||
}, |
||||
|
||||
{ |
||||
name: 'PI', |
||||
doubleValue: Math.PI, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40) |
||||
|
||||
}, |
||||
{ |
||||
name: 'max value', |
||||
doubleValue: Number.MAX_VALUE, |
||||
bufferDecoder: |
||||
createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F) |
||||
}, |
||||
{ |
||||
name: 'min value', |
||||
doubleValue: Number.MIN_VALUE, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) |
||||
}, |
||||
{ |
||||
name: 'Infinity', |
||||
doubleValue: Infinity, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F) |
||||
}, |
||||
{ |
||||
name: 'minus Infinity', |
||||
doubleValue: -Infinity, |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF) |
||||
}, |
||||
{ |
||||
name: 'Number.MAX_SAFE_INTEGER', |
||||
doubleValue: Number.MAX_SAFE_INTEGER, |
||||
bufferDecoder: |
||||
createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x43) |
||||
}, |
||||
{ |
||||
name: 'Number.MIN_SAFE_INTEGER', |
||||
doubleValue: Number.MIN_SAFE_INTEGER, |
||||
bufferDecoder: |
||||
createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xC3) |
||||
}, |
||||
]; |
||||
return [...doublePairs]; |
||||
} |
||||
|
||||
exports = {getDoublePairs}; |
@ -0,0 +1,196 @@ |
||||
/** |
||||
* @fileoverview Contains classes that hold data for a protobuf field. |
||||
*/ |
||||
|
||||
goog.module('protobuf.binary.field'); |
||||
|
||||
const WireType = goog.requireType('protobuf.binary.WireType'); |
||||
const Writer = goog.requireType('protobuf.binary.Writer'); |
||||
const {checkDefAndNotNull, checkState} = goog.require('protobuf.internal.checks'); |
||||
|
||||
/** |
||||
* Number of bits taken to represent a wire type. |
||||
* @const {number} |
||||
*/ |
||||
const WIRE_TYPE_LENGTH_BITS = 3; |
||||
|
||||
/** @const {number} */ |
||||
const WIRE_TYPE_EXTRACTOR = (1 << WIRE_TYPE_LENGTH_BITS) - 1; |
||||
|
||||
/** |
||||
* An IndexEntry consists of the wire type and the position of a field in the |
||||
* binary data. The wire type and the position are encoded into a single number |
||||
* to save memory, which can be decoded using Field.getWireType() and |
||||
* Field.getStartIndex() methods. |
||||
* @typedef {number} |
||||
*/ |
||||
let IndexEntry; |
||||
|
||||
/** |
||||
* An entry containing the index into the binary data and/or the corresponding |
||||
* cached JS object(s) for a field. |
||||
* @template T |
||||
* @final |
||||
* @package |
||||
*/ |
||||
class Field { |
||||
/** |
||||
* Creates a field and inserts the wireType and position of the first |
||||
* occurrence of a field. |
||||
* @param {!WireType} wireType |
||||
* @param {number} startIndex |
||||
* @return {!Field} |
||||
*/ |
||||
static fromFirstIndexEntry(wireType, startIndex) { |
||||
return new Field([Field.encodeIndexEntry(wireType, startIndex)]); |
||||
} |
||||
|
||||
/** |
||||
* @param {T} decodedValue The cached JS object decoded from the binary data. |
||||
* @param {function(!Writer, number, T):void|undefined} encoder Write function |
||||
* to encode the cache into binary bytes. |
||||
* @return {!Field} |
||||
* @template T |
||||
*/ |
||||
static fromDecodedValue(decodedValue, encoder) { |
||||
return new Field(null, decodedValue, encoder); |
||||
} |
||||
|
||||
/** |
||||
* @param {!WireType} wireType |
||||
* @param {number} startIndex |
||||
* @return {!IndexEntry} |
||||
*/ |
||||
static encodeIndexEntry(wireType, startIndex) { |
||||
return startIndex << WIRE_TYPE_LENGTH_BITS | wireType; |
||||
} |
||||
|
||||
/** |
||||
* @param {!IndexEntry} indexEntry |
||||
* @return {!WireType} |
||||
*/ |
||||
static getWireType(indexEntry) { |
||||
return /** @type {!WireType} */ (indexEntry & WIRE_TYPE_EXTRACTOR); |
||||
} |
||||
|
||||
/** |
||||
* @param {!IndexEntry} indexEntry |
||||
* @return {number} |
||||
*/ |
||||
static getStartIndex(indexEntry) { |
||||
return indexEntry >> WIRE_TYPE_LENGTH_BITS; |
||||
} |
||||
|
||||
/** |
||||
* @param {?Array<!IndexEntry>} indexArray |
||||
* @param {T=} decodedValue |
||||
* @param {function(!Writer, number, T):void=} encoder |
||||
* @private |
||||
*/ |
||||
constructor(indexArray, decodedValue = undefined, encoder = undefined) { |
||||
checkState( |
||||
!!indexArray || decodedValue !== undefined, |
||||
'At least one of indexArray and decodedValue must be set'); |
||||
|
||||
/** @private {?Array<!IndexEntry>} */ |
||||
this.indexArray_ = indexArray; |
||||
/** @private {T|undefined} */ |
||||
this.decodedValue_ = decodedValue; |
||||
// TODO: Consider storing an enum to represent encoder
|
||||
/** @private {function(!Writer, number, T)|undefined} */ |
||||
this.encoder_ = encoder; |
||||
} |
||||
|
||||
/** |
||||
* Adds a new IndexEntry. |
||||
* @param {!WireType} wireType |
||||
* @param {number} startIndex |
||||
*/ |
||||
addIndexEntry(wireType, startIndex) { |
||||
checkDefAndNotNull(this.indexArray_) |
||||
.push(Field.encodeIndexEntry(wireType, startIndex)); |
||||
} |
||||
|
||||
/** |
||||
* Returns the array of IndexEntry. |
||||
* @return {?Array<!IndexEntry>} |
||||
*/ |
||||
getIndexArray() { |
||||
return this.indexArray_; |
||||
} |
||||
|
||||
/** |
||||
* Caches the decoded value and sets the write function to encode cache into |
||||
* binary bytes. |
||||
* @param {T} decodedValue |
||||
* @param {function(!Writer, number, T):void|undefined} encoder |
||||
*/ |
||||
setCache(decodedValue, encoder) { |
||||
this.decodedValue_ = decodedValue; |
||||
this.encoder_ = encoder; |
||||
this.maybeRemoveIndexArray_(); |
||||
} |
||||
|
||||
/** |
||||
* If the decoded value has been set. |
||||
* @return {boolean} |
||||
*/ |
||||
hasDecodedValue() { |
||||
return this.decodedValue_ !== undefined; |
||||
} |
||||
|
||||
/** |
||||
* Returns the cached decoded value. The value needs to be set when this |
||||
* method is called. |
||||
* @return {T} |
||||
*/ |
||||
getDecodedValue() { |
||||
// Makes sure that the decoded value in the cache has already been set. This
|
||||
// prevents callers from doing `if (field.getDecodedValue()) {...}` to check
|
||||
// if a value exist in the cache, because the check might return false even
|
||||
// if the cache has a valid value set (e.g. 0 or empty string).
|
||||
checkState(this.decodedValue_ !== undefined); |
||||
return this.decodedValue_; |
||||
} |
||||
|
||||
/** |
||||
* Returns the write function to encode cache into binary bytes. |
||||
* @return {function(!Writer, number, T)|undefined} |
||||
*/ |
||||
getEncoder() { |
||||
return this.encoder_; |
||||
} |
||||
|
||||
/** |
||||
* Returns a copy of the field, containing the original index entries and a |
||||
* shallow copy of the cache. |
||||
* @return {!Field} |
||||
*/ |
||||
shallowCopy() { |
||||
// Repeated fields are arrays in the cache.
|
||||
// We have to copy the array to make sure that modifications to a repeated
|
||||
// field (e.g. add) are not seen on a cloned accessor.
|
||||
const copiedCache = this.hasDecodedValue() ? |
||||
(Array.isArray(this.getDecodedValue()) ? [...this.getDecodedValue()] : |
||||
this.getDecodedValue()) : |
||||
undefined; |
||||
return new Field(this.getIndexArray(), copiedCache, this.getEncoder()); |
||||
} |
||||
|
||||
/** |
||||
* @private |
||||
*/ |
||||
maybeRemoveIndexArray_() { |
||||
checkState( |
||||
this.encoder_ === undefined || this.decodedValue_ !== undefined, |
||||
'Encoder exists but decoded value doesn\'t'); |
||||
if (this.encoder_ !== undefined) { |
||||
this.indexArray_ = null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
exports = { |
||||
IndexEntry, |
||||
Field, |
||||
}; |
@ -0,0 +1,36 @@ |
||||
/** |
||||
* @fileoverview Test data for float encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.fixed32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, intValue: number, bufferDecoder: |
||||
* !BufferDecoder}>} |
||||
*/ |
||||
function getFixed32Pairs() { |
||||
const fixed32Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
intValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
intValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x01, 0x00, 0x00, 0x00) |
||||
}, |
||||
{ |
||||
name: 'max int 2^32 -1', |
||||
intValue: Math.pow(2, 32) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF) |
||||
}, |
||||
]; |
||||
return [...fixed32Pairs]; |
||||
} |
||||
|
||||
exports = {getFixed32Pairs}; |
@ -0,0 +1,78 @@ |
||||
/** |
||||
* @fileoverview Test data for float encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.floatTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, floatValue:number, bufferDecoder: |
||||
* !BufferDecoder}>} |
||||
*/ |
||||
function getFloatPairs() { |
||||
const floatPairs = [ |
||||
{ |
||||
name: 'zero', |
||||
floatValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'minus zero', |
||||
floatValue: -0, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x80) |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
floatValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x80, 0x3F) |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
floatValue: -1, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x80, 0xBF) |
||||
}, |
||||
{ |
||||
name: 'two', |
||||
floatValue: 2, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x40) |
||||
}, |
||||
{ |
||||
name: 'max float32', |
||||
floatValue: Math.pow(2, 127) * (2 - 1 / Math.pow(2, 23)), |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0x7F, 0x7F) |
||||
}, |
||||
|
||||
{ |
||||
name: 'min float32', |
||||
floatValue: 1 / Math.pow(2, 127 - 1), |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x80, 0x00) |
||||
}, |
||||
|
||||
{ |
||||
name: 'Infinity', |
||||
floatValue: Infinity, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x80, 0x7F) |
||||
}, |
||||
{ |
||||
name: 'minus Infinity', |
||||
floatValue: -Infinity, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x80, 0xFF) |
||||
}, |
||||
{ |
||||
name: '1.5', |
||||
floatValue: 1.5, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0xC0, 0x3F) |
||||
}, |
||||
{ |
||||
name: '1.6', |
||||
floatValue: 1.6, |
||||
bufferDecoder: createBufferDecoder(0xCD, 0xCC, 0xCC, 0x3F) |
||||
}, |
||||
]; |
||||
return [...floatPairs]; |
||||
} |
||||
|
||||
exports = {getFloatPairs}; |
@ -0,0 +1,192 @@ |
||||
/** |
||||
* @fileoverview Utilities to index a binary proto by fieldnumbers without |
||||
* relying on strutural proto information. |
||||
*/ |
||||
goog.module('protobuf.binary.indexer'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Storage = goog.require('protobuf.binary.Storage'); |
||||
const WireType = goog.require('protobuf.binary.WireType'); |
||||
const {Field} = goog.require('protobuf.binary.field'); |
||||
const {checkCriticalPositionIndex, checkCriticalState} = goog.require('protobuf.internal.checks'); |
||||
|
||||
/** |
||||
* Appends a new entry in the index array for the given field number. |
||||
* @param {!Storage<!Field>} storage |
||||
* @param {number} fieldNumber |
||||
* @param {!WireType} wireType |
||||
* @param {number} startIndex |
||||
*/ |
||||
function addIndexEntry(storage, fieldNumber, wireType, startIndex) { |
||||
const field = storage.get(fieldNumber); |
||||
if (field !== undefined) { |
||||
field.addIndexEntry(wireType, startIndex); |
||||
} else { |
||||
storage.set(fieldNumber, Field.fromFirstIndexEntry(wireType, startIndex)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns wire type stored in a tag. |
||||
* Protos store the wire type as the first 3 bit of a tag. |
||||
* @param {number} tag |
||||
* @return {!WireType} |
||||
*/ |
||||
function tagToWireType(tag) { |
||||
return /** @type {!WireType} */ (tag & 0x07); |
||||
} |
||||
|
||||
/** |
||||
* Returns the field number stored in a tag. |
||||
* Protos store the field number in the upper 29 bits of a 32 bit number. |
||||
* @param {number} tag |
||||
* @return {number} |
||||
*/ |
||||
function tagToFieldNumber(tag) { |
||||
return tag >>> 3; |
||||
} |
||||
|
||||
/** |
||||
* An Indexer that indexes a given binary protobuf by fieldnumber. |
||||
*/ |
||||
class Indexer { |
||||
/** |
||||
* @param {!BufferDecoder} bufferDecoder |
||||
* @private |
||||
*/ |
||||
constructor(bufferDecoder) { |
||||
/** @private @const {!BufferDecoder} */ |
||||
this.bufferDecoder_ = bufferDecoder; |
||||
/** @private {number} */ |
||||
this.cursor_ = bufferDecoder.startIndex(); |
||||
} |
||||
|
||||
/** |
||||
* @param {number|undefined} pivot |
||||
* @return {!Storage<!Field>} |
||||
*/ |
||||
index(pivot) { |
||||
const storage = new Storage(pivot); |
||||
while (this.hasNextByte_()) { |
||||
const tag = this.readVarInt32_(); |
||||
const wireType = tagToWireType(tag); |
||||
const fieldNumber = tagToFieldNumber(tag); |
||||
checkCriticalState( |
||||
fieldNumber > 0, `Invalid field number ${fieldNumber}`); |
||||
|
||||
addIndexEntry(storage, fieldNumber, wireType, this.cursor_); |
||||
|
||||
checkCriticalState( |
||||
!this.skipField_(wireType, fieldNumber), |
||||
'Found unmatched stop group.'); |
||||
} |
||||
return storage; |
||||
} |
||||
|
||||
/** |
||||
* Skips over fields until the next field of the message. |
||||
* @param {!WireType} wireType |
||||
* @param {number} fieldNumber |
||||
* @return {boolean} Whether the field we skipped over was a stop group. |
||||
* @private |
||||
*/ |
||||
skipField_(wireType, fieldNumber) { |
||||
switch (wireType) { |
||||
case WireType.VARINT: |
||||
this.cursor_ = this.bufferDecoder_.skipVarint(this.cursor_); |
||||
return false; |
||||
case WireType.FIXED64: |
||||
this.skip_(8); |
||||
return false; |
||||
case WireType.DELIMITED: |
||||
const length = this.readVarInt32_(); |
||||
this.skip_(length); |
||||
return false; |
||||
case WireType.START_GROUP: |
||||
checkCriticalState(this.skipGroup_(fieldNumber), 'No end group found.'); |
||||
return false; |
||||
case WireType.END_GROUP: |
||||
// Signal that we found a stop group to the caller
|
||||
return true; |
||||
case WireType.FIXED32: |
||||
this.skip_(4); |
||||
return false; |
||||
default: |
||||
throw new Error(`Invalid wire type: ${wireType}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Seeks forward by the given amount. |
||||
* @param {number} skipAmount |
||||
* @private |
||||
*/ |
||||
skip_(skipAmount) { |
||||
this.cursor_ += skipAmount; |
||||
checkCriticalPositionIndex(this.cursor_, this.bufferDecoder_.endIndex()); |
||||
} |
||||
|
||||
/** |
||||
* Skips over fields until it finds the end of a given group. |
||||
* @param {number} groupFieldNumber |
||||
* @return {boolean} Returns true if an end was found. |
||||
* @private |
||||
*/ |
||||
skipGroup_(groupFieldNumber) { |
||||
// On a start group we need to keep skipping fields until we find a
|
||||
// corresponding stop group
|
||||
// Note: Since we are calling skipField from here nested groups will be
|
||||
// handled by recursion of this method and thus we will not see a nested
|
||||
// STOP GROUP here unless there is something wrong with the input data.
|
||||
while (this.hasNextByte_()) { |
||||
const tag = this.readVarInt32_(); |
||||
const wireType = tagToWireType(tag); |
||||
const fieldNumber = tagToFieldNumber(tag); |
||||
|
||||
if (this.skipField_(wireType, fieldNumber)) { |
||||
checkCriticalState( |
||||
groupFieldNumber === fieldNumber, |
||||
`Expected stop group for fieldnumber ${ |
||||
groupFieldNumber} not found.`);
|
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Returns a JS number for a 32 bit var int. |
||||
* @return {number} |
||||
* @private |
||||
*/ |
||||
readVarInt32_() { |
||||
const {lowBits, dataStart} = this.bufferDecoder_.getVarint(this.cursor_); |
||||
this.cursor_ = dataStart; |
||||
return lowBits; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if there are more bytes to read in the array. |
||||
* @return {boolean} |
||||
* @private |
||||
*/ |
||||
hasNextByte_() { |
||||
return this.cursor_ < this.bufferDecoder_.endIndex(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates an index of field locations in a given binary protobuf. |
||||
* @param {!BufferDecoder} bufferDecoder |
||||
* @param {number|undefined} pivot |
||||
* @return {!Storage<!Field>} |
||||
* @package |
||||
*/ |
||||
function buildIndex(bufferDecoder, pivot) { |
||||
return new Indexer(bufferDecoder).index(pivot); |
||||
} |
||||
|
||||
|
||||
exports = { |
||||
buildIndex, |
||||
}; |
@ -0,0 +1,359 @@ |
||||
/** |
||||
* @fileoverview Tests for indexer.js. |
||||
*/ |
||||
goog.module('protobuf.binary.IndexerTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
// Note to the reader:
|
||||
// Since the index behavior changes with the checking level some of the tests
|
||||
// in this file have to know which checking level is enabled to make correct
|
||||
// assertions.
|
||||
// Test are run in all checking levels.
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Storage = goog.require('protobuf.binary.Storage'); |
||||
const WireType = goog.require('protobuf.binary.WireType'); |
||||
const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); |
||||
const {Field, IndexEntry} = goog.require('protobuf.binary.field'); |
||||
const {buildIndex} = goog.require('protobuf.binary.indexer'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* Returns the number of fields stored. |
||||
* |
||||
* @param {!Storage} storage |
||||
* @return {number} |
||||
*/ |
||||
function getStorageSize(storage) { |
||||
let size = 0; |
||||
storage.forEach(() => void size++); |
||||
return size; |
||||
} |
||||
|
||||
/** |
||||
* @type {number} |
||||
*/ |
||||
const PIVOT = 1; |
||||
|
||||
/** |
||||
* Asserts a single IndexEntry at a given field number. |
||||
* @param {!Storage} storage |
||||
* @param {number} fieldNumber |
||||
* @param {...!IndexEntry} expectedEntries |
||||
*/ |
||||
function assertStorageEntries(storage, fieldNumber, ...expectedEntries) { |
||||
expect(getStorageSize(storage)).toBe(1); |
||||
|
||||
const entryArray = storage.get(fieldNumber).getIndexArray(); |
||||
expect(entryArray).not.toBeUndefined(); |
||||
expect(entryArray.length).toBe(expectedEntries.length); |
||||
|
||||
for (let i = 0; i < entryArray.length; i++) { |
||||
const storageEntry = entryArray[i]; |
||||
const expectedEntry = expectedEntries[i]; |
||||
|
||||
expect(storageEntry).toBe(expectedEntry); |
||||
} |
||||
} |
||||
|
||||
describe('Indexer does', () => { |
||||
it('return empty storage for empty array', () => { |
||||
const storage = buildIndex(createBufferDecoder(), PIVOT); |
||||
expect(storage).not.toBeNull(); |
||||
expect(getStorageSize(storage)).toBe(0); |
||||
}); |
||||
|
||||
it('throw for null array', () => { |
||||
expect( |
||||
() => buildIndex( |
||||
/** @type {!BufferDecoder} */ (/** @type {*} */ (null)), PIVOT)) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('fail for invalid wire type (6)', () => { |
||||
expect(() => buildIndex(createBufferDecoder(0x0E, 0x01), PIVOT)) |
||||
.toThrowError('Invalid wire type: 6'); |
||||
}); |
||||
|
||||
it('fail for invalid wire type (7)', () => { |
||||
expect(() => buildIndex(createBufferDecoder(0x0F, 0x01), PIVOT)) |
||||
.toThrowError('Invalid wire type: 7'); |
||||
}); |
||||
|
||||
it('index varint', () => { |
||||
const data = createBufferDecoder(0x08, 0x01, 0x08, 0x01); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.VARINT, /* startIndex= */ 1), |
||||
Field.encodeIndexEntry(WireType.VARINT, /* startIndex= */ 3)); |
||||
}); |
||||
|
||||
it('index varint with two bytes field number', () => { |
||||
const data = createBufferDecoder(0xF8, 0x01, 0x01); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 31, |
||||
Field.encodeIndexEntry(WireType.VARINT, /* startIndex= */ 2)); |
||||
}); |
||||
|
||||
it('fail for varints that are longer than 10 bytes', () => { |
||||
const data = createBufferDecoder( |
||||
0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 12 size: 11'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.VARINT, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for varints with no data', () => { |
||||
const data = createBufferDecoder(0x08); |
||||
expect(() => buildIndex(data, PIVOT)).toThrow(); |
||||
}); |
||||
|
||||
it('index fixed64', () => { |
||||
const data = createBufferDecoder( |
||||
/* first= */ 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
||||
/* second= */ 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED64, /* startIndex= */ 1), |
||||
Field.encodeIndexEntry(WireType.FIXED64, /* startIndex= */ 10)); |
||||
}); |
||||
|
||||
it('fail for fixed64 data missing in input', () => { |
||||
const data = |
||||
createBufferDecoder(0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 9 size: 8'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED64, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for fixed64 tag that has no data after it', () => { |
||||
if (CHECK_CRITICAL_STATE) { |
||||
const data = createBufferDecoder(0x09); |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 9 size: 1'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const data = createBufferDecoder(0x09); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED64, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('index delimited', () => { |
||||
const data = createBufferDecoder( |
||||
/* first= */ 0x0A, 0x02, 0x00, 0x01, /* second= */ 0x0A, 0x02, 0x00, |
||||
0x01); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.DELIMITED, /* startIndex= */ 1), |
||||
Field.encodeIndexEntry(WireType.DELIMITED, /* startIndex= */ 5)); |
||||
}); |
||||
|
||||
it('fail for length deliimted field data missing in input', () => { |
||||
const data = createBufferDecoder(0x0A, 0x04, 0x00, 0x01); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 6 size: 4'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.DELIMITED, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for delimited tag that has no data after it', () => { |
||||
const data = createBufferDecoder(0x0A); |
||||
expect(() => buildIndex(data, PIVOT)).toThrow(); |
||||
}); |
||||
|
||||
it('index fixed32', () => { |
||||
const data = createBufferDecoder( |
||||
/* first= */ 0x0D, 0x01, 0x02, 0x03, 0x04, /* second= */ 0x0D, 0x01, |
||||
0x02, 0x03, 0x04); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED32, /* startIndex= */ 1), |
||||
Field.encodeIndexEntry(WireType.FIXED32, /* startIndex= */ 6)); |
||||
}); |
||||
|
||||
it('fail for fixed32 data missing in input', () => { |
||||
const data = createBufferDecoder(0x0D, 0x01, 0x02, 0x03); |
||||
|
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 5 size: 4'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED32, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for fixed32 tag that has no data after it', () => { |
||||
if (CHECK_CRITICAL_STATE) { |
||||
const data = createBufferDecoder(0x0D); |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Index out of bounds: index: 5 size: 1'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const data = createBufferDecoder(0x0D); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.FIXED32, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('index group', () => { |
||||
const data = createBufferDecoder( |
||||
/* first= */ 0x0B, 0x08, 0x01, 0x0C, /* second= */ 0x0B, 0x08, 0x01, |
||||
0x0C); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 1), |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 5)); |
||||
}); |
||||
|
||||
it('index group and skips inner group', () => { |
||||
const data = |
||||
createBufferDecoder(0x0B, 0x0B, 0x08, 0x01, 0x0C, 0x08, 0x01, 0x0C); |
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 1)); |
||||
}); |
||||
|
||||
it('fail on unmatched stop group', () => { |
||||
const data = createBufferDecoder(0x0C, 0x01); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Found unmatched stop group.'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
|
||||
expect(getStorageSize(storage)).toBe(1); |
||||
const entryArray = storage.get(1).getIndexArray(); |
||||
expect(entryArray).not.toBeUndefined(); |
||||
expect(entryArray.length).toBe(1); |
||||
const entry = entryArray[0]; |
||||
|
||||
expect(Field.getWireType(entry)).toBe(WireType.END_GROUP); |
||||
expect(Field.getStartIndex(entry)).toBe(1); |
||||
|
||||
const entryArray2 = storage.get(0).getIndexArray(); |
||||
expect(entryArray2).not.toBeUndefined(); |
||||
expect(entryArray2.length).toBe(1); |
||||
const entry2 = entryArray2[0]; |
||||
|
||||
expect(Field.getWireType(entry2)).toBe(WireType.FIXED64); |
||||
expect(Field.getStartIndex(entry2)).toBe(2); |
||||
} |
||||
}); |
||||
|
||||
it('fail for groups without matching stop group', () => { |
||||
const data = createBufferDecoder(0x0B, 0x08, 0x01, 0x1C); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)) |
||||
.toThrowError('Expected stop group for fieldnumber 1 not found.'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for groups without stop group', () => { |
||||
const data = createBufferDecoder(0x0B, 0x08, 0x01); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)).toThrowError('No end group found.'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('fail for group tag that has no data after it', () => { |
||||
const data = createBufferDecoder(0x0B); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => buildIndex(data, PIVOT)).toThrowError('No end group found.'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const storage = buildIndex(data, PIVOT); |
||||
assertStorageEntries( |
||||
storage, /* fieldNumber= */ 1, |
||||
Field.encodeIndexEntry(WireType.START_GROUP, /* startIndex= */ 1)); |
||||
} |
||||
}); |
||||
|
||||
it('index too large tag', () => { |
||||
const data = createBufferDecoder(0xF8, 0xFF, 0xFF, 0xFF, 0xFF); |
||||
expect(() => buildIndex(data, PIVOT)).toThrow(); |
||||
}); |
||||
|
||||
it('fail for varint tag that has no data after it', () => { |
||||
const data = createBufferDecoder(0x08); |
||||
expect(() => buildIndex(data, PIVOT)).toThrow(); |
||||
}); |
||||
}); |
@ -0,0 +1,71 @@ |
||||
/** |
||||
* @fileoverview Test data for int32 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.int32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, intValue:number, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getInt32Pairs() { |
||||
const int32Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
intValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
intValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
intValue: -1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x0F), |
||||
// The writer will encode this with 64 bits, see below
|
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'minus one (64bits)', |
||||
intValue: -1, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
{ |
||||
name: 'max signed int 2^31 - 1', |
||||
intValue: Math.pow(2, 31) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x07), |
||||
|
||||
}, |
||||
{ |
||||
name: 'min signed int -2^31', |
||||
intValue: -Math.pow(2, 31), |
||||
bufferDecoder: createBufferDecoder(0x80, 0x80, 0x80, 0x80, 0x08), |
||||
// The writer will encode this with 64 bits, see below
|
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'value min signed int -2^31 (64 bit)', |
||||
intValue: -Math.pow(2, 31), |
||||
bufferDecoder: createBufferDecoder( |
||||
0x80, 0x80, 0x80, 0x80, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
{ |
||||
name: 'errors out for 11 bytes', |
||||
intValue: -1, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), |
||||
error: true, |
||||
skip_writer: true, |
||||
}, |
||||
]; |
||||
return [...int32Pairs]; |
||||
} |
||||
|
||||
exports = {getInt32Pairs}; |
@ -0,0 +1,59 @@ |
||||
/** |
||||
* @fileoverview Test data for int64 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.int64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, longValue: !Int64, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getInt64Pairs() { |
||||
const int64Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
longValue: Int64.fromInt(0), |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
longValue: Int64.fromInt(1), |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
longValue: Int64.fromInt(-1), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
{ |
||||
name: 'max signed int 2^63 - 1', |
||||
longValue: Int64.fromBits(0xFFFFFFFF, 0x7FFFFFFF), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F), |
||||
|
||||
}, |
||||
{ |
||||
name: 'value min signed int -2^63 (64 bit)', |
||||
longValue: Int64.fromBits(0xFFFFFFFF, 0xFFFFFFFF), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
{ |
||||
name: 'errors out for 11 bytes', |
||||
longValue: Int64.fromInt(-1), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), |
||||
error: true, |
||||
skip_writer: true, |
||||
}, |
||||
]; |
||||
return [...int64Pairs]; |
||||
} |
||||
|
||||
exports = {getInt64Pairs}; |
@ -0,0 +1,24 @@ |
||||
/** |
||||
* @fileoverview Internal interface for messages implemented with the binary |
||||
* kernel. |
||||
*/ |
||||
goog.module('protobuf.binary.InternalMessage'); |
||||
|
||||
const LazyAccessor = goog.requireType('protobuf.binary.LazyAccessor'); |
||||
|
||||
/** |
||||
* Interface that needs to be implemented by messages implemented with the |
||||
* binary kernel. This is an internal only interface and should be used only by |
||||
* the classes in binary kernel. |
||||
* |
||||
* @interface |
||||
*/ |
||||
class InternalMessage { |
||||
/** |
||||
* @package |
||||
* @return {!LazyAccessor} |
||||
*/ |
||||
internalGetKernel() {} |
||||
} |
||||
|
||||
exports = InternalMessage; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,266 @@ |
||||
/** |
||||
* @fileoverview Tests to make sure LazyAccessor can read data in a backward |
||||
* compatible way even when protobuf schema changes according to the rules |
||||
* defined in |
||||
* https://developers.google.com/protocol-buffers/docs/proto#updating and
|
||||
* https://developers.google.com/protocol-buffers/docs/proto3#updating.
|
||||
* |
||||
* third_party/protobuf/conformance/binary_json_conformance_suite.cc already |
||||
* covers many compatibility tests, this file covers only the tests not covered |
||||
* by binary_json_conformance_suite. Ultimately all of the tests in this file |
||||
* should be moved to binary_json_conformance_suite. |
||||
*/ |
||||
goog.module('protobuf.binary.LazyAccessorCompatibilityTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); |
||||
const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); |
||||
const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); |
||||
|
||||
/** |
||||
* @param {...number} bytes |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
function createArrayBuffer(...bytes) { |
||||
return new Uint8Array(bytes).buffer; |
||||
} |
||||
|
||||
/** |
||||
* Returns the Unicode character codes of a string. |
||||
* @param {string} str |
||||
* @return {!Array<number>} |
||||
*/ |
||||
function getCharacterCodes(str) { |
||||
return Array.from(str, (c) => c.charCodeAt(0)); |
||||
} |
||||
|
||||
describe('optional -> repeated compatibility', () => { |
||||
it('is maintained for scalars', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setInt32(1, 1); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0x8, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getRepeatedInt32Size(1)).toEqual(1); |
||||
expect(newAccessor.getRepeatedInt32Element(1, 0)).toEqual(1); |
||||
}); |
||||
|
||||
it('is maintained for messages', () => { |
||||
const message = new TestMessage(LazyAccessor.createEmpty()); |
||||
message.setInt32(1, 1); |
||||
|
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setMessage(1, message); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getRepeatedMessageSize(1, TestMessage.instanceCreator)) |
||||
.toEqual(1); |
||||
expect( |
||||
newAccessor.getRepeatedMessageElement(1, TestMessage.instanceCreator, 0) |
||||
.serialize()) |
||||
.toEqual(message.serialize()); |
||||
}); |
||||
|
||||
it('is maintained for bytes', () => { |
||||
const message = new TestMessage(LazyAccessor.createEmpty()); |
||||
message.setInt32(1, 1); |
||||
|
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setBytes( |
||||
1, ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0xA, 0xB)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getRepeatedBytesSize(1)).toEqual(1); |
||||
expect(newAccessor.getRepeatedBoolElement(1, 0)) |
||||
.toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); |
||||
}); |
||||
|
||||
it('is maintained for strings', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setString(1, 'hello'); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer(0xA, 0x5, 0x68, 0x65, 0x6C, 0x6C, 0x6F)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getRepeatedStringSize(1)).toEqual(1); |
||||
expect(newAccessor.getRepeatedStringElement(1, 0)).toEqual('hello'); |
||||
}); |
||||
}); |
||||
|
||||
describe('LazyAccessor repeated -> optional compatibility', () => { |
||||
it('is maintained for unpacked scalars', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.addUnpackedInt32Element(1, 0); |
||||
oldAccessor.addUnpackedInt32Element(1, 1); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0x8, 0x0, 0x8, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getInt32WithDefault(1)).toEqual(1); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
// repeated -> optional transformation is not supported for packed fields yet:
|
||||
// go/proto-schema-repeated
|
||||
it('is not maintained for packed scalars', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.addPackedInt32Element(1, 0); |
||||
oldAccessor.addPackedInt32Element(1, 1); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x0, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
if (CHECK_CRITICAL_STATE) { |
||||
expect(() => newAccessor.getInt32WithDefault(1)).toThrow(); |
||||
} |
||||
}); |
||||
|
||||
it('is maintained for messages', () => { |
||||
const message1 = new TestMessage(LazyAccessor.createEmpty()); |
||||
message1.setInt32(1, 1); |
||||
const message2 = new TestMessage(LazyAccessor.createEmpty()); |
||||
message2.setInt32(1, 2); |
||||
message2.setInt32(2, 3); |
||||
|
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.addRepeatedMessageElement( |
||||
1, message1, TestMessage.instanceCreator); |
||||
oldAccessor.addRepeatedMessageElement( |
||||
1, message2, TestMessage.instanceCreator); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer( |
||||
0xA, 0x2, 0x8, 0x1, 0xA, 0x4, 0x8, 0x2, 0x10, 0x3)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
// Values from message1 and message2 have been merged
|
||||
const newMessage = newAccessor.getMessage(1, TestMessage.instanceCreator); |
||||
expect(newMessage.getRepeatedInt32Size(1)).toEqual(2); |
||||
expect(newMessage.getRepeatedInt32Element(1, 0)).toEqual(1); |
||||
expect(newMessage.getRepeatedInt32Element(1, 1)).toEqual(2); |
||||
expect(newMessage.getInt32WithDefault(2)).toEqual(3); |
||||
expect(newMessage.serialize()) |
||||
.toEqual(createArrayBuffer(0x8, 0x1, 0x8, 0x2, 0x10, 0x3)); |
||||
}); |
||||
|
||||
it('is maintained for bytes', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.addRepeatedBytesElement( |
||||
1, ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); |
||||
oldAccessor.addRepeatedBytesElement( |
||||
1, ByteString.fromArrayBuffer(createArrayBuffer(0xC, 0xD))); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer(0xA, 0x2, 0xA, 0xB, 0xA, 0x2, 0xC, 0xD)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getBytesWithDefault(1)) |
||||
.toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0xC, 0xD))); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is maintained for strings', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.addRepeatedStringElement(1, 'hello'); |
||||
oldAccessor.addRepeatedStringElement(1, 'world'); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer( |
||||
0xA, 0x5, ...getCharacterCodes('hello'), 0xA, 0x5, |
||||
...getCharacterCodes('world'))); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getStringWithDefault(1)).toEqual('world'); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
}); |
||||
|
||||
describe('Type change', () => { |
||||
it('is supported for fixed32 -> sfixed32', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setFixed32(1, 4294967295); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer(0xD, 0xFF, 0xFF, 0xFF, 0xFF)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getSfixed32WithDefault(1)).toEqual(-1); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is supported for sfixed32 -> fixed32', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setSfixed32(1, -1); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer(0xD, 0xFF, 0xFF, 0xFF, 0xFF)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getFixed32WithDefault(1)).toEqual(4294967295); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is supported for fixed64 -> sfixed64', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setFixed64(1, Int64.fromHexString('0xFFFFFFFFFFFFFFFF')); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer( |
||||
0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(-1)); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is supported for sfixed64 -> fixed64', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setSfixed64(1, Int64.fromInt(-1)); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData) |
||||
.toEqual(createArrayBuffer( |
||||
0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getFixed64WithDefault(1)) |
||||
.toEqual(Int64.fromHexString('0xFFFFFFFFFFFFFFFF')); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is supported for bytes -> message', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
oldAccessor.setBytes( |
||||
1, ByteString.fromArrayBuffer(createArrayBuffer(0x8, 0x1))); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
const message = newAccessor.getMessage(1, TestMessage.instanceCreator); |
||||
expect(message.getInt32WithDefault(1)).toEqual(1); |
||||
expect(message.serialize()).toEqual(createArrayBuffer(0x8, 0x1)); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
|
||||
it('is supported for message -> bytes', () => { |
||||
const oldAccessor = LazyAccessor.createEmpty(); |
||||
const message = new TestMessage(LazyAccessor.createEmpty()); |
||||
message.setInt32(1, 1); |
||||
oldAccessor.setMessage(1, message); |
||||
const serializedData = oldAccessor.serialize(); |
||||
expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); |
||||
|
||||
const newAccessor = LazyAccessor.fromArrayBuffer(serializedData); |
||||
expect(newAccessor.getBytesWithDefault(1)) |
||||
.toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0x8, 0x1))); |
||||
expect(newAccessor.serialize()).toEqual(serializedData); |
||||
}); |
||||
}); |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,59 @@ |
||||
goog.module('protobuf.binary.packedBoolTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed bool values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, boolValues: !Array<boolean>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedBoolPairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
boolValues: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
boolValues: [true], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'single multi-bytes value', |
||||
boolValues: [true], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x80, 0x01), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
boolValues: [true, false], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
{ |
||||
name: 'multiple multi-bytes values', |
||||
boolValues: [true, false], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x0C, // length
|
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x01, // true
|
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x80, |
||||
0x00, // false
|
||||
), |
||||
skip_writer: true, |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedBoolPairs}; |
@ -0,0 +1,52 @@ |
||||
goog.module('protobuf.binary.packedDoubleTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed double values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, doubleValues: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedDoublePairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
doubleValues: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
doubleValues: [1], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
doubleValues: [1, 0], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x10, // length
|
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0xF0, |
||||
0x3F, // 1
|
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, // 0
|
||||
), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedDoublePairs}; |
@ -0,0 +1,34 @@ |
||||
goog.module('protobuf.binary.packedFixed32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed fixed32 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, fixed32Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedFixed32Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
fixed32Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
fixed32Values: [1], |
||||
bufferDecoder: createBufferDecoder(0x04, 0x01, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
fixed32Values: [1, 0], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedFixed32Pairs}; |
@ -0,0 +1,34 @@ |
||||
goog.module('protobuf.binary.packedFloatTestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, floatValues: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedFloatPairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
floatValues: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
floatValues: [1], |
||||
bufferDecoder: createBufferDecoder(0x04, 0x00, 0x00, 0x80, 0x3F), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
floatValues: [1, 0], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x08, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedFloatPairs}; |
@ -0,0 +1,33 @@ |
||||
goog.module('protobuf.binary.packedInt32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed int32 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, int32Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedInt32Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
int32Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
int32Values: [1], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
int32Values: [1, 0], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedInt32Pairs}; |
@ -0,0 +1,34 @@ |
||||
goog.module('protobuf.binary.packedInt64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed int64 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, int64Values: !Array<!Int64>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedInt64Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
int64Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
int64Values: [Int64.fromInt(1)], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
int64Values: [Int64.fromInt(1), Int64.fromInt(0)], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedInt64Pairs}; |
@ -0,0 +1,34 @@ |
||||
goog.module('protobuf.binary.packedSfixed32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed sfixed32 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, sfixed32Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedSfixed32Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
sfixed32Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
sfixed32Values: [1], |
||||
bufferDecoder: createBufferDecoder(0x04, 0x01, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
sfixed32Values: [1, 0], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedSfixed32Pairs}; |
@ -0,0 +1,53 @@ |
||||
goog.module('protobuf.binary.packedSfixed64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed sfixed64 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, sfixed64Values: !Array<!Int64>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedSfixed64Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
sfixed64Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
sfixed64Values: [Int64.fromInt(1)], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
sfixed64Values: [Int64.fromInt(1), Int64.fromInt(0)], |
||||
bufferDecoder: createBufferDecoder( |
||||
0x10, // length
|
||||
0x01, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, // 1
|
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, |
||||
0x00, // 2
|
||||
), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedSfixed64Pairs}; |
@ -0,0 +1,33 @@ |
||||
goog.module('protobuf.binary.packedSint32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed sint32 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, sint32Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedSint32Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
sint32Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
sint32Values: [-1], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
sint32Values: [-1, 0], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedSint32Pairs}; |
@ -0,0 +1,34 @@ |
||||
goog.module('protobuf.binary.packedSint64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed sint64 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, sint64Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedSint64Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
sint64Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
sint64Values: [Int64.fromInt(-1)], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
sint64Values: [Int64.fromInt(-1), Int64.fromInt(0)], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedSint64Pairs}; |
@ -0,0 +1,33 @@ |
||||
goog.module('protobuf.binary.packedUint32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of packed uint32 values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, uint32Values: !Array<number>, |
||||
* bufferDecoder: !BufferDecoder, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getPackedUint32Pairs() { |
||||
return [ |
||||
{ |
||||
name: 'empty value', |
||||
uint32Values: [], |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'single value', |
||||
uint32Values: [1], |
||||
bufferDecoder: createBufferDecoder(0x01, 0x01), |
||||
}, |
||||
{ |
||||
name: 'multiple values', |
||||
uint32Values: [1, 0], |
||||
bufferDecoder: createBufferDecoder(0x02, 0x01, 0x00), |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
exports = {getPackedUint32Pairs}; |
@ -0,0 +1,464 @@ |
||||
/** |
||||
* @fileoverview Helper methods for reading data from the binary wire format. |
||||
*/ |
||||
goog.module('protobuf.binary.reader'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
|
||||
|
||||
/****************************************************************************** |
||||
* OPTIONAL FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
/** |
||||
* Reads a boolean from the binary bytes. |
||||
* Also returns the first position after the boolean. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{value: boolean, nextCursor: number}} |
||||
*/ |
||||
function readBoolValue(bufferDecoder, index) { |
||||
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); |
||||
return {value: lowBits !== 0 || highBits !== 0, nextCursor: dataStart}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a boolean value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {boolean} |
||||
* @package |
||||
*/ |
||||
function readBool(bufferDecoder, start) { |
||||
return readBoolValue(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Reads a double value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!ByteString} |
||||
* @package |
||||
*/ |
||||
function readBytes(bufferDecoder, start) { |
||||
return readDelimited(bufferDecoder, start).asByteString(); |
||||
} |
||||
|
||||
/** |
||||
* Reads a int32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{value: number, nextCursor: number}} |
||||
* @package |
||||
*/ |
||||
function readInt32Value(bufferDecoder, index) { |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(index); |
||||
// Negative 32 bit integers are encoded with 64 bit values.
|
||||
// Clients are expected to truncate back to 32 bits.
|
||||
// This is why we are dropping the upper bytes here.
|
||||
return {value: lowBits | 0, nextCursor: dataStart}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a int32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readInt32(bufferDecoder, start) { |
||||
return readInt32Value(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Reads a int32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{ value: !Int64, nextCursor: number}} |
||||
* @package |
||||
*/ |
||||
function readInt64Value(bufferDecoder, index) { |
||||
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); |
||||
return {value: Int64.fromBits(lowBits, highBits), nextCursor: dataStart}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a int32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Int64} |
||||
* @package |
||||
*/ |
||||
function readInt64(bufferDecoder, start) { |
||||
return readInt64Value(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Reads a fixed int32 value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readFixed32(bufferDecoder, start) { |
||||
return bufferDecoder.getUint32(start); |
||||
} |
||||
|
||||
/** |
||||
* Reads a float value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readFloat(bufferDecoder, start) { |
||||
return bufferDecoder.getFloat32(start); |
||||
} |
||||
|
||||
/** |
||||
* Reads a fixed int64 value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Int64} |
||||
* @package |
||||
*/ |
||||
function readSfixed64(bufferDecoder, start) { |
||||
const lowBits = bufferDecoder.getInt32(start); |
||||
const highBits = bufferDecoder.getInt32(start + 4); |
||||
return Int64.fromBits(lowBits, highBits); |
||||
} |
||||
|
||||
/** |
||||
* Reads a sint32 value from the binary bytes encoded as varint. |
||||
* Also returns the first position after the boolean. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{value: number, nextCursor: number}} |
||||
*/ |
||||
function readSint32Value(bufferDecoder, index) { |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(index); |
||||
// Truncate upper bits and convert from zig zag to signd int
|
||||
return {value: (lowBits >>> 1) ^ -(lowBits & 0x01), nextCursor: dataStart}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a sint32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readSint32(bufferDecoder, start) { |
||||
return readSint32Value(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Reads a sint64 value from the binary bytes encoded as varint. |
||||
* Also returns the first position after the value. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{value: !Int64, nextCursor: number}} |
||||
* @package |
||||
*/ |
||||
function readSint64Value(bufferDecoder, index) { |
||||
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); |
||||
const sign = -(lowBits & 0x01); |
||||
const decodedLowerBits = ((lowBits >>> 1) | (highBits & 0x01) << 31) ^ sign; |
||||
const decodedUpperBits = (highBits >>> 1) ^ sign; |
||||
return { |
||||
value: Int64.fromBits(decodedLowerBits, decodedUpperBits), |
||||
nextCursor: dataStart |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a sint64 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Int64} |
||||
* @package |
||||
*/ |
||||
function readSint64(bufferDecoder, start) { |
||||
return readSint64Value(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Read a subarray of bytes representing a length delimited field. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!BufferDecoder} |
||||
* @package |
||||
*/ |
||||
function readDelimited(bufferDecoder, start) { |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(start); |
||||
const unsignedLength = lowBits >>> 0; |
||||
return bufferDecoder.subBufferDecoder(dataStart, unsignedLength); |
||||
} |
||||
|
||||
/** |
||||
* Reads a string value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {string} |
||||
* @package |
||||
*/ |
||||
function readString(bufferDecoder, start) { |
||||
return readDelimited(bufferDecoder, start).asString(); |
||||
} |
||||
|
||||
/** |
||||
* Reads a uint32 value from the binary bytes encoded as varint. |
||||
* Also returns the first position after the value. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} index Start of the data. |
||||
* @return {{value: number, nextCursor: number}} |
||||
*/ |
||||
function readUint32Value(bufferDecoder, index) { |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(index); |
||||
return {value: lowBits >>> 0, nextCursor: dataStart}; |
||||
} |
||||
|
||||
/** |
||||
* Reads a uint32 value from the binary bytes encoded as varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readUint32(bufferDecoder, start) { |
||||
return readUint32Value(bufferDecoder, start).value; |
||||
} |
||||
|
||||
/** |
||||
* Reads a double value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readDouble(bufferDecoder, start) { |
||||
return bufferDecoder.getFloat64(start); |
||||
} |
||||
|
||||
/** |
||||
* Reads a fixed int32 value from the binary bytes. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {number} |
||||
* @package |
||||
*/ |
||||
function readSfixed32(bufferDecoder, start) { |
||||
return bufferDecoder.getInt32(start); |
||||
} |
||||
|
||||
/****************************************************************************** |
||||
* REPEATED FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
/** |
||||
* Reads a packed bool field, which consists of a length header and a list of |
||||
* unsigned varints. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<boolean>} |
||||
* @package |
||||
*/ |
||||
function readPackedBool(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readBoolValue); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed double field, which consists of a length header and a list of |
||||
* fixed64. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedDouble(bufferDecoder, start) { |
||||
return readPackedFixed(bufferDecoder, start, 8, readDouble); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed fixed32 field, which consists of a length header and a list of |
||||
* fixed32. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedFixed32(bufferDecoder, start) { |
||||
return readPackedFixed(bufferDecoder, start, 4, readFixed32); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed float field, which consists of a length header and a list of |
||||
* fixed64. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedFloat(bufferDecoder, start) { |
||||
return readPackedFixed(bufferDecoder, start, 4, readFloat); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed int32 field, which consists of a length header and a list of |
||||
* varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedInt32(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readInt32Value); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed int64 field, which consists of a length header and a list |
||||
* of int64. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<!Int64>} |
||||
* @package |
||||
*/ |
||||
function readPackedInt64(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readInt64Value); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed sfixed32 field, which consists of a length header and a list |
||||
* of sfixed32. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedSfixed32(bufferDecoder, start) { |
||||
return readPackedFixed(bufferDecoder, start, 4, readSfixed32); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed sfixed64 field, which consists of a length header and a list |
||||
* of sfixed64. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<!Int64>} |
||||
* @package |
||||
*/ |
||||
function readPackedSfixed64(bufferDecoder, start) { |
||||
return readPackedFixed(bufferDecoder, start, 8, readSfixed64); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed sint32 field, which consists of a length header and a list of |
||||
* varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedSint32(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readSint32Value); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed sint64 field, which consists of a length header and a list |
||||
* of sint64. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<!Int64>} |
||||
* @package |
||||
*/ |
||||
function readPackedSint64(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readSint64Value); |
||||
} |
||||
|
||||
/** |
||||
* Reads a packed uint32 field, which consists of a length header and a list of |
||||
* varint. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @return {!Array<number>} |
||||
* @package |
||||
*/ |
||||
function readPackedUint32(bufferDecoder, start) { |
||||
return readPackedVariableLength(bufferDecoder, start, readUint32Value); |
||||
} |
||||
|
||||
/** |
||||
* Read packed variable length values. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @param {function(!BufferDecoder, number):{value:T, nextCursor: number}} |
||||
* valueFunction |
||||
* @return {!Array<T>} |
||||
* @package |
||||
* @template T |
||||
*/ |
||||
function readPackedVariableLength(bufferDecoder, start, valueFunction) { |
||||
const /** !Array<T> */ result = []; |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(start); |
||||
let cursor = dataStart; |
||||
const unsignedLength = lowBits >>> 0; |
||||
while (cursor < dataStart + unsignedLength) { |
||||
const {value, nextCursor} = valueFunction(bufferDecoder, cursor); |
||||
cursor = nextCursor; |
||||
result.push(value); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Read a packed fixed values. |
||||
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. |
||||
* @param {number} start Start of the data. |
||||
* @param {number} size End of the data. |
||||
* @param {function(!BufferDecoder, number):T} valueFunction |
||||
* @return {!Array<T>} |
||||
* @package |
||||
* @template T |
||||
*/ |
||||
function readPackedFixed(bufferDecoder, start, size, valueFunction) { |
||||
const {lowBits, dataStart} = bufferDecoder.getVarint(start); |
||||
const unsignedLength = lowBits >>> 0; |
||||
const noOfEntries = unsignedLength / size; |
||||
const /** !Array<T> */ result = new Array(noOfEntries); |
||||
let cursor = dataStart; |
||||
for (let i = 0; i < noOfEntries; i++) { |
||||
result[i] = valueFunction(bufferDecoder, cursor); |
||||
cursor += size; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
exports = { |
||||
readBool, |
||||
readBytes, |
||||
readDelimited, |
||||
readDouble, |
||||
readFixed32, |
||||
readFloat, |
||||
readInt32, |
||||
readInt64, |
||||
readSint32, |
||||
readSint64, |
||||
readSfixed32, |
||||
readSfixed64, |
||||
readString, |
||||
readUint32, |
||||
readPackedBool, |
||||
readPackedDouble, |
||||
readPackedFixed32, |
||||
readPackedFloat, |
||||
readPackedInt32, |
||||
readPackedInt64, |
||||
readPackedSfixed32, |
||||
readPackedSfixed64, |
||||
readPackedSint32, |
||||
readPackedSint64, |
||||
readPackedUint32, |
||||
}; |
@ -0,0 +1,423 @@ |
||||
/** |
||||
* @fileoverview Tests for reader.js. |
||||
*/ |
||||
goog.module('protobuf.binary.ReaderTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
// Note to the reader:
|
||||
// Since the reader behavior changes with the checking level some of the
|
||||
// tests in this file have to know which checking level is enable to make
|
||||
// correct assertions.
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const reader = goog.require('protobuf.binary.reader'); |
||||
const {CHECK_STATE} = goog.require('protobuf.internal.checks'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
const {encode} = goog.require('protobuf.binary.textencoding'); |
||||
const {getBoolPairs} = goog.require('protobuf.binary.boolTestPairs'); |
||||
const {getDoublePairs} = goog.require('protobuf.binary.doubleTestPairs'); |
||||
const {getFixed32Pairs} = goog.require('protobuf.binary.fixed32TestPairs'); |
||||
const {getFloatPairs} = goog.require('protobuf.binary.floatTestPairs'); |
||||
const {getInt32Pairs} = goog.require('protobuf.binary.int32TestPairs'); |
||||
const {getInt64Pairs} = goog.require('protobuf.binary.int64TestPairs'); |
||||
const {getPackedBoolPairs} = goog.require('protobuf.binary.packedBoolTestPairs'); |
||||
const {getPackedDoublePairs} = goog.require('protobuf.binary.packedDoubleTestPairs'); |
||||
const {getPackedFixed32Pairs} = goog.require('protobuf.binary.packedFixed32TestPairs'); |
||||
const {getPackedFloatPairs} = goog.require('protobuf.binary.packedFloatTestPairs'); |
||||
const {getPackedInt32Pairs} = goog.require('protobuf.binary.packedInt32TestPairs'); |
||||
const {getPackedInt64Pairs} = goog.require('protobuf.binary.packedInt64TestPairs'); |
||||
const {getPackedSfixed32Pairs} = goog.require('protobuf.binary.packedSfixed32TestPairs'); |
||||
const {getPackedSfixed64Pairs} = goog.require('protobuf.binary.packedSfixed64TestPairs'); |
||||
const {getPackedSint32Pairs} = goog.require('protobuf.binary.packedSint32TestPairs'); |
||||
const {getPackedSint64Pairs} = goog.require('protobuf.binary.packedSint64TestPairs'); |
||||
const {getPackedUint32Pairs} = goog.require('protobuf.binary.packedUint32TestPairs'); |
||||
const {getSfixed32Pairs} = goog.require('protobuf.binary.sfixed32TestPairs'); |
||||
const {getSfixed64Pairs} = goog.require('protobuf.binary.sfixed64TestPairs'); |
||||
const {getSint32Pairs} = goog.require('protobuf.binary.sint32TestPairs'); |
||||
const {getSint64Pairs} = goog.require('protobuf.binary.sint64TestPairs'); |
||||
const {getUint32Pairs} = goog.require('protobuf.binary.uint32TestPairs'); |
||||
|
||||
/****************************************************************************** |
||||
* Optional FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
describe('Read bool does', () => { |
||||
for (const pair of getBoolPairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readBool(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readBool( |
||||
pair.bufferDecoder, pair.bufferDecoder.startIndex()); |
||||
expect(d).toEqual(pair.boolValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readBytes does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(); |
||||
expect(() => reader.readBytes(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
it('read bytes by index', () => { |
||||
const bufferDecoder = createBufferDecoder(3, 1, 2, 3); |
||||
const byteString = reader.readBytes(bufferDecoder, 0); |
||||
expect(ByteString.fromArrayBuffer(new Uint8Array([1, 2, 3]).buffer)) |
||||
.toEqual(byteString); |
||||
}); |
||||
}); |
||||
|
||||
describe('readDouble does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(); |
||||
expect(() => reader.readDouble(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getDoublePairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readDouble(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.doubleValue); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readFixed32 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(); |
||||
expect(() => reader.readFixed32(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getFixed32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readFixed32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.intValue); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readFloat does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(); |
||||
expect(() => reader.readFloat(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getFloatPairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readFloat(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(Math.fround(pair.floatValue)); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readInt32 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readInt32(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getInt32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readInt32(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readInt32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.intValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readSfixed32 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readSfixed32(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getSfixed32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readSfixed32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.intValue); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readSfixed64 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readSfixed64(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getSfixed64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readSfixed64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.longValue); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readSint32 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readSint32(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getSint32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readSint32(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readSint32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.intValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readInt64 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readInt64(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getInt64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readInt64(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readInt64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.longValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readSint64 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readSint64(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getSint64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readSint64(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readSint64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.longValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readUint32 does', () => { |
||||
it('throw exception if data is too short', () => { |
||||
const bufferDecoder = createBufferDecoder(0x80); |
||||
expect(() => reader.readUint32(bufferDecoder, 0)).toThrow(); |
||||
}); |
||||
|
||||
for (const pair of getUint32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
if (pair.error && CHECK_STATE) { |
||||
expect(() => reader.readUint32(pair.bufferDecoder, 0)).toThrow(); |
||||
} else { |
||||
const d = reader.readUint32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.intValue); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
/** |
||||
* |
||||
* @param {string} s |
||||
* @return {!Uint8Array} |
||||
*/ |
||||
function encodeString(s) { |
||||
if (typeof TextEncoder !== 'undefined') { |
||||
const textEncoder = new TextEncoder('utf-8'); |
||||
return textEncoder.encode(s); |
||||
} else { |
||||
return encode(s); |
||||
} |
||||
} |
||||
|
||||
/** @param {string} s */ |
||||
function expectEncodedStringToMatch(s) { |
||||
const array = encodeString(s); |
||||
const length = array.length; |
||||
if (length > 127) { |
||||
throw new Error('Test only works for strings shorter than 128'); |
||||
} |
||||
const encodedArray = new Uint8Array(length + 1); |
||||
encodedArray[0] = length; |
||||
encodedArray.set(array, 1); |
||||
const bufferDecoder = BufferDecoder.fromArrayBuffer(encodedArray.buffer); |
||||
expect(reader.readString(bufferDecoder, 0)).toEqual(s); |
||||
} |
||||
|
||||
describe('readString does', () => { |
||||
it('return empty string for zero length string', () => { |
||||
const s = reader.readString(createBufferDecoder(0x00), 0); |
||||
expect(s).toEqual(''); |
||||
}); |
||||
|
||||
it('decode random strings', () => { |
||||
// 1 byte strings
|
||||
expectEncodedStringToMatch('hello'); |
||||
expectEncodedStringToMatch('HELLO1!'); |
||||
|
||||
// 2 byte String
|
||||
expectEncodedStringToMatch('©'); |
||||
|
||||
// 3 byte string
|
||||
expectEncodedStringToMatch('❄'); |
||||
|
||||
// 4 byte string
|
||||
expectEncodedStringToMatch('😁'); |
||||
}); |
||||
|
||||
it('decode 1 byte strings', () => { |
||||
for (let i = 0; i < 0x80; i++) { |
||||
const s = String.fromCharCode(i); |
||||
expectEncodedStringToMatch(s); |
||||
} |
||||
}); |
||||
|
||||
it('decode 2 byte strings', () => { |
||||
for (let i = 0xC0; i < 0x7FF; i++) { |
||||
const s = String.fromCharCode(i); |
||||
expectEncodedStringToMatch(s); |
||||
} |
||||
}); |
||||
|
||||
it('decode 3 byte strings', () => { |
||||
for (let i = 0x7FF; i < 0x8FFF; i++) { |
||||
const s = String.fromCharCode(i); |
||||
expectEncodedStringToMatch(s); |
||||
} |
||||
}); |
||||
|
||||
it('throw exception on invalid bytes', () => { |
||||
// This test will only succeed with the native TextDecoder since
|
||||
// our polyfill does not do any validation. IE10 and IE11 don't support
|
||||
// TextDecoder.
|
||||
// TODO: Remove this check once we no longer need to support IE
|
||||
if (typeof TextDecoder !== 'undefined') { |
||||
expect( |
||||
() => reader.readString( |
||||
createBufferDecoder(0x01, /* invalid utf data point*/ 0xFF), 0)) |
||||
.toThrow(); |
||||
} |
||||
}); |
||||
|
||||
it('throw exception if data is too short', () => { |
||||
const array = createBufferDecoder(0x02, '?'.charCodeAt(0)); |
||||
expect(() => reader.readString(array, 0)).toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
/****************************************************************************** |
||||
* REPEATED FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
describe('readPackedBool does', () => { |
||||
for (const pair of getPackedBoolPairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedBool(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.boolValues); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedDouble does', () => { |
||||
for (const pair of getPackedDoublePairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedDouble(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.doubleValues); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedFixed32 does', () => { |
||||
for (const pair of getPackedFixed32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedFixed32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.fixed32Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedFloat does', () => { |
||||
for (const pair of getPackedFloatPairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedFloat(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.floatValues); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedInt32 does', () => { |
||||
for (const pair of getPackedInt32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedInt32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.int32Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedInt64 does', () => { |
||||
for (const pair of getPackedInt64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedInt64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.int64Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedSfixed32 does', () => { |
||||
for (const pair of getPackedSfixed32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedSfixed32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.sfixed32Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedSfixed64 does', () => { |
||||
for (const pair of getPackedSfixed64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedSfixed64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.sfixed64Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedSint32 does', () => { |
||||
for (const pair of getPackedSint32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedSint32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.sint32Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedSint64 does', () => { |
||||
for (const pair of getPackedSint64Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedSint64(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.sint64Values); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('readPackedUint32 does', () => { |
||||
for (const pair of getPackedUint32Pairs()) { |
||||
it(`decode ${pair.name}`, () => { |
||||
const d = reader.readPackedUint32(pair.bufferDecoder, 0); |
||||
expect(d).toEqual(pair.uint32Values); |
||||
}); |
||||
} |
||||
}); |
@ -0,0 +1,46 @@ |
||||
/** |
||||
* @fileoverview Test data for sfixed32 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.sfixed32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of int values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, intValue: number, bufferDecoder: |
||||
* !BufferDecoder}>} |
||||
*/ |
||||
function getSfixed32Pairs() { |
||||
const sfixed32Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
intValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'one', |
||||
intValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x01, 0x00, 0x00, 0x00) |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
intValue: -1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF), |
||||
}, |
||||
{ |
||||
name: 'max int 2^31 -1', |
||||
intValue: Math.pow(2, 31) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0x7F) |
||||
}, |
||||
{ |
||||
name: 'min int -2^31', |
||||
intValue: -Math.pow(2, 31), |
||||
bufferDecoder: createBufferDecoder(0x00, 0x00, 0x00, 0x80) |
||||
}, |
||||
]; |
||||
return [...sfixed32Pairs]; |
||||
} |
||||
|
||||
exports = {getSfixed32Pairs}; |
@ -0,0 +1,52 @@ |
||||
/** |
||||
* @fileoverview Test data for sfixed32 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.sfixed64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of int values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, longValue: !Int64, bufferDecoder: |
||||
* !BufferDecoder}>} |
||||
*/ |
||||
function getSfixed64Pairs() { |
||||
const sfixed64Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
longValue: Int64.fromInt(0), |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), |
||||
}, |
||||
{ |
||||
name: 'one', |
||||
longValue: Int64.fromInt(1), |
||||
bufferDecoder: |
||||
createBufferDecoder(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
longValue: Int64.fromInt(-1), |
||||
bufferDecoder: |
||||
createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), |
||||
}, |
||||
{ |
||||
name: 'max int 2^63 -1', |
||||
longValue: Int64.getMaxValue(), |
||||
bufferDecoder: |
||||
createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F) |
||||
}, |
||||
{ |
||||
name: 'min int -2^63', |
||||
longValue: Int64.getMinValue(), |
||||
bufferDecoder: |
||||
createBufferDecoder(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80) |
||||
}, |
||||
]; |
||||
return [...sfixed64Pairs]; |
||||
} |
||||
|
||||
exports = {getSfixed64Pairs}; |
@ -0,0 +1,57 @@ |
||||
/** |
||||
* @fileoverview Test data for int32 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.sint32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, intValue:number, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getSint32Pairs() { |
||||
const sint32Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
intValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
intValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x02), |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
intValue: -1, |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'two', |
||||
intValue: 2, |
||||
bufferDecoder: createBufferDecoder(0x04), |
||||
}, |
||||
{ |
||||
name: 'minus two', |
||||
intValue: -2, |
||||
bufferDecoder: createBufferDecoder(0x03), |
||||
}, |
||||
{ |
||||
name: 'int 2^31 - 1', |
||||
intValue: Math.pow(2, 31) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFE, 0xFF, 0xFF, 0xFF, 0x0F), |
||||
|
||||
}, |
||||
{ |
||||
name: '-2^31', |
||||
intValue: -Math.pow(2, 31), |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x0F), |
||||
}, |
||||
]; |
||||
return [...sint32Pairs]; |
||||
} |
||||
|
||||
exports = {getSint32Pairs}; |
@ -0,0 +1,60 @@ |
||||
/** |
||||
* @fileoverview Test data for sint64 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.sint64TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, longValue: !Int64, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getSint64Pairs() { |
||||
const sint64Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
longValue: Int64.fromInt(0), |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
longValue: Int64.fromInt(1), |
||||
bufferDecoder: createBufferDecoder(0x02), |
||||
}, |
||||
{ |
||||
name: 'minus one', |
||||
longValue: Int64.fromInt(-1), |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'two', |
||||
longValue: Int64.fromInt(2), |
||||
bufferDecoder: createBufferDecoder(0x04), |
||||
}, |
||||
{ |
||||
name: 'minus two', |
||||
longValue: Int64.fromInt(-2), |
||||
bufferDecoder: createBufferDecoder(0x03), |
||||
}, |
||||
{ |
||||
name: 'min value', |
||||
longValue: Int64.getMinValue(), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
|
||||
{ |
||||
name: 'max value', |
||||
longValue: Int64.getMaxValue(), |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
}, |
||||
]; |
||||
return [...sint64Pairs]; |
||||
} |
||||
|
||||
exports = {getSint64Pairs}; |
@ -0,0 +1,133 @@ |
||||
goog.module('protobuf.binary.Storage'); |
||||
|
||||
const {checkDefAndNotNull} = goog.require('protobuf.internal.checks'); |
||||
|
||||
/** |
||||
* 85% of the proto fields have a field number <= 24: |
||||
* https://plx.corp.google.com/scripts2/script_5d._f02af6_0000_23b1_a15f_001a1139dd02
|
||||
* |
||||
* @type {number} |
||||
*/ |
||||
// LINT.IfChange
|
||||
const DEFAULT_PIVOT = 24; |
||||
// LINT.ThenChange(//depot/google3/third_party/protobuf/javascript/runtime/kernel/storage_test.js,
|
||||
// //depot/google3/net/proto2/contrib/js_proto/internal/kernel_message_generator.cc)
|
||||
|
||||
/** |
||||
* Class storing all the fields of a protobuf message. |
||||
* |
||||
* @package |
||||
* @template FieldType |
||||
*/ |
||||
class Storage { |
||||
/** |
||||
* @param {number=} pivot |
||||
*/ |
||||
constructor(pivot = DEFAULT_PIVOT) { |
||||
/** |
||||
* Fields having a field number no greater than the pivot value are stored |
||||
* into an array for fast access. A field with field number X is stored into |
||||
* the array position X - 1. |
||||
* |
||||
* @private @const {!Array<!FieldType|undefined>} |
||||
*/ |
||||
this.array_ = new Array(pivot); |
||||
|
||||
/** |
||||
* Fields having a field number higher than the pivot value are stored into |
||||
* the map. We create the map only when it's needed, since even an empty map |
||||
* takes up a significant amount of memory. |
||||
* |
||||
* @private {?Map<number, !FieldType>} |
||||
*/ |
||||
this.map_ = null; |
||||
} |
||||
|
||||
/** |
||||
* Fields having a field number no greater than the pivot value are stored |
||||
* into an array for fast access. A field with field number X is stored into |
||||
* the array position X - 1. |
||||
* @return {number} |
||||
*/ |
||||
getPivot() { |
||||
return this.array_.length; |
||||
} |
||||
|
||||
/** |
||||
* Sets a field in the specified field number. |
||||
* |
||||
* @param {number} fieldNumber |
||||
* @param {!FieldType} field |
||||
*/ |
||||
set(fieldNumber, field) { |
||||
if (fieldNumber <= this.getPivot()) { |
||||
this.array_[fieldNumber - 1] = field; |
||||
} else { |
||||
if (this.map_) { |
||||
this.map_.set(fieldNumber, field); |
||||
} else { |
||||
this.map_ = new Map([[fieldNumber, field]]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a field at the specified field number. |
||||
* |
||||
* @param {number} fieldNumber |
||||
* @return {!FieldType|undefined} |
||||
*/ |
||||
get(fieldNumber) { |
||||
if (fieldNumber <= this.getPivot()) { |
||||
return this.array_[fieldNumber - 1]; |
||||
} else { |
||||
return this.map_ ? this.map_.get(fieldNumber) : undefined; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Deletes a field from the specified field number. |
||||
* |
||||
* @param {number} fieldNumber |
||||
*/ |
||||
delete(fieldNumber) { |
||||
if (fieldNumber <= this.getPivot()) { |
||||
delete this.array_[fieldNumber - 1]; |
||||
} else { |
||||
if (this.map_) { |
||||
this.map_.delete(fieldNumber); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Executes the provided function once for each array element. |
||||
* |
||||
* @param {function(!FieldType, number): void} callback |
||||
*/ |
||||
forEach(callback) { |
||||
this.array_.forEach((field, fieldNumber) => { |
||||
if (field) { |
||||
callback(checkDefAndNotNull(field), fieldNumber + 1); |
||||
} |
||||
}); |
||||
if (this.map_) { |
||||
this.map_.forEach(callback); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a shallow copy of the storage. |
||||
* |
||||
* @return {!Storage} |
||||
*/ |
||||
shallowCopy() { |
||||
const copy = new Storage(this.getPivot()); |
||||
this.forEach( |
||||
(field, fieldNumber) => |
||||
void copy.set(fieldNumber, field.shallowCopy())); |
||||
return copy; |
||||
} |
||||
} |
||||
|
||||
exports = Storage; |
@ -0,0 +1,165 @@ |
||||
/** |
||||
* @fileoverview Tests for storage.js. |
||||
*/ |
||||
goog.module('protobuf.binary.StorageTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
const Storage = goog.require('protobuf.binary.Storage'); |
||||
const {Field} = goog.require('protobuf.binary.field'); |
||||
|
||||
/** |
||||
* @type {number} |
||||
*/ |
||||
const DEFAULT_PIVOT = 24; |
||||
|
||||
const /** !Field */ field1 = |
||||
Field.fromDecodedValue(/* decodedValue= */ 1, /* encoder= */ () => {}); |
||||
const /** !Field */ field2 = |
||||
Field.fromDecodedValue(/* decodedValue= */ 2, /* encoder= */ () => {}); |
||||
const /** !Field */ field3 = |
||||
Field.fromDecodedValue(/* decodedValue= */ 3, /* encoder= */ () => {}); |
||||
const /** !Field */ field4 = |
||||
Field.fromDecodedValue(/* decodedValue= */ 4, /* encoder= */ () => {}); |
||||
|
||||
/** |
||||
* Returns the number of fields stored. |
||||
* |
||||
* @param {!Storage} storage |
||||
* @return {number} |
||||
*/ |
||||
function getStorageSize(storage) { |
||||
let size = 0; |
||||
storage.forEach(() => void size++); |
||||
return size; |
||||
} |
||||
|
||||
describe('Storage', () => { |
||||
it('sets and gets a field not greater than the pivot', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
|
||||
storage.set(1, field1); |
||||
storage.set(DEFAULT_PIVOT, field2); |
||||
|
||||
expect(storage.getPivot()).toBe(DEFAULT_PIVOT); |
||||
expect(storage.get(1)).toBe(field1); |
||||
expect(storage.get(DEFAULT_PIVOT)).toBe(field2); |
||||
}); |
||||
|
||||
it('sets and gets a field greater than the pivot', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
|
||||
storage.set(DEFAULT_PIVOT + 1, field1); |
||||
storage.set(100000, field2); |
||||
|
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBe(field1); |
||||
expect(storage.get(100000)).toBe(field2); |
||||
}); |
||||
|
||||
it('sets and gets a field when pivot is zero', () => { |
||||
const storage = new Storage(0); |
||||
|
||||
storage.set(0, field1); |
||||
storage.set(100000, field2); |
||||
|
||||
expect(storage.getPivot()).toBe(0); |
||||
expect(storage.get(0)).toBe(field1); |
||||
expect(storage.get(100000)).toBe(field2); |
||||
}); |
||||
|
||||
it('sets and gets a field when pivot is undefined', () => { |
||||
const storage = new Storage(); |
||||
|
||||
storage.set(0, field1); |
||||
storage.set(DEFAULT_PIVOT, field2); |
||||
storage.set(DEFAULT_PIVOT + 1, field3); |
||||
|
||||
expect(storage.getPivot()).toBe(DEFAULT_PIVOT); |
||||
expect(storage.get(0)).toBe(field1); |
||||
expect(storage.get(DEFAULT_PIVOT)).toBe(field2); |
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBe(field3); |
||||
}); |
||||
|
||||
it('returns undefined for nonexistent fields', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
|
||||
expect(storage.get(1)).toBeUndefined(); |
||||
expect(storage.get(DEFAULT_PIVOT)).toBeUndefined(); |
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBeUndefined(); |
||||
expect(storage.get(100000)).toBeUndefined(); |
||||
}); |
||||
|
||||
it('returns undefined for nonexistent fields after map initialization', |
||||
() => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(100001, field1); |
||||
|
||||
expect(storage.get(1)).toBeUndefined(); |
||||
expect(storage.get(DEFAULT_PIVOT)).toBeUndefined(); |
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBeUndefined(); |
||||
expect(storage.get(100000)).toBeUndefined(); |
||||
}); |
||||
|
||||
it('deletes a field in delete() when values are only in array', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(1, field1); |
||||
|
||||
storage.delete(1); |
||||
|
||||
expect(storage.get(1)).toBeUndefined(); |
||||
}); |
||||
|
||||
it('deletes a field in delete() when values are both in array and map', |
||||
() => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(DEFAULT_PIVOT, field2); |
||||
storage.set(DEFAULT_PIVOT + 1, field3); |
||||
|
||||
storage.delete(DEFAULT_PIVOT); |
||||
storage.delete(DEFAULT_PIVOT + 1); |
||||
|
||||
expect(storage.get(DEFAULT_PIVOT)).toBeUndefined(); |
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBeUndefined(); |
||||
}); |
||||
|
||||
it('deletes a field in delete() when values are only in map', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(100000, field4); |
||||
|
||||
storage.delete(100000); |
||||
|
||||
expect(storage.get(100000)).toBeUndefined(); |
||||
}); |
||||
|
||||
it('loops over all the elements in forEach()', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(1, field1); |
||||
storage.set(DEFAULT_PIVOT, field2); |
||||
storage.set(DEFAULT_PIVOT + 1, field3); |
||||
storage.set(100000, field4); |
||||
|
||||
const fields = new Map(); |
||||
storage.forEach( |
||||
(field, fieldNumber) => void fields.set(fieldNumber, field)); |
||||
|
||||
expect(fields.size).toEqual(4); |
||||
expect(fields.get(1)).toBe(field1); |
||||
expect(storage.get(DEFAULT_PIVOT)).toBe(field2); |
||||
expect(storage.get(DEFAULT_PIVOT + 1)).toBe(field3); |
||||
expect(fields.get(100000)).toBe(field4); |
||||
}); |
||||
|
||||
it('creates a shallow copy of the storage in shallowCopy()', () => { |
||||
const storage = new Storage(DEFAULT_PIVOT); |
||||
storage.set(1, field1); |
||||
storage.set(100000, field2); |
||||
|
||||
const copy = storage.shallowCopy(); |
||||
|
||||
expect(getStorageSize(copy)).toEqual(2); |
||||
expect(copy.get(1)).not.toBe(field1); |
||||
expect(copy.get(1).getDecodedValue()).toEqual(1); |
||||
expect(copy.get(100000)).not.toBe(field1); |
||||
expect(copy.get(100000).getDecodedValue()).toEqual(2); |
||||
}); |
||||
}); |
@ -0,0 +1,116 @@ |
||||
/** |
||||
* @fileoverview A UTF8 decoder. |
||||
*/ |
||||
goog.module('protobuf.binary.textencoding'); |
||||
|
||||
const {checkElementIndex} = goog.require('protobuf.internal.checks'); |
||||
|
||||
/** |
||||
* Combines an array of codePoints into a string. |
||||
* @param {!Array<number>} codePoints |
||||
* @return {string} |
||||
*/ |
||||
function codePointsToString(codePoints) { |
||||
// Performance: http://jsperf.com/string-fromcharcode-test/13
|
||||
let s = '', i = 0; |
||||
const length = codePoints.length; |
||||
const BATCH_SIZE = 10000; |
||||
while (i < length) { |
||||
const end = Math.min(i + BATCH_SIZE, length); |
||||
s += String.fromCharCode.apply(null, codePoints.slice(i, end)); |
||||
i = end; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
/** |
||||
* Decodes raw bytes into a string. |
||||
* Supports codepoints from U+0000 up to U+10FFFF. |
||||
* (http://en.wikipedia.org/wiki/UTF-8).
|
||||
* @param {!DataView} bytes |
||||
* @return {string} |
||||
*/ |
||||
function decode(bytes) { |
||||
let cursor = 0; |
||||
const codePoints = []; |
||||
|
||||
while (cursor < bytes.byteLength) { |
||||
const c = bytes.getUint8(cursor++); |
||||
if (c < 0x80) { // Regular 7-bit ASCII.
|
||||
codePoints.push(c); |
||||
} else if (c < 0xC0) { |
||||
// UTF-8 continuation mark. We are out of sync. This
|
||||
// might happen if we attempted to read a character
|
||||
// with more than four bytes.
|
||||
continue; |
||||
} else if (c < 0xE0) { // UTF-8 with two bytes.
|
||||
checkElementIndex(cursor, bytes.byteLength); |
||||
const c2 = bytes.getUint8(cursor++); |
||||
codePoints.push(((c & 0x1F) << 6) | (c2 & 0x3F)); |
||||
} else if (c < 0xF0) { // UTF-8 with three bytes.
|
||||
checkElementIndex(cursor + 1, bytes.byteLength); |
||||
const c2 = bytes.getUint8(cursor++); |
||||
const c3 = bytes.getUint8(cursor++); |
||||
codePoints.push(((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); |
||||
} else if (c < 0xF8) { // UTF-8 with 4 bytes.
|
||||
checkElementIndex(cursor + 2, bytes.byteLength); |
||||
const c2 = bytes.getUint8(cursor++); |
||||
const c3 = bytes.getUint8(cursor++); |
||||
const c4 = bytes.getUint8(cursor++); |
||||
// Characters written on 4 bytes have 21 bits for a codepoint.
|
||||
// We can't fit that on 16bit characters, so we use surrogates.
|
||||
let codepoint = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | |
||||
((c3 & 0x3F) << 6) | (c4 & 0x3F); |
||||
// Surrogates formula from wikipedia.
|
||||
// 1. Subtract 0x10000 from codepoint
|
||||
codepoint -= 0x10000; |
||||
// 2. Split this into the high 10-bit value and the low 10-bit value
|
||||
// 3. Add 0xD800 to the high value to form the high surrogate
|
||||
// 4. Add 0xDC00 to the low value to form the low surrogate:
|
||||
const low = (codepoint & 0x3FF) + 0xDC00; |
||||
const high = ((codepoint >> 10) & 0x3FF) + 0xD800; |
||||
codePoints.push(high, low); |
||||
} |
||||
} |
||||
return codePointsToString(codePoints); |
||||
} |
||||
|
||||
/** |
||||
* Writes a UTF16 JavaScript string to the buffer encoded as UTF8. |
||||
* @param {string} value The string to write. |
||||
* @return {!Uint8Array} An array containing the encoded bytes. |
||||
*/ |
||||
function encode(value) { |
||||
const buffer = []; |
||||
|
||||
for (let i = 0; i < value.length; i++) { |
||||
const c1 = value.charCodeAt(i); |
||||
|
||||
if (c1 < 0x80) { |
||||
buffer.push(c1); |
||||
} else if (c1 < 0x800) { |
||||
buffer.push((c1 >> 6) | 0xC0); |
||||
buffer.push((c1 & 0x3F) | 0x80); |
||||
} else if (c1 < 0xD800 || c1 >= 0xE000) { |
||||
buffer.push((c1 >> 12) | 0xE0); |
||||
buffer.push(((c1 >> 6) & 0x3F) | 0x80); |
||||
buffer.push((c1 & 0x3F) | 0x80); |
||||
} else { |
||||
// surrogate pair
|
||||
i++; |
||||
checkElementIndex(i, value.length); |
||||
const c2 = value.charCodeAt(i); |
||||
const paired = 0x10000 + (((c1 & 0x3FF) << 10) | (c2 & 0x3FF)); |
||||
buffer.push((paired >> 18) | 0xF0); |
||||
buffer.push(((paired >> 12) & 0x3F) | 0x80); |
||||
buffer.push(((paired >> 6) & 0x3F) | 0x80); |
||||
buffer.push((paired & 0x3F) | 0x80); |
||||
} |
||||
} |
||||
return new Uint8Array(buffer); |
||||
} |
||||
|
||||
exports = { |
||||
decode, |
||||
encode, |
||||
}; |
@ -0,0 +1,113 @@ |
||||
/** |
||||
* @fileoverview Tests for textdecoder.js. |
||||
*/ |
||||
goog.module('protobuf.binary.TextDecoderTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
const {decode, encode} = goog.require('protobuf.binary.textencoding'); |
||||
|
||||
describe('Decode does', () => { |
||||
it('return empty string for empty array', () => { |
||||
expect(decode(new DataView(new ArrayBuffer(0)))).toEqual(''); |
||||
}); |
||||
|
||||
it('throw on null being passed', () => { |
||||
expect(() => decode(/** @type {!DataView} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
describe('Encode does', () => { |
||||
it('return empty array for empty string', () => { |
||||
expect(encode('')).toEqual(new Uint8Array(0)); |
||||
}); |
||||
|
||||
it('throw on null being passed', () => { |
||||
expect(() => encode(/** @type {string} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
/** @const {!TextEncoder} */ |
||||
const textEncoder = new TextEncoder('utf-8'); |
||||
|
||||
/** |
||||
* A Pair of string and Uint8Array representing the same data. |
||||
* Each pair has the string value and its utf-8 bytes. |
||||
*/ |
||||
class Pair { |
||||
/** |
||||
* Constructs a pair from a given string. |
||||
* @param {string} s |
||||
* @return {!Pair} |
||||
*/ |
||||
static fromString(s) { |
||||
return new Pair(s, textEncoder.encode(s).buffer); |
||||
} |
||||
/** |
||||
* Constructs a pair from a given charCode. |
||||
* @param {number} charCode |
||||
* @return {!Pair} |
||||
*/ |
||||
static fromCharCode(charCode) { |
||||
return Pair.fromString(String.fromCharCode(charCode)); |
||||
} |
||||
|
||||
/** |
||||
* @param {string} stringValue |
||||
* @param {!ArrayBuffer} bytes |
||||
* @private |
||||
*/ |
||||
constructor(stringValue, bytes) { |
||||
/** @const @private {string} */ |
||||
this.stringValue_ = stringValue; |
||||
/** @const @private {!ArrayBuffer} */ |
||||
this.bytes_ = bytes; |
||||
} |
||||
|
||||
/** Ensures that a given pair encodes and decodes round trip*/ |
||||
expectPairToMatch() { |
||||
expect(decode(new DataView(this.bytes_))).toEqual(this.stringValue_); |
||||
expect(encode(this.stringValue_)).toEqual(new Uint8Array(this.bytes_)); |
||||
} |
||||
} |
||||
|
||||
describe('textencoding does', () => { |
||||
it('works for empty string', () => { |
||||
Pair.fromString('').expectPairToMatch(); |
||||
}); |
||||
|
||||
it('decode and encode random strings', () => { |
||||
// 1 byte strings
|
||||
Pair.fromString('hello').expectPairToMatch(); |
||||
Pair.fromString('HELLO1!'); |
||||
|
||||
// 2 byte String
|
||||
Pair.fromString('©').expectPairToMatch(); |
||||
|
||||
// 3 byte string
|
||||
Pair.fromString('❄').expectPairToMatch(); |
||||
|
||||
// 4 byte string
|
||||
Pair.fromString('😁').expectPairToMatch(); |
||||
}); |
||||
|
||||
it('decode and encode 1 byte strings', () => { |
||||
for (let i = 0; i < 0x80; i++) { |
||||
Pair.fromCharCode(i).expectPairToMatch(); |
||||
} |
||||
}); |
||||
|
||||
it('decode and encode 2 byte strings', () => { |
||||
for (let i = 0xC0; i < 0x7FF; i++) { |
||||
Pair.fromCharCode(i).expectPairToMatch(); |
||||
} |
||||
}); |
||||
|
||||
it('decode and encode 3 byte strings', () => { |
||||
for (let i = 0x7FF; i < 0x8FFF; i++) { |
||||
Pair.fromCharCode(i).expectPairToMatch(); |
||||
} |
||||
}); |
||||
}); |
@ -0,0 +1,116 @@ |
||||
/** |
||||
* @fileoverview Helper methods for typed arrays. |
||||
*/ |
||||
goog.module('protobuf.binary.typedArrays'); |
||||
|
||||
const {assert} = goog.require('goog.asserts'); |
||||
|
||||
/** |
||||
* @param {!ArrayBuffer} buffer1 |
||||
* @param {!ArrayBuffer} buffer2 |
||||
* @return {boolean} |
||||
*/ |
||||
function arrayBufferEqual(buffer1, buffer2) { |
||||
if (!buffer1 || !buffer2) { |
||||
throw new Error('Buffer shouldn\'t be empty'); |
||||
} |
||||
|
||||
const array1 = new Uint8Array(buffer1); |
||||
const array2 = new Uint8Array(buffer2); |
||||
|
||||
return uint8ArrayEqual(array1, array2); |
||||
} |
||||
|
||||
/** |
||||
* @param {!Uint8Array} array1 |
||||
* @param {!Uint8Array} array2 |
||||
* @return {boolean} |
||||
*/ |
||||
function uint8ArrayEqual(array1, array2) { |
||||
if (array1 === array2) { |
||||
return true; |
||||
} |
||||
|
||||
if (array1.byteLength !== array2.byteLength) { |
||||
return false; |
||||
} |
||||
|
||||
for (let i = 0; i < array1.byteLength; i++) { |
||||
if (array1[i] !== array2[i]) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* ArrayBuffer.prototype.slice, but fallback to manual copy if missing. |
||||
* @param {!ArrayBuffer} buffer |
||||
* @param {number} start |
||||
* @param {number=} end |
||||
* @return {!ArrayBuffer} New array buffer with given the contents of `buffer`. |
||||
*/ |
||||
function arrayBufferSlice(buffer, start, end = undefined) { |
||||
// The fallback isn't fully compatible with ArrayBuffer.slice, enforce
|
||||
// strict requirements on start/end.
|
||||
// Spec:
|
||||
// https://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer.prototype.slice
|
||||
assert(start >= 0); |
||||
assert(end === undefined || (end >= 0 && end <= buffer.byteLength)); |
||||
assert((end === undefined ? buffer.byteLength : end) >= start); |
||||
if (buffer.slice) { |
||||
const slicedBuffer = buffer.slice(start, end); |
||||
// The ArrayBuffer.prototype.slice function was flawed before iOS 12.2. This
|
||||
// causes 0-length results when passing undefined or a number greater
|
||||
// than 32 bits for the optional end value. In these cases, we fall back to
|
||||
// using our own slice implementation.
|
||||
// More details: https://bugs.webkit.org/show_bug.cgi?id=185127
|
||||
if (slicedBuffer.byteLength !== 0 || (start === end)) { |
||||
return slicedBuffer; |
||||
} |
||||
} |
||||
const realEnd = end == null ? buffer.byteLength : end; |
||||
const length = realEnd - start; |
||||
assert(length >= 0); |
||||
const view = new Uint8Array(buffer, start, length); |
||||
// A TypedArray constructed from another Typed array copies the data.
|
||||
const clone = new Uint8Array(view); |
||||
|
||||
return clone.buffer; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new Uint8Array with the size and contents of the given |
||||
* ArrayBufferView. ArrayBufferView is an interface implemented by DataView, |
||||
* Uint8Array and all typed arrays. |
||||
* @param {!ArrayBufferView} view |
||||
* @return {!Uint8Array} |
||||
*/ |
||||
function cloneArrayBufferView(view) { |
||||
return new Uint8Array(arrayBufferSlice( |
||||
view.buffer, view.byteOffset, view.byteOffset + view.byteLength)); |
||||
} |
||||
|
||||
/** |
||||
* Returns a 32 bit number for the corresponding Uint8Array. |
||||
* @param {!Uint8Array} array |
||||
* @return {number} |
||||
*/ |
||||
function hashUint8Array(array) { |
||||
const prime = 31; |
||||
let result = 17; |
||||
|
||||
for (let i = 0; i < array.length; i++) { |
||||
// '| 0' ensures signed 32 bits
|
||||
result = (result * prime + array[i]) | 0; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
exports = { |
||||
arrayBufferEqual, |
||||
uint8ArrayEqual, |
||||
arrayBufferSlice, |
||||
cloneArrayBufferView, |
||||
hashUint8Array, |
||||
}; |
@ -0,0 +1,191 @@ |
||||
/** |
||||
* @fileoverview Tests for typed_arrays.js. |
||||
*/ |
||||
goog.module('protobuf.binary.typedArraysTest'); |
||||
|
||||
const {arrayBufferEqual, arrayBufferSlice, cloneArrayBufferView, hashUint8Array, uint8ArrayEqual} = goog.require('protobuf.binary.typedArrays'); |
||||
|
||||
describe('arrayBufferEqual', () => { |
||||
it('returns true for empty buffers', () => { |
||||
const buffer1 = new ArrayBuffer(0); |
||||
const buffer2 = new ArrayBuffer(0); |
||||
expect(arrayBufferEqual(buffer1, buffer2)).toBe(true); |
||||
}); |
||||
|
||||
it('throws for first null buffers', () => { |
||||
const buffer = new ArrayBuffer(0); |
||||
expect( |
||||
() => arrayBufferEqual( |
||||
/** @type {!ArrayBuffer} */ (/** @type {*} */ (null)), buffer)) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('throws for second null buffers', () => { |
||||
const buffer = new ArrayBuffer(0); |
||||
expect( |
||||
() => arrayBufferEqual( |
||||
buffer, /** @type {!ArrayBuffer} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('returns true for arrays with same values', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
const array2 = new Uint8Array(4); |
||||
array2[0] = 1; |
||||
expect(arrayBufferEqual(array1.buffer, array2.buffer)).toBe(true); |
||||
}); |
||||
|
||||
it('returns false for arrays with different values', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
const array2 = new Uint8Array(4); |
||||
array2[0] = 2; |
||||
expect(arrayBufferEqual(array1.buffer, array2.buffer)).toBe(false); |
||||
}); |
||||
|
||||
it('returns true same instance', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
expect(arrayBufferEqual(array1.buffer, array1.buffer)).toBe(true); |
||||
}); |
||||
}); |
||||
|
||||
describe('uint8ArrayEqual', () => { |
||||
it('returns true for empty arrays', () => { |
||||
const array1 = new Uint8Array(0); |
||||
const array2 = new Uint8Array(0); |
||||
expect(uint8ArrayEqual(array1, array2)).toBe(true); |
||||
}); |
||||
|
||||
it('throws for first Uint8Array array', () => { |
||||
const array = new Uint8Array(0); |
||||
expect( |
||||
() => uint8ArrayEqual( |
||||
/** @type {!Uint8Array} */ (/** @type {*} */ (null)), array)) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('throws for second null array', () => { |
||||
const array = new Uint8Array(0); |
||||
expect( |
||||
() => uint8ArrayEqual( |
||||
array, /** @type {!Uint8Array} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
|
||||
it('returns true for arrays with same values', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3]).buffer; |
||||
const buffer2 = new Uint8Array([1, 2, 3, 4]).buffer; |
||||
const array1 = new Uint8Array(buffer1, 1, 3); |
||||
const array2 = new Uint8Array(buffer2, 0, 3); |
||||
expect(uint8ArrayEqual(array1, array2)).toBe(true); |
||||
}); |
||||
|
||||
it('returns false for arrays with different values', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
const array2 = new Uint8Array(4); |
||||
array2[0] = 2; |
||||
expect(uint8ArrayEqual(array1, array2)).toBe(false); |
||||
}); |
||||
|
||||
it('returns true same instance', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
expect(uint8ArrayEqual(array1, array1)).toBe(true); |
||||
}); |
||||
}); |
||||
|
||||
describe('arrayBufferSlice', () => { |
||||
it('Returns a new instance.', () => { |
||||
const buffer1 = new ArrayBuffer(0); |
||||
const buffer2 = arrayBufferSlice(buffer1, 0); |
||||
expect(buffer2).not.toBe(buffer1); |
||||
expect(arrayBufferEqual(buffer1, buffer2)).toBe(true); |
||||
}); |
||||
|
||||
it('Copies data with positive start/end.', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).buffer; |
||||
expect(buffer1.byteLength).toEqual(10); |
||||
|
||||
const buffer2 = arrayBufferSlice(buffer1, 2, 6); |
||||
expect(buffer2.byteLength).toEqual(4); |
||||
expect(buffer2).not.toBe(buffer1); |
||||
const expected = new Uint8Array([2, 3, 4, 5]).buffer; |
||||
expect(arrayBufferEqual(expected, buffer2)).toBe(true); |
||||
}); |
||||
|
||||
it('Copies all data without end.', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).buffer; |
||||
expect(buffer1.byteLength).toEqual(10); |
||||
|
||||
const buffer2 = arrayBufferSlice(buffer1, 0); |
||||
expect(buffer2.byteLength).toEqual(10); |
||||
expect(arrayBufferEqual(buffer1, buffer2)).toBe(true); |
||||
}); |
||||
|
||||
if (goog.DEBUG) { |
||||
it('Fails with negative end.', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).buffer; |
||||
expect(() => void arrayBufferSlice(buffer1, 2, -1)).toThrow(); |
||||
}); |
||||
|
||||
it('Fails with negative start.', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).buffer; |
||||
expect(() => void arrayBufferSlice(buffer1, 2, -1)).toThrow(); |
||||
}); |
||||
|
||||
it('Fails when start > end.', () => { |
||||
const buffer1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).buffer; |
||||
expect(() => void arrayBufferSlice(buffer1, 2, 1)).toThrow(); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('cloneArrayBufferView', () => { |
||||
it('Returns a new instance.', () => { |
||||
const array1 = new Uint8Array(0); |
||||
const array2 = cloneArrayBufferView(new Uint8Array(array1)); |
||||
expect(array2).not.toBe(array1); |
||||
expect(uint8ArrayEqual(array1, array2)).toBe(true); |
||||
}); |
||||
|
||||
it('Returns an array of the exact size.', () => { |
||||
const array1 = |
||||
new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).subarray(2, 5); |
||||
expect(array1.length).toEqual(3); |
||||
expect(array1.buffer.byteLength).toEqual(10); |
||||
const array2 = cloneArrayBufferView(array1); |
||||
expect(array2.byteLength).toEqual(3); |
||||
expect(array2).toEqual(new Uint8Array([2, 3, 4])); |
||||
}); |
||||
}); |
||||
|
||||
describe('hashUint8Array', () => { |
||||
it('returns same hashcode for empty Uint8Arrays', () => { |
||||
const array1 = new Uint8Array(0); |
||||
const array2 = new Uint8Array(0); |
||||
expect(hashUint8Array(array1)).toBe(hashUint8Array(array2)); |
||||
}); |
||||
|
||||
it('returns same hashcode for Uint8Arrays with same values', () => { |
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
const array2 = new Uint8Array(4); |
||||
array2[0] = 1; |
||||
expect(hashUint8Array(array1)).toBe(hashUint8Array(array2)); |
||||
}); |
||||
|
||||
it('returns different hashcode for Uint8Arrays with different values', () => { |
||||
// This test might fail in the future if the hashing algorithm is updated
|
||||
// and we end up with a collision here.
|
||||
// We still need this test to make sure that we are not just returning
|
||||
// the same number for all buffers.
|
||||
const array1 = new Uint8Array(4); |
||||
array1[0] = 1; |
||||
const array2 = new Uint8Array(4); |
||||
array2[0] = 2; |
||||
expect(hashUint8Array(array1)).not.toBe(hashUint8Array(array2)); |
||||
}); |
||||
}); |
@ -0,0 +1,61 @@ |
||||
/** |
||||
* @fileoverview Test data for int32 encoding and decoding. |
||||
*/ |
||||
goog.module('protobuf.binary.uint32TestPairs'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); |
||||
|
||||
/** |
||||
* An array of Pairs of float values and their bit representation. |
||||
* This is used to test encoding and decoding from/to the protobuf wire format. |
||||
* @return {!Array<{name: string, intValue:number, bufferDecoder: |
||||
* !BufferDecoder, error: ?boolean, skip_writer: ?boolean}>} |
||||
*/ |
||||
function getUint32Pairs() { |
||||
const uint32Pairs = [ |
||||
{ |
||||
name: 'zero', |
||||
intValue: 0, |
||||
bufferDecoder: createBufferDecoder(0x00), |
||||
}, |
||||
{ |
||||
name: 'one ', |
||||
intValue: 1, |
||||
bufferDecoder: createBufferDecoder(0x01), |
||||
}, |
||||
{ |
||||
name: 'max signed int 2^31 - 1', |
||||
intValue: Math.pow(2, 31) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x07), |
||||
}, |
||||
{ |
||||
name: 'max unsigned int 2^32 - 1', |
||||
intValue: Math.pow(2, 32) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x0F), |
||||
}, |
||||
{ |
||||
name: 'truncates more than 32 bits', |
||||
intValue: Math.pow(2, 32) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'truncates more than 32 bits (bit 33 set)', |
||||
intValue: Math.pow(2, 28) - 1, |
||||
bufferDecoder: createBufferDecoder(0xFF, 0xFF, 0xFF, 0xFF, 0x10), |
||||
skip_writer: true, |
||||
}, |
||||
{ |
||||
name: 'errors out for 11 bytes', |
||||
intValue: Math.pow(2, 32) - 1, |
||||
bufferDecoder: createBufferDecoder( |
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), |
||||
error: true, |
||||
skip_writer: true, |
||||
}, |
||||
]; |
||||
return [...uint32Pairs]; |
||||
} |
||||
|
||||
exports = {getUint32Pairs}; |
@ -0,0 +1,28 @@ |
||||
/** |
||||
* @fileoverview Helper methods for Uint8Arrays. |
||||
*/ |
||||
goog.module('protobuf.binary.uint8arrays'); |
||||
|
||||
/** |
||||
* Combines multiple bytes arrays (either Uint8Array or number array whose |
||||
* values are bytes) into a single Uint8Array. |
||||
* @param {!Array<!Uint8Array>|!Array<!Array<number>>} arrays |
||||
* @return {!Uint8Array} |
||||
*/ |
||||
function concatenateByteArrays(arrays) { |
||||
let totalLength = 0; |
||||
for (const array of arrays) { |
||||
totalLength += array.length; |
||||
} |
||||
const result = new Uint8Array(totalLength); |
||||
let offset = 0; |
||||
for (const array of arrays) { |
||||
result.set(array, offset); |
||||
offset += array.length; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
exports = { |
||||
concatenateByteArrays, |
||||
}; |
@ -0,0 +1,47 @@ |
||||
/** |
||||
* @fileoverview Tests for uint8arrays.js. |
||||
*/ |
||||
goog.module('protobuf.binary.Uint8ArraysTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays'); |
||||
|
||||
describe('concatenateByteArrays does', () => { |
||||
it('concatenate empty array', () => { |
||||
const byteArrays = []; |
||||
expect(concatenateByteArrays(byteArrays)).toEqual(new Uint8Array(0)); |
||||
}); |
||||
|
||||
it('concatenate Uint8Arrays', () => { |
||||
const byteArrays = [new Uint8Array([0x01]), new Uint8Array([0x02])]; |
||||
expect(concatenateByteArrays(byteArrays)).toEqual(new Uint8Array([ |
||||
0x01, 0x02 |
||||
])); |
||||
}); |
||||
|
||||
it('concatenate array of bytes', () => { |
||||
const byteArrays = [[0x01], [0x02]]; |
||||
expect(concatenateByteArrays(byteArrays)).toEqual(new Uint8Array([ |
||||
0x01, 0x02 |
||||
])); |
||||
}); |
||||
|
||||
it('concatenate array of non-bytes', () => { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
const byteArrays = [[40.0], [256]]; |
||||
expect(concatenateByteArrays(byteArrays)).toEqual(new Uint8Array([ |
||||
0x28, 0x00 |
||||
])); |
||||
}); |
||||
|
||||
it('throw for null array', () => { |
||||
expect( |
||||
() => concatenateByteArrays( |
||||
/** @type {!Array<!Uint8Array>} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
}); |
@ -0,0 +1,17 @@ |
||||
goog.module('protobuf.binary.WireType'); |
||||
|
||||
/** |
||||
* Wire-format type codes, taken from proto2/public/wire_format_lite.h. |
||||
* @enum {number} |
||||
*/ |
||||
const WireType = { |
||||
VARINT: 0, |
||||
FIXED64: 1, |
||||
DELIMITED: 2, |
||||
START_GROUP: 3, |
||||
END_GROUP: 4, |
||||
FIXED32: 5, |
||||
INVALID: 6 |
||||
}; |
||||
|
||||
exports = WireType; |
@ -0,0 +1,772 @@ |
||||
/** |
||||
* @fileoverview Implements Writer for writing data as the binary wire format |
||||
* bytes array. |
||||
*/ |
||||
goog.module('protobuf.binary.Writer'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const Int64 = goog.require('protobuf.Int64'); |
||||
const WireType = goog.require('protobuf.binary.WireType'); |
||||
const {POLYFILL_TEXT_ENCODING, checkFieldNumber, checkTypeUnsignedInt32, checkWireType} = goog.require('protobuf.internal.checks'); |
||||
const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays'); |
||||
const {encode} = goog.require('protobuf.binary.textencoding'); |
||||
|
||||
/** |
||||
* Returns a valid utf-8 encoder function based on TextEncoder if available or |
||||
* a polyfill. |
||||
* Some of the environments we run in do not have TextEncoder defined. |
||||
* TextEncoder is faster than our polyfill so we prefer it over the polyfill. |
||||
* @return {function(string):!Uint8Array} |
||||
*/ |
||||
function getEncoderFunction() { |
||||
if (goog.global['TextEncoder']) { |
||||
const textEncoder = new goog.global['TextEncoder']('utf-8'); |
||||
return s => s.length === 0 ? new Uint8Array(0) : textEncoder.encode(s); |
||||
} |
||||
if (POLYFILL_TEXT_ENCODING) { |
||||
return encode; |
||||
} else { |
||||
throw new Error( |
||||
'TextEncoder is missing. ' + |
||||
'Enable protobuf.defines.POLYFILL_TEXT_ENCODING'); |
||||
} |
||||
} |
||||
|
||||
/** @const {function(string): !Uint8Array} */ |
||||
const encoderFunction = getEncoderFunction(); |
||||
|
||||
/** |
||||
* Writer provides methods for encoding all protobuf supported type into a |
||||
* binary format bytes array. |
||||
* Check https://developers.google.com/protocol-buffers/docs/encoding for binary
|
||||
* format definition. |
||||
* @final |
||||
* @package |
||||
*/ |
||||
class Writer { |
||||
constructor() { |
||||
/** |
||||
* Blocks of data that needs to be serialized. After writing all the data, |
||||
* the blocks are concatenated into a single Uint8Array. |
||||
* @private {!Array<!Uint8Array>} |
||||
*/ |
||||
this.blocks_ = []; |
||||
|
||||
/** |
||||
* A buffer for writing varint data (tag number + field number for each |
||||
* field, int32, uint32 etc.). Before writing a non-varint data block |
||||
* (string, fixed32 etc.), the buffer is appended to the block array as a |
||||
* new block, and a new buffer is started. |
||||
* |
||||
* We could've written each varint as a new block instead of writing |
||||
* multiple varints in this buffer. But this will increase the number of |
||||
* blocks, and concatenating many small blocks is slower than concatenating |
||||
* few large blocks. |
||||
* |
||||
* TODO: Experiment with writing data in a fixed-length |
||||
* Uint8Array instead of using a growing buffer. |
||||
* |
||||
* @private {!Array<number>} |
||||
*/ |
||||
this.currentBuffer_ = []; |
||||
} |
||||
|
||||
/** |
||||
* Converts the encoded data into a Uint8Array. |
||||
* The writer is also reset. |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
getAndResetResultBuffer() { |
||||
this.closeAndStartNewBuffer_(); |
||||
const result = concatenateByteArrays(this.blocks_); |
||||
this.blocks_ = []; |
||||
return result.buffer; |
||||
} |
||||
|
||||
/** |
||||
* Encodes a (field number, wire type) tuple into a wire-format field header. |
||||
* @param {number} fieldNumber |
||||
* @param {!WireType} wireType |
||||
*/ |
||||
writeTag(fieldNumber, wireType) { |
||||
checkFieldNumber(fieldNumber); |
||||
checkWireType(wireType); |
||||
const tag = fieldNumber << 3 | wireType; |
||||
this.writeUnsignedVarint32_(tag); |
||||
} |
||||
|
||||
/** |
||||
* Appends the current buffer into the blocks array and starts a new buffer. |
||||
* @private |
||||
*/ |
||||
closeAndStartNewBuffer_() { |
||||
this.blocks_.push(new Uint8Array(this.currentBuffer_)); |
||||
this.currentBuffer_ = []; |
||||
} |
||||
|
||||
/** |
||||
* Encodes a 32-bit integer into its wire-format varint representation and |
||||
* stores it in the buffer. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeUnsignedVarint32_(value) { |
||||
checkTypeUnsignedInt32(value); |
||||
while (value > 0x7f) { |
||||
this.currentBuffer_.push((value & 0x7f) | 0x80); |
||||
value = value >>> 7; |
||||
} |
||||
this.currentBuffer_.push(value); |
||||
} |
||||
|
||||
/**************************************************************************** |
||||
* OPTIONAL METHODS |
||||
****************************************************************************/ |
||||
|
||||
/** |
||||
* Writes a boolean value field to the buffer as a varint. |
||||
* @param {boolean} value |
||||
* @private |
||||
*/ |
||||
writeBoolValue_(value) { |
||||
this.currentBuffer_.push(value ? 1 : 0); |
||||
} |
||||
|
||||
/** |
||||
* Writes a boolean value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {boolean} value |
||||
*/ |
||||
writeBool(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeBoolValue_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a bytes value field to the buffer as a length delimited field. |
||||
* @param {number} fieldNumber |
||||
* @param {!ByteString} value |
||||
*/ |
||||
writeBytes(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.DELIMITED); |
||||
const buffer = value.toArrayBuffer(); |
||||
this.writeUnsignedVarint32_(buffer.byteLength); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a double value field to the buffer without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeDoubleValue_(value) { |
||||
const buffer = new ArrayBuffer(8); |
||||
const view = new DataView(buffer); |
||||
view.setFloat64(0, value, true); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a double value field to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeDouble(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.FIXED64); |
||||
this.writeDoubleValue_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a fixed32 value field to the buffer without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeFixed32Value_(value) { |
||||
const buffer = new ArrayBuffer(4); |
||||
const view = new DataView(buffer); |
||||
view.setUint32(0, value, true); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a fixed32 value field to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeFixed32(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.FIXED32); |
||||
this.writeFixed32Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a float value field to the buffer without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeFloatValue_(value) { |
||||
const buffer = new ArrayBuffer(4); |
||||
const view = new DataView(buffer); |
||||
view.setFloat32(0, value, true); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a float value field to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeFloat(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.FIXED32); |
||||
this.writeFloatValue_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a int32 value field to the buffer as a varint without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeInt32Value_(value) { |
||||
if (value >= 0) { |
||||
this.writeVarint64_(0, value); |
||||
} else { |
||||
this.writeVarint64_(0xFFFFFFFF, value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Writes a int32 value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeInt32(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeInt32Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a int64 value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Int64} value |
||||
*/ |
||||
writeInt64(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeVarint64_(value.getHighBits(), value.getLowBits()); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sfixed32 value field to the buffer. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeSfixed32Value_(value) { |
||||
const buffer = new ArrayBuffer(4); |
||||
const view = new DataView(buffer); |
||||
view.setInt32(0, value, true); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sfixed32 value field to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeSfixed32(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.FIXED32); |
||||
this.writeSfixed32Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sfixed64 value field to the buffer without tag. |
||||
* @param {!Int64} value |
||||
* @private |
||||
*/ |
||||
writeSfixed64Value_(value) { |
||||
const buffer = new ArrayBuffer(8); |
||||
const view = new DataView(buffer); |
||||
view.setInt32(0, value.getLowBits(), true); |
||||
view.setInt32(4, value.getHighBits(), true); |
||||
this.writeRaw_(buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sfixed64 value field to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {!Int64} value |
||||
*/ |
||||
writeSfixed64(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.FIXED64); |
||||
this.writeSfixed64Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a uint32 value field to the buffer as a varint without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeUint32Value_(value) { |
||||
this.writeVarint64_(0, value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a uint32 value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeUint32(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeUint32Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes the bits of a 64 bit number to the buffer as a varint. |
||||
* @param {number} highBits |
||||
* @param {number} lowBits |
||||
* @private |
||||
*/ |
||||
writeVarint64_(highBits, lowBits) { |
||||
for (let i = 0; i < 28; i = i + 7) { |
||||
const shift = lowBits >>> i; |
||||
const hasNext = !((shift >>> 7) === 0 && highBits === 0); |
||||
const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; |
||||
this.currentBuffer_.push(byte); |
||||
if (!hasNext) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
const splitBits = ((lowBits >>> 28) & 0x0F) | ((highBits & 0x07) << 4); |
||||
const hasMoreBits = !((highBits >> 3) === 0); |
||||
this.currentBuffer_.push( |
||||
(hasMoreBits ? splitBits | 0x80 : splitBits) & 0xFF); |
||||
|
||||
if (!hasMoreBits) { |
||||
return; |
||||
} |
||||
|
||||
for (let i = 3; i < 31; i = i + 7) { |
||||
const shift = highBits >>> i; |
||||
const hasNext = !((shift >>> 7) === 0); |
||||
const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; |
||||
this.currentBuffer_.push(byte); |
||||
if (!hasNext) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
this.currentBuffer_.push((highBits >>> 31) & 0x01); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sint32 value field to the buffer as a varint without tag. |
||||
* @param {number} value |
||||
* @private |
||||
*/ |
||||
writeSint32Value_(value) { |
||||
value = (value << 1) ^ (value >> 31); |
||||
this.writeVarint64_(0, value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sint32 value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {number} value |
||||
*/ |
||||
writeSint32(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeSint32Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sint64 value field to the buffer as a varint without tag. |
||||
* @param {!Int64} value |
||||
* @private |
||||
*/ |
||||
writeSint64Value_(value) { |
||||
const highBits = value.getHighBits(); |
||||
const lowBits = value.getLowBits(); |
||||
|
||||
const sign = highBits >> 31; |
||||
const encodedLowBits = (lowBits << 1) ^ sign; |
||||
const encodedHighBits = ((highBits << 1) | (lowBits >>> 31)) ^ sign; |
||||
this.writeVarint64_(encodedHighBits, encodedLowBits); |
||||
} |
||||
|
||||
/** |
||||
* Writes a sint64 value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Int64} value |
||||
*/ |
||||
writeSint64(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.VARINT); |
||||
this.writeSint64Value_(value); |
||||
} |
||||
|
||||
/** |
||||
* Writes a string value field to the buffer as a varint. |
||||
* @param {number} fieldNumber |
||||
* @param {string} value |
||||
*/ |
||||
writeString(fieldNumber, value) { |
||||
this.writeTag(fieldNumber, WireType.DELIMITED); |
||||
const array = encoderFunction(value); |
||||
this.writeUnsignedVarint32_(array.length); |
||||
this.writeRaw_(array.buffer); |
||||
} |
||||
|
||||
/** |
||||
* Writes raw bytes to the buffer. |
||||
* @param {!ArrayBuffer} arrayBuffer |
||||
* @private |
||||
*/ |
||||
writeRaw_(arrayBuffer) { |
||||
this.closeAndStartNewBuffer_(); |
||||
this.blocks_.push(new Uint8Array(arrayBuffer)); |
||||
} |
||||
|
||||
/** |
||||
* Writes raw bytes to the buffer. |
||||
* @param {!BufferDecoder} bufferDecoder |
||||
* @param {number} start |
||||
* @param {!WireType} wireType |
||||
* @package |
||||
*/ |
||||
writeBufferDecoder(bufferDecoder, start, wireType) { |
||||
this.closeAndStartNewBuffer_(); |
||||
const dataLength = this.getLength_(bufferDecoder, start, wireType); |
||||
this.blocks_.push( |
||||
bufferDecoder.subBufferDecoder(start, dataLength).asUint8Array()); |
||||
} |
||||
|
||||
/** |
||||
* Returns the length of the data to serialize. Returns -1 when a STOP GROUP |
||||
* is found. |
||||
* @param {!BufferDecoder} bufferDecoder |
||||
* @param {number} start |
||||
* @param {!WireType} wireType |
||||
* @return {number} |
||||
* @private |
||||
*/ |
||||
getLength_(bufferDecoder, start, wireType) { |
||||
switch (wireType) { |
||||
case WireType.VARINT: |
||||
return bufferDecoder.skipVarint(start) - start; |
||||
case WireType.FIXED64: |
||||
return 8; |
||||
case WireType.DELIMITED: |
||||
const {lowBits: dataLength, dataStart} = bufferDecoder.getVarint(start); |
||||
return dataLength + dataStart - start; |
||||
case WireType.START_GROUP: |
||||
return this.getGroupLength_(bufferDecoder, start); |
||||
case WireType.FIXED32: |
||||
return 4; |
||||
default: |
||||
throw new Error(`Invalid wire type: ${wireType}`); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Skips over fields until it finds the end of a given group. |
||||
* @param {!BufferDecoder} bufferDecoder |
||||
* @param {number} start |
||||
* @return {number} |
||||
* @private |
||||
*/ |
||||
getGroupLength_(bufferDecoder, start) { |
||||
// On a start group we need to keep skipping fields until we find a
|
||||
// corresponding stop group
|
||||
let cursor = start; |
||||
while (cursor < bufferDecoder.endIndex()) { |
||||
const {lowBits: tag, dataStart} = bufferDecoder.getVarint(cursor); |
||||
const wireType = /** @type {!WireType} */ (tag & 0x07); |
||||
if (wireType === WireType.END_GROUP) { |
||||
return dataStart - start; |
||||
} |
||||
cursor = dataStart + this.getLength_(bufferDecoder, dataStart, wireType); |
||||
} |
||||
throw new Error('No end group found'); |
||||
} |
||||
|
||||
/** |
||||
* Write the whole bytes as a length delimited field. |
||||
* @param {number} fieldNumber |
||||
* @param {!ArrayBuffer} arrayBuffer |
||||
*/ |
||||
writeDelimited(fieldNumber, arrayBuffer) { |
||||
this.writeTag(fieldNumber, WireType.DELIMITED); |
||||
this.writeUnsignedVarint32_(arrayBuffer.byteLength); |
||||
this.writeRaw_(arrayBuffer); |
||||
} |
||||
|
||||
/**************************************************************************** |
||||
* REPEATED METHODS |
||||
****************************************************************************/ |
||||
|
||||
/** |
||||
* Writes repeated boolean values to the buffer as unpacked varints. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<boolean>} values |
||||
*/ |
||||
writeRepeatedBool(fieldNumber, values) { |
||||
values.forEach(val => this.writeBool(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated boolean values to the buffer as packed varints. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<boolean>} values |
||||
*/ |
||||
writePackedBool(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeBoolValue_(val), 1); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated double values to the buffer as unpacked fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedDouble(fieldNumber, values) { |
||||
values.forEach(val => this.writeDouble(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated double values to the buffer as packed fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedDouble(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeDoubleValue_(val), 8); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated fixed32 values to the buffer as unpacked fixed32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedFixed32(fieldNumber, values) { |
||||
values.forEach(val => this.writeFixed32(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated fixed32 values to the buffer as packed fixed32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedFixed32(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeFixed32Value_(val), 4); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated float values to the buffer as unpacked fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedFloat(fieldNumber, values) { |
||||
values.forEach(val => this.writeFloat(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated float values to the buffer as packed fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedFloat(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeFloatValue_(val), 4); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated int32 values to the buffer as unpacked int32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedInt32(fieldNumber, values) { |
||||
values.forEach(val => this.writeInt32(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated int32 values to the buffer as packed int32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedInt32(fieldNumber, values) { |
||||
this.writeVariablePacked_( |
||||
fieldNumber, values, (writer, val) => writer.writeInt32Value_(val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated int64 values to the buffer as unpacked varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writeRepeatedInt64(fieldNumber, values) { |
||||
values.forEach(val => this.writeInt64(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated int64 values to the buffer as packed varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writePackedInt64(fieldNumber, values) { |
||||
this.writeVariablePacked_( |
||||
fieldNumber, values, |
||||
(writer, val) => |
||||
writer.writeVarint64_(val.getHighBits(), val.getLowBits())); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sfixed32 values to the buffer as unpacked fixed32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedSfixed32(fieldNumber, values) { |
||||
values.forEach(val => this.writeSfixed32(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sfixed32 values to the buffer as packed fixed32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedSfixed32(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeSfixed32Value_(val), 4); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sfixed64 values to the buffer as unpacked fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writeRepeatedSfixed64(fieldNumber, values) { |
||||
values.forEach(val => this.writeSfixed64(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sfixed64 values to the buffer as packed fixed64. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writePackedSfixed64(fieldNumber, values) { |
||||
this.writeFixedPacked_( |
||||
fieldNumber, values, val => this.writeSfixed64Value_(val), 8); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sint32 values to the buffer as unpacked sint32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedSint32(fieldNumber, values) { |
||||
values.forEach(val => this.writeSint32(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sint32 values to the buffer as packed sint32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedSint32(fieldNumber, values) { |
||||
this.writeVariablePacked_( |
||||
fieldNumber, values, (writer, val) => writer.writeSint32Value_(val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sint64 values to the buffer as unpacked varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writeRepeatedSint64(fieldNumber, values) { |
||||
values.forEach(val => this.writeSint64(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated sint64 values to the buffer as packed varint. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!Int64>} values |
||||
*/ |
||||
writePackedSint64(fieldNumber, values) { |
||||
this.writeVariablePacked_( |
||||
fieldNumber, values, (writer, val) => writer.writeSint64Value_(val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated uint32 values to the buffer as unpacked uint32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writeRepeatedUint32(fieldNumber, values) { |
||||
values.forEach(val => this.writeUint32(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated uint32 values to the buffer as packed uint32. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<number>} values |
||||
*/ |
||||
writePackedUint32(fieldNumber, values) { |
||||
this.writeVariablePacked_( |
||||
fieldNumber, values, (writer, val) => writer.writeUint32Value_(val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated bytes values to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<!ByteString>} values |
||||
*/ |
||||
writeRepeatedBytes(fieldNumber, values) { |
||||
values.forEach(val => this.writeBytes(fieldNumber, val)); |
||||
} |
||||
|
||||
/** |
||||
* Writes packed fields with fixed length. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<T>} values |
||||
* @param {function(T)} valueWriter |
||||
* @param {number} entitySize |
||||
* @template T |
||||
* @private |
||||
*/ |
||||
writeFixedPacked_(fieldNumber, values, valueWriter, entitySize) { |
||||
if (values.length === 0) { |
||||
return; |
||||
} |
||||
this.writeTag(fieldNumber, WireType.DELIMITED); |
||||
this.writeUnsignedVarint32_(values.length * entitySize); |
||||
this.closeAndStartNewBuffer_(); |
||||
values.forEach(value => valueWriter(value)); |
||||
} |
||||
|
||||
/** |
||||
* Writes packed fields with variable length. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<T>} values |
||||
* @param {function(!Writer, T)} valueWriter |
||||
* @template T |
||||
* @private |
||||
*/ |
||||
writeVariablePacked_(fieldNumber, values, valueWriter) { |
||||
if (values.length === 0) { |
||||
return; |
||||
} |
||||
const writer = new Writer(); |
||||
values.forEach(val => valueWriter(writer, val)); |
||||
const bytes = writer.getAndResetResultBuffer(); |
||||
this.writeDelimited(fieldNumber, bytes); |
||||
} |
||||
|
||||
/** |
||||
* Writes repeated string values to the buffer. |
||||
* @param {number} fieldNumber |
||||
* @param {!Array<string>} values |
||||
*/ |
||||
writeRepeatedString(fieldNumber, values) { |
||||
values.forEach(val => this.writeString(fieldNumber, val)); |
||||
} |
||||
} |
||||
|
||||
exports = Writer; |
@ -0,0 +1,910 @@ |
||||
/** |
||||
* @fileoverview Tests for writer.js. |
||||
*/ |
||||
goog.module('protobuf.binary.WriterTest'); |
||||
|
||||
goog.setTestOnly(); |
||||
|
||||
// Note to the reader:
|
||||
// Since the writer behavior changes with the checking level some of the tests
|
||||
// in this file have to know which checking level is enable to make correct
|
||||
// assertions.
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const WireType = goog.require('protobuf.binary.WireType'); |
||||
const Writer = goog.require('protobuf.binary.Writer'); |
||||
const {CHECK_BOUNDS, CHECK_TYPE, MAX_FIELD_NUMBER} = goog.require('protobuf.internal.checks'); |
||||
const {arrayBufferSlice} = goog.require('protobuf.binary.typedArrays'); |
||||
const {getDoublePairs} = goog.require('protobuf.binary.doubleTestPairs'); |
||||
const {getFixed32Pairs} = goog.require('protobuf.binary.fixed32TestPairs'); |
||||
const {getFloatPairs} = goog.require('protobuf.binary.floatTestPairs'); |
||||
const {getInt32Pairs} = goog.require('protobuf.binary.int32TestPairs'); |
||||
const {getInt64Pairs} = goog.require('protobuf.binary.int64TestPairs'); |
||||
const {getPackedBoolPairs} = goog.require('protobuf.binary.packedBoolTestPairs'); |
||||
const {getPackedDoublePairs} = goog.require('protobuf.binary.packedDoubleTestPairs'); |
||||
const {getPackedFixed32Pairs} = goog.require('protobuf.binary.packedFixed32TestPairs'); |
||||
const {getPackedFloatPairs} = goog.require('protobuf.binary.packedFloatTestPairs'); |
||||
const {getPackedInt32Pairs} = goog.require('protobuf.binary.packedInt32TestPairs'); |
||||
const {getPackedInt64Pairs} = goog.require('protobuf.binary.packedInt64TestPairs'); |
||||
const {getPackedSfixed32Pairs} = goog.require('protobuf.binary.packedSfixed32TestPairs'); |
||||
const {getPackedSfixed64Pairs} = goog.require('protobuf.binary.packedSfixed64TestPairs'); |
||||
const {getPackedSint32Pairs} = goog.require('protobuf.binary.packedSint32TestPairs'); |
||||
const {getPackedSint64Pairs} = goog.require('protobuf.binary.packedSint64TestPairs'); |
||||
const {getPackedUint32Pairs} = goog.require('protobuf.binary.packedUint32TestPairs'); |
||||
const {getSfixed32Pairs} = goog.require('protobuf.binary.sfixed32TestPairs'); |
||||
const {getSfixed64Pairs} = goog.require('protobuf.binary.sfixed64TestPairs'); |
||||
const {getSint32Pairs} = goog.require('protobuf.binary.sint32TestPairs'); |
||||
const {getSint64Pairs} = goog.require('protobuf.binary.sint64TestPairs'); |
||||
const {getUint32Pairs} = goog.require('protobuf.binary.uint32TestPairs'); |
||||
|
||||
|
||||
/** |
||||
* @param {...number} bytes |
||||
* @return {!ArrayBuffer} |
||||
*/ |
||||
function createArrayBuffer(...bytes) { |
||||
return new Uint8Array(bytes).buffer; |
||||
} |
||||
|
||||
/****************************************************************************** |
||||
* OPTIONAL FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
describe('Writer does', () => { |
||||
it('return an empty ArrayBuffer when nothing is encoded', () => { |
||||
const writer = new Writer(); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
it('encode tag', () => { |
||||
const writer = new Writer(); |
||||
writer.writeTag(1, WireType.VARINT); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer(0x08)); |
||||
}); |
||||
|
||||
it('reset after calling getAndResetResultBuffer', () => { |
||||
const writer = new Writer(); |
||||
writer.writeTag(1, WireType.VARINT); |
||||
writer.getAndResetResultBuffer(); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
it('fail when field number is too large for writeTag', () => { |
||||
const writer = new Writer(); |
||||
if (CHECK_TYPE) { |
||||
expect(() => writer.writeTag(MAX_FIELD_NUMBER + 1, WireType.VARINT)) |
||||
.toThrowError('Field number is out of range: 536870912'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
writer.writeTag(MAX_FIELD_NUMBER + 1, WireType.VARINT); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer(0)); |
||||
} |
||||
}); |
||||
|
||||
it('fail when field number is negative for writeTag', () => { |
||||
const writer = new Writer(); |
||||
if (CHECK_TYPE) { |
||||
expect(() => writer.writeTag(-1, WireType.VARINT)) |
||||
.toThrowError('Field number is out of range: -1'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
writer.writeTag(-1, WireType.VARINT); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer(0xF8)); |
||||
} |
||||
}); |
||||
|
||||
it('fail when wire type is invalid for writeTag', () => { |
||||
const writer = new Writer(); |
||||
if (CHECK_TYPE) { |
||||
expect(() => writer.writeTag(1, /** @type {!WireType} */ (0x08))) |
||||
.toThrowError('Invalid wire type: 8'); |
||||
} else { |
||||
// Note in unchecked mode we produce invalid output for invalid inputs.
|
||||
// This test just documents our behavior in those cases.
|
||||
// These values might change at any point and are not considered
|
||||
// what the implementation should be doing here.
|
||||
writer.writeTag(1, /** @type {!WireType} */ (0x08)); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer(0x08)); |
||||
} |
||||
}); |
||||
|
||||
it('encode singular boolean value', () => { |
||||
const writer = new Writer(); |
||||
writer.writeBool(1, true); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer(0x08, 0x01)); |
||||
}); |
||||
|
||||
it('encode length delimited', () => { |
||||
const writer = new Writer(); |
||||
writer.writeDelimited(1, createArrayBuffer(0x01, 0x02)); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer(0x0A, 0x02, 0x01, 0x02)); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeBufferDecoder does', () => { |
||||
it('encode BufferDecoder containing a varint value', () => { |
||||
const writer = new Writer(); |
||||
const expected = createArrayBuffer( |
||||
0x08, /* varint start= */ 0xFF, /* varint end= */ 0x01, 0x08, 0x01); |
||||
writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(expected), 1, WireType.VARINT); |
||||
const result = writer.getAndResetResultBuffer(); |
||||
expect(result).toEqual(arrayBufferSlice(expected, 1, 3)); |
||||
}); |
||||
|
||||
it('encode BufferDecoder containing a fixed64 value', () => { |
||||
const writer = new Writer(); |
||||
const expected = createArrayBuffer( |
||||
0x09, /* fixed64 start= */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
||||
/* fixed64 end= */ 0x08, 0x08, 0x01); |
||||
writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(expected), 1, WireType.FIXED64); |
||||
const result = writer.getAndResetResultBuffer(); |
||||
expect(result).toEqual(arrayBufferSlice(expected, 1, 9)); |
||||
}); |
||||
|
||||
it('encode BufferDecoder containing a length delimited value', () => { |
||||
const writer = new Writer(); |
||||
const expected = createArrayBuffer( |
||||
0xA, /* length= */ 0x03, /* data start= */ 0x01, 0x02, |
||||
/* data end= */ 0x03, 0x08, 0x01); |
||||
writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(expected), 1, WireType.DELIMITED); |
||||
const result = writer.getAndResetResultBuffer(); |
||||
expect(result).toEqual(arrayBufferSlice(expected, 1, 5)); |
||||
}); |
||||
|
||||
it('encode BufferDecoder containing a group', () => { |
||||
const writer = new Writer(); |
||||
const expected = createArrayBuffer( |
||||
0xB, /* group start= */ 0x08, 0x01, /* nested group start= */ 0x0B, |
||||
/* nested group end= */ 0x0C, /* group end= */ 0x0C, 0x08, 0x01); |
||||
writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(expected), 1, WireType.START_GROUP); |
||||
const result = writer.getAndResetResultBuffer(); |
||||
expect(result).toEqual(arrayBufferSlice(expected, 1, 6)); |
||||
}); |
||||
|
||||
it('encode BufferDecoder containing a fixed32 value', () => { |
||||
const writer = new Writer(); |
||||
const expected = createArrayBuffer( |
||||
0x09, /* fixed64 start= */ 0x01, 0x02, 0x03, /* fixed64 end= */ 0x04, |
||||
0x08, 0x01); |
||||
writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(expected), 1, WireType.FIXED32); |
||||
const result = writer.getAndResetResultBuffer(); |
||||
expect(result).toEqual(arrayBufferSlice(expected, 1, 5)); |
||||
}); |
||||
|
||||
it('fail when encoding out of bound data', () => { |
||||
const writer = new Writer(); |
||||
const buffer = createArrayBuffer(0x4, 0x0, 0x1, 0x2, 0x3); |
||||
const subBuffer = arrayBufferSlice(buffer, 0, 2); |
||||
expect( |
||||
() => writer.writeBufferDecoder( |
||||
BufferDecoder.fromArrayBuffer(subBuffer), 0, WireType.DELIMITED)) |
||||
.toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeBytes does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encodes empty ByteString', () => { |
||||
writer.writeBytes(1, ByteString.EMPTY); |
||||
const buffer = writer.getAndResetResultBuffer(); |
||||
expect(buffer.byteLength).toBe(2); |
||||
}); |
||||
|
||||
it('encodes empty array', () => { |
||||
writer.writeBytes(1, ByteString.fromArrayBuffer(new ArrayBuffer(0))); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
1 << 3 | 0x02, // tag (fieldnumber << 3 | (length delimited))
|
||||
0, // length of the bytes
|
||||
)); |
||||
}); |
||||
|
||||
it('encodes ByteString', () => { |
||||
const array = createArrayBuffer(1, 2, 3); |
||||
writer.writeBytes(1, ByteString.fromArrayBuffer(array)); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
1 << 3 | 0x02, // tag (fieldnumber << 3 | (length delimited))
|
||||
3, // length of the bytes
|
||||
1, |
||||
2, |
||||
3, |
||||
)); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeDouble does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getDoublePairs()) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeDouble(1, pair.doubleValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(9); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x09); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, 9)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* NaN may have different value in different browsers. Thus, we need to make |
||||
* the test lenient. |
||||
*/ |
||||
it('encode NaN', () => { |
||||
writer.writeDouble(1, NaN); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(9); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x09); |
||||
// Encoded values are stored right after the tag
|
||||
const float64 = new DataView(buffer.buffer); |
||||
expect(float64.getFloat64(1, true)).toBeNaN(); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeFixed32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getFixed32Pairs()) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeFixed32(1, pair.intValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(5); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0D); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, 5)).toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeFloat does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getFloatPairs()) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeFloat(1, pair.floatValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(5); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0D); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, 5)).toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* NaN may have different value in different browsers. Thus, we need to make |
||||
* the test lenient. |
||||
*/ |
||||
it('encode NaN', () => { |
||||
writer.writeFloat(1, NaN); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(5); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0D); |
||||
// Encoded values are stored right after the tag
|
||||
const float32 = new DataView(buffer.buffer); |
||||
expect(float32.getFloat32(1, true)).toBeNaN(); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeInt32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getInt32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeInt32(1, pair.intValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x08); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeSfixed32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedSfixed32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getSfixed32Pairs()) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeSfixed32(1, pair.intValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(5); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0D); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, 5)).toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeSfixed64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getSfixed64Pairs()) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeSfixed64(1, pair.longValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
expect(buffer.length).toBe(9); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x09); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, 9)).toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeSint32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getSint32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeSint32(1, pair.intValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x08); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeSint64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getSint64Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeSint64(1, pair.longValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x08); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeInt64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getInt64Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeInt64(1, pair.longValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x08); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeUint32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
for (const pair of getUint32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writeUint32(1, pair.intValue); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x08); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeString does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty string', () => { |
||||
writer.writeString(1, ''); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
1 << 3 | 0x02, // tag (fieldnumber << 3 | (length delimited))
|
||||
0, // length of the string
|
||||
)); |
||||
}); |
||||
|
||||
it('encode simple string', () => { |
||||
writer.writeString(1, 'hello'); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
1 << 3 | 0x02, // tag (fieldnumber << 3 | (length delimited))
|
||||
5, // length of the string
|
||||
'h'.charCodeAt(0), |
||||
'e'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'o'.charCodeAt(0), |
||||
)); |
||||
}); |
||||
|
||||
it('throw for invalid fieldnumber', () => { |
||||
if (CHECK_BOUNDS) { |
||||
expect(() => writer.writeString(-1, 'a')) |
||||
.toThrowError('Field number is out of range: -1'); |
||||
} else { |
||||
writer.writeString(-1, 'a'); |
||||
expect(new Uint8Array(writer.getAndResetResultBuffer())) |
||||
.toEqual(new Uint8Array(createArrayBuffer( |
||||
-6, // invalid tag
|
||||
1, // string length
|
||||
'a'.charCodeAt(0), |
||||
))); |
||||
} |
||||
}); |
||||
|
||||
it('throw for null string value', () => { |
||||
expect( |
||||
() => writer.writeString( |
||||
1, /** @type {string} */ (/** @type {*} */ (null)))) |
||||
.toThrow(); |
||||
}); |
||||
}); |
||||
|
||||
|
||||
/****************************************************************************** |
||||
* REPEATED FUNCTIONS |
||||
******************************************************************************/ |
||||
|
||||
describe('Writer.writePackedBool does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedBool(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedBoolPairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedBool(1, pair.boolValues); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeRepeatedBool does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writeRepeatedBool(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
it('encode repeated unpacked boolean values', () => { |
||||
const writer = new Writer(); |
||||
writer.writeRepeatedBool(1, [true, false]); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
1 << 3 | 0x00, // tag (fieldnumber << 3 | (varint))
|
||||
0x01, // value[0]
|
||||
1 << 3 | 0x00, // tag (fieldnumber << 3 | (varint))
|
||||
0x00, // value[1]
|
||||
)); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writePackedDouble does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedDouble(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedDoublePairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedDouble(1, pair.doubleValues); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedFixed32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedFixed32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedFixed32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedFixed32(1, pair.fixed32Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedFloat does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedFloat(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedFloatPairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedFloat(1, pair.floatValues); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedInt32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedInt32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedInt32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedInt32(1, pair.int32Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedInt64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedInt64(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedInt64Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedInt64(1, pair.int64Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedSfixed32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedSfixed32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedSfixed32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedSfixed32(1, pair.sfixed32Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedSfixed64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedSfixed64(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedSfixed64Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedSfixed64(1, pair.sfixed64Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedSint32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedSint32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedSint32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedSint32(1, pair.sint32Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedSint64 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedSint64(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedSint64Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedSint64(1, pair.sint64Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writePackedUint32 does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writePackedUint32(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
for (const pair of getPackedUint32Pairs()) { |
||||
if (!pair.skip_writer) { |
||||
it(`encode ${pair.name}`, () => { |
||||
writer.writePackedUint32(1, pair.uint32Values); |
||||
const buffer = new Uint8Array(writer.getAndResetResultBuffer()); |
||||
// ensure we have a correct tag
|
||||
expect(buffer[0]).toEqual(0x0A); |
||||
// Encoded values are stored right after the tag
|
||||
expect(buffer.subarray(1, buffer.length)) |
||||
.toEqual(pair.bufferDecoder.asUint8Array()); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
describe('Writer.writeRepeatedBytes does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writeRepeatedBytes(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
it('encode single value', () => { |
||||
const value = createArrayBuffer(0x61); |
||||
writer.writeRepeatedBytes(1, [ByteString.fromArrayBuffer(value)]); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
0x0A, |
||||
0x01, |
||||
0x61, // a
|
||||
)); |
||||
}); |
||||
|
||||
it('encode multiple values', () => { |
||||
const value1 = createArrayBuffer(0x61); |
||||
const value2 = createArrayBuffer(0x62); |
||||
writer.writeRepeatedBytes(1, [ |
||||
ByteString.fromArrayBuffer(value1), |
||||
ByteString.fromArrayBuffer(value2), |
||||
]); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
0x0A, |
||||
0x01, |
||||
0x61, // a
|
||||
0x0A, |
||||
0x01, |
||||
0x62, // b
|
||||
)); |
||||
}); |
||||
}); |
||||
|
||||
describe('Writer.writeRepeatedString does', () => { |
||||
let writer; |
||||
beforeEach(() => { |
||||
writer = new Writer(); |
||||
}); |
||||
|
||||
it('encode empty array', () => { |
||||
writer.writeRepeatedString(1, []); |
||||
expect(writer.getAndResetResultBuffer()).toEqual(createArrayBuffer()); |
||||
}); |
||||
|
||||
it('encode single value', () => { |
||||
writer.writeRepeatedString(1, ['a']); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
0x0A, |
||||
0x01, |
||||
0x61, // a
|
||||
)); |
||||
}); |
||||
|
||||
it('encode multiple values', () => { |
||||
writer.writeRepeatedString(1, ['a', 'b']); |
||||
expect(writer.getAndResetResultBuffer()) |
||||
.toEqual(createArrayBuffer( |
||||
0x0A, |
||||
0x01, |
||||
0x61, // a
|
||||
0x0A, |
||||
0x01, |
||||
0x62, // b
|
||||
)); |
||||
}); |
||||
}); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,44 @@ |
||||
/** |
||||
* @fileoverview Tests in this file will fail if our custom equality have not |
||||
* been installed. |
||||
* see b/131864652 |
||||
*/ |
||||
|
||||
goog.module('protobuf.testing.ensureCustomEqualityTest'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
|
||||
describe('Custom equality', () => { |
||||
it('ensure that custom equality for ArrayBuffer is installed', () => { |
||||
const buffer1 = new ArrayBuffer(4); |
||||
const buffer2 = new ArrayBuffer(4); |
||||
const array = new Uint8Array(buffer1); |
||||
array[0] = 1; |
||||
expect(buffer1).not.toEqual(buffer2); |
||||
}); |
||||
|
||||
it('ensure that custom equality for ByteString is installed', () => { |
||||
const HALLO_IN_BASE64 = 'aGFsbG8='; |
||||
const BYTES_WITH_HALLO = new Uint8Array([ |
||||
'h'.charCodeAt(0), |
||||
'a'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'l'.charCodeAt(0), |
||||
'o'.charCodeAt(0), |
||||
]); |
||||
|
||||
const byteString1 = ByteString.fromBase64String(HALLO_IN_BASE64); |
||||
const byteString2 = ByteString.fromArrayBufferView(BYTES_WITH_HALLO); |
||||
expect(byteString1).toEqual(byteString2); |
||||
}); |
||||
|
||||
it('ensure that custom equality for BufferDecoder is installed', () => { |
||||
const arrayBuffer1 = new Uint8Array([0, 1, 2]).buffer; |
||||
const arrayBuffer2 = new Uint8Array([0, 1, 2]).buffer; |
||||
|
||||
const bufferDecoder1 = BufferDecoder.fromArrayBuffer(arrayBuffer1); |
||||
const bufferDecoder2 = BufferDecoder.fromArrayBuffer(arrayBuffer2); |
||||
expect(bufferDecoder1).toEqual(bufferDecoder2); |
||||
}); |
||||
}); |
@ -0,0 +1,88 @@ |
||||
/** |
||||
* @fileoverview Installs our custom equality matchers in Jasmine. |
||||
*/ |
||||
goog.module('protobuf.testing.jasmineProtoBuf'); |
||||
|
||||
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
||||
const ByteString = goog.require('protobuf.ByteString'); |
||||
const {arrayBufferEqual} = goog.require('protobuf.binary.typedArrays'); |
||||
|
||||
/** |
||||
* A function that ensures custom equality for ByteStrings. |
||||
* Since Jasmine compare structure by default Bytestrings might be equal that |
||||
* are not equal since ArrayBuffers still compare content in g3. |
||||
* (Jasmine fix upstream: https://github.com/jasmine/jasmine/issues/1687)
|
||||
* Also ByteStrings that are equal might compare non equal in jasmine of the |
||||
* base64 string has been initialized. |
||||
* @param {*} first |
||||
* @param {*} second |
||||
* @return {boolean|undefined} |
||||
*/ |
||||
const byteStringEquality = (first, second) => { |
||||
if (second instanceof ByteString) { |
||||
return second.equals(first); |
||||
} |
||||
|
||||
// Intentionally not returning anything, this signals to jasmine that we
|
||||
// did not perform any equality on the given objects.
|
||||
}; |
||||
|
||||
/** |
||||
* A function that ensures custom equality for ArrayBuffers. |
||||
* By default Jasmine does not compare the content of an ArrayBuffer and thus |
||||
* will return true for buffers with the same length but different content. |
||||
* @param {*} first |
||||
* @param {*} second |
||||
* @return {boolean|undefined} |
||||
*/ |
||||
const arrayBufferCustomEquality = (first, second) => { |
||||
if (first instanceof ArrayBuffer && second instanceof ArrayBuffer) { |
||||
return arrayBufferEqual(first, second); |
||||
} |
||||
// Intentionally not returning anything, this signals to jasmine that we
|
||||
// did not perform any equality on the given objects.
|
||||
}; |
||||
|
||||
/** |
||||
* A function that ensures custom equality for ArrayBuffers. |
||||
* By default Jasmine does not compare the content of an ArrayBuffer and thus |
||||
* will return true for buffers with the same length but different content. |
||||
* @param {*} first |
||||
* @param {*} second |
||||
* @return {boolean|undefined} |
||||
*/ |
||||
const bufferDecoderCustomEquality = (first, second) => { |
||||
if (first instanceof BufferDecoder && second instanceof BufferDecoder) { |
||||
return first.asByteString().equals(second.asByteString()); |
||||
} |
||||
// Intentionally not returning anything, this signals to jasmine that we
|
||||
// did not perform any equality on the given objects.
|
||||
}; |
||||
|
||||
/** |
||||
* Overrides the default ArrayBuffer toString method ([object ArrayBuffer]) with |
||||
* a more readable representation. |
||||
*/ |
||||
function overrideArrayBufferToString() { |
||||
/** |
||||
* Returns the hex values of the underlying bytes of the ArrayBuffer. |
||||
* |
||||
* @override |
||||
* @return {string} |
||||
*/ |
||||
ArrayBuffer.prototype.toString = function() { |
||||
const arr = Array.from(new Uint8Array(this)); |
||||
return 'ArrayBuffer[' + |
||||
arr.map((b) => '0x' + (b & 0xFF).toString(16).toUpperCase()) |
||||
.join(', ') + |
||||
']'; |
||||
}; |
||||
} |
||||
|
||||
beforeEach(() => { |
||||
jasmine.addCustomEqualityTester(arrayBufferCustomEquality); |
||||
jasmine.addCustomEqualityTester(bufferDecoderCustomEquality); |
||||
jasmine.addCustomEqualityTester(byteStringEquality); |
||||
|
||||
overrideArrayBufferToString(); |
||||
}); |
Loading…
Reference in new issue