Home | History | Annotate | Download | only in binary
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 /**
     32  * @fileoverview Test cases for jspb's binary protocol buffer reader.
     33  *
     34  * There are two particular magic numbers that need to be pointed out -
     35  * 2^64-1025 is the largest number representable as both a double and an
     36  * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
     37  * both a double and a signed 64-bit integer.
     38  *
     39  * Test suite is written using Jasmine -- see http://jasmine.github.io/
     40  *
     41  * @author aappleby (a] google.com (Austin Appleby)
     42  */
     43 
     44 goog.require('goog.testing.asserts');
     45 goog.require('jspb.BinaryConstants');
     46 goog.require('jspb.BinaryDecoder');
     47 goog.require('jspb.BinaryReader');
     48 goog.require('jspb.BinaryWriter');
     49 
     50 
     51 
     52 describe('binaryReaderTest', function() {
     53   /**
     54    * Tests the reader instance cache.
     55    * @suppress {visibility}
     56    */
     57   it('testInstanceCaches', function() {
     58     var writer = new jspb.BinaryWriter();
     59     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
     60     writer.writeMessage(1, dummyMessage, goog.nullFunction);
     61     writer.writeMessage(2, dummyMessage, goog.nullFunction);
     62 
     63     var buffer = writer.getResultBuffer();
     64 
     65     // Empty the instance caches.
     66     jspb.BinaryReader.instanceCache_ = [];
     67 
     68     // Allocating and then freeing three decoders should leave us with three in
     69     // the cache.
     70 
     71     var decoder1 = jspb.BinaryDecoder.alloc();
     72     var decoder2 = jspb.BinaryDecoder.alloc();
     73     var decoder3 = jspb.BinaryDecoder.alloc();
     74     decoder1.free();
     75     decoder2.free();
     76     decoder3.free();
     77 
     78     assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
     79     assertEquals(0, jspb.BinaryReader.instanceCache_.length);
     80 
     81     // Allocating and then freeing a reader should remove one decoder from its
     82     // cache, but it should stay stuck to the reader afterwards since we can't
     83     // have a reader without a decoder.
     84     jspb.BinaryReader.alloc().free();
     85 
     86     assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
     87     assertEquals(1, jspb.BinaryReader.instanceCache_.length);
     88 
     89     // Allocating a reader should remove a reader from the cache.
     90     var reader = jspb.BinaryReader.alloc(buffer);
     91 
     92     assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
     93     assertEquals(0, jspb.BinaryReader.instanceCache_.length);
     94 
     95     // Processing the message reuses the current reader.
     96     reader.nextField();
     97     assertEquals(1, reader.getFieldNumber());
     98     reader.readMessage(dummyMessage, function() {
     99       assertEquals(0, jspb.BinaryReader.instanceCache_.length);
    100     });
    101 
    102     reader.nextField();
    103     assertEquals(2, reader.getFieldNumber());
    104     reader.readMessage(dummyMessage, function() {
    105       assertEquals(0, jspb.BinaryReader.instanceCache_.length);
    106     });
    107 
    108     assertEquals(false, reader.nextField());
    109 
    110     assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
    111     assertEquals(0, jspb.BinaryReader.instanceCache_.length);
    112 
    113     // Freeing the reader should put it back into the cache.
    114     reader.free();
    115 
    116     assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
    117     assertEquals(1, jspb.BinaryReader.instanceCache_.length);
    118   });
    119 
    120 
    121   /**
    122    * @param {number} x
    123    * @return {number}
    124    */
    125   function truncate(x) {
    126     var temp = new Float32Array(1);
    127     temp[0] = x;
    128     return temp[0];
    129   }
    130 
    131 
    132   /**
    133    * Verifies that misuse of the reader class triggers assertions.
    134    * @suppress {checkTypes|visibility}
    135    */
    136   it('testReadErrors', function() {
    137     // Calling readMessage on a non-delimited field should trigger an
    138     // assertion.
    139     var reader = jspb.BinaryReader.alloc([8, 1]);
    140     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
    141     reader.nextField();
    142     assertThrows(function() {
    143       reader.readMessage(dummyMessage, goog.nullFunction);
    144     });
    145 
    146     // Reading past the end of the stream should trigger an assertion.
    147     reader = jspb.BinaryReader.alloc([9, 1]);
    148     reader.nextField();
    149     assertThrows(function() {reader.readFixed64()});
    150 
    151     // Reading past the end of a submessage should trigger an assertion.
    152     reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]);
    153     reader.nextField();
    154     reader.readMessage(dummyMessage, function() {
    155       reader.nextField();
    156       assertThrows(function() {reader.readFixed32()});
    157     });
    158 
    159     // Skipping an invalid field should trigger an assertion.
    160     reader = jspb.BinaryReader.alloc([12, 1]);
    161     reader.nextWireType_ = 1000;
    162     assertThrows(function() {reader.skipField()});
    163 
    164     // Reading fields with the wrong wire type should assert.
    165     reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]);
    166     reader.nextField();
    167     assertThrows(function() {reader.readInt32()});
    168     assertThrows(function() {reader.readInt32String()});
    169     assertThrows(function() {reader.readInt64()});
    170     assertThrows(function() {reader.readInt64String()});
    171     assertThrows(function() {reader.readUint32()});
    172     assertThrows(function() {reader.readUint32String()});
    173     assertThrows(function() {reader.readUint64()});
    174     assertThrows(function() {reader.readUint64String()});
    175     assertThrows(function() {reader.readSint32()});
    176     assertThrows(function() {reader.readBool()});
    177     assertThrows(function() {reader.readEnum()});
    178 
    179     reader = jspb.BinaryReader.alloc([8, 1]);
    180     reader.nextField();
    181     assertThrows(function() {reader.readFixed32()});
    182     assertThrows(function() {reader.readFixed64()});
    183     assertThrows(function() {reader.readSfixed32()});
    184     assertThrows(function() {reader.readSfixed64()});
    185     assertThrows(function() {reader.readFloat()});
    186     assertThrows(function() {reader.readDouble()});
    187 
    188     assertThrows(function() {reader.readString()});
    189     assertThrows(function() {reader.readBytes()});
    190   });
    191 
    192 
    193   /**
    194    * Tests encoding and decoding of unsigned field types.
    195    * @param {Function} readField
    196    * @param {Function} writeField
    197    * @param {number} epsilon
    198    * @param {number} upperLimit
    199    * @param {Function} filter
    200    * @private
    201    * @suppress {missingProperties}
    202    */
    203   function doTestUnsignedField_(readField,
    204       writeField, epsilon, upperLimit, filter) {
    205     assertNotNull(readField);
    206     assertNotNull(writeField);
    207 
    208     var writer = new jspb.BinaryWriter();
    209 
    210     // Encode zero and limits.
    211     writeField.call(writer, 1, filter(0));
    212     writeField.call(writer, 2, filter(epsilon));
    213     writeField.call(writer, 3, filter(upperLimit));
    214 
    215     // Encode positive values.
    216     for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
    217       writeField.call(writer, 4, filter(cursor));
    218     }
    219 
    220     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    221 
    222     // Check zero and limits.
    223     reader.nextField();
    224     assertEquals(1, reader.getFieldNumber());
    225     assertEquals(filter(0), readField.call(reader));
    226 
    227     reader.nextField();
    228     assertEquals(2, reader.getFieldNumber());
    229     assertEquals(filter(epsilon), readField.call(reader));
    230 
    231     reader.nextField();
    232     assertEquals(3, reader.getFieldNumber());
    233     assertEquals(filter(upperLimit), readField.call(reader));
    234 
    235     // Check positive values.
    236     for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
    237       reader.nextField();
    238       if (4 != reader.getFieldNumber()) throw 'fail!';
    239       if (filter(cursor) != readField.call(reader)) throw 'fail!';
    240     }
    241   };
    242 
    243 
    244   /**
    245    * Tests encoding and decoding of signed field types.
    246    * @param {Function} readField
    247    * @param {Function} writeField
    248    * @param {number} epsilon
    249    * @param {number} lowerLimit
    250    * @param {number} upperLimit
    251    * @param {Function} filter
    252    * @private
    253    * @suppress {missingProperties}
    254    */
    255   function doTestSignedField_(readField,
    256       writeField, epsilon, lowerLimit, upperLimit, filter) {
    257     var writer = new jspb.BinaryWriter();
    258 
    259     // Encode zero and limits.
    260     writeField.call(writer, 1, filter(lowerLimit));
    261     writeField.call(writer, 2, filter(-epsilon));
    262     writeField.call(writer, 3, filter(0));
    263     writeField.call(writer, 4, filter(epsilon));
    264     writeField.call(writer, 5, filter(upperLimit));
    265 
    266     var inputValues = [];
    267 
    268     // Encode negative values.
    269     for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
    270       var val = filter(cursor);
    271       writeField.call(writer, 6, val);
    272       inputValues.push({
    273         fieldNumber: 6,
    274         value: val
    275       });
    276     }
    277 
    278     // Encode positive values.
    279     for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
    280       var val = filter(cursor);
    281       writeField.call(writer, 7, val);
    282       inputValues.push({
    283         fieldNumber: 7,
    284         value: val
    285       });
    286     }
    287 
    288     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    289 
    290     // Check zero and limits.
    291     reader.nextField();
    292     assertEquals(1, reader.getFieldNumber());
    293     assertEquals(filter(lowerLimit), readField.call(reader));
    294 
    295     reader.nextField();
    296     assertEquals(2, reader.getFieldNumber());
    297     assertEquals(filter(-epsilon), readField.call(reader));
    298 
    299     reader.nextField();
    300     assertEquals(3, reader.getFieldNumber());
    301     assertEquals(filter(0), readField.call(reader));
    302 
    303     reader.nextField();
    304     assertEquals(4, reader.getFieldNumber());
    305     assertEquals(filter(epsilon), readField.call(reader));
    306 
    307     reader.nextField();
    308     assertEquals(5, reader.getFieldNumber());
    309     assertEquals(filter(upperLimit), readField.call(reader));
    310 
    311     for (var i = 0; i < inputValues.length; i++) {
    312       var expected = inputValues[i];
    313       reader.nextField();
    314       assertEquals(expected.fieldNumber, reader.getFieldNumber());
    315       assertEquals(expected.value, readField.call(reader));
    316     }
    317   };
    318 
    319 
    320   /**
    321    * Tests fields that use varint encoding.
    322    */
    323   it('testVarintFields', function() {
    324     assertNotNull(jspb.BinaryReader.prototype.readUint32);
    325     assertNotNull(jspb.BinaryReader.prototype.writeUint32);
    326     assertNotNull(jspb.BinaryReader.prototype.readUint64);
    327     assertNotNull(jspb.BinaryReader.prototype.writeUint64);
    328     assertNotNull(jspb.BinaryReader.prototype.readBool);
    329     assertNotNull(jspb.BinaryReader.prototype.writeBool);
    330     doTestUnsignedField_(
    331         jspb.BinaryReader.prototype.readUint32,
    332         jspb.BinaryWriter.prototype.writeUint32,
    333         1, Math.pow(2, 32) - 1, Math.round);
    334 
    335     doTestUnsignedField_(
    336         jspb.BinaryReader.prototype.readUint64,
    337         jspb.BinaryWriter.prototype.writeUint64,
    338         1, Math.pow(2, 64) - 1025, Math.round);
    339 
    340     doTestSignedField_(
    341         jspb.BinaryReader.prototype.readInt32,
    342         jspb.BinaryWriter.prototype.writeInt32,
    343         1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
    344 
    345     doTestSignedField_(
    346         jspb.BinaryReader.prototype.readInt64,
    347         jspb.BinaryWriter.prototype.writeInt64,
    348         1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
    349 
    350     doTestSignedField_(
    351         jspb.BinaryReader.prototype.readEnum,
    352         jspb.BinaryWriter.prototype.writeEnum,
    353         1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
    354 
    355     doTestUnsignedField_(
    356         jspb.BinaryReader.prototype.readBool,
    357         jspb.BinaryWriter.prototype.writeBool,
    358         1, 1, function(x) { return !!x; });
    359   });
    360 
    361 
    362   /**
    363    * Tests 64-bit fields that are handled as strings.
    364    */
    365   it('testStringInt64Fields', function() {
    366     var writer = new jspb.BinaryWriter();
    367 
    368     var testSignedData = [
    369       '2730538252207801776',
    370       '-2688470994844604560',
    371       '3398529779486536359',
    372       '3568577411627971000',
    373       '272477188847484900',
    374       '-6649058714086158188',
    375       '-7695254765712060806',
    376       '-4525541438037104029',
    377       '-4993706538836508568',
    378       '4990160321893729138'
    379     ];
    380     var testUnsignedData = [
    381       '7822732630241694882',
    382       '6753602971916687352',
    383       '2399935075244442116',
    384       '8724292567325338867',
    385       '16948784802625696584',
    386       '4136275908516066934',
    387       '3575388346793700364',
    388       '5167142028379259461',
    389       '1557573948689737699',
    390       '17100725280812548567'
    391     ];
    392 
    393     for (var i = 0; i < testSignedData.length; i++) {
    394       writer.writeInt64String(2 * i + 1, testSignedData[i]);
    395       writer.writeUint64String(2 * i + 2, testUnsignedData[i]);
    396     }
    397 
    398     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    399 
    400     for (var i = 0; i < testSignedData.length; i++) {
    401       reader.nextField();
    402       assertEquals(2 * i + 1, reader.getFieldNumber());
    403       assertEquals(testSignedData[i], reader.readInt64String());
    404       reader.nextField();
    405       assertEquals(2 * i + 2, reader.getFieldNumber());
    406       assertEquals(testUnsignedData[i], reader.readUint64String());
    407     }
    408   });
    409 
    410 
    411   /**
    412    * Tests fields that use zigzag encoding.
    413    */
    414   it('testZigzagFields', function() {
    415     doTestSignedField_(
    416         jspb.BinaryReader.prototype.readSint32,
    417         jspb.BinaryWriter.prototype.writeSint32,
    418         1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
    419 
    420     doTestSignedField_(
    421         jspb.BinaryReader.prototype.readSint64,
    422         jspb.BinaryWriter.prototype.writeSint64,
    423         1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
    424   });
    425 
    426 
    427   /**
    428    * Tests fields that use fixed-length encoding.
    429    */
    430   it('testFixedFields', function() {
    431     doTestUnsignedField_(
    432         jspb.BinaryReader.prototype.readFixed32,
    433         jspb.BinaryWriter.prototype.writeFixed32,
    434         1, Math.pow(2, 32) - 1, Math.round);
    435 
    436     doTestUnsignedField_(
    437         jspb.BinaryReader.prototype.readFixed64,
    438         jspb.BinaryWriter.prototype.writeFixed64,
    439         1, Math.pow(2, 64) - 1025, Math.round);
    440 
    441     doTestSignedField_(
    442         jspb.BinaryReader.prototype.readSfixed32,
    443         jspb.BinaryWriter.prototype.writeSfixed32,
    444         1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
    445 
    446     doTestSignedField_(
    447         jspb.BinaryReader.prototype.readSfixed64,
    448         jspb.BinaryWriter.prototype.writeSfixed64,
    449         1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
    450   });
    451 
    452 
    453   /**
    454    * Tests floating point fields.
    455    */
    456   it('testFloatFields', function() {
    457     doTestSignedField_(
    458         jspb.BinaryReader.prototype.readFloat,
    459         jspb.BinaryWriter.prototype.writeFloat,
    460         jspb.BinaryConstants.FLOAT32_MIN,
    461         -jspb.BinaryConstants.FLOAT32_MAX,
    462         jspb.BinaryConstants.FLOAT32_MAX,
    463         truncate);
    464 
    465     doTestSignedField_(
    466         jspb.BinaryReader.prototype.readDouble,
    467         jspb.BinaryWriter.prototype.writeDouble,
    468         jspb.BinaryConstants.FLOAT64_EPS * 10,
    469         -jspb.BinaryConstants.FLOAT64_MIN,
    470         jspb.BinaryConstants.FLOAT64_MIN,
    471         function(x) { return x; });
    472   });
    473 
    474 
    475   /**
    476    * Tests length-delimited string fields.
    477    */
    478   it('testStringFields', function() {
    479     var s1 = 'The quick brown fox jumps over the lazy dog.';
    480     var s2 = '';
    481 
    482     var writer = new jspb.BinaryWriter();
    483 
    484     writer.writeString(1, s1);
    485     writer.writeString(2, s2);
    486 
    487     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    488 
    489     reader.nextField();
    490     assertEquals(1, reader.getFieldNumber());
    491     assertEquals(s1, reader.readString());
    492 
    493     reader.nextField();
    494     assertEquals(2, reader.getFieldNumber());
    495     assertEquals(s2, reader.readString());
    496   });
    497 
    498 
    499   /**
    500    * Tests length-delimited byte fields.
    501    */
    502   it('testByteFields', function() {
    503     var message = [];
    504     var lowerLimit = 1;
    505     var upperLimit = 256;
    506     var scale = 1.1;
    507 
    508     var writer = new jspb.BinaryWriter();
    509 
    510     for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) {
    511       var len = Math.round(cursor);
    512       var bytes = [];
    513       for (var i = 0; i < len; i++) bytes.push(i % 256);
    514 
    515       writer.writeBytes(len, bytes);
    516     }
    517 
    518     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    519 
    520     for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) {
    521       var len = Math.round(cursor);
    522       if (len != reader.getFieldNumber()) throw 'fail!';
    523 
    524       var bytes = reader.readBytes();
    525       if (len != bytes.length) throw 'fail!';
    526       for (var i = 0; i < bytes.length; i++) {
    527         if (i % 256 != bytes[i]) throw 'fail!';
    528       }
    529     }
    530   });
    531 
    532 
    533   /**
    534    * Tests nested messages.
    535    */
    536   it('testNesting', function() {
    537     var writer = new jspb.BinaryWriter();
    538     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
    539 
    540     writer.writeInt32(1, 100);
    541 
    542     // Add one message with 3 int fields.
    543     writer.writeMessage(2, dummyMessage, function() {
    544       writer.writeInt32(3, 300);
    545       writer.writeInt32(4, 400);
    546       writer.writeInt32(5, 500);
    547     });
    548 
    549     // Add one empty message.
    550     writer.writeMessage(6, dummyMessage, goog.nullFunction);
    551 
    552     writer.writeInt32(7, 700);
    553 
    554     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    555 
    556     // Validate outermost message.
    557 
    558     reader.nextField();
    559     assertEquals(1, reader.getFieldNumber());
    560     assertEquals(100, reader.readInt32());
    561 
    562     reader.nextField();
    563     assertEquals(2, reader.getFieldNumber());
    564     reader.readMessage(dummyMessage, function() {
    565       // Validate embedded message 1.
    566       reader.nextField();
    567       assertEquals(3, reader.getFieldNumber());
    568       assertEquals(300, reader.readInt32());
    569 
    570       reader.nextField();
    571       assertEquals(4, reader.getFieldNumber());
    572       assertEquals(400, reader.readInt32());
    573 
    574       reader.nextField();
    575       assertEquals(5, reader.getFieldNumber());
    576       assertEquals(500, reader.readInt32());
    577 
    578       assertEquals(false, reader.nextField());
    579     });
    580 
    581     reader.nextField();
    582     assertEquals(6, reader.getFieldNumber());
    583     reader.readMessage(dummyMessage, function() {
    584       // Validate embedded message 2.
    585 
    586       assertEquals(false, reader.nextField());
    587     });
    588 
    589     reader.nextField();
    590     assertEquals(7, reader.getFieldNumber());
    591     assertEquals(700, reader.readInt32());
    592 
    593     assertEquals(false, reader.nextField());
    594   });
    595 
    596   /**
    597    * Tests skipping fields of each type by interleaving them with sentinel
    598    * values and skipping everything that's not a sentinel.
    599    */
    600   it('testSkipField', function() {
    601     var writer = new jspb.BinaryWriter();
    602 
    603     var sentinel = 123456789;
    604 
    605     // Write varint fields of different sizes.
    606     writer.writeInt32(1, sentinel);
    607     writer.writeInt32(1, 1);
    608     writer.writeInt32(1, 1000);
    609     writer.writeInt32(1, 1000000);
    610     writer.writeInt32(1, 1000000000);
    611 
    612     // Write fixed 64-bit encoded fields.
    613     writer.writeInt32(2, sentinel);
    614     writer.writeDouble(2, 1);
    615     writer.writeFixed64(2, 1);
    616     writer.writeSfixed64(2, 1);
    617 
    618     // Write fixed 32-bit encoded fields.
    619     writer.writeInt32(3, sentinel);
    620     writer.writeFloat(3, 1);
    621     writer.writeFixed32(3, 1);
    622     writer.writeSfixed32(3, 1);
    623 
    624     // Write delimited fields.
    625     writer.writeInt32(4, sentinel);
    626     writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    627     writer.writeString(4, 'The quick brown fox jumps over the lazy dog');
    628 
    629     // Write a group with a nested group inside.
    630     writer.writeInt32(5, sentinel);
    631     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
    632     writer.writeGroup(5, dummyMessage, function() {
    633       writer.writeInt64(42, 42);
    634       writer.writeGroup(6, dummyMessage, function() {
    635         writer.writeInt64(84, 42);
    636       });
    637     });
    638 
    639     // Write final sentinel.
    640     writer.writeInt32(6, sentinel);
    641 
    642     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    643 
    644     function skip(field, count) {
    645       for (var i = 0; i < count; i++) {
    646         reader.nextField();
    647         if (field != reader.getFieldNumber()) throw 'fail!';
    648         reader.skipField();
    649       }
    650     }
    651 
    652     reader.nextField();
    653     assertEquals(1, reader.getFieldNumber());
    654     assertEquals(sentinel, reader.readInt32());
    655     skip(1, 4);
    656 
    657     reader.nextField();
    658     assertEquals(2, reader.getFieldNumber());
    659     assertEquals(sentinel, reader.readInt32());
    660     skip(2, 3);
    661 
    662     reader.nextField();
    663     assertEquals(3, reader.getFieldNumber());
    664     assertEquals(sentinel, reader.readInt32());
    665     skip(3, 3);
    666 
    667     reader.nextField();
    668     assertEquals(4, reader.getFieldNumber());
    669     assertEquals(sentinel, reader.readInt32());
    670     skip(4, 2);
    671 
    672     reader.nextField();
    673     assertEquals(5, reader.getFieldNumber());
    674     assertEquals(sentinel, reader.readInt32());
    675     skip(5, 1);
    676 
    677     reader.nextField();
    678     assertEquals(6, reader.getFieldNumber());
    679     assertEquals(sentinel, reader.readInt32());
    680   });
    681 
    682 
    683   /**
    684    * Tests packed fields.
    685    */
    686   it('testPackedFields', function() {
    687     var writer = new jspb.BinaryWriter();
    688 
    689     var sentinel = 123456789;
    690 
    691     var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    692     var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10];
    693     var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
    694     var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
    695     var boolData = [true, false, true, true, false, false, true, false];
    696 
    697     for (var i = 0; i < floatData.length; i++) {
    698       floatData[i] = truncate(floatData[i]);
    699     }
    700 
    701     writer.writeInt32(1, sentinel);
    702 
    703     writer.writePackedInt32(2, signedData);
    704     writer.writePackedInt64(2, signedData);
    705     writer.writePackedUint32(2, unsignedData);
    706     writer.writePackedUint64(2, unsignedData);
    707     writer.writePackedSint32(2, signedData);
    708     writer.writePackedSint64(2, signedData);
    709     writer.writePackedFixed32(2, unsignedData);
    710     writer.writePackedFixed64(2, unsignedData);
    711     writer.writePackedSfixed32(2, signedData);
    712     writer.writePackedSfixed64(2, signedData);
    713     writer.writePackedFloat(2, floatData);
    714     writer.writePackedDouble(2, doubleData);
    715     writer.writePackedBool(2, boolData);
    716     writer.writePackedEnum(2, unsignedData);
    717 
    718     writer.writeInt32(3, sentinel);
    719 
    720     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    721 
    722     reader.nextField();
    723     assertEquals(sentinel, reader.readInt32());
    724 
    725     reader.nextField();
    726     assertElementsEquals(reader.readPackedInt32(), signedData);
    727 
    728     reader.nextField();
    729     assertElementsEquals(reader.readPackedInt64(), signedData);
    730 
    731     reader.nextField();
    732     assertElementsEquals(reader.readPackedUint32(), unsignedData);
    733 
    734     reader.nextField();
    735     assertElementsEquals(reader.readPackedUint64(), unsignedData);
    736 
    737     reader.nextField();
    738     assertElementsEquals(reader.readPackedSint32(), signedData);
    739 
    740     reader.nextField();
    741     assertElementsEquals(reader.readPackedSint64(), signedData);
    742 
    743     reader.nextField();
    744     assertElementsEquals(reader.readPackedFixed32(), unsignedData);
    745 
    746     reader.nextField();
    747     assertElementsEquals(reader.readPackedFixed64(), unsignedData);
    748 
    749     reader.nextField();
    750     assertElementsEquals(reader.readPackedSfixed32(), signedData);
    751 
    752     reader.nextField();
    753     assertElementsEquals(reader.readPackedSfixed64(), signedData);
    754 
    755     reader.nextField();
    756     assertElementsEquals(reader.readPackedFloat(), floatData);
    757 
    758     reader.nextField();
    759     assertElementsEquals(reader.readPackedDouble(), doubleData);
    760 
    761     reader.nextField();
    762     assertElementsEquals(reader.readPackedBool(), boolData);
    763 
    764     reader.nextField();
    765     assertElementsEquals(reader.readPackedEnum(), unsignedData);
    766 
    767     reader.nextField();
    768     assertEquals(sentinel, reader.readInt32());
    769   });
    770 
    771 
    772   /**
    773    * Byte blobs inside nested messages should always have their byte offset set
    774    * relative to the start of the outermost blob, not the start of their parent
    775    * blob.
    776    */
    777   it('testNestedBlobs', function() {
    778     // Create a proto consisting of two nested messages, with the inner one
    779     // containing a blob of bytes.
    780 
    781     var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED;
    782     var blob = [1, 2, 3, 4, 5];
    783     var writer = new jspb.BinaryWriter();
    784     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
    785 
    786     writer.writeMessage(1, dummyMessage, function() {
    787       writer.writeMessage(1, dummyMessage, function() {
    788         writer.writeBytes(1, blob);
    789       });
    790     });
    791 
    792     // Peel off the outer two message layers. Each layer should have two bytes
    793     // of overhead, one for the field tag and one for the length of the inner
    794     // blob.
    795 
    796     var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer());
    797     assertEquals(fieldTag, decoder1.readUnsignedVarint32());
    798     assertEquals(blob.length + 4, decoder1.readUnsignedVarint32());
    799 
    800     var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4));
    801     assertEquals(fieldTag, decoder2.readUnsignedVarint32());
    802     assertEquals(blob.length + 2, decoder2.readUnsignedVarint32());
    803 
    804     assertEquals(fieldTag, decoder2.readUnsignedVarint32());
    805     assertEquals(blob.length, decoder2.readUnsignedVarint32());
    806     var bytes = decoder2.readBytes(blob.length);
    807 
    808     assertElementsEquals(bytes, blob);
    809   });
    810 
    811 
    812   /**
    813    * Tests read callbacks.
    814    */
    815   it('testReadCallbacks', function() {
    816     var writer = new jspb.BinaryWriter();
    817     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
    818 
    819     // Add an int, a submessage, and another int.
    820     writer.writeInt32(1, 100);
    821 
    822     writer.writeMessage(2, dummyMessage, function() {
    823       writer.writeInt32(3, 300);
    824       writer.writeInt32(4, 400);
    825       writer.writeInt32(5, 500);
    826     });
    827 
    828     writer.writeInt32(7, 700);
    829 
    830     // Create the reader and register a custom read callback.
    831     var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
    832 
    833     /**
    834      * @param {!jspb.BinaryReader} reader
    835      * @return {*}
    836      */
    837     function readCallback(reader) {
    838       reader.nextField();
    839       assertEquals(3, reader.getFieldNumber());
    840       assertEquals(300, reader.readInt32());
    841 
    842       reader.nextField();
    843       assertEquals(4, reader.getFieldNumber());
    844       assertEquals(400, reader.readInt32());
    845 
    846       reader.nextField();
    847       assertEquals(5, reader.getFieldNumber());
    848       assertEquals(500, reader.readInt32());
    849 
    850       assertEquals(false, reader.nextField());
    851     };
    852 
    853     reader.registerReadCallback('readCallback', readCallback);
    854 
    855     // Read the container message.
    856     reader.nextField();
    857     assertEquals(1, reader.getFieldNumber());
    858     assertEquals(100, reader.readInt32());
    859 
    860     reader.nextField();
    861     assertEquals(2, reader.getFieldNumber());
    862     reader.readMessage(dummyMessage, function() {
    863       // Decode the embedded message using the registered callback.
    864       reader.runReadCallback('readCallback');
    865     });
    866 
    867     reader.nextField();
    868     assertEquals(7, reader.getFieldNumber());
    869     assertEquals(700, reader.readInt32());
    870 
    871     assertEquals(false, reader.nextField());
    872   });
    873 });
    874