From 5a76e633ea9b5adb215e93fdc11e1c0c08b3fc74 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Thu, 17 Nov 2016 16:48:38 -0800 Subject: Integrated internal changes from Google --- js/binary/decoder.js | 46 ++++++ js/binary/encoder.js | 47 +++++- js/binary/proto_test.js | 41 ++++- js/binary/reader.js | 96 ++++++++++- js/binary/utils.js | 10 ++ js/binary/writer.js | 415 +++++++++++++++++++++++++----------------------- js/map.js | 54 ++++++- js/maps_test.js | 21 ++- js/message.js | 5 +- js/message_test.js | 8 +- js/proto3_test.js | 38 +++++ js/test.proto | 9 +- 12 files changed, 567 insertions(+), 223 deletions(-) (limited to 'js') diff --git a/js/binary/decoder.js b/js/binary/decoder.js index 41094a36..0e28e17c 100644 --- a/js/binary/decoder.js +++ b/js/binary/decoder.js @@ -732,6 +732,24 @@ jspb.BinaryDecoder.prototype.readZigzagVarint64 = function() { }; +/** + * Reads a signed, zigzag-encoded 64-bit varint from the binary stream and + * returns its valud as a string. + * + * Zigzag encoding is a modification of varint encoding that reduces the + * storage overhead for small negative integers - for more details on the + * format, see https://developers.google.com/protocol-buffers/docs/encoding + * + * @return {string} The decoded signed, zigzag-encoded 64-bit varint as a + * string. + */ +jspb.BinaryDecoder.prototype.readZigzagVarint64String = function() { + // TODO(haberman): write lossless 64-bit zig-zag math. + var value = this.readZigzagVarint64(); + return value.toString(); +}; + + /** * Reads a raw unsigned 8-bit integer from the binary stream. * @@ -790,6 +808,20 @@ jspb.BinaryDecoder.prototype.readUint64 = function() { }; +/** + * Reads a raw unsigned 64-bit integer from the binary stream. Note that since + * Javascript represents all numbers as double-precision floats, there will be + * precision lost if the absolute value of the integer is larger than 2^53. + * + * @return {string} The unsigned 64-bit integer read from the binary stream. + */ +jspb.BinaryDecoder.prototype.readUint64String = function() { + var bitsLow = this.readUint32(); + var bitsHigh = this.readUint32(); + return jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh); +}; + + /** * Reads a raw signed 8-bit integer from the binary stream. * @@ -848,6 +880,20 @@ jspb.BinaryDecoder.prototype.readInt64 = function() { }; +/** + * Reads a raw signed 64-bit integer from the binary stream and returns it as a + * string. + * + * @return {string} The signed 64-bit integer read from the binary stream. + * Precision will be lost if the integer exceeds 2^53. + */ +jspb.BinaryDecoder.prototype.readInt64String = function() { + var bitsLow = this.readUint32(); + var bitsHigh = this.readUint32(); + return jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh); +}; + + /** * Reads a 32-bit floating-point number from the binary stream, using the * temporary buffer to realign the data. diff --git a/js/binary/encoder.js b/js/binary/encoder.js index c9b0c2ae..30a7f2a1 100644 --- a/js/binary/encoder.js +++ b/js/binary/encoder.js @@ -99,6 +99,24 @@ jspb.BinaryEncoder.prototype.writeSplitVarint64 = function(lowBits, highBits) { }; +/** + * Encodes a 64-bit integer in 32:32 split representation into its wire-format + * fixed representation and stores it in the buffer. + * @param {number} lowBits The low 32 bits of the int. + * @param {number} highBits The high 32 bits of the int. + */ +jspb.BinaryEncoder.prototype.writeSplitFixed64 = function(lowBits, highBits) { + goog.asserts.assert(lowBits == Math.floor(lowBits)); + goog.asserts.assert(highBits == Math.floor(highBits)); + goog.asserts.assert((lowBits >= 0) && + (lowBits < jspb.BinaryConstants.TWO_TO_32)); + goog.asserts.assert((highBits >= 0) && + (highBits < jspb.BinaryConstants.TWO_TO_32)); + this.writeUint32(lowBits); + this.writeUint32(highBits); +}; + + /** * Encodes a 32-bit unsigned integer into its wire-format varint representation * and stores it in the buffer. @@ -207,6 +225,18 @@ jspb.BinaryEncoder.prototype.writeZigzagVarint64 = function(value) { }; +/** + * Encodes a JavaScript decimal string into its wire-format, zigzag-encoded + * varint representation and stores it in the buffer. Integers not representable + * in 64 bits will be truncated. + * @param {string} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeZigzagVarint64String = function(value) { + // TODO(haberman): write lossless 64-bit zig-zag math. + this.writeZigzagVarint64(parseInt(value, 10)); +}; + + /** * Writes a 8-bit unsigned integer to the buffer. Numbers outside the range * [0,2^8) will be truncated. @@ -314,8 +344,21 @@ jspb.BinaryEncoder.prototype.writeInt64 = function(value) { goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && (value < jspb.BinaryConstants.TWO_TO_63)); jspb.utils.splitInt64(value); - this.writeUint32(jspb.utils.split64Low); - this.writeUint32(jspb.utils.split64High); + this.writeSplitFixed64(jspb.utils.split64Low, jspb.utils.split64High); +}; + + +/** + * Writes a 64-bit integer decimal strings to the buffer. Numbers outside the + * range [-2^63,2^63) will be truncated. + * @param {string} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeInt64String = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && + (value < jspb.BinaryConstants.TWO_TO_63)); + jspb.utils.splitHash64(jspb.utils.decimalStringToHash64(value)); + this.writeSplitFixed64(jspb.utils.split64Low, jspb.utils.split64High); }; diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js index 26e1d30f..ae50a703 100644 --- a/js/binary/proto_test.js +++ b/js/binary/proto_test.js @@ -32,6 +32,7 @@ goog.require('goog.crypt.base64'); goog.require('goog.testing.asserts'); +goog.require('jspb.BinaryWriter'); goog.require('jspb.Message'); // CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test @@ -87,6 +88,9 @@ goog.require('proto.jspb.test.extendRepeatedStringList'); goog.require('proto.jspb.test.extendRepeatedUint32List'); goog.require('proto.jspb.test.extendRepeatedUint64List'); +// CommonJS-LoadFromFile: google/protobuf/any_pb proto.google.protobuf +goog.require('proto.google.protobuf.Any'); + var suite = {}; @@ -194,8 +198,6 @@ function bytesCompare(arr, expected) { * @param {proto.jspb.test.TestAllTypes} copy */ function checkAllFields(original, copy) { - assertTrue(jspb.Message.equals(original, copy)); - assertEquals(copy.getOptionalInt32(), -42); assertEquals(copy.getOptionalInt64(), -0x7fffffff00000000); assertEquals(copy.getOptionalUint32(), 0x80000000); @@ -270,6 +272,9 @@ function checkAllFields(original, copy) { assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]); assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]); + + // Check last so we get more granular errors first. + assertTrue(jspb.Message.equals(original, copy)); } @@ -625,4 +630,36 @@ describe('protoBinaryTest', function() { var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded); checkExtensions(decoded); }); + + /** + * Tests that unknown extensions don't cause deserialization failure. + */ + it('testUnknownExtension', function() { + var msg = new proto.jspb.test.TestExtendable(); + fillExtensions(msg); + var writer = new jspb.BinaryWriter(); + writer.writeBool((1 << 29) - 1, true); + proto.jspb.test.TestExtendable.serializeBinaryToWriter(msg, writer); + var encoded = writer.getResultBuffer(); + var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded); + checkExtensions(decoded); + }); + + it('testAnyWellKnownType', function() { + var any = new proto.google.protobuf.Any(); + var msg = new proto.jspb.test.TestAllTypes(); + + fillAllFields(msg); + + any.pack(msg.serializeBinary(), 'jspb.test.TestAllTypes'); + + assertEquals('type.googleapis.com/jspb.test.TestAllTypes', + any.getTypeUrl()); + + var msg2 = any.unpack( + proto.jspb.test.TestAllTypes.deserializeBinary, + 'jspb.test.TestAllTypes'); + + checkAllFields(msg, msg2); + }); }); diff --git a/js/binary/reader.js b/js/binary/reader.js index 15f90432..8c5a4e88 100644 --- a/js/binary/reader.js +++ b/js/binary/reader.js @@ -743,6 +743,20 @@ jspb.BinaryReader.prototype.readSint64 = function() { }; +/** + * Reads a signed zigzag-encoded 64-bit integer field from the binary stream, + * or throws an error if the next field in the stream is not of the correct + * wire type. + * + * @return {string} The value of the signed 64-bit integer field as a decimal string. + */ +jspb.BinaryReader.prototype.readSint64String = function() { + goog.asserts.assert( + this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT); + return this.decoder_.readZigzagVarint64String(); +}; + + /** * Reads an unsigned 32-bit fixed-length integer fiield from the binary stream, * or throws an error if the next field in the stream is not of the correct @@ -771,12 +785,29 @@ jspb.BinaryReader.prototype.readFixed64 = function() { }; +/** + * Reads a signed 64-bit integer field from the binary stream as a string, or + * throws an error if the next field in the stream is not of the correct wire + * type. + * + * Returns the value as a string. + * + * @return {string} The value of the unsigned 64-bit integer field as a decimal + * string. + */ +jspb.BinaryReader.prototype.readFixed64String = function() { + goog.asserts.assert( + this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64); + return this.decoder_.readUint64String(); +}; + + /** * Reads a signed 32-bit fixed-length integer fiield from the binary stream, or * throws an error if the next field in the stream is not of the correct wire * type. * - * @return {number} The value of the double field. + * @return {number} The value of the signed 32-bit integer field. */ jspb.BinaryReader.prototype.readSfixed32 = function() { goog.asserts.assert( @@ -785,12 +816,27 @@ jspb.BinaryReader.prototype.readSfixed32 = function() { }; +/** + * Reads a signed 32-bit fixed-length integer fiield from the binary stream, or + * throws an error if the next field in the stream is not of the correct wire + * type. + * + * @return {string} The value of the signed 32-bit integer field as a decimal + * string. + */ +jspb.BinaryReader.prototype.readSfixed32String = function() { + goog.asserts.assert( + this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED32); + return this.decoder_.readInt32().toString(); +}; + + /** * Reads a signed 64-bit fixed-length integer fiield from the binary stream, or * throws an error if the next field in the stream is not of the correct wire * type. * - * @return {number} The value of the float field. + * @return {number} The value of the sfixed64 field. */ jspb.BinaryReader.prototype.readSfixed64 = function() { goog.asserts.assert( @@ -799,6 +845,22 @@ jspb.BinaryReader.prototype.readSfixed64 = function() { }; +/** + * Reads a signed 64-bit fixed-length integer fiield from the binary stream, or + * throws an error if the next field in the stream is not of the correct wire + * type. + * + * Returns the value as a string. + * + * @return {string} The value of the sfixed64 field as a decimal string. + */ +jspb.BinaryReader.prototype.readSfixed64String = function() { + goog.asserts.assert( + this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64); + return this.decoder_.readInt64String(); +}; + + /** * Reads a 32-bit floating-point field from the binary stream, or throws an * error if the next field in the stream is not of the correct wire type. @@ -1027,6 +1089,16 @@ jspb.BinaryReader.prototype.readPackedSint64 = function() { }; +/** + * Reads a packed sint64 field, which consists of a length header and a list of + * zigzag varints. Returns a list of strings. + * @return {!Array.} + */ +jspb.BinaryReader.prototype.readPackedSint64String = function() { + return this.readPackedField_(this.decoder_.readZigzagVarint64String); +}; + + /** * Reads a packed fixed32 field, which consists of a length header and a list * of unsigned 32-bit ints. @@ -1047,6 +1119,16 @@ jspb.BinaryReader.prototype.readPackedFixed64 = function() { }; +/** + * Reads a packed fixed64 field, which consists of a length header and a list + * of unsigned 64-bit ints. Returns a list of strings. + * @return {!Array.} + */ +jspb.BinaryReader.prototype.readPackedFixed64String = function() { + return this.readPackedField_(this.decoder_.readUint64String); +}; + + /** * Reads a packed sfixed32 field, which consists of a length header and a list * of 32-bit ints. @@ -1067,6 +1149,16 @@ jspb.BinaryReader.prototype.readPackedSfixed64 = function() { }; +/** + * Reads a packed sfixed64 field, which consists of a length header and a list + * of 64-bit ints. Returns a list of strings. + * @return {!Array.} + */ +jspb.BinaryReader.prototype.readPackedSfixed64String = function() { + return this.readPackedField_(this.decoder_.readInt64String); +}; + + /** * Reads a packed float field, which consists of a length header and a list of * floats. diff --git a/js/binary/utils.js b/js/binary/utils.js index 51405553..bbf99cdf 100644 --- a/js/binary/utils.js +++ b/js/binary/utils.js @@ -617,6 +617,16 @@ jspb.utils.decimalStringToHash64 = function(dec) { }; +/** + * Converts a signed or unsigned decimal string into two 32-bit halves, and + * stores them in the temp variables listed above. + * @param {string} value The decimal string to convert. + */ +jspb.utils.splitDecimalString = function(value) { + jspb.utils.splitHash64(jspb.utils.decimalStringToHash64(value)); +}; + + /** * Converts an 8-character hash string into its hexadecimal representation. * @param {string} hash diff --git a/js/binary/writer.js b/js/binary/writer.js index 3eb2f1bd..c3009dbb 100644 --- a/js/binary/writer.js +++ b/js/binary/writer.js @@ -434,6 +434,20 @@ jspb.BinaryWriter.prototype.writeZigzagVarint64_ = function(field, value) { }; +/** + * Writes a zigzag varint field to the buffer without range checking. + * @param {number} field The field number. + * @param {string?} value The value to write. + * @private + */ +jspb.BinaryWriter.prototype.writeZigzagVarint64String_ = function( + field, value) { + if (value == null) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeZigzagVarint64String(value); +}; + + /** * Writes an int32 field to the buffer. Numbers outside the range [-2^31,2^31) * will be truncated. @@ -574,6 +588,20 @@ jspb.BinaryWriter.prototype.writeSint64 = function(field, value) { }; +/** + * Writes a sint64 field to the buffer. Numbers outside the range [-2^63,2^63) + * will be truncated. + * @param {number} field The field number. + * @param {string?} value The decimal string to write. + */ +jspb.BinaryWriter.prototype.writeSint64String = function(field, value) { + if (value == null) return; + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && + (value < jspb.BinaryConstants.TWO_TO_63)); + this.writeZigzagVarint64String_(field, value); +}; + + /** * Writes a fixed32 field to the buffer. Numbers outside the range [0,2^32) * will be truncated. @@ -604,6 +632,19 @@ jspb.BinaryWriter.prototype.writeFixed64 = function(field, value) { }; +/** + * Writes a fixed64 field (with value as a string) to the buffer. + * @param {number} field The field number. + * @param {string?} value The value to write. + */ +jspb.BinaryWriter.prototype.writeFixed64String = function(field, value) { + if (value == null) return; + var num = jspb.arith.UInt64.fromString(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeSplitFixed64(num.lo, num.hi); +}; + + /** * Writes a sfixed32 field to the buffer. Numbers outside the range * [-2^31,2^31) will be truncated. @@ -634,6 +675,20 @@ jspb.BinaryWriter.prototype.writeSfixed64 = function(field, value) { }; +/** + * Writes a sfixed64 string field to the buffer. Numbers outside the range + * [-2^63,2^63) will be truncated. + * @param {number} field The field number. + * @param {string?} value The value to write. + */ +jspb.BinaryWriter.prototype.writeSfixed64String = function(field, value) { + if (value == null) return; + var num = jspb.arith.Int64.fromString(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeSplitFixed64(num.lo, num.hi); +}; + + /** * Writes a single-precision floating point field to the buffer. Numbers * requiring more than 32 bits of precision will be truncated. @@ -796,28 +851,11 @@ jspb.BinaryWriter.prototype.writeVarintHash64 = function(field, value) { /** - * Writes an array of numbers to the buffer as a repeated varint field. + * Writes an array of numbers to the buffer as a repeated 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_ = - function(field, value) { - if (value == null) return; - for (var i = 0; i < value.length; i++) { - this.writeUnsignedVarint32_(field, value[i]); - } -}; - - -/** - * Writes an array of numbers to the buffer as a repeated varint field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private - */ -jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedInt32 = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { this.writeSignedVarint32_(field, value[i]); @@ -826,28 +864,25 @@ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ = /** - * Writes an array of numbers to the buffer as a repeated varint field. + * Writes an array of numbers formatted as strings to the buffer as a repeated + * 32-bit int field. * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private + * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_ = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedInt32String = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeUnsignedVarint64_(field, value[i]); + this.writeInt32String(field, value[i]); } }; /** - * Writes an array of numbers to the buffer as a repeated varint field. + * Writes an array of numbers to the buffer as a repeated 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_ = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedInt64 = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { this.writeSignedVarint64_(field, value[i]); @@ -856,147 +891,112 @@ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_ = /** - * Writes an array of numbers to the buffer as a repeated zigzag field. + * Writes an array of numbers formatted as strings to the buffer as a repeated + * 64-bit int field. * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private + * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedZigzag32_ = function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedInt64String = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeZigzagVarint32_(field, value[i]); + this.writeInt64String(field, value[i]); } }; /** - * Writes an array of numbers to the buffer as a repeated zigzag field. + * Writes an array numbers to the buffer as a repeated unsigned 32-bit int + * field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writeRepeatedZigzag_ = function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedUint32 = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeZigzagVarint64_(field, value[i]); + this.writeUnsignedVarint32_(field, value[i]); } }; -/** - * Writes an array of numbers to the buffer as a repeated 32-bit int field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writeRepeatedInt32 = - jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_; - - /** * Writes an array of numbers formatted as strings to the buffer as a repeated - * 32-bit int field. + * unsigned 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedInt32String = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedUint32String = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeInt32String(field, value[i]); + this.writeUint32String(field, value[i]); } }; /** - * Writes an array of numbers to the buffer as a repeated 64-bit int field. + * Writes an array numbers to the buffer as a repeated unsigned 64-bit int + * field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedInt64 = - jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_; - - -/** - * Writes an array of numbers formatted as strings to the buffer as a repeated - * 64-bit int field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writeRepeatedInt64String = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedUint64 = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeInt64String(field, value[i]); + this.writeUnsignedVarint64_(field, value[i]); } }; -/** - * Writes an array numbers to the buffer as a repeated unsigned 32-bit int - * field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writeRepeatedUint32 = - jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_; - - /** * Writes an array of numbers formatted as strings to the buffer as a repeated - * unsigned 32-bit int field. + * unsigned 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedUint32String = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedUint64String = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeUint32String(field, value[i]); + this.writeUint64String(field, value[i]); } }; /** - * Writes an array numbers to the buffer as a repeated unsigned 64-bit int - * field. + * Writes an array numbers to the buffer as a repeated signed 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedUint64 = - jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_; - - -/** - * Writes an array of numbers formatted as strings to the buffer as a repeated - * unsigned 64-bit int field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writeRepeatedUint64String = - function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedSint32 = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeUint64String(field, value[i]); + this.writeZigzagVarint32_(field, value[i]); } }; /** - * Writes an array numbers to the buffer as a repeated signed 32-bit int field. + * Writes an array numbers to the buffer as a repeated signed 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedSint32 = - jspb.BinaryWriter.prototype.writeRepeatedZigzag32_; +jspb.BinaryWriter.prototype.writeRepeatedSint64 = function(field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeZigzagVarint64_(field, value[i]); + } +}; /** * Writes an array numbers to the buffer as a repeated signed 64-bit int field. * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. + * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writeRepeatedSint64 = - jspb.BinaryWriter.prototype.writeRepeatedZigzag_; +jspb.BinaryWriter.prototype.writeRepeatedSint64String = function(field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeZigzagVarint64String_(field, value[i]); + } +}; /** @@ -1027,6 +1027,21 @@ jspb.BinaryWriter.prototype.writeRepeatedFixed64 = function(field, value) { }; +/** + * Writes an array of numbers to the buffer as a repeated fixed64 field. This + * works for both signed and unsigned fixed64s. + * @param {number} field The field number. + * @param {?Array.} value The array of decimal strings to write. + */ +jspb.BinaryWriter.prototype.writeRepeatedFixed64String = function( + field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeFixed64String(field, value[i]); + } +}; + + /** * Writes an array of numbers to the buffer as a repeated sfixed32 field. * @param {number} field The field number. @@ -1053,6 +1068,20 @@ jspb.BinaryWriter.prototype.writeRepeatedSfixed64 = function(field, value) { }; +/** + * Writes an array of decimal strings to the buffer as a repeated sfixed64 + * field. + * @param {number} field The field number. + * @param {?Array.} value The array of decimal strings to write. + */ +jspb.BinaryWriter.prototype.writeRepeatedSfixed64String = function(field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeSfixed64String(field, value[i]); + } +}; + + /** * Writes an array of numbers to the buffer as a repeated float field. * @param {number} field The field number. @@ -1203,151 +1232,127 @@ jspb.BinaryWriter.prototype.writeRepeatedVarintHash64 = /** - * Writes an array of numbers to the buffer as a packed varint field. + * Writes an array of numbers to the buffer as a packed 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_ = function( - field, value) { +jspb.BinaryWriter.prototype.writePackedInt32 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeUnsignedVarint32(value[i]); + this.encoder_.writeSignedVarint32(value[i]); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed varint field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private + * Writes an array of numbers represented as strings to the buffer as a packed + * 32-bit int field. + * @param {number} field + * @param {?Array.} value */ -jspb.BinaryWriter.prototype.writePackedSignedVarint32_ = function( - field, value) { +jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeSignedVarint32(value[i]); + this.encoder_.writeSignedVarint32(parseInt(value[i], 10)); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed varint field. + * Writes an array of numbers to the buffer as a packed 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_ = function( - field, value) { +jspb.BinaryWriter.prototype.writePackedInt64 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeUnsignedVarint64(value[i]); + this.encoder_.writeSignedVarint64(value[i]); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed varint field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private + * Writes an array of numbers represented as strings to the buffer as a packed + * 64-bit int field. + * @param {number} field + * @param {?Array.} value */ -jspb.BinaryWriter.prototype.writePackedSignedVarint64_ = function( - field, value) { +jspb.BinaryWriter.prototype.writePackedInt64String = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeSignedVarint64(value[i]); + var num = jspb.arith.Int64.fromString(value[i]); + this.encoder_.writeSplitVarint64(num.lo, num.hi); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed zigzag field. + * Writes an array numbers to the buffer as a packed unsigned 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @private */ -jspb.BinaryWriter.prototype.writePackedZigzag32_ = function(field, value) { +jspb.BinaryWriter.prototype.writePackedUint32 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeZigzagVarint32(value[i]); + this.encoder_.writeUnsignedVarint32(value[i]); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed zigzag field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - * @private + * Writes an array of numbers represented as strings to the buffer as a packed + * unsigned 32-bit int field. + * @param {number} field + * @param {?Array.} value */ -jspb.BinaryWriter.prototype.writePackedZigzag64_ = function(field, value) { +jspb.BinaryWriter.prototype.writePackedUint32String = + function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeZigzagVarint64(value[i]); + this.encoder_.writeUnsignedVarint32(parseInt(value[i], 10)); } this.endDelimited_(bookmark); }; /** - * Writes an array of numbers to the buffer as a packed 32-bit int field. + * Writes an array numbers to the buffer as a packed unsigned 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writePackedInt32 = - jspb.BinaryWriter.prototype.writePackedSignedVarint32_; - - -/** - * Writes an array of numbers represented as strings to the buffer as a packed - * 32-bit int field. - * @param {number} field - * @param {?Array.} value - */ -jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) { +jspb.BinaryWriter.prototype.writePackedUint64 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeSignedVarint32(parseInt(value[i], 10)); + this.encoder_.writeUnsignedVarint64(value[i]); } this.endDelimited_(bookmark); }; -/** - * Writes an array of numbers to the buffer as a packed 64-bit int field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writePackedInt64 = - jspb.BinaryWriter.prototype.writePackedSignedVarint64_; - - /** * Writes an array of numbers represented as strings to the buffer as a packed - * 64-bit int field. + * unsigned 64-bit int field. * @param {number} field * @param {?Array.} value */ -jspb.BinaryWriter.prototype.writePackedInt64String = +jspb.BinaryWriter.prototype.writePackedUint64String = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - var num = jspb.arith.Int64.fromString(value[i]); + var num = jspb.arith.UInt64.fromString(value[i]); this.encoder_.writeSplitVarint64(num.lo, num.hi); } this.endDelimited_(bookmark); @@ -1355,74 +1360,50 @@ jspb.BinaryWriter.prototype.writePackedInt64String = /** - * Writes an array numbers to the buffer as a packed unsigned 32-bit int field. + * Writes an array numbers to the buffer as a packed signed 32-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writePackedUint32 = - jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_; - - -/** - * Writes an array of numbers represented as strings to the buffer as a packed - * unsigned 32-bit int field. - * @param {number} field - * @param {?Array.} value - */ -jspb.BinaryWriter.prototype.writePackedUint32String = - function(field, value) { +jspb.BinaryWriter.prototype.writePackedSint32 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.encoder_.writeUnsignedVarint32(parseInt(value[i], 10)); + this.encoder_.writeZigzagVarint32(value[i]); } this.endDelimited_(bookmark); }; /** - * Writes an array numbers to the buffer as a packed unsigned 64-bit int field. + * Writes an array of numbers to the buffer as a packed signed 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. */ -jspb.BinaryWriter.prototype.writePackedUint64 = - jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_; - - -/** - * Writes an array of numbers represented as strings to the buffer as a packed - * unsigned 64-bit int field. - * @param {number} field - * @param {?Array.} value - */ -jspb.BinaryWriter.prototype.writePackedUint64String = - function(field, value) { +jspb.BinaryWriter.prototype.writePackedSint64 = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - var num = jspb.arith.UInt64.fromString(value[i]); - this.encoder_.writeSplitVarint64(num.lo, num.hi); + this.encoder_.writeZigzagVarint64(value[i]); } this.endDelimited_(bookmark); }; /** - * Writes an array numbers to the buffer as a packed signed 32-bit int field. + * Writes an array of decimal strings to the buffer as a packed signed 64-bit + * int field. * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. + * @param {?Array.} value The array of decimal strings to write. */ -jspb.BinaryWriter.prototype.writePackedSint32 = - jspb.BinaryWriter.prototype.writePackedZigzag32_; - - -/** - * Writes an array numbers to the buffer as a packed signed 64-bit int field. - * @param {number} field The field number. - * @param {?Array.} value The array of ints to write. - */ -jspb.BinaryWriter.prototype.writePackedSint64 = - jspb.BinaryWriter.prototype.writePackedZigzag64_; +jspb.BinaryWriter.prototype.writePackedSint64String = function(field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + // TODO(haberman): make lossless + this.encoder_.writeZigzagVarint64(parseInt(value[i], 10)); + } + this.endDelimited_(bookmark); +}; /** @@ -1455,6 +1436,23 @@ jspb.BinaryWriter.prototype.writePackedFixed64 = function(field, value) { }; +/** + * Writes an array of numbers represented as strings to the buffer as a packed + * fixed64 field. + * @param {number} field The field number. + * @param {?Array.} value The array of strings to write. + */ +jspb.BinaryWriter.prototype.writePackedFixed64String = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + var num = jspb.arith.UInt64.fromString(value[i]); + this.encoder_.writeSplitFixed64(num.lo, num.hi); + } +}; + + /** * Writes an array of numbers to the buffer as a packed sfixed32 field. * @param {number} field The field number. @@ -1485,6 +1483,21 @@ jspb.BinaryWriter.prototype.writePackedSfixed64 = function(field, value) { }; +/** + * Writes an array of numbers to the buffer as a packed sfixed64 field. + * @param {number} field The field number. + * @param {?Array.} value The array of decimal strings to write. + */ +jspb.BinaryWriter.prototype.writePackedSfixed64String = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeInt64String(value[i]); + } +}; + + /** * Writes an array of numbers to the buffer as a packed float field. * @param {number} field The field number. diff --git a/js/map.js b/js/map.js index 0f8401c4..93f08e03 100644 --- a/js/map.js +++ b/js/map.js @@ -130,6 +130,58 @@ jspb.Map.prototype.toArray = function() { }; +/** + * Returns the map formatted as an array of key-value pairs, suitable for the + * toObject() form of a message. + * + * @param {boolean=} includeInstance Whether to include the JSPB instance for + * transitional soy proto support: http://goto/soy-param-migration + * @param {!function((boolean|undefined),!V):!Object=} valueToObject + * The static toObject() method, if V is a message type. + * @return {!Array>} + */ +jspb.Map.prototype.toObject = function(includeInstance, valueToObject) { + var rawArray = this.toArray(); + var entries = []; + for (var i = 0; i < rawArray.length; i++) { + var entry = this.map_[rawArray[i][0].toString()]; + this.wrapEntry_(entry); + var valueWrapper = /** @type {!V|undefined} */ (entry.valueWrapper); + if (valueWrapper) { + goog.asserts.assert(valueToObject); + entries.push([entry.key, valueToObject(includeInstance, valueWrapper)]); + } else { + entries.push([entry.key, entry.value]); + } + } + return entries; +}; + + +/** + * Returns a Map from the given array of key-value pairs when the values are of + * message type. The values in the array must match the format returned by their + * message type's toObject() method. + * + * @template K, V + * @param {!Array>} entries + * @param {!function(new:V)|function(new:V,?)} valueCtor + * The constructor for type V. + * @param {!function(!Object):V} valueFromObject + * The fromObject function for type V. + * @return {!jspb.Map} + */ +jspb.Map.fromObject = function(entries, valueCtor, valueFromObject) { + var result = new jspb.Map([], valueCtor); + for (var i = 0; i < entries.length; i++) { + var key = entries[i][0]; + var value = valueFromObject(entries[i][1]); + result.set(key, value); + } + return result; +}; + + /** * Helper: return an iterator over an array. * @template T @@ -193,7 +245,7 @@ jspb.Map.prototype.del = function(key) { * to help out Angular 1.x users. Still evaluating whether this is the best * option. * - * @return {!Array} + * @return {!Array>} */ jspb.Map.prototype.getEntryList = function() { var entries = []; diff --git a/js/maps_test.js b/js/maps_test.js index 0d442f4f..6e6ddc29 100755 --- a/js/maps_test.js +++ b/js/maps_test.js @@ -262,6 +262,7 @@ function makeTests(msgInfo, submessageCtor, suffix) { }); } + /** * Exercises the lazy map<->underlying array sync. */ @@ -290,12 +291,16 @@ function makeTests(msgInfo, submessageCtor, suffix) { } describe('mapsTest', function() { - makeTests({ - constructor: proto.jspb.test.TestMapFields, - deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary - }, proto.jspb.test.MapValueMessage, "_Binary"); - makeTests({ - constructor: proto.jspb.test.TestMapFieldsNoBinary, - deserializeBinary: null - }, proto.jspb.test.MapValueMessageNoBinary, "_NoBinary"); + makeTests( + { + constructor: proto.jspb.test.TestMapFields, + deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary + }, + proto.jspb.test.MapValueMessage, '_Binary'); + makeTests( + { + constructor: proto.jspb.test.TestMapFieldsNoBinary, + deserializeBinary: null + }, + proto.jspb.test.MapValueMessageNoBinary, '_NoBinary'); }); diff --git a/js/message.js b/js/message.js index 8def763e..1eb88aef 100644 --- a/js/message.js +++ b/js/message.js @@ -550,11 +550,11 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, jspb.Message.readBinaryExtension = function(msg, reader, extensions, getExtensionFn, setExtensionFn) { var binaryFieldInfo = extensions[reader.getFieldNumber()]; - var fieldInfo = binaryFieldInfo.fieldInfo; if (!binaryFieldInfo) { reader.skipField(); return; } + var fieldInfo = binaryFieldInfo.fieldInfo; if (!binaryFieldInfo.binaryReaderFn) { throw new Error('Deserializing extension whose generated code does not ' + 'support binary format'); @@ -972,7 +972,8 @@ jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) { * Sets a proto field and syncs it to the backing array. * @param {!jspb.Message} msg A jspb proto. * @param {number} fieldNumber The field number. - * @param {jspb.Message|undefined} value A new value for this proto field. + * @param {?jspb.Message|?jspb.Map|undefined} value A new value for this proto + * field. * @protected */ jspb.Message.setWrapperField = function(msg, fieldNumber, value) { diff --git a/js/message_test.js b/js/message_test.js index b0a0a72e..38ed1360 100644 --- a/js/message_test.js +++ b/js/message_test.js @@ -73,10 +73,12 @@ goog.require('proto.jspb.test.TestGroup1'); goog.require('proto.jspb.test.TestMessageWithOneof'); goog.require('proto.jspb.test.TestReservedNames'); goog.require('proto.jspb.test.TestReservedNamesExtension'); +goog.require('proto.jspb.test.Deeply.Nested.Message'); // CommonJS-LoadFromFile: test2_pb proto.jspb.test goog.require('proto.jspb.test.ExtensionMessage'); goog.require('proto.jspb.test.TestExtensionsMessage'); +goog.require('proto.jspb.test.ForeignNestedFieldMessage'); @@ -1050,8 +1052,10 @@ describe('Message test suite', function() { // After a serialization-deserialization round trip we should get back the // same data we started with. - var serialized = msg.serializeBinary(); - var deserialized = proto.jspb.test.ForeignNestedFieldMessage.deserializeBinary(serialized); + var serialized = msg.toObject(); + var deserialized = + proto.jspb.test.ForeignNestedFieldMessage.fromObject(serialized); assertEquals(5, deserialized.getDeeplyNestedMessage().getCount()); }); + }); diff --git a/js/proto3_test.js b/js/proto3_test.js index 3c929eff..81d6de2f 100644 --- a/js/proto3_test.js +++ b/js/proto3_test.js @@ -38,6 +38,12 @@ goog.require('proto.jspb.test.ForeignMessage'); goog.require('proto.jspb.test.Proto3Enum'); goog.require('proto.jspb.test.TestProto3'); +// CommonJS-LoadFromFile: google/protobuf/timestamp_pb proto.google.protobuf +goog.require('proto.google.protobuf.Timestamp'); + +// CommonJS-LoadFromFile: google/protobuf/struct_pb proto.google.protobuf +goog.require('proto.google.protobuf.Struct'); + var BYTES = new Uint8Array([1, 2, 8, 9]); var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES); @@ -326,4 +332,36 @@ describe('proto3Test', function() { assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); }); + + it('testTimestampWellKnownType', function() { + var msg = new proto.google.protobuf.Timestamp(); + msg.fromDate(new Date(123456789)); + assertEquals(123456, msg.getSeconds()); + assertEquals(789000000, msg.getNanos()); + var date = msg.toDate(); + assertEquals(123456789, date.getTime()); + }); + + it('testStructWellKnownType', function() { + var jsObj = { + abc: "def", + number: 12345.678, + nullKey: null, + boolKey: true, + listKey: [1, null, true, false, "abc"], + structKey: {foo: "bar", somenum: 123}, + complicatedKey: [{xyz: {abc: [3, 4, null, false]}}, "zzz"] + }; + + var struct = proto.google.protobuf.Struct.fromJavaScript(jsObj); + var jsObj2 = struct.toJavaScript(); + + assertEquals("def", jsObj2.abc); + assertEquals(12345.678, jsObj2.number); + assertEquals(null, jsObj2.nullKey); + assertEquals(true, jsObj2.boolKey); + assertEquals("abc", jsObj2.listKey[4]); + assertEquals("bar", jsObj2.structKey.foo); + assertEquals(4, jsObj2.complicatedKey[0].xyz.abc[1]); + }); }); diff --git a/js/test.proto b/js/test.proto index db238e1a..52ba2cc1 100644 --- a/js/test.proto +++ b/js/test.proto @@ -234,7 +234,9 @@ message TestEndsWithBytes { optional bytes data = 2; } + message TestMapFieldsNoBinary { + map map_string_string = 1; map map_string_int32 = 2; map map_string_int64 = 3; @@ -252,12 +254,13 @@ message TestMapFieldsNoBinary { } enum MapValueEnumNoBinary { - MAP_VALUE_FOO_NOBINARY = 0; - MAP_VALUE_BAR_NOBINARY = 1; - MAP_VALUE_BAZ_NOBINARY = 2; + MAP_VALUE_FOO = 0; + MAP_VALUE_BAR = 1; + MAP_VALUE_BAZ = 2; } message MapValueMessageNoBinary { + optional int32 foo = 1; } -- cgit v1.2.3