|  | // 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. | 
|  |  | 
|  | /** | 
|  | * @fileoverview Test cases for jspb's binary protocol buffer reader. | 
|  | * | 
|  | * There are two particular magic numbers that need to be pointed out - | 
|  | * 2^64-1025 is the largest number representable as both a double and an | 
|  | * unsigned 64-bit integer, and 2^63-513 is the largest number representable as | 
|  | * both a double and a signed 64-bit integer. | 
|  | * | 
|  | * Test suite is written using Jasmine -- see http://jasmine.github.io/ | 
|  | * | 
|  | * @author aappleby@google.com (Austin Appleby) | 
|  | */ | 
|  |  | 
|  | goog.require('goog.testing.asserts'); | 
|  | goog.require('jspb.BinaryConstants'); | 
|  | goog.require('jspb.BinaryDecoder'); | 
|  | goog.require('jspb.BinaryReader'); | 
|  | goog.require('jspb.BinaryWriter'); | 
|  |  | 
|  |  | 
|  |  | 
|  | describe('binaryReaderTest', function() { | 
|  | /** | 
|  | * Tests the reader instance cache. | 
|  | */ | 
|  | it('testInstanceCaches', /** @suppress {visibility} */ function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  | writer.writeMessage(1, dummyMessage, goog.nullFunction); | 
|  | writer.writeMessage(2, dummyMessage, goog.nullFunction); | 
|  |  | 
|  | var buffer = writer.getResultBuffer(); | 
|  |  | 
|  | // Empty the instance caches. | 
|  | jspb.BinaryReader.instanceCache_ = []; | 
|  |  | 
|  | // Allocating and then freeing three decoders should leave us with three in | 
|  | // the cache. | 
|  |  | 
|  | var decoder1 = jspb.BinaryDecoder.alloc(); | 
|  | var decoder2 = jspb.BinaryDecoder.alloc(); | 
|  | var decoder3 = jspb.BinaryDecoder.alloc(); | 
|  | decoder1.free(); | 
|  | decoder2.free(); | 
|  | decoder3.free(); | 
|  |  | 
|  | assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); | 
|  | assertEquals(0, jspb.BinaryReader.instanceCache_.length); | 
|  |  | 
|  | // Allocating and then freeing a reader should remove one decoder from its | 
|  | // cache, but it should stay stuck to the reader afterwards since we can't | 
|  | // have a reader without a decoder. | 
|  | jspb.BinaryReader.alloc().free(); | 
|  |  | 
|  | assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); | 
|  | assertEquals(1, jspb.BinaryReader.instanceCache_.length); | 
|  |  | 
|  | // Allocating a reader should remove a reader from the cache. | 
|  | var reader = jspb.BinaryReader.alloc(buffer); | 
|  |  | 
|  | assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); | 
|  | assertEquals(0, jspb.BinaryReader.instanceCache_.length); | 
|  |  | 
|  | // Processing the message reuses the current reader. | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | assertEquals(0, jspb.BinaryReader.instanceCache_.length); | 
|  | }); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | assertEquals(0, jspb.BinaryReader.instanceCache_.length); | 
|  | }); | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  |  | 
|  | assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); | 
|  | assertEquals(0, jspb.BinaryReader.instanceCache_.length); | 
|  |  | 
|  | // Freeing the reader should put it back into the cache. | 
|  | reader.free(); | 
|  |  | 
|  | assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); | 
|  | assertEquals(1, jspb.BinaryReader.instanceCache_.length); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * @param {number} x | 
|  | * @return {number} | 
|  | */ | 
|  | function truncate(x) { | 
|  | var temp = new Float32Array(1); | 
|  | temp[0] = x; | 
|  | return temp[0]; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Verifies that misuse of the reader class triggers assertions. | 
|  | */ | 
|  | it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() { | 
|  | // Calling readMessage on a non-delimited field should trigger an | 
|  | // assertion. | 
|  | var reader = jspb.BinaryReader.alloc([8, 1]); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  | reader.nextField(); | 
|  | assertThrows(function() { | 
|  | reader.readMessage(dummyMessage, goog.nullFunction); | 
|  | }); | 
|  |  | 
|  | // Reading past the end of the stream should trigger an assertion. | 
|  | reader = jspb.BinaryReader.alloc([9, 1]); | 
|  | reader.nextField(); | 
|  | assertThrows(function() {reader.readFixed64()}); | 
|  |  | 
|  | // Reading past the end of a submessage should trigger an assertion. | 
|  | reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]); | 
|  | reader.nextField(); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | reader.nextField(); | 
|  | assertThrows(function() {reader.readFixed32()}); | 
|  | }); | 
|  |  | 
|  | // Skipping an invalid field should trigger an assertion. | 
|  | reader = jspb.BinaryReader.alloc([12, 1]); | 
|  | reader.nextWireType_ = 1000; | 
|  | assertThrows(function() {reader.skipField()}); | 
|  |  | 
|  | // Reading fields with the wrong wire type should assert. | 
|  | reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]); | 
|  | reader.nextField(); | 
|  | assertThrows(function() {reader.readInt32()}); | 
|  | assertThrows(function() {reader.readInt32String()}); | 
|  | assertThrows(function() {reader.readInt64()}); | 
|  | assertThrows(function() {reader.readInt64String()}); | 
|  | assertThrows(function() {reader.readUint32()}); | 
|  | assertThrows(function() {reader.readUint32String()}); | 
|  | assertThrows(function() {reader.readUint64()}); | 
|  | assertThrows(function() {reader.readUint64String()}); | 
|  | assertThrows(function() {reader.readSint32()}); | 
|  | assertThrows(function() {reader.readBool()}); | 
|  | assertThrows(function() {reader.readEnum()}); | 
|  |  | 
|  | reader = jspb.BinaryReader.alloc([8, 1]); | 
|  | reader.nextField(); | 
|  | assertThrows(function() {reader.readFixed32()}); | 
|  | assertThrows(function() {reader.readFixed64()}); | 
|  | assertThrows(function() {reader.readSfixed32()}); | 
|  | assertThrows(function() {reader.readSfixed64()}); | 
|  | assertThrows(function() {reader.readFloat()}); | 
|  | assertThrows(function() {reader.readDouble()}); | 
|  |  | 
|  | assertThrows(function() {reader.readString()}); | 
|  | assertThrows(function() {reader.readBytes()}); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests encoding and decoding of unsigned field types. | 
|  | * @param {Function} readField | 
|  | * @param {Function} writeField | 
|  | * @param {number} epsilon | 
|  | * @param {number} upperLimit | 
|  | * @param {Function} filter | 
|  | * @private | 
|  | * @suppress {missingProperties} | 
|  | */ | 
|  | var doTestUnsignedField_ = function(readField, | 
|  | writeField, epsilon, upperLimit, filter) { | 
|  | assertNotNull(readField); | 
|  | assertNotNull(writeField); | 
|  |  | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | // Encode zero and limits. | 
|  | writeField.call(writer, 1, filter(0)); | 
|  | writeField.call(writer, 2, filter(epsilon)); | 
|  | writeField.call(writer, 3, filter(upperLimit)); | 
|  |  | 
|  | // Encode positive values. | 
|  | for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { | 
|  | writeField.call(writer, 4, filter(cursor)); | 
|  | } | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | // Check zero and limits. | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(filter(0), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | assertEquals(filter(epsilon), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(3, reader.getFieldNumber()); | 
|  | assertEquals(filter(upperLimit), readField.call(reader)); | 
|  |  | 
|  | // Check positive values. | 
|  | for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { | 
|  | reader.nextField(); | 
|  | if (4 != reader.getFieldNumber()) throw 'fail!'; | 
|  | if (filter(cursor) != readField.call(reader)) throw 'fail!'; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests encoding and decoding of signed field types. | 
|  | * @param {Function} readField | 
|  | * @param {Function} writeField | 
|  | * @param {number} epsilon | 
|  | * @param {number} lowerLimit | 
|  | * @param {number} upperLimit | 
|  | * @param {Function} filter | 
|  | * @private | 
|  | * @suppress {missingProperties} | 
|  | */ | 
|  | var doTestSignedField_ = function(readField, | 
|  | writeField, epsilon, lowerLimit, upperLimit, filter) { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | // Encode zero and limits. | 
|  | writeField.call(writer, 1, filter(lowerLimit)); | 
|  | writeField.call(writer, 2, filter(-epsilon)); | 
|  | writeField.call(writer, 3, filter(0)); | 
|  | writeField.call(writer, 4, filter(epsilon)); | 
|  | writeField.call(writer, 5, filter(upperLimit)); | 
|  |  | 
|  | var inputValues = []; | 
|  |  | 
|  | // Encode negative values. | 
|  | for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { | 
|  | var val = filter(cursor); | 
|  | writeField.call(writer, 6, val); | 
|  | inputValues.push({ | 
|  | fieldNumber: 6, | 
|  | value: val | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Encode positive values. | 
|  | for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { | 
|  | var val = filter(cursor); | 
|  | writeField.call(writer, 7, val); | 
|  | inputValues.push({ | 
|  | fieldNumber: 7, | 
|  | value: val | 
|  | }); | 
|  | } | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | // Check zero and limits. | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(filter(lowerLimit), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | assertEquals(filter(-epsilon), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(3, reader.getFieldNumber()); | 
|  | assertEquals(filter(0), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(4, reader.getFieldNumber()); | 
|  | assertEquals(filter(epsilon), readField.call(reader)); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(5, reader.getFieldNumber()); | 
|  | assertEquals(filter(upperLimit), readField.call(reader)); | 
|  |  | 
|  | for (var i = 0; i < inputValues.length; i++) { | 
|  | var expected = inputValues[i]; | 
|  | reader.nextField(); | 
|  | assertEquals(expected.fieldNumber, reader.getFieldNumber()); | 
|  | assertEquals(expected.value, readField.call(reader)); | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests fields that use varint encoding. | 
|  | */ | 
|  | it('testVarintFields', function() { | 
|  | assertNotUndefined(jspb.BinaryReader.prototype.readUint32); | 
|  | assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32); | 
|  | assertNotUndefined(jspb.BinaryReader.prototype.readUint64); | 
|  | assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64); | 
|  | assertNotUndefined(jspb.BinaryReader.prototype.readBool); | 
|  | assertNotUndefined(jspb.BinaryWriter.prototype.writeBool); | 
|  | doTestUnsignedField_( | 
|  | jspb.BinaryReader.prototype.readUint32, | 
|  | jspb.BinaryWriter.prototype.writeUint32, | 
|  | 1, Math.pow(2, 32) - 1, Math.round); | 
|  |  | 
|  | doTestUnsignedField_( | 
|  | jspb.BinaryReader.prototype.readUint64, | 
|  | jspb.BinaryWriter.prototype.writeUint64, | 
|  | 1, Math.pow(2, 64) - 1025, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readInt32, | 
|  | jspb.BinaryWriter.prototype.writeInt32, | 
|  | 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readInt64, | 
|  | jspb.BinaryWriter.prototype.writeInt64, | 
|  | 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readEnum, | 
|  | jspb.BinaryWriter.prototype.writeEnum, | 
|  | 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); | 
|  |  | 
|  | doTestUnsignedField_( | 
|  | jspb.BinaryReader.prototype.readBool, | 
|  | jspb.BinaryWriter.prototype.writeBool, | 
|  | 1, 1, function(x) { return !!x; }); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests reading a field from hexadecimal string (format: '08 BE EF'). | 
|  | * @param {Function} readField | 
|  | * @param {number} expected | 
|  | * @param {string} hexString | 
|  | */ | 
|  | function doTestHexStringVarint_(readField, expected, hexString) { | 
|  | var bytesCount = (hexString.length + 1) / 3; | 
|  | var bytes = new Uint8Array(bytesCount); | 
|  | for (var i = 0; i < bytesCount; i++) { | 
|  | bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16); | 
|  | } | 
|  | var reader = jspb.BinaryReader.alloc(bytes); | 
|  | reader.nextField(); | 
|  | assertEquals(expected, readField.call(reader)); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests non-canonical redundant varint decoding. | 
|  | */ | 
|  | it('testRedundantVarintFields', function() { | 
|  | assertNotNull(jspb.BinaryReader.prototype.readUint32); | 
|  | assertNotNull(jspb.BinaryReader.prototype.readUint64); | 
|  | assertNotNull(jspb.BinaryReader.prototype.readSint32); | 
|  | assertNotNull(jspb.BinaryReader.prototype.readSint64); | 
|  |  | 
|  | // uint32 and sint32 take no more than 5 bytes | 
|  | // 08 - field prefix (type = 0 means varint) | 
|  | doTestHexStringVarint_( | 
|  | jspb.BinaryReader.prototype.readUint32, | 
|  | 12, '08 8C 80 80 80 00'); | 
|  |  | 
|  | // 11 stands for -6 in zigzag encoding | 
|  | doTestHexStringVarint_( | 
|  | jspb.BinaryReader.prototype.readSint32, | 
|  | -6, '08 8B 80 80 80 00'); | 
|  |  | 
|  | // uint64 and sint64 take no more than 10 bytes | 
|  | // 08 - field prefix (type = 0 means varint) | 
|  | doTestHexStringVarint_( | 
|  | jspb.BinaryReader.prototype.readUint64, | 
|  | 12, '08 8C 80 80 80 80 80 80 80 80 00'); | 
|  |  | 
|  | // 11 stands for -6 in zigzag encoding | 
|  | doTestHexStringVarint_( | 
|  | jspb.BinaryReader.prototype.readSint64, | 
|  | -6, '08 8B 80 80 80 80 80 80 80 80 00'); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests 64-bit fields that are handled as strings. | 
|  | */ | 
|  | it('testStringInt64Fields', function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | var testSignedData = [ | 
|  | '2730538252207801776', | 
|  | '-2688470994844604560', | 
|  | '3398529779486536359', | 
|  | '3568577411627971000', | 
|  | '272477188847484900', | 
|  | '-6649058714086158188', | 
|  | '-7695254765712060806', | 
|  | '-4525541438037104029', | 
|  | '-4993706538836508568', | 
|  | '4990160321893729138' | 
|  | ]; | 
|  | var testUnsignedData = [ | 
|  | '7822732630241694882', | 
|  | '6753602971916687352', | 
|  | '2399935075244442116', | 
|  | '8724292567325338867', | 
|  | '16948784802625696584', | 
|  | '4136275908516066934', | 
|  | '3575388346793700364', | 
|  | '5167142028379259461', | 
|  | '1557573948689737699', | 
|  | '17100725280812548567' | 
|  | ]; | 
|  |  | 
|  | for (var i = 0; i < testSignedData.length; i++) { | 
|  | writer.writeInt64String(2 * i + 1, testSignedData[i]); | 
|  | writer.writeUint64String(2 * i + 2, testUnsignedData[i]); | 
|  | } | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | for (var i = 0; i < testSignedData.length; i++) { | 
|  | reader.nextField(); | 
|  | assertEquals(2 * i + 1, reader.getFieldNumber()); | 
|  | assertEquals(testSignedData[i], reader.readInt64String()); | 
|  | reader.nextField(); | 
|  | assertEquals(2 * i + 2, reader.getFieldNumber()); | 
|  | assertEquals(testUnsignedData[i], reader.readUint64String()); | 
|  | } | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests fields that use zigzag encoding. | 
|  | */ | 
|  | it('testZigzagFields', function() { | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readSint32, | 
|  | jspb.BinaryWriter.prototype.writeSint32, | 
|  | 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readSint64, | 
|  | jspb.BinaryWriter.prototype.writeSint64, | 
|  | 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests fields that use fixed-length encoding. | 
|  | */ | 
|  | it('testFixedFields', function() { | 
|  | doTestUnsignedField_( | 
|  | jspb.BinaryReader.prototype.readFixed32, | 
|  | jspb.BinaryWriter.prototype.writeFixed32, | 
|  | 1, Math.pow(2, 32) - 1, Math.round); | 
|  |  | 
|  | doTestUnsignedField_( | 
|  | jspb.BinaryReader.prototype.readFixed64, | 
|  | jspb.BinaryWriter.prototype.writeFixed64, | 
|  | 1, Math.pow(2, 64) - 1025, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readSfixed32, | 
|  | jspb.BinaryWriter.prototype.writeSfixed32, | 
|  | 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readSfixed64, | 
|  | jspb.BinaryWriter.prototype.writeSfixed64, | 
|  | 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests floating point fields. | 
|  | */ | 
|  | it('testFloatFields', function() { | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readFloat, | 
|  | jspb.BinaryWriter.prototype.writeFloat, | 
|  | jspb.BinaryConstants.FLOAT32_MIN, | 
|  | -jspb.BinaryConstants.FLOAT32_MAX, | 
|  | jspb.BinaryConstants.FLOAT32_MAX, | 
|  | truncate); | 
|  |  | 
|  | doTestSignedField_( | 
|  | jspb.BinaryReader.prototype.readDouble, | 
|  | jspb.BinaryWriter.prototype.writeDouble, | 
|  | jspb.BinaryConstants.FLOAT64_EPS * 10, | 
|  | -jspb.BinaryConstants.FLOAT64_MIN, | 
|  | jspb.BinaryConstants.FLOAT64_MIN, | 
|  | function(x) { return x; }); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests length-delimited string fields. | 
|  | */ | 
|  | it('testStringFields', function() { | 
|  | var s1 = 'The quick brown fox jumps over the lazy dog.'; | 
|  | var s2 = '人人生而自由,在尊嚴和權利上一律平ç‰ã€‚'; | 
|  |  | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | writer.writeString(1, s1); | 
|  | writer.writeString(2, s2); | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(s1, reader.readString()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | assertEquals(s2, reader.readString()); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests length-delimited byte fields. | 
|  | */ | 
|  | it('testByteFields', function() { | 
|  | var message = []; | 
|  | var lowerLimit = 1; | 
|  | var upperLimit = 256; | 
|  | var scale = 1.1; | 
|  |  | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) { | 
|  | var len = Math.round(cursor); | 
|  | var bytes = []; | 
|  | for (var i = 0; i < len; i++) bytes.push(i % 256); | 
|  |  | 
|  | writer.writeBytes(len, bytes); | 
|  | } | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) { | 
|  | var len = Math.round(cursor); | 
|  | if (len != reader.getFieldNumber()) throw 'fail!'; | 
|  |  | 
|  | var bytes = reader.readBytes(); | 
|  | if (len != bytes.length) throw 'fail!'; | 
|  | for (var i = 0; i < bytes.length; i++) { | 
|  | if (i % 256 != bytes[i]) throw 'fail!'; | 
|  | } | 
|  | } | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests nested messages. | 
|  | */ | 
|  | it('testNesting', function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  |  | 
|  | writer.writeInt32(1, 100); | 
|  |  | 
|  | // Add one message with 3 int fields. | 
|  | writer.writeMessage(2, dummyMessage, function() { | 
|  | writer.writeInt32(3, 300); | 
|  | writer.writeInt32(4, 400); | 
|  | writer.writeInt32(5, 500); | 
|  | }); | 
|  |  | 
|  | // Add one empty message. | 
|  | writer.writeMessage(6, dummyMessage, goog.nullFunction); | 
|  |  | 
|  | writer.writeInt32(7, 700); | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | // Validate outermost message. | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(100, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | // Validate embedded message 1. | 
|  | reader.nextField(); | 
|  | assertEquals(3, reader.getFieldNumber()); | 
|  | assertEquals(300, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(4, reader.getFieldNumber()); | 
|  | assertEquals(400, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(5, reader.getFieldNumber()); | 
|  | assertEquals(500, reader.readInt32()); | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  | }); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(6, reader.getFieldNumber()); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | // Validate embedded message 2. | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  | }); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(7, reader.getFieldNumber()); | 
|  | assertEquals(700, reader.readInt32()); | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  | }); | 
|  |  | 
|  | /** | 
|  | * Tests skipping fields of each type by interleaving them with sentinel | 
|  | * values and skipping everything that's not a sentinel. | 
|  | */ | 
|  | it('testSkipField', function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | var sentinel = 123456789; | 
|  |  | 
|  | // Write varint fields of different sizes. | 
|  | writer.writeInt32(1, sentinel); | 
|  | writer.writeInt32(1, 1); | 
|  | writer.writeInt32(1, 1000); | 
|  | writer.writeInt32(1, 1000000); | 
|  | writer.writeInt32(1, 1000000000); | 
|  |  | 
|  | // Write fixed 64-bit encoded fields. | 
|  | writer.writeInt32(2, sentinel); | 
|  | writer.writeDouble(2, 1); | 
|  | writer.writeFixed64(2, 1); | 
|  | writer.writeSfixed64(2, 1); | 
|  |  | 
|  | // Write fixed 32-bit encoded fields. | 
|  | writer.writeInt32(3, sentinel); | 
|  | writer.writeFloat(3, 1); | 
|  | writer.writeFixed32(3, 1); | 
|  | writer.writeSfixed32(3, 1); | 
|  |  | 
|  | // Write delimited fields. | 
|  | writer.writeInt32(4, sentinel); | 
|  | writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | 
|  | writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); | 
|  |  | 
|  | // Write a group with a nested group inside. | 
|  | writer.writeInt32(5, sentinel); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  | writer.writeGroup(5, dummyMessage, function() { | 
|  | writer.writeInt64(42, 42); | 
|  | writer.writeGroup(6, dummyMessage, function() { | 
|  | writer.writeInt64(84, 42); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | // Write final sentinel. | 
|  | writer.writeInt32(6, sentinel); | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | function skip(field, count) { | 
|  | for (var i = 0; i < count; i++) { | 
|  | reader.nextField(); | 
|  | if (field != reader.getFieldNumber()) throw 'fail!'; | 
|  | reader.skipField(); | 
|  | } | 
|  | } | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | skip(1, 4); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | skip(2, 3); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(3, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | skip(3, 3); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(4, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | skip(4, 2); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(5, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | skip(5, 1); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(6, reader.getFieldNumber()); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests packed fields. | 
|  | */ | 
|  | it('testPackedFields', function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  |  | 
|  | var sentinel = 123456789; | 
|  |  | 
|  | var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | 
|  | var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]; | 
|  | var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; | 
|  | var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; | 
|  | var boolData = [true, false, true, true, false, false, true, false]; | 
|  |  | 
|  | for (var i = 0; i < floatData.length; i++) { | 
|  | floatData[i] = truncate(floatData[i]); | 
|  | } | 
|  |  | 
|  | writer.writeInt32(1, sentinel); | 
|  |  | 
|  | writer.writePackedInt32(2, signedData); | 
|  | writer.writePackedInt64(2, signedData); | 
|  | writer.writePackedUint32(2, unsignedData); | 
|  | writer.writePackedUint64(2, unsignedData); | 
|  | writer.writePackedSint32(2, signedData); | 
|  | writer.writePackedSint64(2, signedData); | 
|  | writer.writePackedFixed32(2, unsignedData); | 
|  | writer.writePackedFixed64(2, unsignedData); | 
|  | writer.writePackedSfixed32(2, signedData); | 
|  | writer.writePackedSfixed64(2, signedData); | 
|  | writer.writePackedFloat(2, floatData); | 
|  | writer.writePackedDouble(2, doubleData); | 
|  | writer.writePackedBool(2, boolData); | 
|  | writer.writePackedEnum(2, unsignedData); | 
|  |  | 
|  | writer.writeInt32(3, sentinel); | 
|  |  | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedInt32(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedInt64(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedUint32(), unsignedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedUint64(), unsignedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedSint32(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedSint64(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedFixed32(), unsignedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedFixed64(), unsignedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedSfixed32(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedSfixed64(), signedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedFloat(), floatData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedDouble(), doubleData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedBool(), boolData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertElementsEquals(reader.readPackedEnum(), unsignedData); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(sentinel, reader.readInt32()); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Byte blobs inside nested messages should always have their byte offset set | 
|  | * relative to the start of the outermost blob, not the start of their parent | 
|  | * blob. | 
|  | */ | 
|  | it('testNestedBlobs', function() { | 
|  | // Create a proto consisting of two nested messages, with the inner one | 
|  | // containing a blob of bytes. | 
|  |  | 
|  | var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED; | 
|  | var blob = [1, 2, 3, 4, 5]; | 
|  | var writer = new jspb.BinaryWriter(); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  |  | 
|  | writer.writeMessage(1, dummyMessage, function() { | 
|  | writer.writeMessage(1, dummyMessage, function() { | 
|  | writer.writeBytes(1, blob); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | // Peel off the outer two message layers. Each layer should have two bytes | 
|  | // of overhead, one for the field tag and one for the length of the inner | 
|  | // blob. | 
|  |  | 
|  | var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer()); | 
|  | assertEquals(fieldTag, decoder1.readUnsignedVarint32()); | 
|  | assertEquals(blob.length + 4, decoder1.readUnsignedVarint32()); | 
|  |  | 
|  | var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4)); | 
|  | assertEquals(fieldTag, decoder2.readUnsignedVarint32()); | 
|  | assertEquals(blob.length + 2, decoder2.readUnsignedVarint32()); | 
|  |  | 
|  | assertEquals(fieldTag, decoder2.readUnsignedVarint32()); | 
|  | assertEquals(blob.length, decoder2.readUnsignedVarint32()); | 
|  | var bytes = decoder2.readBytes(blob.length); | 
|  |  | 
|  | assertElementsEquals(bytes, blob); | 
|  | }); | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Tests read callbacks. | 
|  | */ | 
|  | it('testReadCallbacks', function() { | 
|  | var writer = new jspb.BinaryWriter(); | 
|  | var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); | 
|  |  | 
|  | // Add an int, a submessage, and another int. | 
|  | writer.writeInt32(1, 100); | 
|  |  | 
|  | writer.writeMessage(2, dummyMessage, function() { | 
|  | writer.writeInt32(3, 300); | 
|  | writer.writeInt32(4, 400); | 
|  | writer.writeInt32(5, 500); | 
|  | }); | 
|  |  | 
|  | writer.writeInt32(7, 700); | 
|  |  | 
|  | // Create the reader and register a custom read callback. | 
|  | var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); | 
|  |  | 
|  | /** | 
|  | * @param {!jspb.BinaryReader} reader | 
|  | * @return {*} | 
|  | */ | 
|  | function readCallback(reader) { | 
|  | reader.nextField(); | 
|  | assertEquals(3, reader.getFieldNumber()); | 
|  | assertEquals(300, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(4, reader.getFieldNumber()); | 
|  | assertEquals(400, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(5, reader.getFieldNumber()); | 
|  | assertEquals(500, reader.readInt32()); | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  | }; | 
|  |  | 
|  | reader.registerReadCallback('readCallback', readCallback); | 
|  |  | 
|  | // Read the container message. | 
|  | reader.nextField(); | 
|  | assertEquals(1, reader.getFieldNumber()); | 
|  | assertEquals(100, reader.readInt32()); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(2, reader.getFieldNumber()); | 
|  | reader.readMessage(dummyMessage, function() { | 
|  | // Decode the embedded message using the registered callback. | 
|  | reader.runReadCallback('readCallback'); | 
|  | }); | 
|  |  | 
|  | reader.nextField(); | 
|  | assertEquals(7, reader.getFieldNumber()); | 
|  | assertEquals(700, reader.readInt32()); | 
|  |  | 
|  | assertEquals(false, reader.nextField()); | 
|  | }); | 
|  | }); |