From cc938ea39a9a4b79de981f87d60cdf00085239f3 Mon Sep 17 00:00:00 2001 From: Protobuf Team Date: Tue, 4 Feb 2020 07:08:23 -0800 Subject: [PATCH] Project import generated by Copybara. PiperOrigin-RevId: 293139128 --- .../code_size/apps_jspb/all_types_proto2.js | 314 + .../code_size/apps_jspb/all_types_proto3.js | 314 + .../apps_jspb/popular_types_proto2.js | 53 + .../apps_jspb/popular_types_proto3.js | 53 + .../benchmarks/code_size/code_size_base.js | 57 + .../benchmarks/code_size/kernel/all_types.js | 227 + .../code_size/kernel/popular_types.js | 68 + js/experimental/runtime/bytestring.js | 183 + .../runtime/bytestring_internal.js | 33 + js/experimental/runtime/bytestring_test.js | 277 + js/experimental/runtime/int64.js | 403 + js/experimental/runtime/int64_test.js | 213 + js/experimental/runtime/internal/checks.js | 708 ++ .../runtime/internal/checks_test.js | 58 + .../runtime/kernel/bool_test_pairs.js | 79 + .../runtime/kernel/buffer_decoder.js | 256 + .../runtime/kernel/buffer_decoder_helper.js | 18 + .../runtime/kernel/buffer_decoder_test.js | 104 + .../kernel/conformance/conformance_request.js | 91 + .../conformance/conformance_response.js | 76 + .../kernel/conformance/conformance_testee.js | 103 + .../conformance_testee_runner_node.js | 62 + .../conformance/test_all_types_proto2.js | 309 + .../conformance/test_all_types_proto3.js | 310 + .../runtime/kernel/conformance/wire_format.js | 16 + .../runtime/kernel/double_test_pairs.js | 89 + js/experimental/runtime/kernel/field.js | 196 + .../runtime/kernel/fixed32_test_pairs.js | 36 + .../runtime/kernel/float_test_pairs.js | 78 + js/experimental/runtime/kernel/indexer.js | 192 + .../runtime/kernel/indexer_test.js | 359 + .../runtime/kernel/int32_test_pairs.js | 71 + .../runtime/kernel/int64_test_pairs.js | 59 + .../runtime/kernel/internal_message.js | 24 + .../runtime/kernel/lazy_accessor.js | 3767 +++++++++ .../lazy_accessor_compatibility_test.js | 266 + .../kernel/lazy_accessor_repeated_test.js | 7443 +++++++++++++++++ .../runtime/kernel/lazy_accessor_test.js | 2054 +++++ .../runtime/kernel/packed_bool_test_pairs.js | 59 + .../kernel/packed_double_test_pairs.js | 52 + .../kernel/packed_fixed32_test_pairs.js | 34 + .../runtime/kernel/packed_float_test_pairs.js | 34 + .../runtime/kernel/packed_int32_test_pairs.js | 33 + .../runtime/kernel/packed_int64_test_pairs.js | 34 + .../kernel/packed_sfixed32_test_pairs.js | 34 + .../kernel/packed_sfixed64_test_pairs.js | 53 + .../kernel/packed_sint32_test_pairs.js | 33 + .../kernel/packed_sint64_test_pairs.js | 34 + .../kernel/packed_uint32_test_pairs.js | 33 + js/experimental/runtime/kernel/reader.js | 464 + js/experimental/runtime/kernel/reader_test.js | 423 + .../runtime/kernel/sfixed32_test_pairs.js | 46 + .../runtime/kernel/sfixed64_test_pairs.js | 52 + .../runtime/kernel/sint32_test_pairs.js | 57 + .../runtime/kernel/sint64_test_pairs.js | 60 + js/experimental/runtime/kernel/storage.js | 133 + .../runtime/kernel/storage_test.js | 165 + .../runtime/kernel/textencoding.js | 116 + .../runtime/kernel/textencoding_test.js | 113 + .../runtime/kernel/typed_arrays.js | 116 + .../runtime/kernel/typed_arrays_test.js | 191 + .../runtime/kernel/uint32_test_pairs.js | 61 + js/experimental/runtime/kernel/uint8arrays.js | 28 + .../runtime/kernel/uint8arrays_test.js | 47 + js/experimental/runtime/kernel/wire_type.js | 17 + js/experimental/runtime/kernel/writer.js | 772 ++ js/experimental/runtime/kernel/writer_test.js | 910 ++ .../runtime/testing/binary/test_message.js | 1763 ++++ .../testing/ensure_custom_equality_test.js | 44 + .../runtime/testing/jasmine_protobuf.js | 88 + 70 files changed, 25118 insertions(+) create mode 100644 js/experimental/benchmarks/code_size/apps_jspb/all_types_proto2.js create mode 100644 js/experimental/benchmarks/code_size/apps_jspb/all_types_proto3.js create mode 100644 js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto2.js create mode 100644 js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto3.js create mode 100644 js/experimental/benchmarks/code_size/code_size_base.js create mode 100644 js/experimental/benchmarks/code_size/kernel/all_types.js create mode 100644 js/experimental/benchmarks/code_size/kernel/popular_types.js create mode 100644 js/experimental/runtime/bytestring.js create mode 100644 js/experimental/runtime/bytestring_internal.js create mode 100644 js/experimental/runtime/bytestring_test.js create mode 100644 js/experimental/runtime/int64.js create mode 100644 js/experimental/runtime/int64_test.js create mode 100644 js/experimental/runtime/internal/checks.js create mode 100644 js/experimental/runtime/internal/checks_test.js create mode 100644 js/experimental/runtime/kernel/bool_test_pairs.js create mode 100644 js/experimental/runtime/kernel/buffer_decoder.js create mode 100644 js/experimental/runtime/kernel/buffer_decoder_helper.js create mode 100644 js/experimental/runtime/kernel/buffer_decoder_test.js create mode 100644 js/experimental/runtime/kernel/conformance/conformance_request.js create mode 100644 js/experimental/runtime/kernel/conformance/conformance_response.js create mode 100755 js/experimental/runtime/kernel/conformance/conformance_testee.js create mode 100755 js/experimental/runtime/kernel/conformance/conformance_testee_runner_node.js create mode 100644 js/experimental/runtime/kernel/conformance/test_all_types_proto2.js create mode 100644 js/experimental/runtime/kernel/conformance/test_all_types_proto3.js create mode 100644 js/experimental/runtime/kernel/conformance/wire_format.js create mode 100644 js/experimental/runtime/kernel/double_test_pairs.js create mode 100644 js/experimental/runtime/kernel/field.js create mode 100644 js/experimental/runtime/kernel/fixed32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/float_test_pairs.js create mode 100644 js/experimental/runtime/kernel/indexer.js create mode 100644 js/experimental/runtime/kernel/indexer_test.js create mode 100644 js/experimental/runtime/kernel/int32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/int64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/internal_message.js create mode 100644 js/experimental/runtime/kernel/lazy_accessor.js create mode 100644 js/experimental/runtime/kernel/lazy_accessor_compatibility_test.js create mode 100644 js/experimental/runtime/kernel/lazy_accessor_repeated_test.js create mode 100644 js/experimental/runtime/kernel/lazy_accessor_test.js create mode 100644 js/experimental/runtime/kernel/packed_bool_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_double_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_fixed32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_float_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_int32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_int64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_sfixed32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_sfixed64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_sint32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_sint64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/packed_uint32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/reader.js create mode 100644 js/experimental/runtime/kernel/reader_test.js create mode 100644 js/experimental/runtime/kernel/sfixed32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/sfixed64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/sint32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/sint64_test_pairs.js create mode 100644 js/experimental/runtime/kernel/storage.js create mode 100644 js/experimental/runtime/kernel/storage_test.js create mode 100644 js/experimental/runtime/kernel/textencoding.js create mode 100644 js/experimental/runtime/kernel/textencoding_test.js create mode 100644 js/experimental/runtime/kernel/typed_arrays.js create mode 100644 js/experimental/runtime/kernel/typed_arrays_test.js create mode 100644 js/experimental/runtime/kernel/uint32_test_pairs.js create mode 100644 js/experimental/runtime/kernel/uint8arrays.js create mode 100644 js/experimental/runtime/kernel/uint8arrays_test.js create mode 100644 js/experimental/runtime/kernel/wire_type.js create mode 100644 js/experimental/runtime/kernel/writer.js create mode 100644 js/experimental/runtime/kernel/writer_test.js create mode 100644 js/experimental/runtime/testing/binary/test_message.js create mode 100644 js/experimental/runtime/testing/ensure_custom_equality_test.js create mode 100644 js/experimental/runtime/testing/jasmine_protobuf.js diff --git a/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto2.js b/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto2.js new file mode 100644 index 000000000000..252520f71c47 --- /dev/null +++ b/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto2.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(); diff --git a/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto3.js b/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto3.js new file mode 100644 index 000000000000..3637df612f8d --- /dev/null +++ b/js/experimental/benchmarks/code_size/apps_jspb/all_types_proto3.js @@ -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(); diff --git a/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto2.js b/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto2.js new file mode 100644 index 000000000000..7bea88a27448 --- /dev/null +++ b/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto2.js @@ -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(); diff --git a/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto3.js b/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto3.js new file mode 100644 index 000000000000..9b804283d1c3 --- /dev/null +++ b/js/experimental/benchmarks/code_size/apps_jspb/popular_types_proto3.js @@ -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(); diff --git a/js/experimental/benchmarks/code_size/code_size_base.js b/js/experimental/benchmarks/code_size/code_size_base.js new file mode 100644 index 000000000000..04f6a4734f81 --- /dev/null +++ b/js/experimental/benchmarks/code_size/code_size_base.js @@ -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}; diff --git a/js/experimental/benchmarks/code_size/kernel/all_types.js b/js/experimental/benchmarks/code_size/kernel/all_types.js new file mode 100644 index 000000000000..218a4b9de4f3 --- /dev/null +++ b/js/experimental/benchmarks/code_size/kernel/all_types.js @@ -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(); diff --git a/js/experimental/benchmarks/code_size/kernel/popular_types.js b/js/experimental/benchmarks/code_size/kernel/popular_types.js new file mode 100644 index 000000000000..fb026225262d --- /dev/null +++ b/js/experimental/benchmarks/code_size/kernel/popular_types.js @@ -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(); diff --git a/js/experimental/runtime/bytestring.js b/js/experimental/runtime/bytestring.js new file mode 100644 index 000000000000..d6af5f148ed0 --- /dev/null +++ b/js/experimental/runtime/bytestring.js @@ -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; diff --git a/js/experimental/runtime/bytestring_internal.js b/js/experimental/runtime/bytestring_internal.js new file mode 100644 index 000000000000..2ea3f609b2da --- /dev/null +++ b/js/experimental/runtime/bytestring_internal.js @@ -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, +}; diff --git a/js/experimental/runtime/bytestring_test.js b/js/experimental/runtime/bytestring_test.js new file mode 100644 index 000000000000..b9928b6e7b8c --- /dev/null +++ b/js/experimental/runtime/bytestring_test.js @@ -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); + }); +}); diff --git a/js/experimental/runtime/int64.js b/js/experimental/runtime/int64.js new file mode 100644 index 000000000000..45585b752519 --- /dev/null +++ b/js/experimental/runtime/int64.js @@ -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; diff --git a/js/experimental/runtime/int64_test.js b/js/experimental/runtime/int64_test.js new file mode 100644 index 000000000000..76af14eabc05 --- /dev/null +++ b/js/experimental/runtime/int64_test.js @@ -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}`; +} diff --git a/js/experimental/runtime/internal/checks.js b/js/experimental/runtime/internal/checks.js new file mode 100644 index 000000000000..369c6af6ef55 --- /dev/null +++ b/js/experimental/runtime/internal/checks.js @@ -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, +}; diff --git a/js/experimental/runtime/internal/checks_test.js b/js/experimental/runtime/internal/checks_test.js new file mode 100644 index 000000000000..50e857b3e099 --- /dev/null +++ b/js/experimental/runtime/internal/checks_test.js @@ -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); + }); +}); \ No newline at end of file diff --git a/js/experimental/runtime/kernel/bool_test_pairs.js b/js/experimental/runtime/kernel/bool_test_pairs.js new file mode 100644 index 000000000000..4323f5baa859 --- /dev/null +++ b/js/experimental/runtime/kernel/bool_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/buffer_decoder.js b/js/experimental/runtime/kernel/buffer_decoder.js new file mode 100644 index 000000000000..614659a1e6fe --- /dev/null +++ b/js/experimental/runtime/kernel/buffer_decoder.js @@ -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} 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; diff --git a/js/experimental/runtime/kernel/buffer_decoder_helper.js b/js/experimental/runtime/kernel/buffer_decoder_helper.js new file mode 100644 index 000000000000..eed3c42d07d0 --- /dev/null +++ b/js/experimental/runtime/kernel/buffer_decoder_helper.js @@ -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, +}; diff --git a/js/experimental/runtime/kernel/buffer_decoder_test.js b/js/experimental/runtime/kernel/buffer_decoder_test.js new file mode 100644 index 000000000000..aed045a8e5f7 --- /dev/null +++ b/js/experimental/runtime/kernel/buffer_decoder_test.js @@ -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(); + } + }); +}); diff --git a/js/experimental/runtime/kernel/conformance/conformance_request.js b/js/experimental/runtime/kernel/conformance/conformance_request.js new file mode 100644 index 000000000000..c9f70affd25d --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/conformance_request.js @@ -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; diff --git a/js/experimental/runtime/kernel/conformance/conformance_response.js b/js/experimental/runtime/kernel/conformance/conformance_response.js new file mode 100644 index 000000000000..3711f84acedb --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/conformance_response.js @@ -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; diff --git a/js/experimental/runtime/kernel/conformance/conformance_testee.js b/js/experimental/runtime/kernel/conformance/conformance_testee.js new file mode 100755 index 000000000000..2945228dd9cd --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/conformance_testee.js @@ -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); diff --git a/js/experimental/runtime/kernel/conformance/conformance_testee_runner_node.js b/js/experimental/runtime/kernel/conformance/conformance_testee_runner_node.js new file mode 100755 index 000000000000..c12f363ecb02 --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/conformance_testee_runner_node.js @@ -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; + } +} diff --git a/js/experimental/runtime/kernel/conformance/test_all_types_proto2.js b/js/experimental/runtime/kernel/conformance/test_all_types_proto2.js new file mode 100644 index 000000000000..34336ee884b8 --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/test_all_types_proto2.js @@ -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; diff --git a/js/experimental/runtime/kernel/conformance/test_all_types_proto3.js b/js/experimental/runtime/kernel/conformance/test_all_types_proto3.js new file mode 100644 index 000000000000..645026b433f7 --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/test_all_types_proto3.js @@ -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; diff --git a/js/experimental/runtime/kernel/conformance/wire_format.js b/js/experimental/runtime/kernel/conformance/wire_format.js new file mode 100644 index 000000000000..636e827bcdb2 --- /dev/null +++ b/js/experimental/runtime/kernel/conformance/wire_format.js @@ -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; diff --git a/js/experimental/runtime/kernel/double_test_pairs.js b/js/experimental/runtime/kernel/double_test_pairs.js new file mode 100644 index 000000000000..86e1f2608046 --- /dev/null +++ b/js/experimental/runtime/kernel/double_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/field.js b/js/experimental/runtime/kernel/field.js new file mode 100644 index 000000000000..46d69997baa2 --- /dev/null +++ b/js/experimental/runtime/kernel/field.js @@ -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} 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} */ + 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} + */ + 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, +}; diff --git a/js/experimental/runtime/kernel/fixed32_test_pairs.js b/js/experimental/runtime/kernel/fixed32_test_pairs.js new file mode 100644 index 000000000000..1bbab3eb0440 --- /dev/null +++ b/js/experimental/runtime/kernel/fixed32_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/float_test_pairs.js b/js/experimental/runtime/kernel/float_test_pairs.js new file mode 100644 index 000000000000..816bdc2c9eca --- /dev/null +++ b/js/experimental/runtime/kernel/float_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/indexer.js b/js/experimental/runtime/kernel/indexer.js new file mode 100644 index 000000000000..c247c2faa6c9 --- /dev/null +++ b/js/experimental/runtime/kernel/indexer.js @@ -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} 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} + */ + 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} + * @package + */ +function buildIndex(bufferDecoder, pivot) { + return new Indexer(bufferDecoder).index(pivot); +} + + +exports = { + buildIndex, +}; diff --git a/js/experimental/runtime/kernel/indexer_test.js b/js/experimental/runtime/kernel/indexer_test.js new file mode 100644 index 000000000000..1c95a07b5272 --- /dev/null +++ b/js/experimental/runtime/kernel/indexer_test.js @@ -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(); + }); +}); diff --git a/js/experimental/runtime/kernel/int32_test_pairs.js b/js/experimental/runtime/kernel/int32_test_pairs.js new file mode 100644 index 000000000000..ef4f2e9c134f --- /dev/null +++ b/js/experimental/runtime/kernel/int32_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/int64_test_pairs.js b/js/experimental/runtime/kernel/int64_test_pairs.js new file mode 100644 index 000000000000..19ef46ba4359 --- /dev/null +++ b/js/experimental/runtime/kernel/int64_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/internal_message.js b/js/experimental/runtime/kernel/internal_message.js new file mode 100644 index 000000000000..2e85ebd5e39d --- /dev/null +++ b/js/experimental/runtime/kernel/internal_message.js @@ -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; \ No newline at end of file diff --git a/js/experimental/runtime/kernel/lazy_accessor.js b/js/experimental/runtime/kernel/lazy_accessor.js new file mode 100644 index 000000000000..cebf0c0bd27c --- /dev/null +++ b/js/experimental/runtime/kernel/lazy_accessor.js @@ -0,0 +1,3767 @@ +/** + * @fileoverview LazyAccessor is a class to provide type-checked accessing + * (read/write bool/int32/string/...) on binary data. + * + * When creating the LazyAccessor with the binary data, there is no deep + * decoding done (which requires full type information). The deep decoding is + * deferred until the first time accessing (when accessors can provide + * full type information). + * + * Because accessors can be statically analyzed and stripped, unlike eager + * binary decoding (which requires the full type information of all defined + * fields), LazyAccessor will only need the full type information of used + * fields. + */ +goog.module('protobuf.binary.LazyAccessor'); + +const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); +const ByteString = goog.require('protobuf.ByteString'); +const Int64 = goog.require('protobuf.Int64'); +const InternalMessage = goog.require('protobuf.binary.InternalMessage'); +const Storage = goog.require('protobuf.binary.Storage'); +const WireType = goog.require('protobuf.binary.WireType'); +const Writer = goog.require('protobuf.binary.Writer'); +const reader = goog.require('protobuf.binary.reader'); +const {CHECK_TYPE, checkCriticalElementIndex, checkCriticalState, checkCriticalType, checkCriticalTypeBool, checkCriticalTypeBoolArray, checkCriticalTypeByteString, checkCriticalTypeByteStringArray, checkCriticalTypeDouble, checkCriticalTypeDoubleArray, checkCriticalTypeFloat, checkCriticalTypeFloatIterable, checkCriticalTypeMessageArray, checkCriticalTypeSignedInt32, checkCriticalTypeSignedInt32Array, checkCriticalTypeSignedInt64, checkCriticalTypeSignedInt64Array, checkCriticalTypeString, checkCriticalTypeStringArray, checkCriticalTypeUnsignedInt32, checkCriticalTypeUnsignedInt32Array, checkDefAndNotNull, checkElementIndex, checkFieldNumber, checkFunctionExists, checkState, checkTypeDouble, checkTypeFloat, checkTypeSignedInt32, checkTypeSignedInt64, checkTypeUnsignedInt32} = goog.require('protobuf.internal.checks'); +const {Field, IndexEntry} = goog.require('protobuf.binary.field'); +const {buildIndex} = goog.require('protobuf.binary.indexer'); + + +/** + * Validates the index entry has the correct wire type. + * @param {!IndexEntry} indexEntry + * @param {!WireType} expected + */ +function validateWireType(indexEntry, expected) { + const wireType = Field.getWireType(indexEntry); + checkCriticalState( + wireType === expected, + `Expected wire type: ${expected} but found: ${wireType}`); +} + +/** + * Checks if the object implements InternalMessage interface. + * @param {?} obj + * @return {!InternalMessage} + */ +function checkIsInternalMessage(obj) { + const message = /** @type {!InternalMessage} */ (obj); + checkFunctionExists(message.internalGetKernel); + return message; +} + +/** + * Checks if the instanceCreator returns an instance that implements the + * InternalMessage interface. + * @param {function(!LazyAccessor):T} instanceCreator + * @template T + */ +function checkInstanceCreator(instanceCreator) { + if (CHECK_TYPE) { + const emptyMessage = instanceCreator(LazyAccessor.createEmpty()); + checkFunctionExists(emptyMessage.internalGetKernel); + } +} + +/** + * Reads the last entry of the index array using the given read function. + * This is used to implement parsing singular primitive fields. + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @param {function(!BufferDecoder, number):T} readFunc + * @param {!WireType} wireType + * @return {T} + * @template T + */ +function readOptional(indexArray, bufferDecoder, readFunc, wireType) { + const index = indexArray.length - 1; + checkElementIndex(index, indexArray.length); + const indexEntry = indexArray[index]; + validateWireType(indexEntry, wireType); + return readFunc(bufferDecoder, Field.getStartIndex(indexEntry)); +} + +/** + * Converts all entries of the index array to the template type using given read + * methods and return an Iterable containing those converted values. + * Primitive repeated fields may be encoded either packed or unpacked. Thus, two + * read methods are needed for those two cases. + * This is used to implement parsing repeated primitive fields. + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @param {function(!BufferDecoder, number):T} singularReadFunc + * @param {function(!BufferDecoder, number):!Array} packedReadFunc + * @param {!WireType} expectedWireType + * @return {!Array} + * @template T + */ +function readRepeatedPrimitive( + indexArray, bufferDecoder, singularReadFunc, packedReadFunc, + expectedWireType) { + // Fast path when there is a single packed entry. + if (indexArray.length === 1 && + Field.getWireType(indexArray[0]) === WireType.DELIMITED) { + return packedReadFunc(bufferDecoder, Field.getStartIndex(indexArray[0])); + } + + let /** !Array */ result = []; + for (const indexEntry of indexArray) { + const wireType = Field.getWireType(indexEntry); + const startIndex = Field.getStartIndex(indexEntry); + if (wireType === WireType.DELIMITED) { + result = result.concat(packedReadFunc(bufferDecoder, startIndex)); + } else { + validateWireType(indexEntry, expectedWireType); + result.push(singularReadFunc(bufferDecoder, startIndex)); + } + } + return result; +} + +/** + * Converts all entries of the index array to the template type using the given + * read function and return an Array containing those converted values. This is + * used to implement parsing repeated non-primitive fields. + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @param {function(!BufferDecoder, number):T} singularReadFunc + * @return {!Array} + * @template T + */ +function readRepeatedNonPrimitive(indexArray, bufferDecoder, singularReadFunc) { + const result = new Array(indexArray.length); + for (let i = 0; i < indexArray.length; i++) { + validateWireType(indexArray[i], WireType.DELIMITED); + result[i] = + singularReadFunc(bufferDecoder, Field.getStartIndex(indexArray[i])); + } + return result; +} + +/** + * Creates a new bytes array to contain all data of a submessage. + * When there are multiple entries, merge them together. + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @return {!BufferDecoder} + */ +function mergeMessageArrays(indexArray, bufferDecoder) { + const dataArrays = indexArray.map( + indexEntry => + reader.readDelimited(bufferDecoder, Field.getStartIndex(indexEntry))); + return BufferDecoder.merge(dataArrays); +} + +/** + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @param {number=} pivot + * @return {!LazyAccessor} + */ +function readAccessor(indexArray, bufferDecoder, pivot = undefined) { + checkState(indexArray.length > 0); + let accessorBuffer; + // Faster access for one member. + if (indexArray.length === 1) { + const indexEntry = indexArray[0]; + validateWireType(indexEntry, WireType.DELIMITED); + accessorBuffer = + reader.readDelimited(bufferDecoder, Field.getStartIndex(indexEntry)); + } else { + indexArray.forEach(indexEntry => { + validateWireType(indexEntry, WireType.DELIMITED); + }); + accessorBuffer = mergeMessageArrays(indexArray, bufferDecoder); + } + return LazyAccessor.fromBufferDecoder_(accessorBuffer, pivot); +} + +/** + * Merges all index entries of the index array using the given read function. + * This is used to implement parsing singular message fields. + * @param {!Array} indexArray + * @param {!BufferDecoder} bufferDecoder + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number=} pivot + * @return {T} + * @template T + */ +function readMessage(indexArray, bufferDecoder, instanceCreator, pivot) { + checkInstanceCreator(instanceCreator); + const accessor = readAccessor(indexArray, bufferDecoder, pivot); + return instanceCreator(accessor); +} + +/** + * @param {!Writer} writer + * @param {number} fieldNumber + * @param {?InternalMessage} value + */ +function writeMessage(writer, fieldNumber, value) { + writer.writeDelimited( + fieldNumber, checkDefAndNotNull(value).internalGetKernel().serialize()); +} + +/** + * Writes the array of Messages into the writer for the given field number. + * @param {!Writer} writer + * @param {number} fieldNumber + * @param {!Iterable} values + */ +function writeRepeatedMessage(writer, fieldNumber, values) { + for (const value of values) { + writeMessage(writer, fieldNumber, value); + } +} + +/** + * Array.from has a weird type definition in google3/javascript/externs/es6.js + * and wants the mapping function accept strings. + * @const {function((string|number)): number} + */ +const fround = /** @type {function((string|number)): number} */ (Math.fround); + +/** + * Wraps an array and exposes it as an Iterable. This class is used to provide + * immutable access of the array to the caller. + * @implements {Iterable} + * @template T + */ +class ArrayIterable { + /** + * @param {!Array} array + */ + constructor(array) { + /** @private @const {!Array} */ + this.array_ = array; + } + + /** @return {!Iterator} */ + [Symbol.iterator]() { + return this.array_[Symbol.iterator](); + } +} + +/** + * Accesses protobuf fields on binary format data. Binary data is decoded lazily + * at the first access. + * @final + */ +class LazyAccessor { + /** + * Create a LazyAccessor for the given binary bytes. + * The bytes array is kept by the LazyAccessor. DON'T MODIFY IT. + * @param {!ArrayBuffer} arrayBuffer Binary bytes. + * @param {number=} pivot Fields with a field number no greater than the pivot + * value will be stored in an array for fast access. Other fields will be + * stored in a map. A higher pivot value can improve runtime performance + * at the expense of requiring more memory. It's recommended to set the + * value to the max field number of the message unless the field numbers + * are too sparse. If the value is not set, a default value specified in + * storage.js will be used. + * @return {!LazyAccessor} + */ + static fromArrayBuffer(arrayBuffer, pivot = undefined) { + const bufferDecoder = BufferDecoder.fromArrayBuffer(arrayBuffer); + return LazyAccessor.fromBufferDecoder_(bufferDecoder, pivot); + } + + /** + * Creates an empty LazyAccessor. + * @param {number=} pivot Fields with a field number no greater than the pivot + * value will be stored in an array for fast access. Other fields will be + * stored in a map. A higher pivot value can improve runtime performance + * at the expense of requiring more memory. It's recommended to set the + * value to the max field number of the message unless the field numbers + * are too sparse. If the value is not set, a default value specified in + * storage.js will be used. + * @return {!LazyAccessor} + */ + static createEmpty(pivot = undefined) { + return new LazyAccessor(/* bufferDecoder= */ null, new Storage(pivot)); + } + + /** + * Create a LazyAccessor for the given binary bytes. + * The bytes array is kept by the LazyAccessor. DON'T MODIFY IT. + * @param {!BufferDecoder} bufferDecoder Binary bytes. + * @param {number|undefined} pivot + * @return {!LazyAccessor} + * @private + */ + static fromBufferDecoder_(bufferDecoder, pivot) { + return new LazyAccessor(bufferDecoder, buildIndex(bufferDecoder, pivot)); + } + + /** + * @param {?BufferDecoder} bufferDecoder Binary bytes. Accessor treats the + * bytes as immutable and will never attempt to write to it. + * @param {!Storage} fields A map of field number to Field. The + * IndexEntry in each Field needs to be populated with the location of the + * field in the binary data. + * @private + */ + constructor(bufferDecoder, fields) { + /** @private @const {?BufferDecoder} */ + this.bufferDecoder_ = bufferDecoder; + /** @private @const {!Storage} */ + this.fields_ = fields; + } + + /** + * Creates a shallow copy of the accessor. + * @return {!LazyAccessor} + */ + shallowCopy() { + return new LazyAccessor(this.bufferDecoder_, this.fields_.shallowCopy()); + } + + /** + * See definition of the pivot parameter on the fromArrayBuffer() method. + * @return {number} + */ + getPivot() { + return this.fields_.getPivot(); + } + + /** + * Clears the field for the given field number. + * @param {number} fieldNumber + */ + clearField(fieldNumber) { + this.fields_.delete(fieldNumber); + } + + /** + * Returns data for a field specified by the given field number. Also cache + * the data if it doesn't already exist in the cache. When no data is + * available, return the given default value. + * @param {number} fieldNumber + * @param {?T} defaultValue + * @param {function(!Array, !BufferDecoder):T} readFunc + * @param {function(!Writer, number, T)=} encoder + * @return {T} + * @template T + * @private + */ + getFieldWithDefault_( + fieldNumber, defaultValue, readFunc, encoder = undefined) { + checkFieldNumber(fieldNumber); + + const field = this.fields_.get(fieldNumber); + if (field === undefined) { + return defaultValue; + } + + if (field.hasDecodedValue()) { + checkState(!encoder || !!field.getEncoder()); + return field.getDecodedValue(); + } + + const parsed = readFunc( + checkDefAndNotNull(field.getIndexArray()), + checkDefAndNotNull(this.bufferDecoder_)); + field.setCache(parsed, encoder); + return parsed; + } + + /** + * Sets data for a singular field specified by the given field number. + * @param {number} fieldNumber + * @param {T} value + * @param {function(!Writer, number, T)} encoder + * @return {T} + * @template T + * @private + */ + setField_(fieldNumber, value, encoder) { + checkFieldNumber(fieldNumber); + this.fields_.set(fieldNumber, Field.fromDecodedValue(value, encoder)); + } + + /** + * Serializes internal contents to binary format bytes array to the + * given writer. + * @param {!Writer} writer + * @package + */ + serializeToWriter(writer) { + // If we use for...of here, jscompiler returns an array of both types for + // fieldNumber and field without specifying which type is for + // field, which prevents us to use fieldNumber. Thus, we use + // forEach here. + this.fields_.forEach((field, fieldNumber) => { + // If encoder doesn't exist, there is no need to encode the value + // because the data in the index is still valid. + if (field.getEncoder() !== undefined) { + const encoder = checkDefAndNotNull(field.getEncoder()); + encoder(writer, fieldNumber, field.getDecodedValue()); + return; + } + + const indexArr = field.getIndexArray(); + if (indexArr) { + for (const indexEntry of indexArr) { + writer.writeTag(fieldNumber, Field.getWireType(indexEntry)); + writer.writeBufferDecoder( + checkDefAndNotNull(this.bufferDecoder_), + Field.getStartIndex(indexEntry), Field.getWireType(indexEntry)); + } + } + }); + } + + /** + * Serializes internal contents to binary format bytes array. + * @return {!ArrayBuffer} + */ + serialize() { + const writer = new Writer(); + this.serializeToWriter(writer); + return writer.getAndResetResultBuffer(); + } + + /** + * Returns whether data exists at the given field number. + * @param {number} fieldNumber + * @return {boolean} + */ + hasFieldNumber(fieldNumber) { + checkFieldNumber(fieldNumber); + const field = this.fields_.get(fieldNumber); + + if (field === undefined) { + return false; + } + + if (field.getIndexArray() !== null) { + return true; + } + + if (Array.isArray(field.getDecodedValue())) { + // For repeated fields, existence is decided by number of elements. + return (/** !Array */ (field.getDecodedValue())).length > 0; + } + return true; + } + + /*************************************************************************** + * OPTIONAL GETTER METHODS + ***************************************************************************/ + + /** + * Returns data as boolean for the given field number. + * If no default is given, use false as the default. + * @param {number} fieldNumber + * @param {boolean=} defaultValue + * @return {boolean} + */ + getBoolWithDefault(fieldNumber, defaultValue = false) { + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => + readOptional(indexArray, bytes, reader.readBool, WireType.VARINT)); + } + + /** + * Returns data as a ByteString for the given field number. + * If no default is given, use false as the default. + * @param {number} fieldNumber + * @param {!ByteString=} defaultValue + * @return {!ByteString} + */ + getBytesWithDefault(fieldNumber, defaultValue = ByteString.EMPTY) { + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readBytes, WireType.DELIMITED)); + } + + /** + * Returns a double for the given field number. + * If no default is given uses zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getDoubleWithDefault(fieldNumber, defaultValue = 0) { + checkTypeDouble(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readDouble, WireType.FIXED64)); + } + + /** + * Returns a fixed32 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getFixed32WithDefault(fieldNumber, defaultValue = 0) { + checkTypeUnsignedInt32(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readFixed32, WireType.FIXED32)); + } + + /** + * Returns a fixed64 for the given field number. + * Note: Since g.m.Long does not support unsigned int64 values we are going + * the Java route here for now and simply output the number as a signed int64. + * Users can get to individual bits by themselves. + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getFixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.getSfixed64WithDefault(fieldNumber, defaultValue); + } + + /** + * Returns a float for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getFloatWithDefault(fieldNumber, defaultValue = 0) { + checkTypeFloat(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readFloat, WireType.FIXED32)); + } + + /** + * Returns a int32 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getInt32WithDefault(fieldNumber, defaultValue = 0) { + checkTypeSignedInt32(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => + readOptional(indexArray, bytes, reader.readInt32, WireType.VARINT)); + } + + /** + * Returns a int64 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getInt64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + checkTypeSignedInt64(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => + readOptional(indexArray, bytes, reader.readInt64, WireType.VARINT)); + } + + /** + * Returns a sfixed32 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getSfixed32WithDefault(fieldNumber, defaultValue = 0) { + checkTypeSignedInt32(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readSfixed32, WireType.FIXED32)); + } + + /** + * Returns a sfixed64 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getSfixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + checkTypeSignedInt64(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readSfixed64, WireType.FIXED64)); + } + + /** + * Returns a sint32 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getSint32WithDefault(fieldNumber, defaultValue = 0) { + checkTypeSignedInt32(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readSint32, WireType.VARINT)); + } + + /** + * Returns a sint64 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getSint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + checkTypeSignedInt64(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readSint64, WireType.VARINT)); + } + + /** + * Returns a string for the given field number. + * If no default is given uses empty string as the default. + * @param {number} fieldNumber + * @param {string=} defaultValue + * @return {string} + */ + getStringWithDefault(fieldNumber, defaultValue = '') { + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readString, WireType.DELIMITED)); + } + + /** + * Returns a uint32 for the given field number. + * If no default is given zero as the default. + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getUint32WithDefault(fieldNumber, defaultValue = 0) { + checkTypeUnsignedInt32(defaultValue); + return this.getFieldWithDefault_( + fieldNumber, defaultValue, + (indexArray, bytes) => readOptional( + indexArray, bytes, reader.readUint32, WireType.VARINT)); + } + + /** + * Returns a uint64 for the given field number. + * Note: Since g.m.Long does not support unsigned int64 values we are going + * the Java route here for now and simply output the number as a signed int64. + * Users can get to individual bits by themselves. + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getUint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.getInt64WithDefault(fieldNumber, defaultValue); + } + + /** + * Returns data as a mutable proto Message for the given field number. + * If no value has been set, return null. + * If hasFieldNumber(fieldNumber) == false before calling, it remains false. + * + * This method should not be used along with getMessage, since calling + * getMessageOrNull after getMessage will not register the encoder. + * + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number=} pivot + * @return {?T} + * @template T + */ + getMessageOrNull(fieldNumber, instanceCreator, pivot = undefined) { + return this.getFieldWithDefault_( + fieldNumber, null, + (indexArray, bytes) => + readMessage(indexArray, bytes, instanceCreator, pivot), + writeMessage); + } + + /** + * Returns data as a mutable proto Message for the given field number. + * If no value has been set previously, creates and attaches an instance. + * Postcondition: hasFieldNumber(fieldNumber) == true. + * + * This method should not be used along with getMessage, since calling + * getMessageAttach after getMessage will not register the encoder. + * + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number=} pivot + * @return {T} + * @template T + */ + getMessageAttach(fieldNumber, instanceCreator, pivot = undefined) { + checkInstanceCreator(instanceCreator); + let instance = this.getMessageOrNull(fieldNumber, instanceCreator, pivot); + if (!instance) { + instance = instanceCreator(LazyAccessor.createEmpty()); + this.setField_(fieldNumber, instance, writeMessage); + } + return instance; + } + + /** + * Returns data as a proto Message for the given field number. + * If no value has been set, return a default instance. + * This default instance is guaranteed to be the same instance, unless this + * field is cleared. + * Does not register the encoder, so changes made to the returned + * sub-message will not be included when serializing the parent message. + * Use getMessageAttach() if the resulting sub-message should be mutable. + * + * This method should not be used along with getMessageOrNull or + * getMessageAttach, since these methods register the encoder. + * + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number=} pivot + * @return {T} + * @template T + */ + getMessage(fieldNumber, instanceCreator, pivot = undefined) { + checkInstanceCreator(instanceCreator); + const message = this.getFieldWithDefault_( + fieldNumber, null, + (indexArray, bytes) => + readMessage(indexArray, bytes, instanceCreator, pivot)); + // Returns an empty message as the default value if the field doesn't exist. + // We don't pass the default value to getFieldWithDefault_ to reduce object + // allocation. + return message === null ? instanceCreator(LazyAccessor.createEmpty()) : + message; + } + + /** + * Returns the accessor for the given singular message, or returns null if + * it hasn't been set. + * @param {number} fieldNumber + * @param {number=} pivot + * @return {?LazyAccessor} + */ + getMessageAccessorOrNull(fieldNumber, pivot = undefined) { + checkFieldNumber(fieldNumber); + const field = this.fields_.get(fieldNumber); + if (field === undefined) { + return null; + } + + if (field.hasDecodedValue()) { + return checkIsInternalMessage(field.getDecodedValue()) + .internalGetKernel(); + } else { + return readAccessor( + checkDefAndNotNull(field.getIndexArray()), + checkDefAndNotNull(this.bufferDecoder_), pivot); + } + } + + /*************************************************************************** + * REPEATED GETTER METHODS + ***************************************************************************/ + + /* Bool */ + + /** + * Returns an Array instance containing boolean values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedBoolArray_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readBool, reader.readPackedBool, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {boolean} + */ + getRepeatedBoolElement(fieldNumber, index) { + const array = this.getRepeatedBoolArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing boolean values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedBoolIterable(fieldNumber) { + const array = this.getRepeatedBoolArray_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedBoolSize(fieldNumber) { + return this.getRepeatedBoolArray_(fieldNumber).length; + } + + /* Double */ + + /** + * Returns an Array instance containing double values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedDoubleArray_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readDouble, reader.readPackedDouble, + WireType.FIXED64)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedDoubleElement(fieldNumber, index) { + const array = this.getRepeatedDoubleArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing double values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedDoubleIterable(fieldNumber) { + const array = this.getRepeatedDoubleArray_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedDoubleSize(fieldNumber) { + return this.getRepeatedDoubleArray_(fieldNumber).length; + } + + /* Fixed32 */ + + /** + * Returns an Array instance containing fixed32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedFixed32Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readFixed32, reader.readPackedFixed32, + WireType.FIXED32)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedFixed32Element(fieldNumber, index) { + const array = this.getRepeatedFixed32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing fixed32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFixed32Iterable(fieldNumber) { + const array = this.getRepeatedFixed32Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFixed32Size(fieldNumber) { + return this.getRepeatedFixed32Array_(fieldNumber).length; + } + + /* Fixed64 */ + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedFixed64Element(fieldNumber, index) { + return this.getRepeatedSfixed64Element(fieldNumber, index); + } + + /** + * Returns an Iterable instance containing fixed64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFixed64Iterable(fieldNumber) { + return this.getRepeatedSfixed64Iterable(fieldNumber); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFixed64Size(fieldNumber) { + return this.getRepeatedSfixed64Size(fieldNumber); + } + + /* Float */ + + /** + * Returns an Array instance containing float values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedFloatArray_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readFloat, reader.readPackedFloat, + WireType.FIXED32)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedFloatElement(fieldNumber, index) { + const array = this.getRepeatedFloatArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing float values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFloatIterable(fieldNumber) { + const array = this.getRepeatedFloatArray_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFloatSize(fieldNumber) { + return this.getRepeatedFloatArray_(fieldNumber).length; + } + + /* Int32 */ + + /** + * Returns an Array instance containing int32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedInt32Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readInt32, reader.readPackedInt32, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedInt32Element(fieldNumber, index) { + const array = this.getRepeatedInt32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing int32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedInt32Iterable(fieldNumber) { + const array = this.getRepeatedInt32Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedInt32Size(fieldNumber) { + return this.getRepeatedInt32Array_(fieldNumber).length; + } + + /* Int64 */ + + /** + * Returns an Array instance containing int64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedInt64Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readInt64, reader.readPackedInt64, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedInt64Element(fieldNumber, index) { + const array = this.getRepeatedInt64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing int64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedInt64Iterable(fieldNumber) { + const array = this.getRepeatedInt64Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedInt64Size(fieldNumber) { + return this.getRepeatedInt64Array_(fieldNumber).length; + } + + /* Sfixed32 */ + + /** + * Returns an Array instance containing sfixed32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedSfixed32Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readSfixed32, reader.readPackedSfixed32, + WireType.FIXED32)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedSfixed32Element(fieldNumber, index) { + const array = this.getRepeatedSfixed32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing sfixed32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSfixed32Iterable(fieldNumber) { + const array = this.getRepeatedSfixed32Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSfixed32Size(fieldNumber) { + return this.getRepeatedSfixed32Array_(fieldNumber).length; + } + + /* Sfixed64 */ + + /** + * Returns an Array instance containing sfixed64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedSfixed64Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readSfixed64, reader.readPackedSfixed64, + WireType.FIXED64)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedSfixed64Element(fieldNumber, index) { + const array = this.getRepeatedSfixed64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing sfixed64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSfixed64Iterable(fieldNumber) { + const array = this.getRepeatedSfixed64Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSfixed64Size(fieldNumber) { + return this.getRepeatedSfixed64Array_(fieldNumber).length; + } + + /* Sint32 */ + + /** + * Returns an Array instance containing sint32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedSint32Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readSint32, reader.readPackedSint32, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedSint32Element(fieldNumber, index) { + const array = this.getRepeatedSint32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing sint32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSint32Iterable(fieldNumber) { + const array = this.getRepeatedSint32Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSint32Size(fieldNumber) { + return this.getRepeatedSint32Array_(fieldNumber).length; + } + + /* Sint64 */ + + /** + * Returns an Array instance containing sint64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedSint64Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readSint64, reader.readPackedSint64, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedSint64Element(fieldNumber, index) { + const array = this.getRepeatedSint64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing sint64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSint64Iterable(fieldNumber) { + const array = this.getRepeatedSint64Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSint64Size(fieldNumber) { + return this.getRepeatedSint64Array_(fieldNumber).length; + } + + /* Uint32 */ + + /** + * Returns an Array instance containing uint32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedUint32Array_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => readRepeatedPrimitive( + indexArray, bytes, reader.readUint32, reader.readPackedUint32, + WireType.VARINT)); + } + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedUint32Element(fieldNumber, index) { + const array = this.getRepeatedUint32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing uint32 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedUint32Iterable(fieldNumber) { + const array = this.getRepeatedUint32Array_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedUint32Size(fieldNumber) { + return this.getRepeatedUint32Array_(fieldNumber).length; + } + + /* Uint64 */ + + /** + * Returns the element at index for the given field number. + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedUint64Element(fieldNumber, index) { + return this.getRepeatedInt64Element(fieldNumber, index); + } + + /** + * Returns an Iterable instance containing uint64 values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedUint64Iterable(fieldNumber) { + return this.getRepeatedInt64Iterable(fieldNumber); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedUint64Size(fieldNumber) { + return this.getRepeatedInt64Size(fieldNumber); + } + + /* Bytes */ + + /** + * Returns an array instance containing bytes values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedBytesArray_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bytes) => + readRepeatedNonPrimitive(indexArray, bytes, reader.readBytes)); + } + + /** + * Returns the element at index for the given field number as a bytes. + * @param {number} fieldNumber + * @param {number} index + * @return {!ByteString} + */ + getRepeatedBytesElement(fieldNumber, index) { + const array = this.getRepeatedBytesArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing bytes values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedBytesIterable(fieldNumber) { + const array = this.getRepeatedBytesArray_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedBytesSize(fieldNumber) { + return this.getRepeatedBytesArray_(fieldNumber).length; + } + + /* String */ + + /** + * Returns an array instance containing string values for the given field + * number. + * @param {number} fieldNumber + * @return {!Array} + * @private + */ + getRepeatedStringArray_(fieldNumber) { + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], + (indexArray, bufferDecoder) => readRepeatedNonPrimitive( + indexArray, bufferDecoder, reader.readString)); + } + + /** + * Returns the element at index for the given field number as a string. + * @param {number} fieldNumber + * @param {number} index + * @return {string} + */ + getRepeatedStringElement(fieldNumber, index) { + const array = this.getRepeatedStringArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing string values for the given field + * number. + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedStringIterable(fieldNumber) { + const array = this.getRepeatedStringArray_(fieldNumber); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedStringSize(fieldNumber) { + return this.getRepeatedStringArray_(fieldNumber).length; + } + + /* Message */ + + /** + * Returns an Array instance containing boolean values for the given field + * number. + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number|undefined} pivot + * @return {!Array} + * @template T + * @private + */ + getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot) { + checkInstanceCreator(instanceCreator); + const bytesInstanceCreator = (bufferDecoder) => + instanceCreator(LazyAccessor.fromBufferDecoder_(bufferDecoder, pivot)); + const readMessageFunc = (bufferDecoder, start) => + bytesInstanceCreator(reader.readDelimited(bufferDecoder, start)); + + const readRepeatedMessageFunc = (indexArray, bufferDecoder) => + readRepeatedNonPrimitive(indexArray, bufferDecoder, readMessageFunc); + const encoder = (writer, fieldNumber, values) => + writeRepeatedMessage(writer, fieldNumber, values); + return this.getFieldWithDefault_( + fieldNumber, /* defaultValue= */[], readRepeatedMessageFunc, encoder); + } + + /** + * Returns the element at index for the given field number as a message. + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number} index + * @param {number=} pivot + * @return {T} + * @template T + */ + getRepeatedMessageElement( + fieldNumber, instanceCreator, index, pivot = undefined) { + const array = + this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot); + checkCriticalElementIndex(index, array.length); + return array[index]; + } + + /** + * Returns an Iterable instance containing message values for the given field + * number. + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number=} pivot + * @return {!Iterable} + * @template T + */ + getRepeatedMessageIterable(fieldNumber, instanceCreator, pivot = undefined) { + const array = + this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot); + return new ArrayIterable(array); + } + + /** + * Returns an Iterable instance containing message accessors for the given + * field number. + * @param {number} fieldNumber + * @param {number=} pivot + * @return {!Iterable} + */ + getRepeatedMessageAccessorIterable(fieldNumber, pivot = undefined) { + checkFieldNumber(fieldNumber); + + const field = this.fields_.get(fieldNumber); + if (!field) { + return []; + } + + if (field.hasDecodedValue()) { + return new ArrayIterable(field.getDecodedValue().map( + value => checkIsInternalMessage(value).internalGetKernel())); + } + + const readMessageFunc = (bufferDecoder, start) => + LazyAccessor.fromBufferDecoder_( + reader.readDelimited(bufferDecoder, start), pivot); + const array = readRepeatedNonPrimitive( + checkDefAndNotNull(field.getIndexArray()), + checkDefAndNotNull(this.bufferDecoder_), readMessageFunc); + return new ArrayIterable(array); + } + + /** + * Returns the size of the repeated field. + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {number} + * @param {number=} pivot + * @template T + */ + getRepeatedMessageSize(fieldNumber, instanceCreator, pivot = undefined) { + return this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot) + .length; + } + + /*************************************************************************** + * OPTIONAL SETTER METHODS + ***************************************************************************/ + + /** + * Sets a boolean value to the field with the given field number. + * @param {number} fieldNumber + * @param {boolean} value + */ + setBool(fieldNumber, value) { + checkCriticalTypeBool(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeBool(fieldNumber, value); + }); + } + + /** + * Sets a boolean value to the field with the given field number. + * @param {number} fieldNumber + * @param {!ByteString} value + */ + setBytes(fieldNumber, value) { + checkCriticalTypeByteString(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeBytes(fieldNumber, value); + }); + } + + /** + * Sets a double value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setDouble(fieldNumber, value) { + checkCriticalTypeDouble(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeDouble(fieldNumber, value); + }); + } + + /** + * Sets a fixed32 value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setFixed32(fieldNumber, value) { + checkCriticalTypeUnsignedInt32(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeFixed32(fieldNumber, value); + }); + } + + /** + * Sets a uint64 value to the field with the given field number.\ + * Note: Since g.m.Long does not support unsigned int64 values we are going + * the Java route here for now and simply output the number as a signed int64. + * Users can get to individual bits by themselves. + * @param {number} fieldNumber + * @param {!Int64} value + */ + setFixed64(fieldNumber, value) { + this.setSfixed64(fieldNumber, value); + } + + /** + * Sets a float value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setFloat(fieldNumber, value) { + checkCriticalTypeFloat(value); + // Eagerly round to 32-bit precision so that reading back after set will + // yield the same value a reader will receive after serialization. + const floatValue = Math.fround(value); + this.setField_(fieldNumber, floatValue, (writer, fieldNumber, value) => { + writer.writeFloat(fieldNumber, value); + }); + } + + /** + * Sets a int32 value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setInt32(fieldNumber, value) { + checkCriticalTypeSignedInt32(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeInt32(fieldNumber, value); + }); + } + + /** + * Sets a int64 value to the field with the given field number. + * @param {number} fieldNumber + * @param {!Int64} value + */ + setInt64(fieldNumber, value) { + checkCriticalTypeSignedInt64(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeInt64(fieldNumber, value); + }); + } + + /** + * Sets a sfixed32 value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setSfixed32(fieldNumber, value) { + checkCriticalTypeSignedInt32(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeSfixed32(fieldNumber, value); + }); + } + + /** + * Sets a sfixed64 value to the field with the given field number. + * @param {number} fieldNumber + * @param {!Int64} value + */ + setSfixed64(fieldNumber, value) { + checkCriticalTypeSignedInt64(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeSfixed64(fieldNumber, value); + }); + } + + /** + * Sets a sint32 value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setSint32(fieldNumber, value) { + checkCriticalTypeSignedInt32(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeSint32(fieldNumber, value); + }); + } + + /** + * Sets a sint64 value to the field with the given field number. + * @param {number} fieldNumber + * @param {!Int64} value + */ + setSint64(fieldNumber, value) { + checkCriticalTypeSignedInt64(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeSint64(fieldNumber, value); + }); + } + + /** + * Sets a boolean value to the field with the given field number. + * @param {number} fieldNumber + * @param {string} value + */ + setString(fieldNumber, value) { + checkCriticalTypeString(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeString(fieldNumber, value); + }); + } + + /** + * Sets a uint32 value to the field with the given field number. + * @param {number} fieldNumber + * @param {number} value + */ + setUint32(fieldNumber, value) { + checkCriticalTypeUnsignedInt32(value); + this.setField_(fieldNumber, value, (writer, fieldNumber, value) => { + writer.writeUint32(fieldNumber, value); + }); + } + + /** + * Sets a uint64 value to the field with the given field number.\ + * Note: Since g.m.Long does not support unsigned int64 values we are going + * the Java route here for now and simply output the number as a signed int64. + * Users can get to individual bits by themselves. + * @param {number} fieldNumber + * @param {!Int64} value + */ + setUint64(fieldNumber, value) { + this.setInt64(fieldNumber, value); + } + + /** + * Sets a proto Message to the field with the given field number. + * Instead of working with the LazyAccessor inside of the message directly, we + * need the message instance to keep its reference equality for subsequent + * gettings. + * @param {number} fieldNumber + * @param {!InternalMessage} value + */ + setMessage(fieldNumber, value) { + checkCriticalType( + value !== null, 'Given value is not a message instance: null'); + this.setField_(fieldNumber, value, writeMessage); + } + + /*************************************************************************** + * REPEATED SETTER METHODS + ***************************************************************************/ + + /* Bool */ + + /** + * Adds all boolean values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedBoolIterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedBoolArray_(fieldNumber), ...values]; + checkCriticalTypeBoolArray(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single boolean value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {boolean} value + */ + addPackedBoolElement(fieldNumber, value) { + this.addRepeatedBoolIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedBool(fieldNumber, values); + }); + } + + /** + * Adds all boolean values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedBoolIterable(fieldNumber, values) { + this.addRepeatedBoolIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedBool(fieldNumber, values); + }); + } + + /** + * Adds a single boolean value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {boolean} value + */ + addUnpackedBoolElement(fieldNumber, value) { + this.addRepeatedBoolIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedBool(fieldNumber, values); + }); + } + + /** + * Adds all boolean values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedBoolIterable(fieldNumber, values) { + this.addRepeatedBoolIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedBool(fieldNumber, values); + }); + } + + /** + * Sets a single boolean value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {boolean} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedBoolElement_(fieldNumber, index, value, encoder) { + checkCriticalTypeBool(value); + const array = this.getRepeatedBoolArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single boolean value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {boolean} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedBoolElement(fieldNumber, index, value) { + this.setRepeatedBoolElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedBool(fieldNumber, values); + }); + } + + /** + * Sets all boolean values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedBoolIterable(fieldNumber, values) { + const /** !Array */ array = Array.from(values); + checkCriticalTypeBoolArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedBool(fieldNumber, values); + }); + } + + /** + * Sets a single boolean value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {boolean} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedBoolElement(fieldNumber, index, value) { + this.setRepeatedBoolElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedBool(fieldNumber, values); + }); + } + + /** + * Sets all boolean values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedBoolIterable(fieldNumber, values) { + const /** !Array */ array = Array.from(values); + checkCriticalTypeBoolArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedBool(fieldNumber, values); + }); + } + + /* Double */ + + /** + * Adds all double values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedDoubleIterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedDoubleArray_(fieldNumber), ...values]; + checkCriticalTypeDoubleArray(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single double value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedDoubleElement(fieldNumber, value) { + this.addRepeatedDoubleIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedDouble(fieldNumber, values); + }); + } + + /** + * Adds all double values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedDoubleIterable(fieldNumber, values) { + this.addRepeatedDoubleIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedDouble(fieldNumber, values); + }); + } + + /** + * Adds a single double value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedDoubleElement(fieldNumber, value) { + this.addRepeatedDoubleIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedDouble(fieldNumber, values); + }); + } + + /** + * Adds all double values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedDoubleIterable(fieldNumber, values) { + this.addRepeatedDoubleIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedDouble(fieldNumber, values); + }); + } + + /** + * Sets a single double value into the field for the given field number at the + * given index. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedDoubleElement_(fieldNumber, index, value, encoder) { + checkCriticalTypeDouble(value); + const array = this.getRepeatedDoubleArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single double value into the field for the given field number at the + * given index. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedDoubleElement(fieldNumber, index, value) { + this.setRepeatedDoubleElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedDouble(fieldNumber, values); + }); + } + + /** + * Sets all double values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedDoubleIterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeDoubleArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedDouble(fieldNumber, values); + }); + } + + /** + * Sets a single double value into the field for the given field number at the + * given index. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedDoubleElement(fieldNumber, index, value) { + this.setRepeatedDoubleElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedDouble(fieldNumber, values); + }); + } + + /** + * Sets all double values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedDoubleIterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeDoubleArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedDouble(fieldNumber, values); + }); + } + + /* Fixed32 */ + + /** + * Adds all fixed32 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedFixed32Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedFixed32Array_(fieldNumber), ...values]; + checkCriticalTypeUnsignedInt32Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single fixed32 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedFixed32Element(fieldNumber, value) { + this.addRepeatedFixed32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedFixed32(fieldNumber, values); + }); + } + + /** + * Adds all fixed32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFixed32Iterable(fieldNumber, values) { + this.addRepeatedFixed32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedFixed32(fieldNumber, values); + }); + } + + /** + * Adds a single fixed32 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedFixed32Element(fieldNumber, value) { + this.addRepeatedFixed32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedFixed32(fieldNumber, values); + }); + } + + /** + * Adds all fixed32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFixed32Iterable(fieldNumber, values) { + this.addRepeatedFixed32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedFixed32(fieldNumber, values); + }); + } + + /** + * Sets a single fixed32 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedFixed32Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeUnsignedInt32(value); + const array = this.getRepeatedFixed32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single fixed32 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFixed32Element(fieldNumber, index, value) { + this.setRepeatedFixed32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedFixed32(fieldNumber, values); + }); + } + + /** + * Sets all fixed32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFixed32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeUnsignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedFixed32(fieldNumber, values); + }); + } + + /** + * Sets a single fixed32 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFixed32Element(fieldNumber, index, value) { + this.setRepeatedFixed32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedFixed32(fieldNumber, values); + }); + } + + /** + * Sets all fixed32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFixed32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeUnsignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedFixed32(fieldNumber, values); + }); + } + + /* Fixed64 */ + + /** + * Adds a single fixed64 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedFixed64Element(fieldNumber, value) { + this.addPackedSfixed64Element(fieldNumber, value); + } + + /** + * Adds all fixed64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFixed64Iterable(fieldNumber, values) { + this.addPackedSfixed64Iterable(fieldNumber, values); + } + + /** + * Adds a single fixed64 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedFixed64Element(fieldNumber, value) { + this.addUnpackedSfixed64Element(fieldNumber, value); + } + + /** + * Adds all fixed64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFixed64Iterable(fieldNumber, values) { + this.addUnpackedSfixed64Iterable(fieldNumber, values); + } + + /** + * Sets a single fixed64 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFixed64Element(fieldNumber, index, value) { + this.setPackedSfixed64Element(fieldNumber, index, value); + } + + /** + * Sets all fixed64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFixed64Iterable(fieldNumber, values) { + this.setPackedSfixed64Iterable(fieldNumber, values); + } + + /** + * Sets a single fixed64 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFixed64Element(fieldNumber, index, value) { + this.setUnpackedSfixed64Element(fieldNumber, index, value); + } + + /** + * Sets all fixed64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFixed64Iterable(fieldNumber, values) { + this.setUnpackedSfixed64Iterable(fieldNumber, values); + } + + /* Float */ + + /** + * Adds all float values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedFloatIterable_(fieldNumber, values, encoder) { + checkCriticalTypeFloatIterable(values); + // Eagerly round to 32-bit precision so that reading back after set will + // yield the same value a reader will receive after serialization. + const floatValues = Array.from(values, fround); + const array = [...this.getRepeatedFloatArray_(fieldNumber), ...floatValues]; + checkCriticalTypeFloatIterable(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single float value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedFloatElement(fieldNumber, value) { + this.addRepeatedFloatIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedFloat(fieldNumber, values); + }); + } + + /** + * Adds all float values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFloatIterable(fieldNumber, values) { + this.addRepeatedFloatIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedFloat(fieldNumber, values); + }); + } + + /** + * Adds a single float value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedFloatElement(fieldNumber, value) { + this.addRepeatedFloatIterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedFloat(fieldNumber, values); + }); + } + + /** + * Adds all float values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFloatIterable(fieldNumber, values) { + this.addRepeatedFloatIterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedFloat(fieldNumber, values); + }); + } + + /** + * Sets a single float value into the field for the given field number at the + * given index. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedFloatElement_(fieldNumber, index, value, encoder) { + checkCriticalTypeFloat(value); + // Eagerly round to 32-bit precision so that reading back after set will + // yield the same value a reader will receive after serialization. + const floatValue = Math.fround(value); + const array = this.getRepeatedFloatArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = floatValue; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single float value into the field for the given field number at the + * given index. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFloatElement(fieldNumber, index, value) { + this.setRepeatedFloatElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedFloat(fieldNumber, values); + }); + } + + /** + * Sets all float values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFloatIterable(fieldNumber, values) { + checkCriticalTypeFloatIterable(values); + // Eagerly round to 32-bit precision so that reading back after set will + // yield the same value a reader will receive after serialization. + const array = Array.from(values, fround); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedFloat(fieldNumber, values); + }); + } + + /** + * Sets a single float value into the field for the given field number at the + * given index. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFloatElement(fieldNumber, index, value) { + this.setRepeatedFloatElement_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedFloat(fieldNumber, values); + }); + } + + /** + * Sets all float values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFloatIterable(fieldNumber, values) { + checkCriticalTypeFloatIterable(values); + // Eagerly round to 32-bit precision so that reading back after set will + // yield the same value a reader will receive after serialization. + const array = Array.from(values, fround); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedFloat(fieldNumber, values); + }); + } + + /* Int32 */ + + /** + * Adds all int32 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedInt32Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedInt32Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt32Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single int32 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedInt32Element(fieldNumber, value) { + this.addRepeatedInt32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedInt32(fieldNumber, values); + }); + } + + /** + * Adds all int32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedInt32Iterable(fieldNumber, values) { + this.addRepeatedInt32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedInt32(fieldNumber, values); + }); + } + + /** + * Adds a single int32 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedInt32Element(fieldNumber, value) { + this.addRepeatedInt32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedInt32(fieldNumber, values); + }); + } + + /** + * Adds all int32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedInt32Iterable(fieldNumber, values) { + this.addRepeatedInt32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedInt32(fieldNumber, values); + }); + } + + /** + * Sets a single int32 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedInt32Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt32(value); + const array = this.getRepeatedInt32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single int32 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedInt32Element(fieldNumber, index, value) { + this.setRepeatedInt32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedInt32(fieldNumber, values); + }); + } + + /** + * Sets all int32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedInt32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedInt32(fieldNumber, values); + }); + } + + /** + * Sets a single int32 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedInt32Element(fieldNumber, index, value) { + this.setRepeatedInt32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedInt32(fieldNumber, values); + }); + } + + /** + * Sets all int32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedInt32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedInt32(fieldNumber, values); + }); + } + + /* Int64 */ + + /** + * Adds all int64 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedInt64Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedInt64Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt64Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single int64 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedInt64Element(fieldNumber, value) { + this.addRepeatedInt64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedInt64(fieldNumber, values); + }); + } + + /** + * Adds all int64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedInt64Iterable(fieldNumber, values) { + this.addRepeatedInt64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedInt64(fieldNumber, values); + }); + } + + /** + * Adds a single int64 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedInt64Element(fieldNumber, value) { + this.addRepeatedInt64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedInt64(fieldNumber, values); + }); + } + + /** + * Adds all int64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedInt64Iterable(fieldNumber, values) { + this.addRepeatedInt64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedInt64(fieldNumber, values); + }); + } + + /** + * Sets a single int64 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedInt64Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt64(value); + const array = this.getRepeatedInt64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single int64 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedInt64Element(fieldNumber, index, value) { + this.setRepeatedInt64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedInt64(fieldNumber, values); + }); + } + + /** + * Sets all int64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedInt64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedInt64(fieldNumber, values); + }); + } + + /** + * Sets a single int64 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedInt64Element(fieldNumber, index, value) { + this.setRepeatedInt64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedInt64(fieldNumber, values); + }); + } + + /** + * Sets all int64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedInt64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedInt64(fieldNumber, values); + }); + } + + /* Sfixed32 */ + + /** + * Adds all sfixed32 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedSfixed32Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedSfixed32Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt32Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single sfixed32 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedSfixed32Element(fieldNumber, value) { + this.addRepeatedSfixed32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedSfixed32(fieldNumber, values); + }); + } + + /** + * Adds all sfixed32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSfixed32Iterable(fieldNumber, values) { + this.addRepeatedSfixed32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedSfixed32(fieldNumber, values); + }); + } + + /** + * Adds a single sfixed32 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedSfixed32Element(fieldNumber, value) { + this.addRepeatedSfixed32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed32(fieldNumber, values); + }); + } + + /** + * Adds all sfixed32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSfixed32Iterable(fieldNumber, values) { + this.addRepeatedSfixed32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed32(fieldNumber, values); + }); + } + + /** + * Sets a single sfixed32 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedSfixed32Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt32(value); + const array = this.getRepeatedSfixed32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single sfixed32 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSfixed32Element(fieldNumber, index, value) { + this.setRepeatedSfixed32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedSfixed32(fieldNumber, values); + }); + } + + /** + * Sets all sfixed32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSfixed32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedSfixed32(fieldNumber, values); + }); + } + + /** + * Sets a single sfixed32 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSfixed32Element(fieldNumber, index, value) { + this.setRepeatedSfixed32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed32(fieldNumber, values); + }); + } + + /** + * Sets all sfixed32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSfixed32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed32(fieldNumber, values); + }); + } + + /* Sfixed64 */ + + /** + * Adds all sfixed64 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedSfixed64Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedSfixed64Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt64Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single sfixed64 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedSfixed64Element(fieldNumber, value) { + this.addRepeatedSfixed64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedSfixed64(fieldNumber, values); + }); + } + + /** + * Adds all sfixed64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSfixed64Iterable(fieldNumber, values) { + this.addRepeatedSfixed64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedSfixed64(fieldNumber, values); + }); + } + + /** + * Adds a single sfixed64 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedSfixed64Element(fieldNumber, value) { + this.addRepeatedSfixed64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed64(fieldNumber, values); + }); + } + + /** + * Adds all sfixed64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSfixed64Iterable(fieldNumber, values) { + this.addRepeatedSfixed64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed64(fieldNumber, values); + }); + } + + /** + * Sets a single sfixed64 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedSfixed64Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt64(value); + const array = this.getRepeatedSfixed64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single sfixed64 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSfixed64Element(fieldNumber, index, value) { + this.setRepeatedSfixed64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedSfixed64(fieldNumber, values); + }); + } + + /** + * Sets all sfixed64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSfixed64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedSfixed64(fieldNumber, values); + }); + } + + /** + * Sets a single sfixed64 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSfixed64Element(fieldNumber, index, value) { + this.setRepeatedSfixed64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed64(fieldNumber, values); + }); + } + + /** + * Sets all sfixed64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSfixed64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedSfixed64(fieldNumber, values); + }); + } + + /* Sint32 */ + + /** + * Adds all sint32 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedSint32Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedSint32Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt32Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single sint32 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedSint32Element(fieldNumber, value) { + this.addRepeatedSint32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedSint32(fieldNumber, values); + }); + } + + /** + * Adds all sint32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSint32Iterable(fieldNumber, values) { + this.addRepeatedSint32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedSint32(fieldNumber, values); + }); + } + + /** + * Adds a single sint32 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedSint32Element(fieldNumber, value) { + this.addRepeatedSint32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedSint32(fieldNumber, values); + }); + } + + /** + * Adds all sint32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSint32Iterable(fieldNumber, values) { + this.addRepeatedSint32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedSint32(fieldNumber, values); + }); + } + + /** + * Sets a single sint32 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedSint32Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt32(value); + const array = this.getRepeatedSint32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single sint32 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSint32Element(fieldNumber, index, value) { + this.setRepeatedSint32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedSint32(fieldNumber, values); + }); + } + + /** + * Sets all sint32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSint32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedSint32(fieldNumber, values); + }); + } + + /** + * Sets a single sint32 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSint32Element(fieldNumber, index, value) { + this.setRepeatedSint32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedSint32(fieldNumber, values); + }); + } + + /** + * Sets all sint32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSint32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedSint32(fieldNumber, values); + }); + } + + /* Sint64 */ + + /** + * Adds all sint64 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedSint64Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedSint64Array_(fieldNumber), ...values]; + checkCriticalTypeSignedInt64Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single sint64 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedSint64Element(fieldNumber, value) { + this.addRepeatedSint64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedSint64(fieldNumber, values); + }); + } + + /** + * Adds all sint64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSint64Iterable(fieldNumber, values) { + this.addRepeatedSint64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedSint64(fieldNumber, values); + }); + } + + /** + * Adds a single sint64 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedSint64Element(fieldNumber, value) { + this.addRepeatedSint64Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedSint64(fieldNumber, values); + }); + } + + /** + * Adds all sint64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSint64Iterable(fieldNumber, values) { + this.addRepeatedSint64Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedSint64(fieldNumber, values); + }); + } + + /** + * Sets a single sint64 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedSint64Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeSignedInt64(value); + const array = this.getRepeatedSint64Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single sint64 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSint64Element(fieldNumber, index, value) { + this.setRepeatedSint64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedSint64(fieldNumber, values); + }); + } + + /** + * Sets all sint64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSint64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedSint64(fieldNumber, values); + }); + } + + /** + * Sets a single sint64 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSint64Element(fieldNumber, index, value) { + this.setRepeatedSint64Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedSint64(fieldNumber, values); + }); + } + + /** + * Sets all sint64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSint64Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeSignedInt64Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedSint64(fieldNumber, values); + }); + } + + /* Uint32 */ + + /** + * Adds all uint32 values into the field for the given field number. + * How these values are encoded depends on the given write function. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!Writer, number, !Array): undefined} encoder + * @private + */ + addRepeatedUint32Iterable_(fieldNumber, values, encoder) { + const array = [...this.getRepeatedUint32Array_(fieldNumber), ...values]; + checkCriticalTypeUnsignedInt32Array(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Adds a single uint32 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} value + */ + addPackedUint32Element(fieldNumber, value) { + this.addRepeatedUint32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writePackedUint32(fieldNumber, values); + }); + } + + /** + * Adds all uint32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedUint32Iterable(fieldNumber, values) { + this.addRepeatedUint32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writePackedUint32(fieldNumber, values); + }); + } + + /** + * Adds a single uint32 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedUint32Element(fieldNumber, value) { + this.addRepeatedUint32Iterable_( + fieldNumber, [value], (writer, fieldNumber, values) => { + writer.writeRepeatedUint32(fieldNumber, values); + }); + } + + /** + * Adds all uint32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedUint32Iterable(fieldNumber, values) { + this.addRepeatedUint32Iterable_( + fieldNumber, values, (writer, fieldNumber, values) => { + writer.writeRepeatedUint32(fieldNumber, values); + }); + } + + /** + * Sets a single uint32 value into the field for the given field number at + * the given index. How these values are encoded depends on the given write + * function. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @param {function(!Writer, number, !Array): undefined} encoder + * @throws {!Error} if index is out of range when check mode is critical + * @private + */ + setRepeatedUint32Element_(fieldNumber, index, value, encoder) { + checkCriticalTypeUnsignedInt32(value); + const array = this.getRepeatedUint32Array_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, encoder); + } + + /** + * Sets a single uint32 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedUint32Element(fieldNumber, index, value) { + this.setRepeatedUint32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writePackedUint32(fieldNumber, values); + }); + } + + /** + * Sets all uint32 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedUint32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeUnsignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writePackedUint32(fieldNumber, values); + }); + } + + /** + * Sets a single uint32 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedUint32Element(fieldNumber, index, value) { + this.setRepeatedUint32Element_( + fieldNumber, index, value, (writer, fieldNumber, values) => { + writer.writeRepeatedUint32(fieldNumber, values); + }); + } + + /** + * Sets all uint32 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedUint32Iterable(fieldNumber, values) { + const array = Array.from(values); + checkCriticalTypeUnsignedInt32Array(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedUint32(fieldNumber, values); + }); + } + + /* Uint64 */ + + /** + * Adds a single uint64 value into the field for the given field number. + * All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedUint64Element(fieldNumber, value) { + this.addPackedInt64Element(fieldNumber, value); + } + + /** + * Adds all uint64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedUint64Iterable(fieldNumber, values) { + this.addPackedInt64Iterable(fieldNumber, values); + } + + /** + * Adds a single uint64 value into the field for the given field number. + * All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedUint64Element(fieldNumber, value) { + this.addUnpackedInt64Element(fieldNumber, value); + } + + /** + * Adds all uint64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedUint64Iterable(fieldNumber, values) { + this.addUnpackedInt64Iterable(fieldNumber, values); + } + + /** + * Sets a single uint64 value into the field for the given field number at + * the given index. All values will be encoded as packed values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedUint64Element(fieldNumber, index, value) { + this.setPackedInt64Element(fieldNumber, index, value); + } + + /** + * Sets all uint64 values into the field for the given field number. + * All these values will be encoded as packed values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedUint64Iterable(fieldNumber, values) { + this.setPackedInt64Iterable(fieldNumber, values); + } + + /** + * Sets a single uint64 value into the field for the given field number at + * the given index. All values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedUint64Element(fieldNumber, index, value) { + this.setUnpackedInt64Element(fieldNumber, index, value); + } + + /** + * Sets all uint64 values into the field for the given field number. + * All these values will be encoded as unpacked values. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedUint64Iterable(fieldNumber, values) { + this.setUnpackedInt64Iterable(fieldNumber, values); + } + + /* Bytes */ + + /** + * Sets all bytes values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setRepeatedBytesIterable(fieldNumber, values) { + const /** !Array */ array = Array.from(values); + checkCriticalTypeByteStringArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedBytes(fieldNumber, values); + }); + } + + /** + * Adds all bytes values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addRepeatedBytesIterable(fieldNumber, values) { + const array = [...this.getRepeatedBytesArray_(fieldNumber), ...values]; + checkCriticalTypeByteStringArray(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedBytes(fieldNumber, values); + }); + } + + /** + * Sets a single bytes value into the field for the given field number at + * the given index. + * @param {number} fieldNumber + * @param {number} index + * @param {!ByteString} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setRepeatedBytesElement(fieldNumber, index, value) { + checkCriticalTypeByteString(value); + const array = this.getRepeatedBytesArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedBytes(fieldNumber, values); + }); + } + + /** + * Adds a single bytes value into the field for the given field number. + * @param {number} fieldNumber + * @param {!ByteString} value + */ + addRepeatedBytesElement(fieldNumber, value) { + this.addRepeatedBytesIterable(fieldNumber, [value]); + } + + /* String */ + + /** + * Sets all string values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setRepeatedStringIterable(fieldNumber, values) { + const /** !Array */ array = Array.from(values); + checkCriticalTypeStringArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedString(fieldNumber, values); + }); + } + + /** + * Adds all string values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addRepeatedStringIterable(fieldNumber, values) { + const array = [...this.getRepeatedStringArray_(fieldNumber), ...values]; + checkCriticalTypeStringArray(array); + // Needs to set it back because the default empty array was not cached. + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedString(fieldNumber, values); + }); + } + + /** + * Sets a single string value into the field for the given field number at + * the given index. + * @param {number} fieldNumber + * @param {number} index + * @param {string} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setRepeatedStringElement(fieldNumber, index, value) { + checkCriticalTypeString(value); + const array = this.getRepeatedStringArray_(fieldNumber); + checkCriticalElementIndex(index, array.length); + array[index] = value; + // Needs to set it back to set encoder. + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writer.writeRepeatedString(fieldNumber, values); + }); + } + + /** + * Adds a single string value into the field for the given field number. + * @param {number} fieldNumber + * @param {string} value + */ + addRepeatedStringElement(fieldNumber, value) { + this.addRepeatedStringIterable(fieldNumber, [value]); + } + + /* Message */ + + /** + * Sets all message values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setRepeatedMessageIterable(fieldNumber, values) { + const /** !Array */ array = Array.from(values); + checkCriticalTypeMessageArray(array); + this.setField_(fieldNumber, array, (writer, fieldNumber, values) => { + writeRepeatedMessage(writer, fieldNumber, values); + }); + } + + /** + * Adds all message values into the field for the given field number. + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!LazyAccessor):!InternalMessage} instanceCreator + * @param {number=} pivot + */ + addRepeatedMessageIterable( + fieldNumber, values, instanceCreator, pivot = undefined) { + const array = [ + ...this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot), + ...values, + ]; + checkCriticalTypeMessageArray(array); + // Needs to set it back with the new array. + this.setField_( + fieldNumber, array, + (writer, fieldNumber, values) => + writeRepeatedMessage(writer, fieldNumber, values)); + } + + /** + * Sets a single message value into the field for the given field number at + * the given index. + * @param {number} fieldNumber + * @param {!InternalMessage} value + * @param {function(!LazyAccessor):!InternalMessage} instanceCreator + * @param {number} index + * @param {number=} pivot + * @throws {!Error} if index is out of range when check mode is critical + */ + setRepeatedMessageElement( + fieldNumber, value, instanceCreator, index, pivot = undefined) { + checkInstanceCreator(instanceCreator); + checkCriticalType( + value !== null, 'Given value is not a message instance: null'); + const array = + this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot); + checkCriticalElementIndex(index, array.length); + array[index] = value; + } + + /** + * Adds a single message value into the field for the given field number. + * @param {number} fieldNumber + * @param {!InternalMessage} value + * @param {function(!LazyAccessor):!InternalMessage} instanceCreator + * @param {number=} pivot + */ + addRepeatedMessageElement( + fieldNumber, value, instanceCreator, pivot = undefined) { + this.addRepeatedMessageIterable( + fieldNumber, [value], instanceCreator, pivot); + } +} + +exports = LazyAccessor; diff --git a/js/experimental/runtime/kernel/lazy_accessor_compatibility_test.js b/js/experimental/runtime/kernel/lazy_accessor_compatibility_test.js new file mode 100644 index 000000000000..d3eab20b9728 --- /dev/null +++ b/js/experimental/runtime/kernel/lazy_accessor_compatibility_test.js @@ -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} + */ +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); + }); +}); diff --git a/js/experimental/runtime/kernel/lazy_accessor_repeated_test.js b/js/experimental/runtime/kernel/lazy_accessor_repeated_test.js new file mode 100644 index 000000000000..77c4c5925ab6 --- /dev/null +++ b/js/experimental/runtime/kernel/lazy_accessor_repeated_test.js @@ -0,0 +1,7443 @@ +/** + * @fileoverview Tests for repeated methods in lazy_accessor.js. + */ +goog.module('protobuf.binary.LazyAccessorTest'); + +goog.setTestOnly(); + +const ByteString = goog.require('protobuf.ByteString'); +const Int64 = goog.require('protobuf.Int64'); +const InternalMessage = goog.require('protobuf.binary.InternalMessage'); +const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); +const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); +// Note to the reader: +// Since the lazy accessor 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 {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); + +/** + * @param {...number} bytes + * @return {!ArrayBuffer} + */ +function createArrayBuffer(...bytes) { + return new Uint8Array(bytes).buffer; +} + +/** + * Expects the Iterable instance yield the same values as the expected array. + * @param {!Iterable} iterable + * @param {!Array} expected + * @template T + * TODO: Implement this as a custom matcher. + */ +function expectEqualToArray(iterable, expected) { + const array = Array.from(iterable); + expect(array).toEqual(expected); +} + +/** + * Expects the Iterable instance yield qualified values. + * @param {!Iterable} iterable + * @param {(function(T): boolean)=} verify + * @template T + */ +function expectQualifiedIterable(iterable, verify) { + if (verify) { + for (const value of iterable) { + expect(verify(value)).toBe(true); + } + } +} + +/** + * Expects the Iterable instance yield the same values as the expected array of + * messages. + * @param {!Iterable} iterable + * @param {!Array} expected + * @template T + * TODO: Implement this as a custom matcher. + */ +function expectEqualToMessageArray(iterable, expected) { + const array = Array.from(iterable); + expect(array.length).toEqual(expected.length); + for (let i = 0; i < array.length; i++) { + const value = array[i].getBoolWithDefault(1, false); + const expectedValue = expected[i].getBoolWithDefault(1, false); + expect(value).toBe(expectedValue); + } +} + +describe('LazyAccessor for repeated boolean does', () => { + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + const list1 = accessor.getRepeatedBoolIterable(1); + const list2 = accessor.getRepeatedBoolIterable(1); + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + expect(accessor.getRepeatedBoolSize(1)).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const list1 = accessor.getRepeatedBoolIterable(1); + const list2 = accessor.getRepeatedBoolIterable(1); + expect(list1).not.toBe(list2); + }); + + it('return unpacked multibytes values from the input', () => { + const bytes = createArrayBuffer(0x08, 0x80, 0x01, 0x08, 0x80, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.addUnpackedBoolElement(1, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.addUnpackedBoolElement(1, false); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.addUnpackedBoolIterable(1, [true]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.addUnpackedBoolIterable(1, [false]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for setting single unpacked value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00, 0x08, 0x01)); + accessor.setUnpackedBoolElement(1, 0, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, true]); + }); + + it('return for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setUnpackedBoolIterable(1, [true]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.setUnpackedBoolIterable(1, [false]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [false]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + accessor.addUnpackedBoolElement(1, true); + accessor.addUnpackedBoolElement(1, false); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + accessor.addUnpackedBoolIterable(1, [true, false]); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for setting single unpacked value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x02, 0x00, 0x01)); + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x01); + accessor.setUnpackedBoolElement(1, 0, true); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + accessor.setUnpackedBoolIterable(1, [true, false]); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('return packed values from the input', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('ensure not the same instance returned for packed values', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const list1 = accessor.getRepeatedBoolIterable(1); + const list2 = accessor.getRepeatedBoolIterable(1); + expect(list1).not.toBe(list2); + }); + + it('return packed multibytes values from the input', () => { + const bytes = createArrayBuffer(0x0A, 0x04, 0x80, 0x01, 0x80, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.addPackedBoolElement(1, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.addPackedBoolElement(1, false); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.addPackedBoolIterable(1, [true]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.addPackedBoolIterable(1, [false]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, false]); + }); + + it('return for setting single packed value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00, 0x08, 0x01)); + accessor.setPackedBoolElement(1, 0, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true, true]); + }); + + it('return for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setPackedBoolIterable(1, [true]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + accessor.setPackedBoolIterable(1, [false]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [false]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + accessor.addPackedBoolElement(1, true); + accessor.addPackedBoolElement(1, false); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + accessor.addPackedBoolIterable(1, [true, false]); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for setting single packed value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00, 0x08, 0x01)); + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x01); + accessor.setPackedBoolElement(1, 0, true); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + accessor.setPackedBoolIterable(1, [true, false]); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('return combined values from the input', () => { + const bytes = + createArrayBuffer(0x08, 0x01, 0x0A, 0x02, 0x01, 0x00, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToArray( + accessor.getRepeatedBoolIterable(1), [true, true, false, false]); + }); + + it('return the repeated field element from the input', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getRepeatedBoolElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toEqual(true); + expect(accessor.getRepeatedBoolElement( + /* fieldNumber= */ 1, /* index= */ 1)) + .toEqual(false); + }); + + it('return the size from the input', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getRepeatedBoolSize(1)).toEqual(2); + }); + + it('fail when getting unpacked bool value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedBoolIterable(1); + }).toThrowError('Expected wire type: 0 but found: 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. + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [true]); + } + }); + + it('fail when adding unpacked bool values with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedBoolIterable(1, [fakeBoolean])) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.addUnpackedBoolIterable(1, [fakeBoolean]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when adding single unpacked bool value with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedBoolElement(1, fakeBoolean)) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.addUnpackedBoolElement(1, fakeBoolean); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when setting unpacked bool values with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedBoolIterable(1, [fakeBoolean])) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.setUnpackedBoolIterable(1, [fakeBoolean]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when setting single unpacked bool value with number value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedBoolElement(1, 0, fakeBoolean)) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.setUnpackedBoolElement(1, 0, fakeBoolean); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when adding packed bool values with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedBoolIterable(1, [fakeBoolean])) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.addPackedBoolIterable(1, [fakeBoolean]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when adding single packed bool value with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedBoolElement(1, fakeBoolean)) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.addPackedBoolElement(1, fakeBoolean); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when setting packed bool values with number value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedBoolIterable(1, [fakeBoolean])) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.setPackedBoolIterable(1, [fakeBoolean]); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when setting single packed bool value with number value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedBoolElement(1, 0, fakeBoolean)) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.setPackedBoolElement(1, 0, fakeBoolean); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [fakeBoolean]); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedBoolElement(1, 1, true)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedBoolElement(1, 1, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [false, true]); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedBoolElement(1, 1, true)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedBoolElement(1, 1, true); + expectEqualToArray(accessor.getRepeatedBoolIterable(1), [false, true]); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedBoolElement( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedBoolElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated double does', () => { + const value1 = 1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer( + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value1 + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const unpackedValue2Value1 = createArrayBuffer( + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value2 + ); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value1 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const packedValue2Value1 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value2 + ); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedDoubleIterable(1); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedDoubleSize(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedDoubleIterable(1); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedDoubleElement(1, value1); + const list1 = accessor.getRepeatedDoubleIterable(1); + accessor.addUnpackedDoubleElement(1, value2); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedDoubleIterable(1, [value1]); + const list1 = accessor.getRepeatedDoubleIterable(1); + accessor.addUnpackedDoubleIterable(1, [value2]); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedDoubleElement(1, 1, value1); + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedDoubleIterable(1, [value1]); + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedDoubleElement(1, value1); + accessor.addUnpackedDoubleElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedDoubleIterable(1, [value1]); + accessor.addUnpackedDoubleIterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedDoubleElement(1, 0, value2); + accessor.setUnpackedDoubleElement(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedDoubleIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedDoubleIterable(1); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedDoubleElement(1, value1); + const list1 = accessor.getRepeatedDoubleIterable(1); + accessor.addPackedDoubleElement(1, value2); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedDoubleIterable(1, [value1]); + const list1 = accessor.getRepeatedDoubleIterable(1); + accessor.addPackedDoubleIterable(1, [value2]); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedDoubleElement(1, 1, value1); + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedDoubleIterable(1, [value1]); + const list1 = accessor.getRepeatedDoubleIterable(1); + accessor.setPackedDoubleIterable(1, [value2]); + const list2 = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedDoubleElement(1, value1); + accessor.addPackedDoubleElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedDoubleIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedDoubleElement(1, 0, value2); + accessor.setPackedDoubleElement(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedDoubleIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value1 + 0x0A, + 0x10, // tag + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x3F, // value1 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + )); + + const list = accessor.getRepeatedDoubleIterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedDoubleElement( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedDoubleElement( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedDoubleSize(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked double value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedDoubleIterable(1); + }).toThrowError('Expected wire type: 1 but found: 0'); + } 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. + expectEqualToArray( + accessor.getRepeatedDoubleIterable(1), [2.937446524422997e-306]); + } + }); + + it('fail when adding unpacked double values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedDoubleIterable(1, [fakeDouble])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedDoubleIterable(1, [fakeDouble]); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when adding single unpacked double value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedDoubleElement(1, fakeDouble)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedDoubleElement(1, fakeDouble); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when setting unpacked double values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedDoubleIterable(1, [fakeDouble])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedDoubleIterable(1, [fakeDouble]); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when setting single unpacked double value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedDoubleElement(1, 0, fakeDouble)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedDoubleElement(1, 0, fakeDouble); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when adding packed double values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedDoubleIterable(1, [fakeDouble])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedDoubleIterable(1, [fakeDouble]); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when adding single packed double value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedDoubleElement(1, fakeDouble)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedDoubleElement(1, fakeDouble); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when setting packed double values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedDoubleIterable(1, [fakeDouble])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedDoubleIterable(1, [fakeDouble]); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when setting single packed double value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + const fakeDouble = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedDoubleElement(1, 0, fakeDouble)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedDoubleElement(1, 0, fakeDouble); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [fakeDouble]); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedDoubleElement(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedDoubleElement(1, 1, 1); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [0, 1]); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedDoubleElement(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedDoubleElement(1, 1, 1); + expectEqualToArray(accessor.getRepeatedDoubleIterable(1), [0, 1]); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedDoubleElement( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedDoubleElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated fixed32 does', () => { + const value1 = 1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer( + 0x0D, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00); + const unpackedValue2Value1 = createArrayBuffer( + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x00); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const packedValue2Value1 = createArrayBuffer( + 0x0A, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedFixed32Iterable(1); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedFixed32Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedFixed32Iterable(1); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed32Element(1, value1); + const list1 = accessor.getRepeatedFixed32Iterable(1); + accessor.addUnpackedFixed32Element(1, value2); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed32Iterable(1); + accessor.addUnpackedFixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedFixed32Element(1, 1, value1); + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFixed32Iterable(1, [value1]); + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed32Element(1, value1); + accessor.addUnpackedFixed32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed32Iterable(1, [value1]); + accessor.addUnpackedFixed32Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedFixed32Element(1, 0, value2); + accessor.setUnpackedFixed32Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedFixed32Iterable(1); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed32Element(1, value1); + const list1 = accessor.getRepeatedFixed32Iterable(1); + accessor.addPackedFixed32Element(1, value2); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed32Iterable(1); + accessor.addPackedFixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFixed32Element(1, 1, value1); + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed32Iterable(1); + accessor.setPackedFixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed32Element(1, value1); + accessor.addPackedFixed32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFixed32Element(1, 0, value2); + accessor.setPackedFixed32Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, + 0x01, + 0x00, + 0x00, + 0x00, // value1 + 0x0A, + 0x08, // tag + 0x01, + 0x00, + 0x00, + 0x00, // value1 + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x0D, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + )); + + const list = accessor.getRepeatedFixed32Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedFixed32Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedFixed32Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedFixed32Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked fixed32 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFixed32Iterable(1); + }).toThrowError('Expected wire type: 5 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedFixed32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when adding unpacked fixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFixed32Iterable(1, [fakeFixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedFixed32Iterable(1, [fakeFixed32]); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when adding single unpacked fixed32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFixed32Element(1, fakeFixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedFixed32Element(1, fakeFixed32); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when setting unpacked fixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed32Iterable(1, [fakeFixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedFixed32Iterable(1, [fakeFixed32]); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when setting single unpacked fixed32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed32Element(1, 0, fakeFixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedFixed32Element(1, 0, fakeFixed32); + expectQualifiedIterable( + accessor.getRepeatedFixed32Iterable(1), + ); + } + }); + + it('fail when adding packed fixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFixed32Iterable(1, [fakeFixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedFixed32Iterable(1, [fakeFixed32]); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when adding single packed fixed32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFixed32Element(1, fakeFixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedFixed32Element(1, fakeFixed32); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when setting packed fixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed32Iterable(1, [fakeFixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedFixed32Iterable(1, [fakeFixed32]); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when setting single packed fixed32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + const fakeFixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed32Element(1, 0, fakeFixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedFixed32Element(1, 0, fakeFixed32); + expectQualifiedIterable(accessor.getRepeatedFixed32Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0A, 0x04, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedFixed32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedFixed32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedFixed32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedFixed32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFixed32Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedFixed32Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated fixed64 does', () => { + const value1 = Int64.fromInt(1); + const value2 = Int64.fromInt(0); + + const unpackedValue1Value2 = createArrayBuffer( + 0x09, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const unpackedValue2Value1 = createArrayBuffer( + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x09, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const packedValue2Value1 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + ); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedFixed64Iterable(1); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedFixed64Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedFixed64Iterable(1); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed64Element(1, value1); + const list1 = accessor.getRepeatedFixed64Iterable(1); + accessor.addUnpackedFixed64Element(1, value2); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed64Iterable(1); + accessor.addUnpackedFixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedFixed64Element(1, 1, value1); + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFixed64Iterable(1, [value1]); + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed64Element(1, value1); + accessor.addUnpackedFixed64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFixed64Iterable(1, [value1]); + accessor.addUnpackedFixed64Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedFixed64Element(1, 0, value2); + accessor.setUnpackedFixed64Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedFixed64Iterable(1); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed64Element(1, value1); + const list1 = accessor.getRepeatedFixed64Iterable(1); + accessor.addPackedFixed64Element(1, value2); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed64Iterable(1); + accessor.addPackedFixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFixed64Element(1, 1, value1); + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedFixed64Iterable(1); + accessor.setPackedFixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed64Element(1, value1); + accessor.addPackedFixed64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFixed64Element(1, 0, value2); + accessor.setPackedFixed64Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value1 + 0x0A, 0x10, // tag + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value2 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // value2 + )); + + const list = accessor.getRepeatedFixed64Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedFixed64Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedFixed64Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedFixed64Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked fixed64 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFixed64Iterable(1); + }).toThrowError('Expected wire type: 1 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedFixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when adding unpacked fixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFixed64Iterable(1, [fakeFixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedFixed64Iterable(1, [fakeFixed64]); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when adding single unpacked fixed64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFixed64Element(1, fakeFixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedFixed64Element(1, fakeFixed64); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when setting unpacked fixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed64Iterable(1, [fakeFixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedFixed64Iterable(1, [fakeFixed64]); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when setting single unpacked fixed64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed64Element(1, 0, fakeFixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedFixed64Element(1, 0, fakeFixed64); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when adding packed fixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFixed64Iterable(1, [fakeFixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedFixed64Iterable(1, [fakeFixed64]); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when adding single packed fixed64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFixed64Element(1, fakeFixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedFixed64Element(1, fakeFixed64); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when setting packed fixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed64Iterable(1, [fakeFixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedFixed64Iterable(1, [fakeFixed64]); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when setting single packed fixed64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + const fakeFixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed64Element(1, 0, fakeFixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedFixed64Element(1, 0, fakeFixed64); + expectQualifiedIterable(accessor.getRepeatedFixed64Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFixed64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedFixed64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedFixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFixed64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedFixed64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedFixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFixed64Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedFixed64Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated float does', () => { + const value1 = 1.6; + const value1Float = Math.fround(1.6); + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer( + 0x0D, 0xCD, 0xCC, 0xCC, 0x3F, 0x0D, 0x00, 0x00, 0x00, 0x00); + const unpackedValue2Value1 = createArrayBuffer( + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xCD, 0xCC, 0xCC, 0x3F); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, 0x08, 0xCD, 0xCC, 0xCC, 0x3F, 0x00, 0x00, 0x00, 0x00); + const packedValue2Value1 = createArrayBuffer( + 0x0A, 0x08, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0x3F); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedFloatIterable(1); + const list2 = accessor.getRepeatedFloatIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedFloatSize(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedFloatIterable(1); + const list2 = accessor.getRepeatedFloatIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFloatElement(1, value1); + const list1 = accessor.getRepeatedFloatIterable(1); + accessor.addUnpackedFloatElement(1, value2); + const list2 = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list1, [value1Float]); + expectEqualToArray(list2, [value1Float, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFloatIterable(1, [value1]); + const list1 = accessor.getRepeatedFloatIterable(1); + accessor.addUnpackedFloatIterable(1, [value2]); + const list2 = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list1, [value1Float]); + expectEqualToArray(list2, [value1Float, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedFloatElement(1, 1, value1); + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float, value1Float]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFloatIterable(1, [value1]); + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFloatElement(1, value1); + accessor.addUnpackedFloatElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedFloatIterable(1, [value1]); + accessor.addUnpackedFloatIterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedFloatElement(1, 0, value2); + accessor.setUnpackedFloatElement(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedFloatIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedFloatIterable(1); + const list2 = accessor.getRepeatedFloatIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFloatElement(1, value1); + const list1 = accessor.getRepeatedFloatIterable(1); + accessor.addPackedFloatElement(1, value2); + const list2 = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list1, [value1Float]); + expectEqualToArray(list2, [value1Float, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFloatIterable(1, [value1]); + const list1 = accessor.getRepeatedFloatIterable(1); + accessor.addPackedFloatIterable(1, [value2]); + const list2 = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list1, [value1Float]); + expectEqualToArray(list2, [value1Float, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFloatElement(1, 1, value1); + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float, value1Float]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFloatIterable(1, [value1]); + const list1 = accessor.getRepeatedFloatIterable(1); + accessor.setPackedFloatIterable(1, [value2]); + const list2 = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list1, [value1Float]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFloatElement(1, value1); + accessor.addPackedFloatElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedFloatIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedFloatElement(1, 0, value2); + accessor.setPackedFloatElement(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedFloatIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, + 0xCD, + 0xCC, + 0xCC, + 0x3F, // value1 + 0x0A, + 0x08, // tag + 0xCD, + 0xCC, + 0xCC, + 0x3F, // value1 + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x0D, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + )); + + const list = accessor.getRepeatedFloatIterable(1); + + expectEqualToArray(list, [value1Float, value1Float, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedFloatElement( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedFloatElement( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1Float); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedFloatSize(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked float value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFloatIterable(1); + }).toThrowError('Expected wire type: 5 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedFloatIterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when adding unpacked float values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFloatIterable(1, [fakeFloat])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedFloatIterable(1, [fakeFloat]); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when adding single unpacked float value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedFloatElement(1, fakeFloat)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedFloatElement(1, fakeFloat); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when setting unpacked float values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFloatIterable(1, [fakeFloat])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedFloatIterable(1, [fakeFloat]); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when setting single unpacked float value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFloatElement(1, 0, fakeFloat)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedFloatElement(1, 0, fakeFloat); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when adding packed float values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFloatIterable(1, [fakeFloat])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedFloatIterable(1, [fakeFloat]); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when adding single packed float value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedFloatElement(1, fakeFloat)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedFloatElement(1, fakeFloat); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when setting packed float values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFloatIterable(1, [fakeFloat])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedFloatIterable(1, [fakeFloat]); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when setting single packed float value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + const fakeFloat = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFloatElement(1, 0, fakeFloat)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedFloatElement(1, 0, fakeFloat); + expectQualifiedIterable(accessor.getRepeatedFloatIterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedFloatElement(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedFloatElement(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedFloatIterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedFloatElement(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedFloatElement(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedFloatIterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedFloatElement( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedFloatElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated int32 does', () => { + const value1 = 1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedInt32Iterable(1); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedInt32Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedInt32Iterable(1); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt32Element(1, value1); + const list1 = accessor.getRepeatedInt32Iterable(1); + accessor.addUnpackedInt32Element(1, value2); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt32Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt32Iterable(1); + accessor.addUnpackedInt32Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedInt32Element(1, 1, value1); + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedInt32Iterable(1, [value1]); + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt32Element(1, value1); + accessor.addUnpackedInt32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt32Iterable(1, [value1]); + accessor.addUnpackedInt32Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedInt32Element(1, 0, value2); + accessor.setUnpackedInt32Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedInt32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedInt32Iterable(1); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt32Element(1, value1); + const list1 = accessor.getRepeatedInt32Iterable(1); + accessor.addPackedInt32Element(1, value2); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt32Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt32Iterable(1); + accessor.addPackedInt32Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedInt32Element(1, 1, value1); + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedInt32Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt32Iterable(1); + accessor.setPackedInt32Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt32Element(1, value1); + accessor.addPackedInt32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedInt32Element(1, 0, value2); + accessor.setPackedInt32Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedInt32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedInt32Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedInt32Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedInt32Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedInt32Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked int32 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedInt32Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedInt32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when adding unpacked int32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedInt32Iterable(1, [fakeInt32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedInt32Iterable(1, [fakeInt32]); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when adding single unpacked int32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedInt32Element(1, fakeInt32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedInt32Element(1, fakeInt32); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when setting unpacked int32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt32Iterable(1, [fakeInt32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedInt32Iterable(1, [fakeInt32]); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when setting single unpacked int32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt32Element(1, 0, fakeInt32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedInt32Element(1, 0, fakeInt32); + expectQualifiedIterable( + accessor.getRepeatedInt32Iterable(1), + ); + } + }); + + it('fail when adding packed int32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedInt32Iterable(1, [fakeInt32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedInt32Iterable(1, [fakeInt32]); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when adding single packed int32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedInt32Element(1, fakeInt32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedInt32Element(1, fakeInt32); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when setting packed int32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt32Iterable(1, [fakeInt32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedInt32Iterable(1, [fakeInt32]); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when setting single packed int32 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeInt32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt32Element(1, 0, fakeInt32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedInt32Element(1, 0, fakeInt32); + expectQualifiedIterable(accessor.getRepeatedInt32Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedInt32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedInt32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedInt32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedInt32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedInt32Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedInt32Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated int64 does', () => { + const value1 = Int64.fromInt(1); + const value2 = Int64.fromInt(0); + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedInt64Iterable(1); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedInt64Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedInt64Iterable(1); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt64Element(1, value1); + const list1 = accessor.getRepeatedInt64Iterable(1); + accessor.addUnpackedInt64Element(1, value2); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt64Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt64Iterable(1); + accessor.addUnpackedInt64Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedInt64Element(1, 1, value1); + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedInt64Iterable(1, [value1]); + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt64Element(1, value1); + accessor.addUnpackedInt64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedInt64Iterable(1, [value1]); + accessor.addUnpackedInt64Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedInt64Element(1, 0, value2); + accessor.setUnpackedInt64Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedInt64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedInt64Iterable(1); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt64Element(1, value1); + const list1 = accessor.getRepeatedInt64Iterable(1); + accessor.addPackedInt64Element(1, value2); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt64Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt64Iterable(1); + accessor.addPackedInt64Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedInt64Element(1, 1, value1); + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedInt64Iterable(1, [value1]); + const list1 = accessor.getRepeatedInt64Iterable(1); + accessor.setPackedInt64Iterable(1, [value2]); + const list2 = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt64Element(1, value1); + accessor.addPackedInt64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedInt64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedInt64Element(1, 0, value2); + accessor.setPackedInt64Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedInt64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedInt64Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedInt64Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedInt64Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedInt64Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked int64 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedInt64Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedInt64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when adding unpacked int64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedInt64Iterable(1, [fakeInt64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedInt64Iterable(1, [fakeInt64]); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when adding single unpacked int64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedInt64Element(1, fakeInt64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedInt64Element(1, fakeInt64); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when setting unpacked int64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt64Iterable(1, [fakeInt64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedInt64Iterable(1, [fakeInt64]); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when setting single unpacked int64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt64Element(1, 0, fakeInt64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedInt64Element(1, 0, fakeInt64); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when adding packed int64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedInt64Iterable(1, [fakeInt64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedInt64Iterable(1, [fakeInt64]); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when adding single packed int64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedInt64Element(1, fakeInt64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedInt64Element(1, fakeInt64); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when setting packed int64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt64Iterable(1, [fakeInt64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedInt64Iterable(1, [fakeInt64]); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when setting single packed int64 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeInt64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt64Element(1, 0, fakeInt64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedInt64Element(1, 0, fakeInt64); + expectQualifiedIterable(accessor.getRepeatedInt64Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedInt64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedInt64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedInt64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedInt64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedInt64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedInt64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedInt64Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedInt64Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated sfixed32 does', () => { + const value1 = 1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer( + 0x0D, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00); + const unpackedValue2Value1 = createArrayBuffer( + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x00); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const packedValue2Value1 = createArrayBuffer( + 0x0A, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedSfixed32Iterable(1); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedSfixed32Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedSfixed32Iterable(1); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed32Element(1, value1); + const list1 = accessor.getRepeatedSfixed32Iterable(1); + accessor.addUnpackedSfixed32Element(1, value2); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed32Iterable(1); + accessor.addUnpackedSfixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedSfixed32Element(1, 1, value1); + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSfixed32Iterable(1, [value1]); + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed32Element(1, value1); + accessor.addUnpackedSfixed32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed32Iterable(1, [value1]); + accessor.addUnpackedSfixed32Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedSfixed32Element(1, 0, value2); + accessor.setUnpackedSfixed32Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSfixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedSfixed32Iterable(1); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed32Element(1, value1); + const list1 = accessor.getRepeatedSfixed32Iterable(1); + accessor.addPackedSfixed32Element(1, value2); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed32Iterable(1); + accessor.addPackedSfixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSfixed32Element(1, 1, value1); + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSfixed32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed32Iterable(1); + accessor.setPackedSfixed32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed32Element(1, value1); + accessor.addPackedSfixed32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSfixed32Element(1, 0, value2); + accessor.setPackedSfixed32Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSfixed32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, + 0x01, + 0x00, + 0x00, + 0x00, // value1 + 0x0A, + 0x08, // tag + 0x01, + 0x00, + 0x00, + 0x00, // value1 + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x0D, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + )); + + const list = accessor.getRepeatedSfixed32Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedSfixed32Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedSfixed32Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedSfixed32Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked sfixed32 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSfixed32Iterable(1); + }).toThrowError('Expected wire type: 5 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedSfixed32Iterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when adding unpacked sfixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSfixed32Iterable(1, [fakeSfixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedSfixed32Iterable(1, [fakeSfixed32]); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when adding single unpacked sfixed32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSfixed32Element(1, fakeSfixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedSfixed32Element(1, fakeSfixed32); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when setting unpacked sfixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed32Iterable(1, [fakeSfixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedSfixed32Iterable(1, [fakeSfixed32]); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when setting single unpacked sfixed32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed32Element(1, 0, fakeSfixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedSfixed32Element(1, 0, fakeSfixed32); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when adding packed sfixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSfixed32Iterable(1, [fakeSfixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedSfixed32Iterable(1, [fakeSfixed32]); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when adding single packed sfixed32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSfixed32Element(1, fakeSfixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedSfixed32Element(1, fakeSfixed32); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when setting packed sfixed32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed32Iterable(1, [fakeSfixed32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedSfixed32Iterable(1, [fakeSfixed32]); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when setting single packed sfixed32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + const fakeSfixed32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed32Element(1, 0, fakeSfixed32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedSfixed32Element(1, 0, fakeSfixed32); + expectQualifiedIterable(accessor.getRepeatedSfixed32Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedSfixed32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedSfixed32Iterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedSfixed32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedSfixed32Iterable(1), + (value) => typeof value === 'number'); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSfixed32Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedSfixed32Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated sfixed64 does', () => { + const value1 = Int64.fromInt(1); + const value2 = Int64.fromInt(0); + + const unpackedValue1Value2 = createArrayBuffer( + 0x09, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const unpackedValue2Value1 = createArrayBuffer( + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x09, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + + const packedValue1Value2 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + ); + const packedValue2Value1 = createArrayBuffer( + 0x0A, + 0x10, // tag + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value2 + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // value1 + ); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedSfixed64Iterable(1); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedSfixed64Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedSfixed64Iterable(1); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed64Element(1, value1); + const list1 = accessor.getRepeatedSfixed64Iterable(1); + accessor.addUnpackedSfixed64Element(1, value2); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed64Iterable(1); + accessor.addUnpackedSfixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedSfixed64Element(1, 1, value1); + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSfixed64Iterable(1, [value1]); + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed64Element(1, value1); + accessor.addUnpackedSfixed64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSfixed64Iterable(1, [value1]); + accessor.addUnpackedSfixed64Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedSfixed64Element(1, 0, value2); + accessor.setUnpackedSfixed64Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSfixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedSfixed64Iterable(1); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed64Element(1, value1); + const list1 = accessor.getRepeatedSfixed64Iterable(1); + accessor.addPackedSfixed64Element(1, value2); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed64Iterable(1); + accessor.addPackedSfixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSfixed64Element(1, 1, value1); + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSfixed64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSfixed64Iterable(1); + accessor.setPackedSfixed64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed64Element(1, value1); + accessor.addPackedSfixed64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSfixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSfixed64Element(1, 0, value2); + accessor.setPackedSfixed64Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSfixed64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value1 + 0x0A, 0x10, // tag + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value2 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // value2 + )); + + const list = accessor.getRepeatedSfixed64Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedSfixed64Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedSfixed64Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedSfixed64Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked sfixed64 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSfixed64Iterable(1); + }).toThrowError('Expected wire type: 1 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedSfixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when adding unpacked sfixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSfixed64Iterable(1, [fakeSfixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedSfixed64Iterable(1, [fakeSfixed64]); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when adding single unpacked sfixed64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSfixed64Element(1, fakeSfixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedSfixed64Element(1, fakeSfixed64); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when setting unpacked sfixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed64Iterable(1, [fakeSfixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedSfixed64Iterable(1, [fakeSfixed64]); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when setting single unpacked sfixed64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed64Element(1, 0, fakeSfixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedSfixed64Element(1, 0, fakeSfixed64); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when adding packed sfixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSfixed64Iterable(1, [fakeSfixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedSfixed64Iterable(1, [fakeSfixed64]); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when adding single packed sfixed64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSfixed64Element(1, fakeSfixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedSfixed64Element(1, fakeSfixed64); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when setting packed sfixed64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed64Iterable(1, [fakeSfixed64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedSfixed64Iterable(1, [fakeSfixed64]); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when setting single packed sfixed64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + const fakeSfixed64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed64Element(1, 0, fakeSfixed64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedSfixed64Element(1, 0, fakeSfixed64); + expectQualifiedIterable(accessor.getRepeatedSfixed64Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSfixed64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedSfixed64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedSfixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSfixed64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedSfixed64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedSfixed64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSfixed64Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedSfixed64Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated sint32 does', () => { + const value1 = -1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedSint32Iterable(1); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedSint32Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedSint32Iterable(1); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint32Element(1, value1); + const list1 = accessor.getRepeatedSint32Iterable(1); + accessor.addUnpackedSint32Element(1, value2); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint32Iterable(1); + accessor.addUnpackedSint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedSint32Element(1, 1, value1); + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSint32Iterable(1, [value1]); + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint32Element(1, value1); + accessor.addUnpackedSint32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint32Iterable(1, [value1]); + accessor.addUnpackedSint32Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedSint32Element(1, 0, value2); + accessor.setUnpackedSint32Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedSint32Iterable(1); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint32Element(1, value1); + const list1 = accessor.getRepeatedSint32Iterable(1); + accessor.addPackedSint32Element(1, value2); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint32Iterable(1); + accessor.addPackedSint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSint32Element(1, 1, value1); + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint32Iterable(1); + accessor.setPackedSint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint32Element(1, value1); + accessor.addPackedSint32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSint32Element(1, 0, value2); + accessor.setPackedSint32Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedSint32Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedSint32Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedSint32Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedSint32Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked sint32 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSint32Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedSint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when adding unpacked sint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSint32Iterable(1, [fakeSint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedSint32Iterable(1, [fakeSint32]); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when adding single unpacked sint32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSint32Element(1, fakeSint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedSint32Element(1, fakeSint32); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when setting unpacked sint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint32Iterable(1, [fakeSint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedSint32Iterable(1, [fakeSint32]); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when setting single unpacked sint32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint32Element(1, 0, fakeSint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedSint32Element(1, 0, fakeSint32); + expectQualifiedIterable( + accessor.getRepeatedSint32Iterable(1), + ); + } + }); + + it('fail when adding packed sint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSint32Iterable(1, [fakeSint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedSint32Iterable(1, [fakeSint32]); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when adding single packed sint32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSint32Element(1, fakeSint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedSint32Element(1, fakeSint32); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when setting packed sint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint32Iterable(1, [fakeSint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedSint32Iterable(1, [fakeSint32]); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when setting single packed sint32 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeSint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint32Element(1, 0, fakeSint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedSint32Element(1, 0, fakeSint32); + expectQualifiedIterable(accessor.getRepeatedSint32Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedSint32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedSint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedSint32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedSint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSint32Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedSint32Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated sint64 does', () => { + const value1 = Int64.fromInt(-1); + const value2 = Int64.fromInt(0); + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedSint64Iterable(1); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedSint64Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedSint64Iterable(1); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint64Element(1, value1); + const list1 = accessor.getRepeatedSint64Iterable(1); + accessor.addUnpackedSint64Element(1, value2); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint64Iterable(1); + accessor.addUnpackedSint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedSint64Element(1, 1, value1); + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSint64Iterable(1, [value1]); + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint64Element(1, value1); + accessor.addUnpackedSint64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedSint64Iterable(1, [value1]); + accessor.addUnpackedSint64Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedSint64Element(1, 0, value2); + accessor.setUnpackedSint64Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedSint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedSint64Iterable(1); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint64Element(1, value1); + const list1 = accessor.getRepeatedSint64Iterable(1); + accessor.addPackedSint64Element(1, value2); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint64Iterable(1); + accessor.addPackedSint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSint64Element(1, 1, value1); + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedSint64Iterable(1); + accessor.setPackedSint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint64Element(1, value1); + accessor.addPackedSint64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedSint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedSint64Element(1, 0, value2); + accessor.setPackedSint64Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedSint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedSint64Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedSint64Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedSint64Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedSint64Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked sint64 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSint64Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedSint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when adding unpacked sint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSint64Iterable(1, [fakeSint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedSint64Iterable(1, [fakeSint64]); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when adding single unpacked sint64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedSint64Element(1, fakeSint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedSint64Element(1, fakeSint64); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when setting unpacked sint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint64Iterable(1, [fakeSint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedSint64Iterable(1, [fakeSint64]); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when setting single unpacked sint64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint64Element(1, 0, fakeSint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedSint64Element(1, 0, fakeSint64); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when adding packed sint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSint64Iterable(1, [fakeSint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedSint64Iterable(1, [fakeSint64]); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when adding single packed sint64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedSint64Element(1, fakeSint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedSint64Element(1, fakeSint64); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when setting packed sint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint64Iterable(1, [fakeSint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedSint64Iterable(1, [fakeSint64]); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when setting single packed sint64 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeSint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint64Element(1, 0, fakeSint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedSint64Element(1, 0, fakeSint64); + expectQualifiedIterable(accessor.getRepeatedSint64Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedSint64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedSint64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedSint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedSint64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedSint64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedSint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedSint64Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedSint64Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated uint32 does', () => { + const value1 = 1; + const value2 = 0; + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedUint32Iterable(1); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedUint32Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedUint32Iterable(1); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint32Element(1, value1); + const list1 = accessor.getRepeatedUint32Iterable(1); + accessor.addUnpackedUint32Element(1, value2); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint32Iterable(1); + accessor.addUnpackedUint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedUint32Element(1, 1, value1); + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedUint32Iterable(1, [value1]); + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint32Element(1, value1); + accessor.addUnpackedUint32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint32Iterable(1, [value1]); + accessor.addUnpackedUint32Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedUint32Element(1, 0, value2); + accessor.setUnpackedUint32Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedUint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedUint32Iterable(1); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint32Element(1, value1); + const list1 = accessor.getRepeatedUint32Iterable(1); + accessor.addPackedUint32Element(1, value2); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint32Iterable(1); + accessor.addPackedUint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedUint32Element(1, 1, value1); + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedUint32Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint32Iterable(1); + accessor.setPackedUint32Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint32Element(1, value1); + accessor.addPackedUint32Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedUint32Element(1, 0, value2); + accessor.setPackedUint32Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedUint32Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedUint32Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedUint32Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedUint32Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedUint32Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked uint32 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedUint32Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedUint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when adding unpacked uint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedUint32Iterable(1, [fakeUint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedUint32Iterable(1, [fakeUint32]); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when adding single unpacked uint32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedUint32Element(1, fakeUint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addUnpackedUint32Element(1, fakeUint32); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when setting unpacked uint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint32Iterable(1, [fakeUint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedUint32Iterable(1, [fakeUint32]); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when setting single unpacked uint32 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint32Element(1, 0, fakeUint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setUnpackedUint32Element(1, 0, fakeUint32); + expectQualifiedIterable( + accessor.getRepeatedUint32Iterable(1), + ); + } + }); + + it('fail when adding packed uint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedUint32Iterable(1, [fakeUint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedUint32Iterable(1, [fakeUint32]); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when adding single packed uint32 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedUint32Element(1, fakeUint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.addPackedUint32Element(1, fakeUint32); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when setting packed uint32 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint32Iterable(1, [fakeUint32])) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedUint32Iterable(1, [fakeUint32]); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when setting single packed uint32 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeUint32 = /** @type {number} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint32Element(1, 0, fakeUint32)) + .toThrowError('Must be a number, but got: null'); + } 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. + accessor.setPackedUint32Element(1, 0, fakeUint32); + expectQualifiedIterable(accessor.getRepeatedUint32Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedUint32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedUint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint32Element(1, 1, 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedUint32Element(1, 1, 1); + expectQualifiedIterable( + accessor.getRepeatedUint32Iterable(1), + (value) => Number.isInteger(value)); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedUint32Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedUint32Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated uint64 does', () => { + const value1 = Int64.fromInt(1); + const value2 = Int64.fromInt(0); + + const unpackedValue1Value2 = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const unpackedValue2Value1 = createArrayBuffer(0x08, 0x00, 0x08, 0x01); + + const packedValue1Value2 = createArrayBuffer(0x0A, 0x02, 0x01, 0x00); + const packedValue2Value1 = createArrayBuffer(0x0A, 0x02, 0x00, 0x01); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedUint64Iterable(1); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedUint64Size(1); + + expect(size).toEqual(0); + }); + + it('return unpacked values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for unpacked values', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const list1 = accessor.getRepeatedUint64Iterable(1); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint64Element(1, value1); + const list1 = accessor.getRepeatedUint64Iterable(1); + accessor.addUnpackedUint64Element(1, value2); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint64Iterable(1); + accessor.addUnpackedUint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + accessor.setUnpackedUint64Element(1, 1, value1); + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedUint64Iterable(1, [value1]); + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single unpacked value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint64Element(1, value1); + accessor.addUnpackedUint64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for adding unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedUint64Iterable(1, [value1]); + accessor.addUnpackedUint64Iterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('encode for setting single unpacked value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setUnpackedUint64Element(1, 0, value2); + accessor.setUnpackedUint64Element(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue2Value1); + }); + + it('encode for setting unpacked values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setUnpackedUint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(unpackedValue1Value2); + }); + + it('return packed values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for packed values', () => { + const accessor = LazyAccessor.fromArrayBuffer(packedValue1Value2); + + const list1 = accessor.getRepeatedUint64Iterable(1); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint64Element(1, value1); + const list1 = accessor.getRepeatedUint64Iterable(1); + accessor.addPackedUint64Element(1, value2); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint64Iterable(1); + accessor.addPackedUint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedUint64Element(1, 1, value1); + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedUint64Iterable(1, [value1]); + const list1 = accessor.getRepeatedUint64Iterable(1); + accessor.setPackedUint64Iterable(1, [value2]); + const list2 = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value2]); + }); + + it('encode for adding single packed value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint64Element(1, value1); + accessor.addPackedUint64Element(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for adding packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addPackedUint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('encode for setting single packed value', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + accessor.setPackedUint64Element(1, 0, value2); + accessor.setPackedUint64Element(1, 1, value1); + + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue2Value1); + }); + + it('encode for setting packed values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setPackedUint64Iterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(packedValue1Value2); + }); + + it('return combined values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, + 0x01, // unpacked value1 + 0x0A, + 0x02, + 0x01, + 0x00, // packed value1 and value2 + 0x08, + 0x00, // unpacked value2 + )); + + const list = accessor.getRepeatedUint64Iterable(1); + + expectEqualToArray(list, [value1, value1, value2, value2]); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const result1 = accessor.getRepeatedUint64Element( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedUint64Element( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(unpackedValue1Value2); + + const size = accessor.getRepeatedUint64Size(1); + + expect(size).toEqual(2); + }); + + it('fail when getting unpacked uint64 value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedUint64Iterable(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedUint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when adding unpacked uint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedUint64Iterable(1, [fakeUint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedUint64Iterable(1, [fakeUint64]); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when adding single unpacked uint64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addUnpackedUint64Element(1, fakeUint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addUnpackedUint64Element(1, fakeUint64); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when setting unpacked uint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint64Iterable(1, [fakeUint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedUint64Iterable(1, [fakeUint64]); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when setting single unpacked uint64 value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x80, 0x80, 0x80, 0x00)); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint64Element(1, 0, fakeUint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setUnpackedUint64Element(1, 0, fakeUint64); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when adding packed uint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedUint64Iterable(1, [fakeUint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedUint64Iterable(1, [fakeUint64]); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when adding single packed uint64 value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addPackedUint64Element(1, fakeUint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.addPackedUint64Element(1, fakeUint64); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when setting packed uint64 values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint64Iterable(1, [fakeUint64])) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedUint64Iterable(1, [fakeUint64]); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when setting single packed uint64 value with null value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + const fakeUint64 = /** @type {!Int64} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint64Element(1, 0, fakeUint64)) + .toThrowError('Must be Int64 instance, but got: null'); + } 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. + accessor.setPackedUint64Element(1, 0, fakeUint64); + expectQualifiedIterable(accessor.getRepeatedUint64Iterable(1)); + } + }); + + it('fail when setting single unpacked with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setUnpackedUint64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setUnpackedUint64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedUint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when setting single packed with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setPackedUint64Element(1, 1, Int64.fromInt(1))) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setPackedUint64Element(1, 1, Int64.fromInt(1)); + expectQualifiedIterable( + accessor.getRepeatedUint64Iterable(1), + (value) => value instanceof Int64); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedUint64Element( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedUint64Element( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated bytes does', () => { + const value1 = ByteString.fromArrayBuffer((createArrayBuffer(0x61))); + const value2 = ByteString.fromArrayBuffer((createArrayBuffer(0x62))); + + const repeatedValue1Value2 = createArrayBuffer( + 0x0A, + 0x01, + 0x61, // value1 + 0x0A, + 0x01, + 0x62, // value2 + ); + const repeatedValue2Value1 = createArrayBuffer( + 0x0A, + 0x01, + 0x62, // value2 + 0x0A, + 0x01, + 0x61, // value1 + ); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedBytesIterable(1); + const list2 = accessor.getRepeatedBytesIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedBytesSize(1); + + expect(size).toEqual(0); + }); + + it('return values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const list = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for values', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const list1 = accessor.getRepeatedBytesIterable(1); + const list2 = accessor.getRepeatedBytesIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedBytesElement(1, value1); + const list1 = accessor.getRepeatedBytesIterable(1); + accessor.addRepeatedBytesElement(1, value2); + const list2 = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedBytesIterable(1, [value1]); + const list1 = accessor.getRepeatedBytesIterable(1); + accessor.addRepeatedBytesIterable(1, [value2]); + const list2 = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single value', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + accessor.setRepeatedBytesElement(1, 1, value1); + const list = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setRepeatedBytesIterable(1, [value1]); + const list = accessor.getRepeatedBytesIterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedBytesElement(1, value1); + accessor.addRepeatedBytesElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('encode for adding values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedBytesIterable(1, [value1]); + accessor.addRepeatedBytesIterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('encode for setting single value', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + accessor.setRepeatedBytesElement(1, 0, value2); + accessor.setRepeatedBytesElement(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue2Value1); + }); + + it('encode for setting values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setRepeatedBytesIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const result1 = accessor.getRepeatedBytesElement( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedBytesElement( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const size = accessor.getRepeatedBytesSize(1); + + expect(size).toEqual(2); + }); + + it('fail when getting bytes value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedBytesIterable(1); + }).toThrowError('Expected wire type: 2 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedBytesIterable(1), + (value) => value instanceof ByteString); + } + }); + + it('fail when adding bytes values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBytes = /** @type {!ByteString} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addRepeatedBytesIterable(1, [fakeBytes])) + .toThrowError('Must be a ByteString, but got: null'); + } 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. + accessor.addRepeatedBytesIterable(1, [fakeBytes]); + expectQualifiedIterable(accessor.getRepeatedBytesIterable(1)); + } + }); + + it('fail when adding single bytes value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBytes = /** @type {!ByteString} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addRepeatedBytesElement(1, fakeBytes)) + .toThrowError('Must be a ByteString, but got: null'); + } 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. + accessor.addRepeatedBytesElement(1, fakeBytes); + expectQualifiedIterable(accessor.getRepeatedBytesIterable(1)); + } + }); + + it('fail when setting bytes values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeBytes = /** @type {!ByteString} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedBytesIterable(1, [fakeBytes])) + .toThrowError('Must be a ByteString, but got: null'); + } 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. + accessor.setRepeatedBytesIterable(1, [fakeBytes]); + expectQualifiedIterable(accessor.getRepeatedBytesIterable(1)); + } + }); + + it('fail when setting single bytes value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + const fakeBytes = /** @type {!ByteString} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedBytesElement(1, 0, fakeBytes)) + .toThrowError('Must be a ByteString, but got: null'); + } 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. + accessor.setRepeatedBytesElement(1, 0, fakeBytes); + expectQualifiedIterable(accessor.getRepeatedBytesIterable(1)); + } + }); + + it('fail when setting single with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x61)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedBytesElement(1, 1, value1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setRepeatedBytesElement(1, 1, value1); + expectQualifiedIterable( + accessor.getRepeatedBytesIterable(1), + (value) => value instanceof ByteString); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedBytesElement( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedBytesElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated string does', () => { + const value1 = 'a'; + const value2 = 'b'; + + const repeatedValue1Value2 = createArrayBuffer( + 0x0A, + 0x01, + 0x61, // value1 + 0x0A, + 0x01, + 0x62, // value2 + ); + const repeatedValue2Value1 = createArrayBuffer( + 0x0A, + 0x01, + 0x62, // value2 + 0x0A, + 0x01, + 0x61, // value1 + ); + + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list, []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const list1 = accessor.getRepeatedStringIterable(1); + const list2 = accessor.getRepeatedStringIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + + const size = accessor.getRepeatedStringSize(1); + + expect(size).toEqual(0); + }); + + it('return values from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const list = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list, [value1, value2]); + }); + + it('ensure not the same instance returned for values', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const list1 = accessor.getRepeatedStringIterable(1); + const list2 = accessor.getRepeatedStringIterable(1); + + expect(list1).not.toBe(list2); + }); + + it('add single value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedStringElement(1, value1); + const list1 = accessor.getRepeatedStringIterable(1); + accessor.addRepeatedStringElement(1, value2); + const list2 = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('add values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedStringIterable(1, [value1]); + const list1 = accessor.getRepeatedStringIterable(1); + accessor.addRepeatedStringIterable(1, [value2]); + const list2 = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list1, [value1]); + expectEqualToArray(list2, [value1, value2]); + }); + + it('set a single value', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + accessor.setRepeatedStringElement(1, 1, value1); + const list = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list, [value1, value1]); + }); + + it('set values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setRepeatedStringIterable(1, [value1]); + const list = accessor.getRepeatedStringIterable(1); + + expectEqualToArray(list, [value1]); + }); + + it('encode for adding single value', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedStringElement(1, value1); + accessor.addRepeatedStringElement(1, value2); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('encode for adding values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addRepeatedStringIterable(1, [value1]); + accessor.addRepeatedStringIterable(1, [value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('encode for setting single value', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + accessor.setRepeatedStringElement(1, 0, value2); + accessor.setRepeatedStringElement(1, 1, value1); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue2Value1); + }); + + it('encode for setting values', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.setRepeatedStringIterable(1, [value1, value2]); + const serialized = accessor.serialize(); + + expect(serialized).toEqual(repeatedValue1Value2); + }); + + it('return the repeated field element from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const result1 = accessor.getRepeatedStringElement( + /* fieldNumber= */ 1, /* index= */ 0); + const result2 = accessor.getRepeatedStringElement( + /* fieldNumber= */ 1, /* index= */ 1); + + expect(result1).toEqual(value1); + expect(result2).toEqual(value2); + }); + + it('return the size from the input', () => { + const accessor = LazyAccessor.fromArrayBuffer(repeatedValue1Value2); + + const size = accessor.getRepeatedStringSize(1); + + expect(size).toEqual(2); + }); + + it('fail when getting string value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedStringIterable(1); + }).toThrowError('Expected wire type: 2 but found: 0'); + } 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. + expectQualifiedIterable( + accessor.getRepeatedStringIterable(1), + (value) => typeof value === 'string'); + } + }); + + it('fail when adding string values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeString = /** @type {string} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addRepeatedStringIterable(1, [fakeString])) + .toThrowError('Must be string, but got: null'); + } 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. + accessor.addRepeatedStringIterable(1, [fakeString]); + expectQualifiedIterable(accessor.getRepeatedStringIterable(1)); + } + }); + + it('fail when adding single string value with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeString = /** @type {string} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.addRepeatedStringElement(1, fakeString)) + .toThrowError('Must be string, but got: null'); + } 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. + accessor.addRepeatedStringElement(1, fakeString); + expectQualifiedIterable(accessor.getRepeatedStringIterable(1)); + } + }); + + it('fail when setting string values with null value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeString = /** @type {string} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedStringIterable(1, [fakeString])) + .toThrowError('Must be string, but got: null'); + } 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. + accessor.setRepeatedStringIterable(1, [fakeString]); + expectQualifiedIterable(accessor.getRepeatedStringIterable(1)); + } + }); + + it('fail when setting single string value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); + const fakeString = /** @type {string} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedStringElement(1, 0, fakeString)) + .toThrowError('Must be string, but got: null'); + } 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. + accessor.setRepeatedStringElement(1, 0, fakeString); + expectQualifiedIterable(accessor.getRepeatedStringIterable(1)); + } + }); + + it('fail when setting single with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x61)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedStringElement(1, 1, value1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setRepeatedStringElement(1, 1, value1); + expectQualifiedIterable( + accessor.getRepeatedStringIterable(1), + (value) => typeof value === 'string'); + } + }); + + it('fail when getting element with out-of-range index', () => { + const accessor = LazyAccessor.createEmpty(); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedStringElement( + /* fieldNumber= */ 1, /* index= */ 0); + }).toThrowError('Index out of bounds: index: 0 size: 0'); + } 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(accessor.getRepeatedStringElement( + /* fieldNumber= */ 1, /* index= */ 0)) + .toBe(undefined); + } + }); +}); + +describe('LazyAccessor for repeated message does', () => { + it('return empty array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + expectEqualToArray( + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator), + []); + }); + + it('return empty accessor array for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + expectEqualToArray(accessor.getRepeatedMessageAccessorIterable(1), []); + }); + + it('ensure not the same instance returned for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + const list1 = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + const list2 = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(list1).not.toBe(list2); + }); + + it('return size for the empty input', () => { + const accessor = LazyAccessor.createEmpty(); + expect(accessor.getRepeatedMessageSize(1, TestMessage.instanceCreator)) + .toEqual(0); + }); + + it('return values from the input', () => { + const bytes1 = createArrayBuffer(0x08, 0x01); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expectEqualToMessageArray( + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator), + [msg1, msg2]); + }); + + it('ensure not the same array instance returned', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const list1 = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + const list2 = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(list1).not.toBe(list2); + }); + + it('ensure the same array element returned for get iterable', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const list1 = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + const list2 = accessor.getRepeatedMessageIterable( + 1, TestMessage.instanceCreator, /* pivot= */ 0); + const array1 = Array.from(list1); + const array2 = Array.from(list2); + for (let i = 0; i < array1.length; i++) { + expect(array1[i]).toBe(array2[i]); + } + }); + + it('return accessors from the input', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const [accessor1, accessor2] = + [...accessor.getRepeatedMessageAccessorIterable(1)]; + expect(accessor1.getInt32WithDefault(1)).toEqual(1); + expect(accessor2.getInt32WithDefault(1)).toEqual(0); + }); + + it('return accessors from the input when pivot is set', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const [accessor1, accessor2] = + [...accessor.getRepeatedMessageAccessorIterable(1, /* pivot= */ 0)]; + expect(accessor1.getInt32WithDefault(1)).toEqual(1); + expect(accessor2.getInt32WithDefault(1)).toEqual(0); + }); + + it('return the repeated field element from the input', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const msg1 = accessor.getRepeatedMessageElement( + /* fieldNumber= */ 1, TestMessage.instanceCreator, + /* index= */ 0); + const msg2 = accessor.getRepeatedMessageElement( + /* fieldNumber= */ 1, TestMessage.instanceCreator, + /* index= */ 1, /* pivot= */ 0); + expect(msg1.getBoolWithDefault( + /* fieldNumber= */ 1, /* default= */ false)) + .toEqual(true); + expect(msg2.getBoolWithDefault( + /* fieldNumber= */ 1, /* default= */ false)) + .toEqual(false); + }); + + it('ensure the same array element returned', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const msg1 = accessor.getRepeatedMessageElement( + /* fieldNumber= */ 1, TestMessage.instanceCreator, + /* index= */ 0); + const msg2 = accessor.getRepeatedMessageElement( + /* fieldNumber= */ 1, TestMessage.instanceCreator, + /* index= */ 0); + expect(msg1).toBe(msg2); + }); + + it('return the size from the input', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getRepeatedMessageSize(1, TestMessage.instanceCreator)) + .toEqual(2); + }); + + it('encode repeated message from the input', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('add a single value', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes1 = createArrayBuffer(0x08, 0x01); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + + accessor.addRepeatedMessageElement(1, msg1, TestMessage.instanceCreator); + accessor.addRepeatedMessageElement(1, msg2, TestMessage.instanceCreator); + const result = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + + expect(Array.from(result)).toEqual([msg1, msg2]); + }); + + it('add values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes1 = createArrayBuffer(0x08, 0x01); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + + accessor.addRepeatedMessageIterable(1, [msg1], TestMessage.instanceCreator); + accessor.addRepeatedMessageIterable(1, [msg2], TestMessage.instanceCreator); + const result = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + + expect(Array.from(result)).toEqual([msg1, msg2]); + }); + + it('set a single value', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const subbytes = createArrayBuffer(0x08, 0x01); + const submsg = new TestMessage(LazyAccessor.fromArrayBuffer(subbytes)); + + accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, submsg, TestMessage.instanceCreator, + /* index= */ 0); + const result = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + + expect(Array.from(result)).toEqual([submsg]); + }); + + it('write submessage changes made via getRepeatedMessagElement', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x05); + const expected = createArrayBuffer(0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const submsg = accessor.getRepeatedMessageElement( + /* fieldNumber= */ 1, TestMessage.instanceCreator, + /* index= */ 0); + expect(submsg.getInt32WithDefault(1, 0)).toEqual(5); + submsg.setInt32(1, 0); + + expect(accessor.serialize()).toEqual(expected); + }); + + it('set values', () => { + const accessor = LazyAccessor.createEmpty(); + const subbytes = createArrayBuffer(0x08, 0x01); + const submsg = new TestMessage(LazyAccessor.fromArrayBuffer(subbytes)); + + accessor.setRepeatedMessageIterable(1, [submsg]); + const result = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + + expect(Array.from(result)).toEqual([submsg]); + }); + + it('encode for adding single value', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes1 = createArrayBuffer(0x08, 0x01); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + const expected = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + + accessor.addRepeatedMessageElement(1, msg1, TestMessage.instanceCreator); + accessor.addRepeatedMessageElement(1, msg2, TestMessage.instanceCreator); + const result = accessor.serialize(); + + expect(result).toEqual(expected); + }); + + it('encode for adding values', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes1 = createArrayBuffer(0x08, 0x01); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + const expected = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x08, 0x00); + + accessor.addRepeatedMessageIterable( + 1, [msg1, msg2], TestMessage.instanceCreator); + const result = accessor.serialize(); + + expect(result).toEqual(expected); + }); + + it('encode for setting single value', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const subbytes = createArrayBuffer(0x08, 0x01); + const submsg = new TestMessage(LazyAccessor.fromArrayBuffer(subbytes)); + const expected = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + + accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, submsg, TestMessage.instanceCreator, + /* index= */ 0); + const result = accessor.serialize(); + + expect(result).toEqual(expected); + }); + + it('encode for setting values', () => { + const accessor = LazyAccessor.createEmpty(); + const subbytes = createArrayBuffer(0x08, 0x01); + const submsg = new TestMessage(LazyAccessor.fromArrayBuffer(subbytes)); + const expected = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + + accessor.setRepeatedMessageIterable(1, [submsg]); + const result = accessor.serialize(); + + expect(result).toEqual(expected); + }); + + it('get accessors from set values.', () => { + const accessor = LazyAccessor.createEmpty(); + const bytes1 = createArrayBuffer(0x08, 0x01); + const msg1 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes1)); + const bytes2 = createArrayBuffer(0x08, 0x00); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + + accessor.addRepeatedMessageIterable( + 1, [msg1, msg2], TestMessage.instanceCreator); + + const [accessor1, accessor2] = + [...accessor.getRepeatedMessageAccessorIterable(1)]; + expect(accessor1.getInt32WithDefault(1)).toEqual(1); + expect(accessor2.getInt32WithDefault(1)).toEqual(0); + + // Retrieved accessors are the exact same accessors as the added messages. + expect(accessor1).toBe( + (/** @type {!InternalMessage} */ (msg1)).internalGetKernel()); + expect(accessor2).toBe( + (/** @type {!InternalMessage} */ (msg2)).internalGetKernel()); + }); + + it('fail when getting message value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_STATE) { + expect(() => { + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + }).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. + const [msg1] = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(msg1.serialize()).toEqual(createArrayBuffer()); + } + }); + + it('fail when adding message values with wrong type value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeValue = /** @type {!TestMessage} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect( + () => accessor.addRepeatedMessageIterable( + 1, [fakeValue], TestMessage.instanceCreator)) + .toThrowError('Given value is not a message instance: null'); + } 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. + accessor.addRepeatedMessageIterable( + 1, [fakeValue], TestMessage.instanceCreator); + const list = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(Array.from(list)).toEqual([null]); + } + }); + + it('fail when adding single message value with wrong type value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeValue = /** @type {!TestMessage} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect( + () => accessor.addRepeatedMessageElement( + 1, fakeValue, TestMessage.instanceCreator)) + .toThrowError('Given value is not a message instance: null'); + } 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. + accessor.addRepeatedMessageElement( + 1, fakeValue, TestMessage.instanceCreator); + const list = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(Array.from(list)).toEqual([null]); + } + }); + + it('fail when setting message values with wrong type value', () => { + const accessor = LazyAccessor.createEmpty(); + const fakeValue = /** @type {!TestMessage} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect(() => accessor.setRepeatedMessageIterable(1, [fakeValue])) + .toThrowError('Given value is not a message instance: null'); + } 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. + accessor.setRepeatedMessageIterable(1, [fakeValue]); + const list = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(Array.from(list)).toEqual([null]); + } + }); + + it('fail when setting single value with wrong type value', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x02, 0x08, 0x00)); + const fakeValue = /** @type {!TestMessage} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_STATE) { + expect( + () => accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, fakeValue, TestMessage.instanceCreator, + /* index= */ 0)) + .toThrowError('Given value is not a message instance: null'); + } 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. + accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, fakeValue, TestMessage.instanceCreator, + /* index= */ 0); + const list = + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator); + expect(Array.from(list).length).toEqual(1); + } + }); + + it('fail when setting single value with out-of-bound index', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x02, 0x08, 0x00)); + const msg1 = + accessor.getRepeatedMessageElement(1, TestMessage.instanceCreator, 0); + const bytes2 = createArrayBuffer(0x08, 0x01); + const msg2 = new TestMessage(LazyAccessor.fromArrayBuffer(bytes2)); + if (CHECK_CRITICAL_STATE) { + expect( + () => accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, msg2, TestMessage.instanceCreator, + /* index= */ 1)) + .toThrowError('Index out of bounds: index: 1 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. + accessor.setRepeatedMessageElement( + /* fieldNumber= */ 1, msg2, TestMessage.instanceCreator, + /* index= */ 1); + expectEqualToArray( + accessor.getRepeatedMessageIterable(1, TestMessage.instanceCreator), + [msg1, msg2]); + } + }); +}); diff --git a/js/experimental/runtime/kernel/lazy_accessor_test.js b/js/experimental/runtime/kernel/lazy_accessor_test.js new file mode 100644 index 000000000000..ad4576dfce1d --- /dev/null +++ b/js/experimental/runtime/kernel/lazy_accessor_test.js @@ -0,0 +1,2054 @@ +/** + * @fileoverview Tests for lazy_accessor.js. + */ +goog.module('protobuf.binary.LazyAccessorTest'); + +goog.setTestOnly(); + +const ByteString = goog.require('protobuf.ByteString'); +const Int64 = goog.require('protobuf.Int64'); +const InternalMessage = goog.require('protobuf.binary.InternalMessage'); +const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); +const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); +// Note to the reader: +// Since the lazy accessor 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 {CHECK_BOUNDS, CHECK_CRITICAL_STATE, CHECK_CRITICAL_TYPE, CHECK_TYPE, MAX_FIELD_NUMBER} = goog.require('protobuf.internal.checks'); + +/** + * @param {...number} bytes + * @return {!ArrayBuffer} + */ +function createArrayBuffer(...bytes) { + return new Uint8Array(bytes).buffer; +} + +describe('LazyAccessor', () => { + it('encodes none for the empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + expect(accessor.serialize()).toEqual(new ArrayBuffer(0)); + }); + + it('uses the default pivot point', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + expect(accessor.getPivot()).toBe(24); + }); + + it('makes the pivot point configurable', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0), 50); + expect(accessor.getPivot()).toBe(50); + }); +}); + +describe('LazyAccessor hasFieldNumber', () => { + it('returns false for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + expect(accessor.hasFieldNumber(1)).toBe(false); + }); + + it('returns true for non-empty input', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.hasFieldNumber(1)).toBe(true); + }); + + it('returns false for empty array', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setPackedBoolIterable(1, []); + expect(accessor.hasFieldNumber(1)).toBe(false); + }); + + it('returns true for non-empty array', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setPackedBoolIterable(1, [true]); + expect(accessor.hasFieldNumber(1)).toBe(true); + }); + + it('updates value after write', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + expect(accessor.hasFieldNumber(1)).toBe(false); + accessor.setBool(1, false); + expect(accessor.hasFieldNumber(1)).toBe(true); + }); +}); + +describe('LazyAccessor clear field does', () => { + it('clear the field set', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setBool(1, true); + accessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(false); + expect(accessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(accessor.getBoolWithDefault(1)).toEqual(false); + }); + + it('clear the field decoded', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(false); + expect(accessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(accessor.getBoolWithDefault(1)).toEqual(false); + }); + + it('clear the field read', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBoolWithDefault(1)).toEqual(true); + accessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(false); + expect(accessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(accessor.getBoolWithDefault(1)).toEqual(false); + }); + + it('clear set and copied fields without affecting the old', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setBool(1, true); + + const clonedAccessor = accessor.shallowCopy(); + clonedAccessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(true); + expect(accessor.getBoolWithDefault(1)).toEqual(true); + expect(clonedAccessor.hasFieldNumber(1)).toEqual(false); + expect(clonedAccessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(false); + }); + + it('clear decoded and copied fields without affecting the old', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + + const clonedAccessor = accessor.shallowCopy(); + clonedAccessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(true); + expect(accessor.getBoolWithDefault(1)).toEqual(true); + expect(clonedAccessor.hasFieldNumber(1)).toEqual(false); + expect(clonedAccessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(false); + }); + + it('clear read and copied fields without affecting the old', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBoolWithDefault(1)).toEqual(true); + + const clonedAccessor = accessor.shallowCopy(); + clonedAccessor.clearField(1); + + expect(accessor.hasFieldNumber(1)).toEqual(true); + expect(accessor.getBoolWithDefault(1)).toEqual(true); + expect(clonedAccessor.hasFieldNumber(1)).toEqual(false); + expect(clonedAccessor.serialize()).toEqual(new ArrayBuffer(0)); + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(false); + }); +}); + +describe('LazyAccessor shallow copy does', () => { + it('work for singular fields', () => { + const accessor = LazyAccessor.createEmpty(); + accessor.setBool(1, true); + const clonedAccessor = accessor.shallowCopy(); + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(true); + + accessor.setBool(1, false); + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(true); + }); + + it('work for repeated fields', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedBoolIterable(2, [true, true]); + + const clonedAccessor = accessor.shallowCopy(); + + // Modify a repeated field after clone + accessor.addUnpackedBoolElement(2, true); + + const array = Array.from(clonedAccessor.getRepeatedBoolIterable(2)); + expect(array).toEqual([true, true]); + }); + + it('work for repeated fields', () => { + const accessor = LazyAccessor.createEmpty(); + + accessor.addUnpackedBoolIterable(2, [true, true]); + + const clonedAccessor = accessor.shallowCopy(); + + // Modify a repeated field after clone + accessor.addUnpackedBoolElement(2, true); + + const array = Array.from(clonedAccessor.getRepeatedBoolIterable(2)); + expect(array).toEqual([true, true]); + }); + + it('return the correct bytes after serialization', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x10, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes, /* pivot= */ 1); + const clonedAccessor = accessor.shallowCopy(); + + accessor.setBool(1, false); + + expect(clonedAccessor.getBoolWithDefault(1)).toEqual(true); + expect(clonedAccessor.serialize()).toEqual(bytes); + }); +}); + +describe('LazyAccessor for singular boolean does', () => { + it('return false for the empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(false); + }); + + it('return the value from the input', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(true); + }); + + it('encode the value from the input', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode the value from the input after read', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.getBoolWithDefault( + /* fieldNumber= */ 1); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('return the value from multiple inputs', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(false); + }); + + it('encode the value from multiple inputs', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode the value from multiple inputs after read', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.getBoolWithDefault(/* fieldNumber= */ 1); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('return the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setBool(1, true); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(true); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01, 0x08, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x01); + accessor.setBool(1, true); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('return the bool value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(true); + // Make sure the value is cached. + bytes[1] = 0x00; + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(true); + }); + + it('fail when getting bool value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getBoolWithDefault(/* fieldNumber= */ 1); + }).toThrowError('Expected wire type: 0 but found: 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. + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(true); + } + }); + + it('fail when setting bool value with out-of-range field number', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + if (CHECK_TYPE) { + expect(() => accessor.setBool(MAX_FIELD_NUMBER + 1, false)) + .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. + accessor.setBool(MAX_FIELD_NUMBER + 1, false); + expect(accessor.getBoolWithDefault(MAX_FIELD_NUMBER + 1)).toBe(false); + } + }); + + it('fail when setting bool value with number value', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const fakeBoolean = /** @type {boolean} */ (/** @type {*} */ (2)); + if (CHECK_CRITICAL_TYPE) { + expect(() => accessor.setBool(1, fakeBoolean)) + .toThrowError('Must be a boolean, but got: 2'); + } 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. + accessor.setBool(1, fakeBoolean); + expect(accessor.getBoolWithDefault( + /* fieldNumber= */ 1)) + .toBe(2); + } + }); +}); + +describe('LazyAccessor for singular message does', () => { + it('return message from the input', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const msg = accessor.getMessageOrNull(1, TestMessage.instanceCreator); + expect(msg.getBoolWithDefault(1, false)).toBe(true); + }); + + it('return message from the input when pivot is set', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes, /* pivot= */ 0); + const msg = accessor.getMessageOrNull(1, TestMessage.instanceCreator); + expect(msg.getBoolWithDefault(1, false)).toBe(true); + }); + + it('encode message from the input', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode message from the input after read', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('return message from multiple inputs', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x10, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const msg = accessor.getMessageOrNull(1, TestMessage.instanceCreator); + expect(msg.getBoolWithDefault(1, false)).toBe(true); + expect(msg.getBoolWithDefault(2, false)).toBe(true); + }); + + it('encode message from multiple inputs', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x10, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.serialize()).toEqual(bytes); + }); + + it('encode message merged from multiple inputs after read', () => { + const bytes = + createArrayBuffer(0x0A, 0x02, 0x08, 0x01, 0x0A, 0x02, 0x10, 0x01); + const expected = createArrayBuffer(0x0A, 0x04, 0x08, 0x01, 0x10, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + expect(accessor.serialize()).toEqual(expected); + }); + + it('return null for generic accessor', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const accessor1 = accessor.getMessageAccessorOrNull(7); + expect(accessor1).toBe(null); + }); + + it('return null for generic accessor when pivot is set', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const accessor1 = accessor.getMessageAccessorOrNull(7, /* pivot= */ 0); + expect(accessor1).toBe(null); + }); + + it('return generic accessor from the input', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const accessor1 = accessor.getMessageAccessorOrNull(1); + expect(accessor1.getBoolWithDefault(1, false)).toBe(true); + // Second call returns a new instance, isn't cached. + const accessor2 = accessor.getMessageAccessorOrNull(1); + expect(accessor2.getBoolWithDefault(1, false)).toBe(true); + expect(accessor2).not.toBe(accessor1); + }); + + it('return generic accessor from the cached input', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const wrappedMessage = + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + + // Returns accessor from the cached wrapper instance. + const accessor1 = accessor.getMessageAccessorOrNull(1); + expect(accessor1.getBoolWithDefault(1, false)).toBe(true); + expect(accessor1).toBe( + (/** @type {!InternalMessage} */ (wrappedMessage)).internalGetKernel()); + + // Second call returns exact same instance. + const accessor2 = accessor.getMessageAccessorOrNull(1); + expect(accessor2.getBoolWithDefault(1, false)).toBe(true); + expect(accessor2).toBe( + (/** @type {!InternalMessage} */ (wrappedMessage)).internalGetKernel()); + expect(accessor2).toBe(accessor1); + }); + + it('return message from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const subaccessor = LazyAccessor.fromArrayBuffer(bytes); + const submsg1 = new TestMessage(subaccessor); + accessor.setMessage(1, submsg1); + const submsg2 = accessor.getMessage(1, TestMessage.instanceCreator); + expect(submsg1).toBe(submsg2); + }); + + it('encode message from setter', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const subaccessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const subsubaccessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + const subsubmsg = new TestMessage(subsubaccessor); + subaccessor.setMessage(1, subsubmsg); + const submsg = new TestMessage(subaccessor); + accessor.setMessage(1, submsg); + const expected = createArrayBuffer(0x0A, 0x04, 0x0A, 0x02, 0x08, 0x01); + expect(accessor.serialize()).toEqual(expected); + }); + + it('encode message with multiple submessage from setter', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const subaccessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const subsubaccessor1 = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + const subsubaccessor2 = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x02)); + + const subsubmsg1 = new TestMessage(subsubaccessor1); + const subsubmsg2 = new TestMessage(subsubaccessor2); + + subaccessor.setMessage(1, subsubmsg1); + subaccessor.setMessage(2, subsubmsg2); + + const submsg = new TestMessage(subaccessor); + accessor.setMessage(1, submsg); + + const expected = createArrayBuffer( + 0x0A, 0x08, 0x0A, 0x02, 0x08, 0x01, 0x12, 0x02, 0x08, 0x02); + expect(accessor.serialize()).toEqual(expected); + }); + + it('leave hasFieldNumber unchanged after getMessageOrNull', () => { + const accessor = LazyAccessor.createEmpty(); + expect(accessor.hasFieldNumber(1)).toBe(false); + expect(accessor.getMessageOrNull(1, TestMessage.instanceCreator)) + .toBe(null); + expect(accessor.hasFieldNumber(1)).toBe(false); + }); + + it('serialize changes to submessages made with getMessageOrNull', () => { + const intTwoBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(intTwoBytes); + const mutableSubMessage = + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + mutableSubMessage.setInt32(1, 10); + const intTenBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x0A); + expect(accessor.serialize()).toEqual(intTenBytes); + }); + + it('serialize additions to submessages made with getMessageOrNull', () => { + const intTwoBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(intTwoBytes); + const mutableSubMessage = + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + mutableSubMessage.setInt32(2, 3); + // Sub message contains the original field, plus the new one. + expect(accessor.serialize()) + .toEqual(createArrayBuffer(0x0A, 0x04, 0x08, 0x02, 0x10, 0x03)); + }); + + it('fail with getMessageOrNull if immutable message exist in cache', () => { + const intTwoBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(intTwoBytes); + + const readOnly = accessor.getMessage(1, TestMessage.instanceCreator); + if (CHECK_TYPE) { + expect(() => accessor.getMessageOrNull(1, TestMessage.instanceCreator)) + .toThrow(); + } else { + const mutableSubMessage = + accessor.getMessageOrNull(1, TestMessage.instanceCreator); + // The instance returned by getMessageOrNull is the exact same instance. + expect(mutableSubMessage).toBe(readOnly); + + // Serializing the submessage does not write the changes + mutableSubMessage.setInt32(1, 0); + expect(accessor.serialize()).toEqual(intTwoBytes); + } + }); + + it('change hasFieldNumber after getMessageAttach', () => { + const accessor = LazyAccessor.createEmpty(); + expect(accessor.hasFieldNumber(1)).toBe(false); + expect(accessor.getMessageAttach(1, TestMessage.instanceCreator)) + .not.toBe(null); + expect(accessor.hasFieldNumber(1)).toBe(true); + }); + + it('change hasFieldNumber after getMessageAttach when pivot is set', () => { + const accessor = LazyAccessor.createEmpty(); + expect(accessor.hasFieldNumber(1)).toBe(false); + expect(accessor.getMessageAttach( + 1, TestMessage.instanceCreator, /* pivot= */ 1)) + .not.toBe(null); + expect(accessor.hasFieldNumber(1)).toBe(true); + }); + + it('serialize submessages made with getMessageAttach', () => { + const accessor = LazyAccessor.createEmpty(); + const mutableSubMessage = + accessor.getMessageAttach(1, TestMessage.instanceCreator); + mutableSubMessage.setInt32(1, 10); + const intTenBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x0A); + expect(accessor.serialize()).toEqual(intTenBytes); + }); + + it('serialize additions to submessages using getMessageAttach', () => { + const intTwoBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(intTwoBytes); + const mutableSubMessage = + accessor.getMessageAttach(1, TestMessage.instanceCreator); + mutableSubMessage.setInt32(2, 3); + // Sub message contains the original field, plus the new one. + expect(accessor.serialize()) + .toEqual(createArrayBuffer(0x0A, 0x04, 0x08, 0x02, 0x10, 0x03)); + }); + + it('fail with getMessageAttach if immutable message exist in cache', () => { + const intTwoBytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(intTwoBytes); + + const readOnly = accessor.getMessage(1, TestMessage.instanceCreator); + if (CHECK_TYPE) { + expect(() => accessor.getMessageAttach(1, TestMessage.instanceCreator)) + .toThrow(); + } else { + const mutableSubMessage = + accessor.getMessageAttach(1, TestMessage.instanceCreator); + // The instance returned by getMessageOrNull is the exact same instance. + expect(mutableSubMessage).toBe(readOnly); + + // Serializing the submessage does not write the changes + mutableSubMessage.setInt32(1, 0); + expect(accessor.serialize()).toEqual(intTwoBytes); + } + }); + + it('read default message return empty message with getMessage', () => { + const bytes = new ArrayBuffer(0); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getMessage(1, TestMessage.instanceCreator)).toBeTruthy(); + expect(accessor.getMessage(1, TestMessage.instanceCreator).serialize()) + .toEqual(bytes); + }); + + it('read default message return null with getMessageOrNull', () => { + const bytes = new ArrayBuffer(0); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getMessageOrNull(1, TestMessage.instanceCreator)) + .toBe(null); + }); + + it('read message preserve reference equality', () => { + const bytes = createArrayBuffer(0x0A, 0x02, 0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const msg1 = accessor.getMessageOrNull(1, TestMessage.instanceCreator); + const msg2 = accessor.getMessageOrNull(1, TestMessage.instanceCreator); + const msg3 = accessor.getMessageAttach(1, TestMessage.instanceCreator); + expect(msg1).toBe(msg2); + expect(msg1).toBe(msg3); + }); + + it('fail when getting message with other wire types', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + expect(() => accessor.getMessageOrNull(1, TestMessage.instanceCreator)) + .toThrow(); + }); + + it('fail when submessage has incomplete data', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x08)); + expect(() => accessor.getMessageOrNull(1, TestMessage.instanceCreator)) + .toThrow(); + }); + + it('fail when mutable submessage has incomplete data', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x08)); + expect(() => accessor.getMessageAttach(1, TestMessage.instanceCreator)) + .toThrow(); + }); + + it('fail when getting message with null instance constructor', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x02, 0x08, 0x01)); + const nullMessage = /** @type {function(!LazyAccessor):!TestMessage} */ + (/** @type {*} */ (null)); + expect(() => accessor.getMessageOrNull(1, nullMessage)).toThrow(); + }); + + it('fail when setting message value with null value', () => { + const accessor = LazyAccessor.fromArrayBuffer(new ArrayBuffer(0)); + const fakeMessage = /** @type {!TestMessage} */ (/** @type {*} */ (null)); + if (CHECK_CRITICAL_TYPE) { + expect(() => accessor.setMessage(1, fakeMessage)) + .toThrowError('Given value is not a message instance: null'); + } 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. + accessor.setMessage(1, fakeMessage); + expect(accessor.getMessageOrNull( + /* fieldNumber= */ 1, TestMessage.instanceCreator)) + .toBeNull(); + } + }); +}); + +describe('Bytes access', () => { + const simpleByteString = ByteString.fromArrayBuffer(createArrayBuffer(1)); + + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getBytesWithDefault(1)).toEqual(ByteString.EMPTY); + }); + + it('returns the default from parameter', () => { + const defaultByteString = ByteString.fromArrayBuffer(createArrayBuffer(1)); + const returnValue = ByteString.fromArrayBuffer(createArrayBuffer(1)); + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getBytesWithDefault(1, defaultByteString)) + .toEqual(returnValue); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x01)); + expect(accessor.getBytesWithDefault(1)).toEqual(simpleByteString); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0A, 0x01, 0x00, 0x0A, 0x01, 0x01)); + expect(accessor.getBytesWithDefault(1)).toEqual(simpleByteString); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getBytesWithDefault(1); + }).toThrowError('Expected wire type: 2 but found: 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 arrayBuffer = createArrayBuffer(1); + expect(accessor.getBytesWithDefault(1)) + .toEqual(ByteString.fromArrayBuffer(arrayBuffer)); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getBytesWithDefault( + -1, simpleByteString)) + .toThrowError('Field number is out of range: -1'); + } else { + expect( + LazyAccessor.createEmpty().getBytesWithDefault(-1, simpleByteString)) + .toEqual(simpleByteString); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setBytes(1, simpleByteString); + expect(accessor.getBytesWithDefault(1)).toEqual(simpleByteString); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x0A, 0x01, 0x01); + accessor.setBytes(1, simpleByteString); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getBytesWithDefault(1)).toEqual(simpleByteString); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getBytesWithDefault(1)).toEqual(simpleByteString); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setBytes(-1, simpleByteString)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setBytes(-1, simpleByteString); + expect(accessor.getBytesWithDefault(-1)).toEqual(simpleByteString); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setBytes( + 1, /** @type {!ByteString} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setBytes( + 1, /** @type {!ByteString} */ (/** @type {*} */ (null))); + expect(accessor.getBytesWithDefault(1)).toEqual(null); + } + }); +}); + +describe('Fixed32 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFixed32WithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFixed32WithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00)); + expect(accessor.getFixed32WithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, 0x01, 0x00, 0x80, 0x00, 0x0D, 0x02, 0x00, 0x00, 0x00)); + expect(accessor.getFixed32WithDefault(1)).toEqual(2); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getFixed32WithDefault(1); + }).toThrowError('Expected wire type: 5 but found: 0'); + } 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(accessor.getFixed32WithDefault(1)).toEqual(8421504); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getFixed32WithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getFixed32WithDefault(-1, 1)) + .toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setFixed32(1, 2); + expect(accessor.getFixed32WithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00); + accessor.setFixed32(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getFixed32WithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getFixed32WithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setFixed32(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFixed32(-1, 1); + expect(accessor.getFixed32WithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setFixed32( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFixed32(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getFixed32WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Fixed64 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(0)); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFixed64WithDefault(1, Int64.fromInt(2))) + .toEqual(Int64.fromInt(2)); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + if (CHECK_CRITICAL_STATE) { + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + expect(() => { + accessor.getFixed64WithDefault(1); + }).toThrow(); + }); + } + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getFixed64WithDefault( + -1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getFixed64WithDefault( + -1, Int64.fromInt(1))) + .toEqual(Int64.fromInt(1)); + } + }); + + it('returns the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setFixed64(1, Int64.fromInt(2)); + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('encode the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + accessor.setFixed64(1, Int64.fromInt(0)); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getFixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setFixed64(-1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFixed64(-1, Int64.fromInt(1)); + expect(accessor.getFixed64WithDefault(-1)).toEqual(Int64.fromInt(1)); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setSfixed64( + 1, /** @type {!Int64} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFixed64(1, /** @type {!Int64} */ (/** @type {*} */ (null))); + expect(accessor.getFixed64WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Float access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFloatWithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getFloatWithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x80, 0x3F)); + expect(accessor.getFloatWithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, 0x00, 0x00, 0x80, 0x3F, 0x0D, 0x00, 0x00, 0x80, 0xBF)); + expect(accessor.getFloatWithDefault(1)).toEqual(-1); + }); + + if (CHECK_CRITICAL_STATE) { + it('fails when getting float value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F)); + expect(() => { + accessor.getFloatWithDefault(1); + }).toThrow(); + }); + } + + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getFloatWithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getFloatWithDefault(-1, 1)).toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x80, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setFloat(1, 1.6); + expect(accessor.getFloatWithDefault(1)).toEqual(Math.fround(1.6)); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x80, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00); + accessor.setFloat(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns float value from cache', () => { + const bytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x80, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getFloatWithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getFloatWithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setFloat(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFloat(-1, 1); + expect(accessor.getFloatWithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setFloat( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFloat(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getFloatWithDefault(1)).toEqual(0); + } + }); + + it('throws in setter for value outside of float32 precision', () => { + if (CHECK_CRITICAL_TYPE) { + expect(() => LazyAccessor.createEmpty().setFloat(1, Number.MAX_VALUE)) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setFloat(1, Number.MAX_VALUE); + expect(accessor.getFloatWithDefault(1)).toEqual(Infinity); + } + }); +}); + +describe('Int32 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getInt32WithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getInt32WithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + expect(accessor.getInt32WithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01, 0x08, 0x02)); + expect(accessor.getInt32WithDefault(1)).toEqual(2); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getInt32WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getInt32WithDefault(1)).toEqual(0); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getInt32WithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getInt32WithDefault(-1, 1)).toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setInt32(1, 2); + expect(accessor.getInt32WithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setInt32(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getInt32WithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getInt32WithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setInt32(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setInt32(-1, 1); + expect(accessor.getInt32WithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setInt32( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setInt32(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getInt32WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Int64 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(0)); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getInt64WithDefault(1, Int64.fromInt(2))) + .toEqual(Int64.fromInt(2)); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01, 0x08, 0x02)); + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getInt64WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(0)); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getInt64WithDefault( + -1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + expect( + LazyAccessor.createEmpty().getInt64WithDefault(-1, Int64.fromInt(1))) + .toEqual(Int64.fromInt(1)); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setInt64(1, Int64.fromInt(2)); + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setInt64(1, Int64.fromInt(0)); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(1)); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getInt64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setInt64(-1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setInt64(-1, Int64.fromInt(1)); + expect(accessor.getInt64WithDefault(-1)).toEqual(Int64.fromInt(1)); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setInt64( + 1, /** @type {!Int64} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setInt64(1, /** @type {!Int64} */ (/** @type {*} */ (null))); + expect(accessor.getInt64WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Sfixed32 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSfixed32WithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSfixed32WithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00)); + expect(accessor.getSfixed32WithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x0D, 0x01, 0x00, 0x80, 0x00, 0x0D, 0x02, 0x00, 0x00, 0x00)); + expect(accessor.getSfixed32WithDefault(1)).toEqual(2); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x80, 0x80, 0x80, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getSfixed32WithDefault(1); + }).toThrowError('Expected wire type: 5 but found: 0'); + } 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(accessor.getSfixed32WithDefault(1)).toEqual(8421504); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getSfixed32WithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getSfixed32WithDefault(-1, 1)) + .toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setSfixed32(1, 2); + expect(accessor.getSfixed32WithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00); + accessor.setSfixed32(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x0D, 0x01, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getSfixed32WithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getSfixed32WithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setSfixed32(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSfixed32(-1, 1); + expect(accessor.getSfixed32WithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setSfixed32( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSfixed32(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getSfixed32WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Sfixed64 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(0)); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSfixed64WithDefault(1, Int64.fromInt(2))) + .toEqual(Int64.fromInt(2)); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + if (CHECK_CRITICAL_STATE) { + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + expect(() => { + accessor.getSfixed64WithDefault(1); + }).toThrow(); + }); + } + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getSfixed64WithDefault( + -1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getSfixed64WithDefault( + -1, Int64.fromInt(1))) + .toEqual(Int64.fromInt(1)); + } + }); + + it('returns the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setSfixed64(1, Int64.fromInt(2)); + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('encode the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + accessor.setSfixed64(1, Int64.fromInt(0)); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = + createArrayBuffer(0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setSfixed64(-1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSfixed64(-1, Int64.fromInt(1)); + expect(accessor.getSfixed64WithDefault(-1)).toEqual(Int64.fromInt(1)); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setSfixed64( + 1, /** @type {!Int64} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSfixed64(1, /** @type {!Int64} */ (/** @type {*} */ (null))); + expect(accessor.getSfixed64WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Sint32 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSint32WithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSint32WithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x02)); + expect(accessor.getSint32WithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x03, 0x08, 0x02)); + expect(accessor.getSint32WithDefault(1)).toEqual(1); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getSint32WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getSint32WithDefault(1)).toEqual(0); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getSint32WithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getSint32WithDefault(-1, 1)).toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setSint32(1, 2); + expect(accessor.getSint32WithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setSint32(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getSint32WithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getSint32WithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setSint32(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSint32(-1, 1); + expect(accessor.getSint32WithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setSint32( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSint32(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getSint32WithDefault(1)).toEqual(null); + } + }); +}); + +describe('SInt64 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(0)); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getSint64WithDefault(1, Int64.fromInt(2))) + .toEqual(Int64.fromInt(2)); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x02)); + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01, 0x08, 0x02)); + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getSint64WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(0)); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getSint64WithDefault( + -1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + expect( + LazyAccessor.createEmpty().getSint64WithDefault(-1, Int64.fromInt(1))) + .toEqual(Int64.fromInt(1)); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setSint64(1, Int64.fromInt(2)); + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setSint64(1, Int64.fromInt(0)); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x02); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(1)); + // Make sure the value is cached. + bytes[1] = 0x00; + expect(accessor.getSint64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setSint64(-1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setInt64(-1, Int64.fromInt(1)); + expect(accessor.getSint64WithDefault(-1)).toEqual(Int64.fromInt(1)); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setSint64( + 1, /** @type {!Int64} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setSint64(1, /** @type {!Int64} */ (/** @type {*} */ (null))); + expect(accessor.getSint64WithDefault(1)).toEqual(null); + } + }); +}); + +describe('String access', () => { + it('returns empty string for the empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getStringWithDefault(1)).toEqual(''); + }); + + it('returns the default for the empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getStringWithDefault(1, 'bar')).toEqual('bar'); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x0A, 0x01, 0x61)); + expect(accessor.getStringWithDefault(1)).toEqual('a'); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0A, 0x01, 0x60, 0x0A, 0x01, 0x61)); + expect(accessor.getStringWithDefault(1)).toEqual('a'); + }); + + if (CHECK_CRITICAL_STATE) { + it('fails when getting string value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x08, 0x02, 0x08, 0x08)); + expect(() => { + accessor.getStringWithDefault(1); + }).toThrow(); + }); + } + + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getStringWithDefault(-1, 'a')) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getStringWithDefault(-1, 'a')) + .toEqual('a'); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x61); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setString(1, 'b'); + expect(accessor.getStringWithDefault(1)).toEqual('b'); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x61); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x0A, 0x01, 0x62); + accessor.setString(1, 'b'); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns string value from cache', () => { + const bytes = createArrayBuffer(0x0A, 0x01, 0x61); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getStringWithDefault(1)).toBe('a'); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getStringWithDefault(1)).toBe('a'); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_TYPE) { + expect(() => LazyAccessor.createEmpty().setString(-1, 'a')) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setString(-1, 'a'); + expect(accessor.getStringWithDefault(-1)).toEqual('a'); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setString( + 1, /** @type {string} */ (/** @type {*} */ (null)))) + .toThrowError('Must be string, but got: null'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setString(1, /** @type {string} */ (/** @type {*} */ (null))); + expect(accessor.getStringWithDefault(1)).toEqual(null); + } + }); +}); + +describe('Uint32 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getUint32WithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getUint32WithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + expect(accessor.getUint32WithDefault(1)).toEqual(1); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01, 0x08, 0x02)); + expect(accessor.getUint32WithDefault(1)).toEqual(2); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getUint32WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getUint32WithDefault(1)).toEqual(0); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getUint32WithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getUint32WithDefault(-1, 1)).toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setUint32(1, 2); + expect(accessor.getUint32WithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setUint32(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getUint32WithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getUint32WithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setInt32(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setUint32(-1, 1); + expect(accessor.getUint32WithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setUint32( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setUint32(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getUint32WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Uint64 access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(0)); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getUint64WithDefault(1, Int64.fromInt(2))) + .toEqual(Int64.fromInt(2)); + }); + + it('decodes value from wire', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01)); + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('decodes value from wire with multple values being present', () => { + const accessor = + LazyAccessor.fromArrayBuffer(createArrayBuffer(0x08, 0x01, 0x08, 0x02)); + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('fails when getting value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0x00, 0x00)); + if (CHECK_CRITICAL_TYPE) { + expect(() => { + accessor.getUint64WithDefault(1); + }).toThrowError('Expected wire type: 0 but found: 5'); + } 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(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(0)); + } + }); + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect( + () => LazyAccessor.createEmpty().getUint64WithDefault( + -1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + expect( + LazyAccessor.createEmpty().getUint64WithDefault(-1, Int64.fromInt(1))) + .toEqual(Int64.fromInt(1)); + } + }); + + it('returns the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setUint64(1, Int64.fromInt(2)); + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(2)); + }); + + it('encode the value from setter', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = createArrayBuffer(0x08, 0x00); + accessor.setUint64(1, Int64.fromInt(0)); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns value from cache', () => { + const bytes = createArrayBuffer(0x08, 0x01); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(1)); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getUint64WithDefault(1)).toEqual(Int64.fromInt(1)); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setUint64(-1, Int64.fromInt(1))) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setUint64(-1, Int64.fromInt(1)); + expect(accessor.getUint64WithDefault(-1)).toEqual(Int64.fromInt(1)); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setUint64( + 1, /** @type {!Int64} */ (/** @type {*} */ (null)))) + .toThrow(); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setUint64(1, /** @type {!Int64} */ (/** @type {*} */ (null))); + expect(accessor.getUint64WithDefault(1)).toEqual(null); + } + }); +}); + +describe('Double access', () => { + it('returns default value for empty input', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getDoubleWithDefault(1)).toEqual(0); + }); + + it('returns the default from parameter', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer()); + expect(accessor.getDoubleWithDefault(1, 2)).toEqual(2); + }); + + it('decodes value from wire', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F)); + expect(accessor.getDoubleWithDefault(1)).toEqual(1); + }); + + + it('decodes value from wire with multple values being present', () => { + const accessor = LazyAccessor.fromArrayBuffer(createArrayBuffer( + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF)); + expect(accessor.getDoubleWithDefault(1)).toEqual(-1); + }); + + if (CHECK_CRITICAL_STATE) { + it('fails when getting double value with other wire types', () => { + const accessor = LazyAccessor.fromArrayBuffer( + createArrayBuffer(0x0D, 0x00, 0x00, 0xF0, 0x3F)); + expect(() => { + accessor.getDoubleWithDefault(1); + }).toThrow(); + }); + } + + + it('throws in getter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().getDoubleWithDefault(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + expect(LazyAccessor.createEmpty().getDoubleWithDefault(-1, 1)).toEqual(1); + } + }); + + it('returns the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + accessor.setDouble(1, 2); + expect(accessor.getDoubleWithDefault(1)).toEqual(2); + }); + + it('encode the value from setter', () => { + const bytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + const newBytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + accessor.setDouble(1, 0); + expect(accessor.serialize()).toEqual(newBytes); + }); + + it('returns string value from cache', () => { + const bytes = + createArrayBuffer(0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F); + const accessor = LazyAccessor.fromArrayBuffer(bytes); + expect(accessor.getDoubleWithDefault(1)).toBe(1); + // Make sure the value is cached. + bytes[2] = 0x00; + expect(accessor.getDoubleWithDefault(1)).toBe(1); + }); + + it('throws in setter for invalid fieldNumber', () => { + if (CHECK_BOUNDS) { + expect(() => LazyAccessor.createEmpty().setDouble(-1, 1)) + .toThrowError('Field number is out of range: -1'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setDouble(-1, 1); + expect(accessor.getDoubleWithDefault(-1)).toEqual(1); + } + }); + + it('throws in setter for invalid value', () => { + if (CHECK_CRITICAL_TYPE) { + expect( + () => LazyAccessor.createEmpty().setDouble( + 1, /** @type {number} */ (/** @type {*} */ (null)))) + .toThrowError('Must be a number, but got: null'); + } else { + const accessor = LazyAccessor.createEmpty(); + accessor.setDouble(1, /** @type {number} */ (/** @type {*} */ (null))); + expect(accessor.getDoubleWithDefault(1)).toEqual(null); + } + }); +}); diff --git a/js/experimental/runtime/kernel/packed_bool_test_pairs.js b/js/experimental/runtime/kernel/packed_bool_test_pairs.js new file mode 100644 index 000000000000..e8dd2f4b65e4 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_bool_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_double_test_pairs.js b/js/experimental/runtime/kernel/packed_double_test_pairs.js new file mode 100644 index 000000000000..de2cb5510ef3 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_double_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_fixed32_test_pairs.js b/js/experimental/runtime/kernel/packed_fixed32_test_pairs.js new file mode 100644 index 000000000000..5c8a54d1eb2e --- /dev/null +++ b/js/experimental/runtime/kernel/packed_fixed32_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_float_test_pairs.js b/js/experimental/runtime/kernel/packed_float_test_pairs.js new file mode 100644 index 000000000000..6d1204664d66 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_float_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_int32_test_pairs.js b/js/experimental/runtime/kernel/packed_int32_test_pairs.js new file mode 100644 index 000000000000..fbe5f40fbe42 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_int32_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_int64_test_pairs.js b/js/experimental/runtime/kernel/packed_int64_test_pairs.js new file mode 100644 index 000000000000..a6cf54e3c523 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_int64_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_sfixed32_test_pairs.js b/js/experimental/runtime/kernel/packed_sfixed32_test_pairs.js new file mode 100644 index 000000000000..d6daa79643bb --- /dev/null +++ b/js/experimental/runtime/kernel/packed_sfixed32_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_sfixed64_test_pairs.js b/js/experimental/runtime/kernel/packed_sfixed64_test_pairs.js new file mode 100644 index 000000000000..5b86703665c8 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_sfixed64_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_sint32_test_pairs.js b/js/experimental/runtime/kernel/packed_sint32_test_pairs.js new file mode 100644 index 000000000000..314ac11fe94f --- /dev/null +++ b/js/experimental/runtime/kernel/packed_sint32_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_sint64_test_pairs.js b/js/experimental/runtime/kernel/packed_sint64_test_pairs.js new file mode 100644 index 000000000000..c8730227853c --- /dev/null +++ b/js/experimental/runtime/kernel/packed_sint64_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/packed_uint32_test_pairs.js b/js/experimental/runtime/kernel/packed_uint32_test_pairs.js new file mode 100644 index 000000000000..34fbbdfed379 --- /dev/null +++ b/js/experimental/runtime/kernel/packed_uint32_test_pairs.js @@ -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, + * 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}; diff --git a/js/experimental/runtime/kernel/reader.js b/js/experimental/runtime/kernel/reader.js new file mode 100644 index 000000000000..fec79cfad533 --- /dev/null +++ b/js/experimental/runtime/kernel/reader.js @@ -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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @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} + * @package + * @template T + */ +function readPackedVariableLength(bufferDecoder, start, valueFunction) { + const /** !Array */ 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} + * @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 */ 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, +}; diff --git a/js/experimental/runtime/kernel/reader_test.js b/js/experimental/runtime/kernel/reader_test.js new file mode 100644 index 000000000000..a0cbcae67fda --- /dev/null +++ b/js/experimental/runtime/kernel/reader_test.js @@ -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); + }); + } +}); diff --git a/js/experimental/runtime/kernel/sfixed32_test_pairs.js b/js/experimental/runtime/kernel/sfixed32_test_pairs.js new file mode 100644 index 000000000000..5a082ce97273 --- /dev/null +++ b/js/experimental/runtime/kernel/sfixed32_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/sfixed64_test_pairs.js b/js/experimental/runtime/kernel/sfixed64_test_pairs.js new file mode 100644 index 000000000000..c7c8d0020b6c --- /dev/null +++ b/js/experimental/runtime/kernel/sfixed64_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/sint32_test_pairs.js b/js/experimental/runtime/kernel/sint32_test_pairs.js new file mode 100644 index 000000000000..954df90c918c --- /dev/null +++ b/js/experimental/runtime/kernel/sint32_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/sint64_test_pairs.js b/js/experimental/runtime/kernel/sint64_test_pairs.js new file mode 100644 index 000000000000..f1b4610e7ee7 --- /dev/null +++ b/js/experimental/runtime/kernel/sint64_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/storage.js b/js/experimental/runtime/kernel/storage.js new file mode 100644 index 000000000000..56dc85fbbcf0 --- /dev/null +++ b/js/experimental/runtime/kernel/storage.js @@ -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} + */ + 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} + */ + 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; diff --git a/js/experimental/runtime/kernel/storage_test.js b/js/experimental/runtime/kernel/storage_test.js new file mode 100644 index 000000000000..e4958d3c8945 --- /dev/null +++ b/js/experimental/runtime/kernel/storage_test.js @@ -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); + }); +}); diff --git a/js/experimental/runtime/kernel/textencoding.js b/js/experimental/runtime/kernel/textencoding.js new file mode 100644 index 000000000000..fe8fd25e6582 --- /dev/null +++ b/js/experimental/runtime/kernel/textencoding.js @@ -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} 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, +}; diff --git a/js/experimental/runtime/kernel/textencoding_test.js b/js/experimental/runtime/kernel/textencoding_test.js new file mode 100644 index 000000000000..c7ecd18a6f11 --- /dev/null +++ b/js/experimental/runtime/kernel/textencoding_test.js @@ -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(); + } + }); +}); diff --git a/js/experimental/runtime/kernel/typed_arrays.js b/js/experimental/runtime/kernel/typed_arrays.js new file mode 100644 index 000000000000..028096510a44 --- /dev/null +++ b/js/experimental/runtime/kernel/typed_arrays.js @@ -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, +}; diff --git a/js/experimental/runtime/kernel/typed_arrays_test.js b/js/experimental/runtime/kernel/typed_arrays_test.js new file mode 100644 index 000000000000..be9ba65da953 --- /dev/null +++ b/js/experimental/runtime/kernel/typed_arrays_test.js @@ -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)); + }); +}); diff --git a/js/experimental/runtime/kernel/uint32_test_pairs.js b/js/experimental/runtime/kernel/uint32_test_pairs.js new file mode 100644 index 000000000000..75be05e8dfe6 --- /dev/null +++ b/js/experimental/runtime/kernel/uint32_test_pairs.js @@ -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}; diff --git a/js/experimental/runtime/kernel/uint8arrays.js b/js/experimental/runtime/kernel/uint8arrays.js new file mode 100644 index 000000000000..c6bed1666732 --- /dev/null +++ b/js/experimental/runtime/kernel/uint8arrays.js @@ -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|!Array>} 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, +}; diff --git a/js/experimental/runtime/kernel/uint8arrays_test.js b/js/experimental/runtime/kernel/uint8arrays_test.js new file mode 100644 index 000000000000..6bb5a75654a1 --- /dev/null +++ b/js/experimental/runtime/kernel/uint8arrays_test.js @@ -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} */ (/** @type {*} */ (null)))) + .toThrow(); + }); +}); diff --git a/js/experimental/runtime/kernel/wire_type.js b/js/experimental/runtime/kernel/wire_type.js new file mode 100644 index 000000000000..dee6fe4bd020 --- /dev/null +++ b/js/experimental/runtime/kernel/wire_type.js @@ -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; diff --git a/js/experimental/runtime/kernel/writer.js b/js/experimental/runtime/kernel/writer.js new file mode 100644 index 000000000000..1fc741869f68 --- /dev/null +++ b/js/experimental/runtime/kernel/writer.js @@ -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} + */ + 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} + */ + 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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} values + */ + writeRepeatedBytes(fieldNumber, values) { + values.forEach(val => this.writeBytes(fieldNumber, val)); + } + + /** + * Writes packed fields with fixed length. + * @param {number} fieldNumber + * @param {!Array} 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} 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} values + */ + writeRepeatedString(fieldNumber, values) { + values.forEach(val => this.writeString(fieldNumber, val)); + } +} + +exports = Writer; diff --git a/js/experimental/runtime/kernel/writer_test.js b/js/experimental/runtime/kernel/writer_test.js new file mode 100644 index 000000000000..e4b2184e8420 --- /dev/null +++ b/js/experimental/runtime/kernel/writer_test.js @@ -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 + )); + }); +}); diff --git a/js/experimental/runtime/testing/binary/test_message.js b/js/experimental/runtime/testing/binary/test_message.js new file mode 100644 index 000000000000..8af6c3813211 --- /dev/null +++ b/js/experimental/runtime/testing/binary/test_message.js @@ -0,0 +1,1763 @@ +/** + * @fileoverview LazyAccessor wrapper message. + */ +goog.module('protobuf.testing.binary.TestMessage'); + +const ByteString = goog.require('protobuf.ByteString'); +const Int64 = goog.require('protobuf.Int64'); +const InternalMessage = goog.require('protobuf.binary.InternalMessage'); +const LazyAccessor = goog.require('protobuf.binary.LazyAccessor'); + +/** + * A protobuf message implemented as a LazyAccessor wrapper. + * @implements {InternalMessage} + */ +class TestMessage { + /** + * @param {!LazyAccessor} kernel + * @return {!TestMessage} + */ + static instanceCreator(kernel) { + return new TestMessage(kernel); + } + + /** + * @param {!LazyAccessor} kernel + */ + constructor(kernel) { + /** @private @const {!LazyAccessor} */ + this.kernel_ = kernel; + } + + /** + * @override + * @package + * @return {!LazyAccessor} + */ + internalGetKernel() { + return this.kernel_; + } + + /** + * @return {!ArrayBuffer} + */ + serialize() { + return this.kernel_.serialize(); + } + + /** + * @param {number} fieldNumber + * @param {boolean=} defaultValue + * @return {boolean} + */ + getBoolWithDefault(fieldNumber, defaultValue = false) { + return this.kernel_.getBoolWithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!ByteString=} defaultValue + * @return {!ByteString} + */ + getBytesWithDefault(fieldNumber, defaultValue = ByteString.EMPTY) { + return this.kernel_.getBytesWithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getDoubleWithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getDoubleWithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getFixed32WithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getFixed32WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getFixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.kernel_.getFixed64WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getFloatWithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getFloatWithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getInt32WithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getInt32WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getInt64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.kernel_.getInt64WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getSfixed32WithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getSfixed32WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getSfixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.kernel_.getSfixed64WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getSint32WithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getSint32WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getSint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.kernel_.getSint64WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {string=} defaultValue + * @return {string} + */ + getStringWithDefault(fieldNumber, defaultValue = '') { + return this.kernel_.getStringWithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {number=} defaultValue + * @return {number} + */ + getUint32WithDefault(fieldNumber, defaultValue = 0) { + return this.kernel_.getUint32WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {!Int64=} defaultValue + * @return {!Int64} + */ + getUint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) { + return this.kernel_.getUint64WithDefault(fieldNumber, defaultValue); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {?T} + * @template T + */ + getMessageOrNull(fieldNumber, instanceCreator) { + return this.kernel_.getMessageOrNull(fieldNumber, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {T} + * @template T + */ + getMessageAttach(fieldNumber, instanceCreator) { + return this.kernel_.getMessageAttach(fieldNumber, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {T} + * @template T + */ + getMessage(fieldNumber, instanceCreator) { + return this.kernel_.getMessage(fieldNumber, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @return {?LazyAccessor} + * @template T + */ + getMessageAccessorOrNull(fieldNumber) { + return this.kernel_.getMessageAccessorOrNull(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {boolean} + */ + getRepeatedBoolElement(fieldNumber, index) { + return this.kernel_.getRepeatedBoolElement(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedBoolIterable(fieldNumber) { + return this.kernel_.getRepeatedBoolIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedBoolSize(fieldNumber) { + return this.kernel_.getRepeatedBoolSize(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedDoubleElement(fieldNumber, index) { + return this.kernel_.getRepeatedDoubleElement(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedDoubleIterable(fieldNumber) { + return this.kernel_.getRepeatedDoubleIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedDoubleSize(fieldNumber) { + return this.kernel_.getRepeatedDoubleSize(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedFixed32Element(fieldNumber, index) { + return this.kernel_.getRepeatedFixed32Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFixed32Iterable(fieldNumber) { + return this.kernel_.getRepeatedFixed32Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFixed32Size(fieldNumber) { + return this.kernel_.getRepeatedFixed32Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedFixed64Element(fieldNumber, index) { + return this.kernel_.getRepeatedFixed64Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFixed64Iterable(fieldNumber) { + return this.kernel_.getRepeatedFixed64Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFixed64Size(fieldNumber) { + return this.kernel_.getRepeatedFixed64Size(fieldNumber); + } + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedFloatElement(fieldNumber, index) { + return this.kernel_.getRepeatedFloatElement(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedFloatIterable(fieldNumber) { + return this.kernel_.getRepeatedFloatIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedFloatSize(fieldNumber) { + return this.kernel_.getRepeatedFloatSize(fieldNumber); + } + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedInt32Element(fieldNumber, index) { + return this.kernel_.getRepeatedInt32Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedInt32Iterable(fieldNumber) { + return this.kernel_.getRepeatedInt32Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedInt32Size(fieldNumber) { + return this.kernel_.getRepeatedInt32Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedInt64Element(fieldNumber, index) { + return this.kernel_.getRepeatedInt64Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedInt64Iterable(fieldNumber) { + return this.kernel_.getRepeatedInt64Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedInt64Size(fieldNumber) { + return this.kernel_.getRepeatedInt64Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedSfixed32Element(fieldNumber, index) { + return this.kernel_.getRepeatedSfixed32Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSfixed32Iterable(fieldNumber) { + return this.kernel_.getRepeatedSfixed32Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSfixed32Size(fieldNumber) { + return this.kernel_.getRepeatedSfixed32Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedSfixed64Element(fieldNumber, index) { + return this.kernel_.getRepeatedSfixed64Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSfixed64Iterable(fieldNumber) { + return this.kernel_.getRepeatedSfixed64Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSfixed64Size(fieldNumber) { + return this.kernel_.getRepeatedSfixed64Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedSint32Element(fieldNumber, index) { + return this.kernel_.getRepeatedSint32Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSint32Iterable(fieldNumber) { + return this.kernel_.getRepeatedSint32Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSint32Size(fieldNumber) { + return this.kernel_.getRepeatedSint32Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedSint64Element(fieldNumber, index) { + return this.kernel_.getRepeatedSint64Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedSint64Iterable(fieldNumber) { + return this.kernel_.getRepeatedSint64Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedSint64Size(fieldNumber) { + return this.kernel_.getRepeatedSint64Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {number} + */ + getRepeatedUint32Element(fieldNumber, index) { + return this.kernel_.getRepeatedUint32Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedUint32Iterable(fieldNumber) { + return this.kernel_.getRepeatedUint32Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedUint32Size(fieldNumber) { + return this.kernel_.getRepeatedUint32Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!Int64} + */ + getRepeatedUint64Element(fieldNumber, index) { + return this.kernel_.getRepeatedUint64Element(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedUint64Iterable(fieldNumber) { + return this.kernel_.getRepeatedUint64Iterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedUint64Size(fieldNumber) { + return this.kernel_.getRepeatedUint64Size(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {!ByteString} + */ + getRepeatedBytesElement(fieldNumber, index) { + return this.kernel_.getRepeatedBytesElement(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedBytesIterable(fieldNumber) { + return this.kernel_.getRepeatedBytesIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedBytesSize(fieldNumber) { + return this.kernel_.getRepeatedBytesSize(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @return {string} + */ + getRepeatedStringElement(fieldNumber, index) { + return this.kernel_.getRepeatedStringElement(fieldNumber, index); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + */ + getRepeatedStringIterable(fieldNumber) { + return this.kernel_.getRepeatedStringIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @return {number} + */ + getRepeatedStringSize(fieldNumber) { + return this.kernel_.getRepeatedStringSize(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number} index + * @return {T} + * @template T + */ + getRepeatedMessageElement(fieldNumber, instanceCreator, index) { + return this.kernel_.getRepeatedMessageElement( + fieldNumber, instanceCreator, index); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {!Iterable} + * @template T + */ + getRepeatedMessageIterable(fieldNumber, instanceCreator) { + return this.kernel_.getRepeatedMessageIterable( + fieldNumber, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @return {!Iterable} + * @template T + */ + getRepeatedMessageAccessorIterable(fieldNumber) { + return this.kernel_.getRepeatedMessageAccessorIterable(fieldNumber); + } + + /** + * @param {number} fieldNumber + * @param {function(!LazyAccessor):T} instanceCreator + * @return {number} + * @template T + */ + getRepeatedMessageSize(fieldNumber, instanceCreator) { + return this.kernel_.getRepeatedMessageSize(fieldNumber, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @param {boolean} value + */ + setBool(fieldNumber, value) { + this.kernel_.setBool(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!ByteString} value + */ + setBytes(fieldNumber, value) { + this.kernel_.setBytes(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setDouble(fieldNumber, value) { + this.kernel_.setDouble(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setFixed32(fieldNumber, value) { + this.kernel_.setFixed32(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + setFixed64(fieldNumber, value) { + this.kernel_.setFixed64(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setFloat(fieldNumber, value) { + this.kernel_.setFloat(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setInt32(fieldNumber, value) { + this.kernel_.setInt32(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + setInt64(fieldNumber, value) { + this.kernel_.setInt64(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setSfixed32(fieldNumber, value) { + this.kernel_.setSfixed32(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + setSfixed64(fieldNumber, value) { + this.kernel_.setSfixed64(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setSint32(fieldNumber, value) { + this.kernel_.setSint32(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + setSint64(fieldNumber, value) { + this.kernel_.setSint64(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {string} value + */ + setString(fieldNumber, value) { + this.kernel_.setString(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + setUint32(fieldNumber, value) { + this.kernel_.setUint32(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + setUint64(fieldNumber, value) { + this.kernel_.setUint64(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {T} value + * @template T + */ + setMessage(fieldNumber, value) { + this.kernel_.setMessage(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {boolean} value + */ + addPackedBoolElement(fieldNumber, value) { + this.kernel_.addPackedBoolElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedBoolIterable(fieldNumber, values) { + this.kernel_.addPackedBoolIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {boolean} value + */ + addUnpackedBoolElement(fieldNumber, value) { + this.kernel_.addUnpackedBoolElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedBoolIterable(fieldNumber, values) { + this.kernel_.addUnpackedBoolIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {boolean} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedBoolElement(fieldNumber, index, value) { + this.kernel_.setPackedBoolElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedBoolIterable(fieldNumber, values) { + this.kernel_.setPackedBoolIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {boolean} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedBoolElement(fieldNumber, index, value) { + this.kernel_.setUnpackedBoolElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedBoolIterable(fieldNumber, values) { + this.kernel_.setUnpackedBoolIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedDoubleElement(fieldNumber, value) { + this.kernel_.addPackedDoubleElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedDoubleIterable(fieldNumber, values) { + this.kernel_.addPackedDoubleIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedDoubleElement(fieldNumber, value) { + this.kernel_.addUnpackedDoubleElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedDoubleIterable(fieldNumber, values) { + this.kernel_.addUnpackedDoubleIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedDoubleElement(fieldNumber, index, value) { + this.kernel_.setPackedDoubleElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedDoubleIterable(fieldNumber, values) { + this.kernel_.setPackedDoubleIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedDoubleElement(fieldNumber, index, value) { + this.kernel_.setUnpackedDoubleElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedDoubleIterable(fieldNumber, values) { + this.kernel_.setUnpackedDoubleIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedFixed32Element(fieldNumber, value) { + this.kernel_.addPackedFixed32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFixed32Iterable(fieldNumber, values) { + this.kernel_.addPackedFixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedFixed32Element(fieldNumber, value) { + this.kernel_.addUnpackedFixed32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFixed32Iterable(fieldNumber, values) { + this.kernel_.addUnpackedFixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFixed32Element(fieldNumber, index, value) { + this.kernel_.setPackedFixed32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFixed32Iterable(fieldNumber, values) { + this.kernel_.setPackedFixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFixed32Element(fieldNumber, index, value) { + this.kernel_.setUnpackedFixed32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFixed32Iterable(fieldNumber, values) { + this.kernel_.setUnpackedFixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedFixed64Element(fieldNumber, value) { + this.kernel_.addPackedFixed64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFixed64Iterable(fieldNumber, values) { + this.kernel_.addPackedFixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedFixed64Element(fieldNumber, value) { + this.kernel_.addUnpackedFixed64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFixed64Iterable(fieldNumber, values) { + this.kernel_.addUnpackedFixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFixed64Element(fieldNumber, index, value) { + this.kernel_.setPackedFixed64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFixed64Iterable(fieldNumber, values) { + this.kernel_.setPackedFixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFixed64Element(fieldNumber, index, value) { + this.kernel_.setUnpackedFixed64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFixed64Iterable(fieldNumber, values) { + this.kernel_.setUnpackedFixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedFloatElement(fieldNumber, value) { + this.kernel_.addPackedFloatElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedFloatIterable(fieldNumber, values) { + this.kernel_.addPackedFloatIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedFloatElement(fieldNumber, value) { + this.kernel_.addUnpackedFloatElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedFloatIterable(fieldNumber, values) { + this.kernel_.addUnpackedFloatIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedFloatElement(fieldNumber, index, value) { + this.kernel_.setPackedFloatElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedFloatIterable(fieldNumber, values) { + this.kernel_.setPackedFloatIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedFloatElement(fieldNumber, index, value) { + this.kernel_.setUnpackedFloatElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedFloatIterable(fieldNumber, values) { + this.kernel_.setUnpackedFloatIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedInt32Element(fieldNumber, value) { + this.kernel_.addPackedInt32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedInt32Iterable(fieldNumber, values) { + this.kernel_.addPackedInt32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedInt32Element(fieldNumber, value) { + this.kernel_.addUnpackedInt32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedInt32Iterable(fieldNumber, values) { + this.kernel_.addUnpackedInt32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedInt32Element(fieldNumber, index, value) { + this.kernel_.setPackedInt32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedInt32Iterable(fieldNumber, values) { + this.kernel_.setPackedInt32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedInt32Element(fieldNumber, index, value) { + this.kernel_.setUnpackedInt32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedInt32Iterable(fieldNumber, values) { + this.kernel_.setUnpackedInt32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedInt64Element(fieldNumber, value) { + this.kernel_.addPackedInt64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedInt64Iterable(fieldNumber, values) { + this.kernel_.addPackedInt64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedInt64Element(fieldNumber, value) { + this.kernel_.addUnpackedInt64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedInt64Iterable(fieldNumber, values) { + this.kernel_.addUnpackedInt64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedInt64Element(fieldNumber, index, value) { + this.kernel_.setPackedInt64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedInt64Iterable(fieldNumber, values) { + this.kernel_.setPackedInt64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedInt64Element(fieldNumber, index, value) { + this.kernel_.setUnpackedInt64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedInt64Iterable(fieldNumber, values) { + this.kernel_.setUnpackedInt64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedSfixed32Element(fieldNumber, value) { + this.kernel_.addPackedSfixed32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSfixed32Iterable(fieldNumber, values) { + this.kernel_.addPackedSfixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedSfixed32Element(fieldNumber, value) { + this.kernel_.addUnpackedSfixed32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSfixed32Iterable(fieldNumber, values) { + this.kernel_.addUnpackedSfixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSfixed32Element(fieldNumber, index, value) { + this.kernel_.setPackedSfixed32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSfixed32Iterable(fieldNumber, values) { + this.kernel_.setPackedSfixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSfixed32Element(fieldNumber, index, value) { + this.kernel_.setUnpackedSfixed32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSfixed32Iterable(fieldNumber, values) { + this.kernel_.setUnpackedSfixed32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedSfixed64Element(fieldNumber, value) { + this.kernel_.addPackedSfixed64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSfixed64Iterable(fieldNumber, values) { + this.kernel_.addPackedSfixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedSfixed64Element(fieldNumber, value) { + this.kernel_.addUnpackedSfixed64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSfixed64Iterable(fieldNumber, values) { + this.kernel_.addUnpackedSfixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSfixed64Element(fieldNumber, index, value) { + this.kernel_.setPackedSfixed64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSfixed64Iterable(fieldNumber, values) { + this.kernel_.setPackedSfixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSfixed64Element(fieldNumber, index, value) { + this.kernel_.setUnpackedSfixed64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSfixed64Iterable(fieldNumber, values) { + this.kernel_.setUnpackedSfixed64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedSint32Element(fieldNumber, value) { + this.kernel_.addPackedSint32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSint32Iterable(fieldNumber, values) { + this.kernel_.addPackedSint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedSint32Element(fieldNumber, value) { + this.kernel_.addUnpackedSint32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSint32Iterable(fieldNumber, values) { + this.kernel_.addUnpackedSint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSint32Element(fieldNumber, index, value) { + this.kernel_.setPackedSint32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSint32Iterable(fieldNumber, values) { + this.kernel_.setPackedSint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSint32Element(fieldNumber, index, value) { + this.kernel_.setUnpackedSint32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSint32Iterable(fieldNumber, values) { + this.kernel_.setUnpackedSint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedSint64Element(fieldNumber, value) { + this.kernel_.addPackedSint64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedSint64Iterable(fieldNumber, values) { + this.kernel_.addPackedSint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedSint64Element(fieldNumber, value) { + this.kernel_.addUnpackedSint64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedSint64Iterable(fieldNumber, values) { + this.kernel_.addUnpackedSint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedSint64Element(fieldNumber, index, value) { + this.kernel_.setPackedSint64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedSint64Iterable(fieldNumber, values) { + this.kernel_.setPackedSint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedSint64Element(fieldNumber, index, value) { + this.kernel_.setUnpackedSint64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedSint64Iterable(fieldNumber, values) { + this.kernel_.setUnpackedSint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addPackedUint32Element(fieldNumber, value) { + this.kernel_.addPackedUint32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedUint32Iterable(fieldNumber, values) { + this.kernel_.addPackedUint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} value + */ + addUnpackedUint32Element(fieldNumber, value) { + this.kernel_.addUnpackedUint32Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedUint32Iterable(fieldNumber, values) { + this.kernel_.addUnpackedUint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedUint32Element(fieldNumber, index, value) { + this.kernel_.setPackedUint32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedUint32Iterable(fieldNumber, values) { + this.kernel_.setPackedUint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {number} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedUint32Element(fieldNumber, index, value) { + this.kernel_.setUnpackedUint32Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedUint32Iterable(fieldNumber, values) { + this.kernel_.setUnpackedUint32Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addPackedUint64Element(fieldNumber, value) { + this.kernel_.addPackedUint64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addPackedUint64Iterable(fieldNumber, values) { + this.kernel_.addPackedUint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Int64} value + */ + addUnpackedUint64Element(fieldNumber, value) { + this.kernel_.addUnpackedUint64Element(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addUnpackedUint64Iterable(fieldNumber, values) { + this.kernel_.addUnpackedUint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setPackedUint64Element(fieldNumber, index, value) { + this.kernel_.setPackedUint64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setPackedUint64Iterable(fieldNumber, values) { + this.kernel_.setPackedUint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!Int64} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setUnpackedUint64Element(fieldNumber, index, value) { + this.kernel_.setUnpackedUint64Element(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setUnpackedUint64Iterable(fieldNumber, values) { + this.kernel_.setUnpackedUint64Iterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setRepeatedBytesIterable(fieldNumber, values) { + this.kernel_.setRepeatedBytesIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addRepeatedBytesIterable(fieldNumber, values) { + this.kernel_.addRepeatedBytesIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {!ByteString} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setRepeatedBytesElement(fieldNumber, index, value) { + this.kernel_.setRepeatedBytesElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {!ByteString} value + */ + addRepeatedBytesElement(fieldNumber, value) { + this.kernel_.addRepeatedBytesElement(fieldNumber, value); + } + + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + setRepeatedStringIterable(fieldNumber, values) { + this.kernel_.setRepeatedStringIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + */ + addRepeatedStringIterable(fieldNumber, values) { + this.kernel_.addRepeatedStringIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {number} index + * @param {string} value + * @throws {!Error} if index is out of range when check mode is critical + */ + setRepeatedStringElement(fieldNumber, index, value) { + this.kernel_.setRepeatedStringElement(fieldNumber, index, value); + } + + /** + * @param {number} fieldNumber + * @param {string} value + */ + addRepeatedStringElement(fieldNumber, value) { + this.kernel_.addRepeatedStringElement(fieldNumber, value); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + * @template T + */ + setRepeatedMessageIterable(fieldNumber, values) { + this.kernel_.setRepeatedMessageIterable(fieldNumber, values); + } + + /** + * @param {number} fieldNumber + * @param {!Iterable} values + * @param {function(!LazyAccessor):T} instanceCreator + * @template T + */ + addRepeatedMessageIterable(fieldNumber, values, instanceCreator) { + this.kernel_.addRepeatedMessageIterable( + fieldNumber, values, instanceCreator); + } + + /** + * @param {number} fieldNumber + * @param {T} value + * @param {function(!LazyAccessor):T} instanceCreator + * @param {number} index + * @throws {!Error} if index is out of range when check mode is critical + * @template T + */ + setRepeatedMessageElement(fieldNumber, value, instanceCreator, index) { + this.kernel_.setRepeatedMessageElement( + fieldNumber, value, instanceCreator, index); + } + + /** + * @param {number} fieldNumber + * @param {T} value + * @param {function(!LazyAccessor):T} instanceCreator + * @template T + */ + addRepeatedMessageElement(fieldNumber, value, instanceCreator) { + this.kernel_.addRepeatedMessageElement(fieldNumber, value, instanceCreator); + } +} + +exports = TestMessage; \ No newline at end of file diff --git a/js/experimental/runtime/testing/ensure_custom_equality_test.js b/js/experimental/runtime/testing/ensure_custom_equality_test.js new file mode 100644 index 000000000000..9323ffd524a9 --- /dev/null +++ b/js/experimental/runtime/testing/ensure_custom_equality_test.js @@ -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); + }); +}); diff --git a/js/experimental/runtime/testing/jasmine_protobuf.js b/js/experimental/runtime/testing/jasmine_protobuf.js new file mode 100644 index 000000000000..d6bc0ffd3676 --- /dev/null +++ b/js/experimental/runtime/testing/jasmine_protobuf.js @@ -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(); +});