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