Project import generated by Copybara.

PiperOrigin-RevId: 293139128
pull/7189/head
Protobuf Team 5 years ago committed by Daniel Kurka
parent 4cf5bfee95
commit cc938ea39a
  1. 314
      js/experimental/benchmarks/code_size/apps_jspb/all_types_proto2.js
  2. 314
      js/experimental/benchmarks/code_size/apps_jspb/all_types_proto3.js
  3. 53
      js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto2.js
  4. 53
      js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto3.js
  5. 57
      js/experimental/benchmarks/code_size/code_size_base.js
  6. 227
      js/experimental/benchmarks/code_size/kernel/all_types.js
  7. 68
      js/experimental/benchmarks/code_size/kernel/popular_types.js
  8. 183
      js/experimental/runtime/bytestring.js
  9. 33
      js/experimental/runtime/bytestring_internal.js
  10. 277
      js/experimental/runtime/bytestring_test.js
  11. 403
      js/experimental/runtime/int64.js
  12. 213
      js/experimental/runtime/int64_test.js
  13. 708
      js/experimental/runtime/internal/checks.js
  14. 58
      js/experimental/runtime/internal/checks_test.js
  15. 79
      js/experimental/runtime/kernel/bool_test_pairs.js
  16. 256
      js/experimental/runtime/kernel/buffer_decoder.js
  17. 18
      js/experimental/runtime/kernel/buffer_decoder_helper.js
  18. 104
      js/experimental/runtime/kernel/buffer_decoder_test.js
  19. 91
      js/experimental/runtime/kernel/conformance/conformance_request.js
  20. 76
      js/experimental/runtime/kernel/conformance/conformance_response.js
  21. 103
      js/experimental/runtime/kernel/conformance/conformance_testee.js
  22. 62
      js/experimental/runtime/kernel/conformance/conformance_testee_runner_node.js
  23. 309
      js/experimental/runtime/kernel/conformance/test_all_types_proto2.js
  24. 310
      js/experimental/runtime/kernel/conformance/test_all_types_proto3.js
  25. 16
      js/experimental/runtime/kernel/conformance/wire_format.js
  26. 89
      js/experimental/runtime/kernel/double_test_pairs.js
  27. 196
      js/experimental/runtime/kernel/field.js
  28. 36
      js/experimental/runtime/kernel/fixed32_test_pairs.js
  29. 78
      js/experimental/runtime/kernel/float_test_pairs.js
  30. 192
      js/experimental/runtime/kernel/indexer.js
  31. 359
      js/experimental/runtime/kernel/indexer_test.js
  32. 71
      js/experimental/runtime/kernel/int32_test_pairs.js
  33. 59
      js/experimental/runtime/kernel/int64_test_pairs.js
  34. 24
      js/experimental/runtime/kernel/internal_message.js
  35. 3767
      js/experimental/runtime/kernel/lazy_accessor.js
  36. 266
      js/experimental/runtime/kernel/lazy_accessor_compatibility_test.js
  37. 7443
      js/experimental/runtime/kernel/lazy_accessor_repeated_test.js
  38. 2054
      js/experimental/runtime/kernel/lazy_accessor_test.js
  39. 59
      js/experimental/runtime/kernel/packed_bool_test_pairs.js
  40. 52
      js/experimental/runtime/kernel/packed_double_test_pairs.js
  41. 34
      js/experimental/runtime/kernel/packed_fixed32_test_pairs.js
  42. 34
      js/experimental/runtime/kernel/packed_float_test_pairs.js
  43. 33
      js/experimental/runtime/kernel/packed_int32_test_pairs.js
  44. 34
      js/experimental/runtime/kernel/packed_int64_test_pairs.js
  45. 34
      js/experimental/runtime/kernel/packed_sfixed32_test_pairs.js
  46. 53
      js/experimental/runtime/kernel/packed_sfixed64_test_pairs.js
  47. 33
      js/experimental/runtime/kernel/packed_sint32_test_pairs.js
  48. 34
      js/experimental/runtime/kernel/packed_sint64_test_pairs.js
  49. 33
      js/experimental/runtime/kernel/packed_uint32_test_pairs.js
  50. 464
      js/experimental/runtime/kernel/reader.js
  51. 423
      js/experimental/runtime/kernel/reader_test.js
  52. 46
      js/experimental/runtime/kernel/sfixed32_test_pairs.js
  53. 52
      js/experimental/runtime/kernel/sfixed64_test_pairs.js
  54. 57
      js/experimental/runtime/kernel/sint32_test_pairs.js
  55. 60
      js/experimental/runtime/kernel/sint64_test_pairs.js
  56. 133
      js/experimental/runtime/kernel/storage.js
  57. 165
      js/experimental/runtime/kernel/storage_test.js
  58. 116
      js/experimental/runtime/kernel/textencoding.js
  59. 113
      js/experimental/runtime/kernel/textencoding_test.js
  60. 116
      js/experimental/runtime/kernel/typed_arrays.js
  61. 191
      js/experimental/runtime/kernel/typed_arrays_test.js
  62. 61
      js/experimental/runtime/kernel/uint32_test_pairs.js
  63. 28
      js/experimental/runtime/kernel/uint8arrays.js
  64. 47
      js/experimental/runtime/kernel/uint8arrays_test.js
  65. 17
      js/experimental/runtime/kernel/wire_type.js
  66. 772
      js/experimental/runtime/kernel/writer.js
  67. 910
      js/experimental/runtime/kernel/writer_test.js
  68. 1763
      js/experimental/runtime/testing/binary/test_message.js
  69. 44
      js/experimental/runtime/testing/ensure_custom_equality_test.js
  70. 88
      js/experimental/runtime/testing/jasmine_protobuf.js

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

@ -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…
Cancel
Save