• 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 helper functions.
33 *
34 * Test suite is written using Jasmine -- see http://jasmine.github.io/
35 *
36 * @author aappleby@google.com (Austin Appleby)
37 */
38
39goog.require('goog.crypt');
40goog.require('goog.crypt.base64');
41goog.require('jspb.BinaryConstants');
42goog.require('jspb.BinaryWriter');
43goog.require('jspb.utils');
44
45
46/**
47 * @param {number} x
48 * @return {number}
49 */
50function truncate(x) {
51  var temp = new Float32Array(1);
52  temp[0] = x;
53  return temp[0];
54}
55
56
57/**
58 * Converts an 64-bit integer in split representation to a 64-bit hash string
59 * (8 bits encoded per character).
60 * @param {number} bitsLow The low 32 bits of the split 64-bit integer.
61 * @param {number} bitsHigh The high 32 bits of the split 64-bit integer.
62 * @return {string} The encoded hash string, 8 bits per character.
63 */
64function toHashString(bitsLow, bitsHigh) {
65  return String.fromCharCode((bitsLow >>> 0) & 0xFF,
66                             (bitsLow >>> 8) & 0xFF,
67                             (bitsLow >>> 16) & 0xFF,
68                             (bitsLow >>> 24) & 0xFF,
69                             (bitsHigh >>> 0) & 0xFF,
70                             (bitsHigh >>> 8) & 0xFF,
71                             (bitsHigh >>> 16) & 0xFF,
72                             (bitsHigh >>> 24) & 0xFF);
73}
74
75
76describe('binaryUtilsTest', function() {
77  /**
78   * Tests lossless binary-to-decimal conversion.
79   */
80  it('testDecimalConversion', function() {
81    // Check some magic numbers.
82    var result =
83        jspb.utils.joinUnsignedDecimalString(0x89e80001, 0x8ac72304);
84    expect(result).toEqual('10000000000000000001');
85
86    result = jspb.utils.joinUnsignedDecimalString(0xacd05f15, 0x1b69b4b);
87    expect(result).toEqual('123456789123456789');
88
89    result = jspb.utils.joinUnsignedDecimalString(0xeb1f0ad2, 0xab54a98c);
90    expect(result).toEqual('12345678901234567890');
91
92    result = jspb.utils.joinUnsignedDecimalString(0xe3b70cb1, 0x891087b8);
93    expect(result).toEqual('9876543210987654321');
94
95    // Check limits.
96    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00000000);
97    expect(result).toEqual('0');
98
99    result = jspb.utils.joinUnsignedDecimalString(0xFFFFFFFF, 0xFFFFFFFF);
100    expect(result).toEqual('18446744073709551615');
101
102    // Check each bit of the low dword.
103    for (var i = 0; i < 32; i++) {
104      var low = (1 << i) >>> 0;
105      result = jspb.utils.joinUnsignedDecimalString(low, 0);
106      expect(result).toEqual('' + Math.pow(2, i));
107    }
108
109    // Check the first 20 bits of the high dword.
110    for (var i = 0; i < 20; i++) {
111      var high = (1 << i) >>> 0;
112      result = jspb.utils.joinUnsignedDecimalString(0, high);
113      expect(result).toEqual('' + Math.pow(2, 32 + i));
114    }
115
116    // V8's internal double-to-string conversion is inaccurate for values above
117    // 2^52, even if they're representable integers - check the rest of the bits
118    // manually against the correct string representations of 2^N.
119
120    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00100000);
121    expect(result).toEqual('4503599627370496');
122
123    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00200000);
124    expect(result).toEqual('9007199254740992');
125
126    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00400000);
127    expect(result).toEqual('18014398509481984');
128
129    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00800000);
130    expect(result).toEqual('36028797018963968');
131
132    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x01000000);
133    expect(result).toEqual('72057594037927936');
134
135    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x02000000);
136    expect(result).toEqual('144115188075855872');
137
138    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x04000000);
139    expect(result).toEqual('288230376151711744');
140
141    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x08000000);
142    expect(result).toEqual('576460752303423488');
143
144    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x10000000);
145    expect(result).toEqual('1152921504606846976');
146
147    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x20000000);
148    expect(result).toEqual('2305843009213693952');
149
150    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x40000000);
151    expect(result).toEqual('4611686018427387904');
152
153    result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x80000000);
154    expect(result).toEqual('9223372036854775808');
155  });
156
157
158  /**
159   * Going from hash strings to decimal strings should also be lossless.
160   */
161  it('testHashToDecimalConversion', function() {
162    var result;
163    var convert = jspb.utils.hash64ToDecimalString;
164
165    result = convert(toHashString(0x00000000, 0x00000000), false);
166    expect(result).toEqual('0');
167
168    result = convert(toHashString(0x00000000, 0x00000000), true);
169    expect(result).toEqual('0');
170
171    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), false);
172    expect(result).toEqual('18446744073709551615');
173
174    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), true);
175    expect(result).toEqual('-1');
176
177    result = convert(toHashString(0x00000000, 0x80000000), false);
178    expect(result).toEqual('9223372036854775808');
179
180    result = convert(toHashString(0x00000000, 0x80000000), true);
181    expect(result).toEqual('-9223372036854775808');
182
183    result = convert(toHashString(0xacd05f15, 0x01b69b4b), false);
184    expect(result).toEqual('123456789123456789');
185
186    result = convert(toHashString(~0xacd05f15 + 1, ~0x01b69b4b), true);
187    expect(result).toEqual('-123456789123456789');
188
189    // And converting arrays of hashes should work the same way.
190    result = jspb.utils.hash64ArrayToDecimalStrings([
191      toHashString(0xFFFFFFFF, 0xFFFFFFFF),
192      toHashString(0x00000000, 0x80000000),
193      toHashString(0xacd05f15, 0x01b69b4b)], false);
194    expect(result.length).toEqual(3);
195    expect(result[0]).toEqual('18446744073709551615');
196    expect(result[1]).toEqual('9223372036854775808');
197    expect(result[2]).toEqual('123456789123456789');
198  });
199
200  /*
201   * Going from decimal strings to hash strings should be lossless.
202   */
203  it('testDecimalToHashConversion', function() {
204    var result;
205    var convert = jspb.utils.decimalStringToHash64;
206
207    result = convert('0');
208    expect(result).toEqual(goog.crypt.byteArrayToString(
209        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
210
211    result = convert('-1');
212    expect(result).toEqual(goog.crypt.byteArrayToString(
213        [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
214
215    result = convert('18446744073709551615');
216    expect(result).toEqual(goog.crypt.byteArrayToString(
217        [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
218
219    result = convert('9223372036854775808');
220    expect(result).toEqual(goog.crypt.byteArrayToString(
221        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]));
222
223    result = convert('-9223372036854775808');
224    expect(result).toEqual(goog.crypt.byteArrayToString(
225        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]));
226
227    result = convert('123456789123456789');
228    expect(result).toEqual(goog.crypt.byteArrayToString(
229        [0x15, 0x5F, 0xD0, 0xAC, 0x4B, 0x9B, 0xB6, 0x01]));
230
231    result = convert('-123456789123456789');
232    expect(result).toEqual(goog.crypt.byteArrayToString(
233        [0xEB, 0xA0, 0x2F, 0x53, 0xB4, 0x64, 0x49, 0xFE]));
234  });
235
236  /**
237   * Going from hash strings to hex strings should be lossless.
238   */
239  it('testHashToHexConversion', function() {
240    var result;
241    var convert = jspb.utils.hash64ToHexString;
242
243    result = convert(toHashString(0x00000000, 0x00000000));
244    expect(result).toEqual('0x0000000000000000');
245
246    result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF));
247    expect(result).toEqual('0xffffffffffffffff');
248
249    result = convert(toHashString(0x12345678, 0x9ABCDEF0));
250    expect(result).toEqual('0x9abcdef012345678');
251  });
252
253
254  /**
255   * Going from hex strings to hash strings should be lossless.
256   */
257  it('testHexToHashConversion', function() {
258    var result;
259    var convert = jspb.utils.hexStringToHash64;
260
261    result = convert('0x0000000000000000');
262    expect(result).toEqual(goog.crypt.byteArrayToString(
263        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
264
265    result = convert('0xffffffffffffffff');
266    expect(result).toEqual(goog.crypt.byteArrayToString(
267        [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
268
269    // Hex string is big-endian, hash string is little-endian.
270    result = convert('0x123456789ABCDEF0');
271    expect(result).toEqual(goog.crypt.byteArrayToString(
272        [0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]));
273
274    // Capitalization should not matter.
275    result = convert('0x0000abcdefABCDEF');
276    expect(result).toEqual(goog.crypt.byteArrayToString(
277        [0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]));
278  });
279
280
281  /**
282   * Going from numbers to hash strings should be lossless for up to 53 bits of
283   * precision.
284   */
285  it('testNumberToHashConversion', function() {
286    var result;
287    var convert = jspb.utils.numberToHash64;
288
289    result = convert(0x0000000000000);
290    expect(jspb.utils.hash64ToHexString(result)).toEqual('0x0000000000000000');
291
292    result = convert(0xFFFFFFFFFFFFF);
293    expect(jspb.utils.hash64ToHexString(result)).toEqual('0x000fffffffffffff');
294
295    result = convert(0x123456789ABCD);
296    expect(jspb.utils.hash64ToHexString(result)).toEqual('0x000123456789abcd');
297
298    result = convert(0xDCBA987654321);
299    expect(jspb.utils.hash64ToHexString(result)).toEqual('0x000dcba987654321');
300
301    // 53 bits of precision should not be truncated.
302    result = convert(0x10000000000001);
303    expect(jspb.utils.hash64ToHexString(result)).toEqual('0x0010000000000001');
304
305    // 54 bits of precision should be truncated.
306    result = convert(0x20000000000001);
307    expect(jspb.utils.hash64ToHexString(result))
308        .not.toEqual('0x0020000000000001');
309  });
310
311
312  /**
313   * Sanity check the behavior of Javascript's strings when doing funny things
314   * with unicode characters.
315   */
316  it('sanityCheckUnicodeStrings', function() {
317    var strings = new Array(65536);
318
319    // All possible unsigned 16-bit values should be storable in a string, they
320    // shouldn't do weird things with the length of the string, and they should
321    // come back out of the string unchanged.
322    for (var i = 0; i < 65536; i++) {
323      strings[i] = 'a' + String.fromCharCode(i) + 'a';
324      expect(strings[i].length).toEqual(3);
325      expect(strings[i].charCodeAt(1)).toEqual(i);
326    }
327
328    // Each unicode character should compare equal to itself and not equal to a
329    // different unicode character.
330    for (var i = 0; i < 65536; i++) {
331      expect(strings[i] == strings[i]).toEqual(true);
332      expect(strings[i] == strings[(i + 1) % 65536]).toEqual(false);
333    }
334  });
335
336
337  /**
338   * Tests conversion from 32-bit floating point numbers to split64 numbers.
339   */
340  it('testFloat32ToSplit64', function() {
341    var f32_eps = jspb.BinaryConstants.FLOAT32_EPS;
342    var f32_min = jspb.BinaryConstants.FLOAT32_MIN;
343    var f32_max = jspb.BinaryConstants.FLOAT32_MAX;
344    var f32_max_safe_int = jspb.utils.joinFloat32(0x4b7fffff, 0);
345    var f32_pi = Math.fround(Math.PI);
346
347    // NaN.
348    jspb.utils.splitFloat32(NaN);
349    expect(isNaN(jspb.utils.joinFloat32(
350               jspb.utils.split64Low, jspb.utils.split64High)))
351        .toEqual(true);
352
353    /**
354     * @param {number} x
355     * @param {number=} opt_bits
356     */
357    function test(x, opt_bits) {
358      jspb.utils.splitFloat32(x);
359      if (opt_bits !== undefined) {
360        if (opt_bits != jspb.utils.split64Low) throw 'fail!';
361      }
362      expect(truncate(x))
363          .toEqual(jspb.utils.joinFloat32(
364              jspb.utils.split64Low, jspb.utils.split64High));
365    }
366
367    // Positive and negative infinity.
368    test(Infinity, 0x7f800000);
369    test(-Infinity, 0xff800000);
370
371    // Positive and negative zero.
372    test(0, 0x00000000);
373    test(-0, 0x80000000);
374
375    // Positive and negative epsilon.
376    test(f32_eps, 0x00000001);
377    test(-f32_eps, 0x80000001);
378
379    // Positive and negative min.
380    test(f32_min, 0x00800000);
381    test(-f32_min, 0x80800000);
382
383    // Positive and negative max.
384    test(f32_max, 0x7F7FFFFF);
385    test(-f32_max, 0xFF7FFFFF);
386
387    // Positive and negative max_safe_int.
388    test(f32_max_safe_int, 0x4B7FFFFF);
389    test(-f32_max_safe_int, 0xCB7FFFFF);
390
391    // Pi.
392    test(f32_pi, 0x40490fdb);
393
394    // Various positive values.
395    var cursor = f32_eps * 10;
396    while (cursor != Infinity) {
397      test(cursor);
398      cursor *= 1.1;
399    }
400
401    // Various negative values.
402    cursor = -f32_eps * 10;
403    while (cursor != -Infinity) {
404      test(cursor);
405      cursor *= 1.1;
406    }
407  });
408
409
410  /**
411   * Tests conversion from 64-bit floating point numbers to split64 numbers.
412   */
413  it('testFloat64ToSplit64', function() {
414    var f64_eps = jspb.BinaryConstants.FLOAT64_EPS;
415    var f64_min = jspb.BinaryConstants.FLOAT64_MIN;
416    var f64_max = jspb.BinaryConstants.FLOAT64_MAX;
417
418    // NaN.
419    jspb.utils.splitFloat64(NaN);
420    expect(isNaN(jspb.utils.joinFloat64(
421               jspb.utils.split64Low, jspb.utils.split64High)))
422        .toEqual(true);
423
424    /**
425     * @param {number} x
426     * @param {number=} opt_highBits
427     * @param {number=} opt_lowBits
428     */
429    function test(x, opt_highBits, opt_lowBits) {
430      jspb.utils.splitFloat64(x);
431      if (opt_highBits !== undefined) {
432        var split64High = jspb.utils.split64High;
433        expect(opt_highBits.toString(16)).toEqual(split64High.toString(16));
434      }
435      if (opt_lowBits !== undefined) {
436        var split64Low = jspb.utils.split64Low;
437        expect(opt_lowBits.toString(16)).toEqual(split64Low.toString(16));
438      }
439      expect(
440          jspb.utils.joinFloat64(jspb.utils.split64Low, jspb.utils.split64High))
441          .toEqual(x);
442    }
443
444    // Positive and negative infinity.
445    test(Infinity, 0x7ff00000, 0x00000000);
446    test(-Infinity, 0xfff00000, 0x00000000);
447
448    // Positive and negative zero.
449    test(0, 0x00000000, 0x00000000);
450    test(-0, 0x80000000, 0x00000000);
451
452    test(1, 0x3FF00000, 0x00000000);
453    test(2, 0x40000000, 0x00000000);
454
455    // Positive and negative epsilon.
456    test(f64_eps, 0x00000000, 0x00000001);
457    test(-f64_eps, 0x80000000, 0x00000001);
458
459    // Positive and negative min.
460    test(f64_min, 0x00100000, 0x00000000);
461    test(-f64_min, 0x80100000, 0x00000000);
462
463    // Positive and negative max.
464    test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF);
465    test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF);
466
467    test(Number.MAX_SAFE_INTEGER, 0x433FFFFF, 0xFFFFFFFF);
468    test(Number.MIN_SAFE_INTEGER, 0xC33FFFFF, 0xFFFFFFFF);
469
470    // Test various edge cases with mantissa of all 1, all 0, or just the
471    // highest or lowest significant bit.
472    test(4503599627370497, 0x43300000, 0x00000001);
473    test(6755399441055744, 0x43380000, 0x00000000);
474    test(1.348269851146737e+308, 0x7FE80000, 0x00000000);
475    test(1.9999999999999998, 0x3FFFFFFF, 0xFFFFFFFF);
476    test(2.225073858507201e-308, 0x000FFFFF, 0xFFFFFFFF);
477    test(Math.PI, 0x400921fb, 0x54442d18);
478    test(jspb.BinaryConstants.FLOAT32_MIN, 0x38100000, 0x00000000);
479
480    // Various positive values.
481    var cursor = f64_eps * 10;
482    while (cursor != Infinity) {
483      test(cursor);
484      cursor *= 1.1;
485    }
486
487    // Various negative values.
488    cursor = -f64_eps * 10;
489    while (cursor != -Infinity) {
490      test(cursor);
491      cursor *= 1.1;
492    }
493  });
494
495  /**
496   * Tests zigzag conversions.
497   */
498  it('can encode and decode zigzag 64', function() {
499    function stringToHiLoPair(str) {
500      jspb.utils.splitDecimalString(str);
501      return {
502        lo: jspb.utils.split64Low >>> 0,
503        hi: jspb.utils.split64High >>> 0
504      };
505    }
506    function makeHiLoPair(lo, hi) {
507      return {lo: lo >>> 0, hi: hi >>> 0};
508    }
509    // Test cases directly from the protobuf dev guide.
510    // https://engdoc.corp.google.com/eng/howto/protocolbuffers/developerguide/encoding.shtml?cl=head#types
511    var testCases = [
512      {original: stringToHiLoPair('0'), zigzag: stringToHiLoPair('0')},
513      {original: stringToHiLoPair('-1'), zigzag: stringToHiLoPair('1')},
514      {original: stringToHiLoPair('1'), zigzag: stringToHiLoPair('2')},
515      {original: stringToHiLoPair('-2'), zigzag: stringToHiLoPair('3')},
516      {
517        original: stringToHiLoPair('2147483647'),
518        zigzag: stringToHiLoPair('4294967294')
519      },
520      {
521        original: stringToHiLoPair('-2147483648'),
522        zigzag: stringToHiLoPair('4294967295')
523      },
524      // 64-bit extremes
525      {
526        original: stringToHiLoPair('9223372036854775807'),
527        zigzag: stringToHiLoPair('18446744073709551614')
528      },
529      {
530        original: stringToHiLoPair('-9223372036854775808'),
531        zigzag: stringToHiLoPair('18446744073709551615')
532      },
533    ];
534    for (const c of testCases) {
535      expect(jspb.utils.toZigzag64(c.original.lo, c.original.hi, makeHiLoPair))
536          .toEqual(c.zigzag);
537      expect(jspb.utils.fromZigzag64(c.zigzag.lo, c.zigzag.hi, makeHiLoPair))
538          .toEqual(c.original);
539    }
540  });
541
542
543  /**
544   * Tests counting packed varints.
545   */
546  it('testCountVarints', function() {
547    var values = [];
548    for (var i = 1; i < 1000000000; i *= 1.1) {
549      values.push(Math.floor(i));
550    }
551
552    var writer = new jspb.BinaryWriter();
553    writer.writePackedUint64(1, values);
554
555    var buffer = new Uint8Array(writer.getResultBuffer());
556
557    // We should have two more varints than we started with - one for the field
558    // tag, one for the packed length.
559    expect(jspb.utils.countVarints(buffer, 0, buffer.length))
560        .toEqual(values.length + 2);
561  });
562
563
564  /**
565   * Tests counting matching varint fields.
566   */
567  it('testCountVarintFields', function() {
568    var writer = new jspb.BinaryWriter();
569
570    var count = 0;
571    for (var i = 1; i < 1000000000; i *= 1.1) {
572      writer.writeUint64(1, Math.floor(i));
573      count++;
574    }
575    writer.writeString(2, 'terminator');
576
577    var buffer = new Uint8Array(writer.getResultBuffer());
578    expect(jspb.utils.countVarintFields(buffer, 0, buffer.length, 1))
579        .toEqual(count);
580
581    writer = new jspb.BinaryWriter();
582
583    count = 0;
584    for (var i = 1; i < 1000000000; i *= 1.1) {
585      writer.writeUint64(123456789, Math.floor(i));
586      count++;
587    }
588    writer.writeString(2, 'terminator');
589
590    buffer = new Uint8Array(writer.getResultBuffer());
591    expect(jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789))
592        .toEqual(count);
593  });
594
595
596  /**
597   * Tests counting matching fixed32 fields.
598   */
599  it('testCountFixed32Fields', function() {
600    var writer = new jspb.BinaryWriter();
601
602    var count = 0;
603    for (var i = 1; i < 1000000000; i *= 1.1) {
604      writer.writeFixed32(1, Math.floor(i));
605      count++;
606    }
607    writer.writeString(2, 'terminator');
608
609    var buffer = new Uint8Array(writer.getResultBuffer());
610    expect(jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1))
611        .toEqual(count);
612
613    writer = new jspb.BinaryWriter();
614
615    count = 0;
616    for (var i = 1; i < 1000000000; i *= 1.1) {
617      writer.writeFixed32(123456789, Math.floor(i));
618      count++;
619    }
620    writer.writeString(2, 'terminator');
621
622    buffer = new Uint8Array(writer.getResultBuffer());
623    expect(jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789))
624        .toEqual(count);
625  });
626
627
628  /**
629   * Tests counting matching fixed64 fields.
630   */
631  it('testCountFixed64Fields', function() {
632    var writer = new jspb.BinaryWriter();
633
634    var count = 0;
635    for (var i = 1; i < 1000000000; i *= 1.1) {
636      writer.writeDouble(1, i);
637      count++;
638    }
639    writer.writeString(2, 'terminator');
640
641    var buffer = new Uint8Array(writer.getResultBuffer());
642    expect(jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1))
643        .toEqual(count);
644
645    writer = new jspb.BinaryWriter();
646
647    count = 0;
648    for (var i = 1; i < 1000000000; i *= 1.1) {
649      writer.writeDouble(123456789, i);
650      count++;
651    }
652    writer.writeString(2, 'terminator');
653
654    buffer = new Uint8Array(writer.getResultBuffer());
655    expect(jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789))
656        .toEqual(count);
657  });
658
659
660  /**
661   * Tests counting matching delimited fields.
662   */
663  it('testCountDelimitedFields', function() {
664    var writer = new jspb.BinaryWriter();
665
666    var count = 0;
667    for (var i = 1; i < 1000; i *= 1.1) {
668      writer.writeBytes(1, [Math.floor(i)]);
669      count++;
670    }
671    writer.writeString(2, 'terminator');
672
673    var buffer = new Uint8Array(writer.getResultBuffer());
674    expect(jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1))
675        .toEqual(count);
676
677    writer = new jspb.BinaryWriter();
678
679    count = 0;
680    for (var i = 1; i < 1000; i *= 1.1) {
681      writer.writeBytes(123456789, [Math.floor(i)]);
682      count++;
683    }
684    writer.writeString(2, 'terminator');
685
686    buffer = new Uint8Array(writer.getResultBuffer());
687    expect(jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789))
688        .toEqual(count);
689  });
690
691
692  /**
693   * Tests byte format for debug strings.
694   */
695  it('testDebugBytesToTextFormat', function() {
696    expect(jspb.utils.debugBytesToTextFormat(null)).toEqual('""');
697    expect(jspb.utils.debugBytesToTextFormat([
698      0, 16, 255
699    ])).toEqual('"\\x00\\x10\\xff"');
700  });
701
702
703  /**
704   * Tests converting byte blob sources into byte blobs.
705   */
706  it('testByteSourceToUint8Array', function() {
707    var convert = jspb.utils.byteSourceToUint8Array;
708
709    var sourceData = [];
710    for (var i = 0; i < 256; i++) {
711      sourceData.push(i);
712    }
713
714    var sourceBytes = new Uint8Array(sourceData);
715    var sourceBuffer = sourceBytes.buffer;
716    var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
717    var sourceString = goog.crypt.byteArrayToString(sourceData);
718
719    function check(result) {
720      expect(result.constructor).toEqual(Uint8Array);
721      expect(result.length).toEqual(sourceData.length);
722      for (var i = 0; i < result.length; i++) {
723        expect(result[i]).toEqual(sourceData[i]);
724      }
725    }
726
727    // Converting Uint8Arrays into Uint8Arrays should be a no-op.
728    expect(convert(sourceBytes)).toEqual(sourceBytes);
729
730    // Converting Array<numbers> into Uint8Arrays should work.
731    check(convert(sourceData));
732
733    // Converting ArrayBuffers into Uint8Arrays should work.
734    check(convert(sourceBuffer));
735
736    // Converting base64-encoded strings into Uint8Arrays should work.
737    check(convert(sourceBase64));
738  });
739});
740