aboutsummaryrefslogtreecommitdiff
path: root/js/binary
diff options
context:
space:
mode:
Diffstat (limited to 'js/binary')
-rw-r--r--js/binary/decoder.js21
-rw-r--r--js/binary/decoder_test.js29
-rw-r--r--js/binary/encoder.js16
-rw-r--r--js/binary/message_test.js60
-rw-r--r--js/binary/proto_test.js6
-rw-r--r--js/binary/reader.js2
-rw-r--r--js/binary/utils.js4
-rw-r--r--js/binary/utils_test.js24
-rw-r--r--js/binary/writer.js14
-rw-r--r--js/binary/writer_test.js4
10 files changed, 132 insertions, 48 deletions
diff --git a/js/binary/decoder.js b/js/binary/decoder.js
index 36828a73..6db28e7c 100644
--- a/js/binary/decoder.js
+++ b/js/binary/decoder.js
@@ -71,7 +71,7 @@ jspb.BinaryIterator = function(opt_decoder, opt_next, opt_elements) {
*/
this.nextMethod_ = null;
- /** @private {Array.<number>} */
+ /** @private {?Array<number|boolean|string>} */
this.elements_ = null;
/** @private {number} */
@@ -100,7 +100,7 @@ jspb.BinaryIterator.prototype.init_ =
this.decoder_ = opt_decoder;
this.nextMethod_ = opt_next;
}
- this.elements_ = opt_elements ? opt_elements : null;
+ this.elements_ = opt_elements || null;
this.cursor_ = 0;
this.nextValue_ = null;
this.atEnd_ = !this.decoder_ && !this.elements_;
@@ -942,7 +942,7 @@ jspb.BinaryDecoder.prototype.readEnum = function() {
/**
* Reads and parses a UTF-8 encoded unicode string from the stream.
* The code is inspired by maps.vectortown.parse.StreamedDataViewReader.
- * Supports codepoints from U+0000 up to U+10FFFF.
+ * Supports codepoints from U+0000 up to U+10FFFF.
* (http://en.wikipedia.org/wiki/UTF-8).
* @param {number} length The length of the string to read.
* @return {string} The decoded string.
@@ -953,6 +953,7 @@ jspb.BinaryDecoder.prototype.readString = function(length) {
var end = cursor + length;
var codeUnits = [];
+ var result = '';
while (cursor < end) {
var c = bytes[cursor++];
if (c < 128) { // Regular 7-bit ASCII.
@@ -973,7 +974,7 @@ jspb.BinaryDecoder.prototype.readString = function(length) {
var c2 = bytes[cursor++];
var c3 = bytes[cursor++];
var c4 = bytes[cursor++];
- // Characters written on 4 bytes have 21 bits for a codepoint.
+ // Characters written on 4 bytes have 21 bits for a codepoint.
// We can't fit that on 16bit characters, so we use surrogates.
var codepoint = ((c & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63);
// Surrogates formula from wikipedia.
@@ -984,12 +985,16 @@ jspb.BinaryDecoder.prototype.readString = function(length) {
// 4. Add 0xDC00 to the low value to form the low surrogate:
var low = (codepoint & 1023) + 0xDC00;
var high = ((codepoint >> 10) & 1023) + 0xD800;
- codeUnits.push(high, low)
+ codeUnits.push(high, low);
+ }
+
+ // Avoid exceeding the maximum stack size when calling {@code apply}.
+ if (codeUnits.length >= 8192) {
+ result += String.fromCharCode.apply(null, codeUnits);
+ codeUnits.length = 0;
}
}
- // String.fromCharCode.apply is faster than manually appending characters on
- // Chrome 25+, and generates no additional cons string garbage.
- var result = String.fromCharCode.apply(null, codeUnits);
+ result += goog.crypt.byteArrayToString(codeUnits);
this.cursor_ = cursor;
return result;
};
diff --git a/js/binary/decoder_test.js b/js/binary/decoder_test.js
index 9f947b99..d0139e29 100644
--- a/js/binary/decoder_test.js
+++ b/js/binary/decoder_test.js
@@ -209,25 +209,44 @@ describe('binaryDecoderTest', function() {
assertEquals(hashC, decoder.readFixedHash64());
assertEquals(hashD, decoder.readFixedHash64());
});
-
+
+ /**
+ * Tests reading and writing large strings
+ */
+ it('testLargeStrings', function() {
+ var encoder = new jspb.BinaryEncoder();
+
+ var len = 150000;
+ var long_string = '';
+ for (var i = 0; i < len; i++) {
+ long_string += 'a';
+ }
+
+ encoder.writeString(long_string);
+
+ var decoder = jspb.BinaryDecoder.alloc(encoder.end());
+
+ assertEquals(long_string, decoder.readString(len));
+ });
+
/**
* Test encoding and decoding utf-8.
*/
it('testUtf8', function() {
var encoder = new jspb.BinaryEncoder();
- var ascii = "ASCII should work in 3, 2, 1..."
+ var ascii = "ASCII should work in 3, 2, 1...";
var utf8_two_bytes = "©";
var utf8_three_bytes = "❄";
var utf8_four_bytes = "😁";
-
+
encoder.writeString(ascii);
encoder.writeString(utf8_two_bytes);
encoder.writeString(utf8_three_bytes);
encoder.writeString(utf8_four_bytes);
-
+
var decoder = jspb.BinaryDecoder.alloc(encoder.end());
-
+
assertEquals(ascii, decoder.readString(ascii.length));
assertEquals(utf8_two_bytes, decoder.readString(utf8_two_bytes.length));
assertEquals(utf8_three_bytes, decoder.readString(utf8_three_bytes.length));
diff --git a/js/binary/encoder.js b/js/binary/encoder.js
index 1663bbd4..8e9f5bbc 100644
--- a/js/binary/encoder.js
+++ b/js/binary/encoder.js
@@ -355,8 +355,8 @@ jspb.BinaryEncoder.prototype.writeInt64 = function(value) {
*/
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));
+ 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);
};
@@ -390,11 +390,13 @@ jspb.BinaryEncoder.prototype.writeDouble = function(value) {
/**
- * Writes a boolean value to the buffer as a varint.
- * @param {boolean} value The value to write.
+ * Writes a boolean value to the buffer as a varint. We allow numbers as input
+ * because the JSPB code generator uses 0/1 instead of true/false to save space
+ * in the string representation of the proto.
+ * @param {boolean|number} value The value to write.
*/
jspb.BinaryEncoder.prototype.writeBool = function(value) {
- goog.asserts.assert(goog.isBoolean(value));
+ goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value));
this.buffer_.push(value ? 1 : 0);
};
@@ -452,9 +454,9 @@ jspb.BinaryEncoder.prototype.writeFixedHash64 = function(hash) {
*/
jspb.BinaryEncoder.prototype.writeString = function(value) {
var oldLength = this.buffer_.length;
-
+
for (var i = 0; i < value.length; i++) {
-
+
var c = value.charCodeAt(i);
if (c < 128) {
diff --git a/js/binary/message_test.js b/js/binary/message_test.js
new file mode 100644
index 00000000..4edc666b
--- /dev/null
+++ b/js/binary/message_test.js
@@ -0,0 +1,60 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test suite is written using Jasmine -- see http://jasmine.github.io/
+
+goog.setTestOnly();
+
+goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: test_pb proto.jspb.test
+goog.require('proto.jspb.test.Deeply.Nested.Message');
+
+// CommonJS-LoadFromFile: test2_pb proto.jspb.test
+goog.require('proto.jspb.test.ForeignNestedFieldMessage');
+
+describe('Message test suite', function() {
+ // Verify that we can successfully use a field referring to a nested message
+ // from a different .proto file.
+ it('testForeignNestedMessage', function() {
+ var msg = new proto.jspb.test.ForeignNestedFieldMessage();
+ var nested = new proto.jspb.test.Deeply.Nested.Message();
+ nested.setCount(5);
+ msg.setDeeplyNestedMessage(nested);
+ assertEquals(5, msg.getDeeplyNestedMessage().getCount());
+
+ // 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);
+ assertEquals(5, deserialized.getDeeplyNestedMessage().getCount());
+ });
+});
diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js
index f86dc645..f5e1b6bb 100644
--- a/js/binary/proto_test.js
+++ b/js/binary/proto_test.js
@@ -283,8 +283,7 @@ function checkAllFields(original, copy) {
* @param {!proto.jspb.test.TestExtendable} msg
*/
function checkExtensions(msg) {
- assertEquals(-42,
- msg.getExtension(proto.jspb.test.extendOptionalInt32));
+ assertEquals(0, msg.getExtension(proto.jspb.test.extendOptionalInt32));
assertEquals(-0x7fffffff00000000,
msg.getExtension(proto.jspb.test.extendOptionalInt64));
assertEquals(0x80000000,
@@ -512,8 +511,7 @@ describe('protoBinaryTest', function() {
* @param {proto.jspb.test.TestExtendable} msg
*/
function fillExtensions(msg) {
- msg.setExtension(
- proto.jspb.test.extendOptionalInt32, -42);
+ msg.setExtension(proto.jspb.test.extendOptionalInt32, 0);
msg.setExtension(
proto.jspb.test.extendOptionalInt64, -0x7fffffff00000000);
msg.setExtension(
diff --git a/js/binary/reader.js b/js/binary/reader.js
index 8c5a4e88..d5d698f7 100644
--- a/js/binary/reader.js
+++ b/js/binary/reader.js
@@ -971,7 +971,7 @@ jspb.BinaryReader.prototype.readFixedHash64 = function() {
/**
* Reads a packed scalar field using the supplied raw reader function.
- * @param {function()} decodeMethod
+ * @param {function(this:jspb.BinaryDecoder)} decodeMethod
* @return {!Array}
* @private
*/
diff --git a/js/binary/utils.js b/js/binary/utils.js
index bbf99cdf..58f11b54 100644
--- a/js/binary/utils.js
+++ b/js/binary/utils.js
@@ -430,7 +430,7 @@ jspb.utils.joinHash64 = function(bitsLow, bitsHigh) {
/**
* Individual digits for number->string conversion.
- * @const {!Array.<number>}
+ * @const {!Array.<string>}
*/
jspb.utils.DIGITS = [
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -613,7 +613,7 @@ jspb.utils.decimalStringToHash64 = function(dec) {
muladd(1, 1);
}
- return String.fromCharCode.apply(null, resultBytes);
+ return goog.crypt.byteArrayToString(resultBytes);
};
diff --git a/js/binary/utils_test.js b/js/binary/utils_test.js
index d27e5ea2..0a2f4f0a 100644
--- a/js/binary/utils_test.js
+++ b/js/binary/utils_test.js
@@ -205,31 +205,31 @@ describe('binaryUtilsTest', function() {
var convert = jspb.utils.decimalStringToHash64;
result = convert('0');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
result = convert('-1');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
result = convert('18446744073709551615');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
result = convert('9223372036854775808');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result);
result = convert('-9223372036854775808');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result);
result = convert('123456789123456789');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0x15, 0x5F, 0xD0, 0xAC, 0x4B, 0x9B, 0xB6, 0x01]), result);
result = convert('-123456789123456789');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xEB, 0xA0, 0x2F, 0x53, 0xB4, 0x64, 0x49, 0xFE]), result);
});
@@ -259,21 +259,21 @@ describe('binaryUtilsTest', function() {
var convert = jspb.utils.hexStringToHash64;
result = convert('0x0000000000000000');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
result = convert('0xffffffffffffffff');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
// Hex string is big-endian, hash string is little-endian.
result = convert('0x123456789ABCDEF0');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]), result);
// Capitalization should not matter.
result = convert('0x0000abcdefABCDEF');
- assertEquals(String.fromCharCode.apply(null,
+ assertEquals(goog.crypt.byteArrayToString(
[0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]), result);
});
@@ -643,7 +643,7 @@ describe('binaryUtilsTest', function() {
var sourceBytes = new Uint8Array(sourceData);
var sourceBuffer = sourceBytes.buffer;
var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
- var sourceString = String.fromCharCode.apply(null, sourceData);
+ var sourceString = goog.crypt.byteArrayToString(sourceData);
function check(result) {
assertEquals(Uint8Array, result.constructor);
diff --git a/js/binary/writer.js b/js/binary/writer.js
index c3009dbb..037e92b2 100644
--- a/js/binary/writer.js
+++ b/js/binary/writer.js
@@ -235,7 +235,7 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() {
/**
- * Converts the encoded data into a bas64-encoded string.
+ * Converts the encoded data into a base64-encoded string.
* @return {string}
*/
jspb.BinaryWriter.prototype.getResultBase64String = function() {
@@ -596,8 +596,8 @@ jspb.BinaryWriter.prototype.writeSint64 = function(field, value) {
*/
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));
+ goog.asserts.assert((+value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (+value < jspb.BinaryConstants.TWO_TO_63));
this.writeZigzagVarint64String_(field, value);
};
@@ -716,13 +716,15 @@ jspb.BinaryWriter.prototype.writeDouble = function(field, value) {
/**
- * Writes a boolean field to the buffer.
+ * Writes a boolean field to the buffer. We allow numbers as input
+ * because the JSPB code generator uses 0/1 instead of true/false to save space
+ * in the string representation of the proto.
* @param {number} field The field number.
- * @param {boolean?} value The value to write.
+ * @param {boolean?|number?} value The value to write.
*/
jspb.BinaryWriter.prototype.writeBool = function(field, value) {
if (value == null) return;
- goog.asserts.assert(goog.isBoolean(value));
+ goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value));
this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
this.encoder_.writeBool(value);
};
diff --git a/js/binary/writer_test.js b/js/binary/writer_test.js
index 54d37a21..118eecfc 100644
--- a/js/binary/writer_test.js
+++ b/js/binary/writer_test.js
@@ -47,9 +47,7 @@ goog.require('jspb.BinaryWriter');
* @param {function()} func This function should throw an error when run.
*/
function assertFails(func) {
- var e = assertThrows(func);
- console.log(e);
- //assertNotNull(e.toString().match(/Error/));
+ assertThrows(func);
}