| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 | // 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());  });});
 |