• 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    // corner cases
395    test(0.9999999762949594, 0x3f800000);
396    test(7.99999999999999, 0x41000000);
397    test(Math.sin(30 * Math.PI / 180), 0x3f000000);  // sin(30 degrees)
398
399    // Various positive values.
400    var cursor = f32_eps * 10;
401    while (cursor != Infinity) {
402      test(cursor);
403      cursor *= 1.1;
404    }
405
406    // Various negative values.
407    cursor = -f32_eps * 10;
408    while (cursor != -Infinity) {
409      test(cursor);
410      cursor *= 1.1;
411    }
412  });
413
414
415  /**
416   * Tests conversion from 64-bit floating point numbers to split64 numbers.
417   */
418  it('testFloat64ToSplit64', function() {
419    var f64_eps = jspb.BinaryConstants.FLOAT64_EPS;
420    var f64_min = jspb.BinaryConstants.FLOAT64_MIN;
421    var f64_max = jspb.BinaryConstants.FLOAT64_MAX;
422
423    // NaN.
424    jspb.utils.splitFloat64(NaN);
425    expect(isNaN(jspb.utils.joinFloat64(
426               jspb.utils.split64Low, jspb.utils.split64High)))
427        .toEqual(true);
428
429    /**
430     * @param {number} x
431     * @param {number=} opt_highBits
432     * @param {number=} opt_lowBits
433     */
434    function test(x, opt_highBits, opt_lowBits) {
435      jspb.utils.splitFloat64(x);
436      if (opt_highBits !== undefined) {
437        var split64High = jspb.utils.split64High;
438        expect(opt_highBits.toString(16)).toEqual(split64High.toString(16));
439      }
440      if (opt_lowBits !== undefined) {
441        var split64Low = jspb.utils.split64Low;
442        expect(opt_lowBits.toString(16)).toEqual(split64Low.toString(16));
443      }
444      expect(
445          jspb.utils.joinFloat64(jspb.utils.split64Low, jspb.utils.split64High))
446          .toEqual(x);
447    }
448
449    // Positive and negative infinity.
450    test(Infinity, 0x7ff00000, 0x00000000);
451    test(-Infinity, 0xfff00000, 0x00000000);
452
453    // Positive and negative zero.
454    test(0, 0x00000000, 0x00000000);
455    test(-0, 0x80000000, 0x00000000);
456
457    test(1, 0x3FF00000, 0x00000000);
458    test(2, 0x40000000, 0x00000000);
459
460    // Positive and negative epsilon.
461    test(f64_eps, 0x00000000, 0x00000001);
462    test(-f64_eps, 0x80000000, 0x00000001);
463
464    // Positive and negative min.
465    test(f64_min, 0x00100000, 0x00000000);
466    test(-f64_min, 0x80100000, 0x00000000);
467
468    // Positive and negative max.
469    test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF);
470    test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF);
471
472    test(Number.MAX_SAFE_INTEGER, 0x433FFFFF, 0xFFFFFFFF);
473    test(Number.MIN_SAFE_INTEGER, 0xC33FFFFF, 0xFFFFFFFF);
474
475    // Test various edge cases with mantissa of all 1, all 0, or just the
476    // highest or lowest significant bit.
477    test(4503599627370497, 0x43300000, 0x00000001);
478    test(6755399441055744, 0x43380000, 0x00000000);
479    test(1.348269851146737e+308, 0x7FE80000, 0x00000000);
480    test(1.9999999999999998, 0x3FFFFFFF, 0xFFFFFFFF);
481    test(2.225073858507201e-308, 0x000FFFFF, 0xFFFFFFFF);
482    test(Math.PI, 0x400921fb, 0x54442d18);
483    test(jspb.BinaryConstants.FLOAT32_MIN, 0x38100000, 0x00000000);
484
485    // Various positive values.
486    var cursor = f64_eps * 10;
487    while (cursor != Infinity) {
488      test(cursor);
489      cursor *= 1.1;
490    }
491
492    // Various negative values.
493    cursor = -f64_eps * 10;
494    while (cursor != -Infinity) {
495      test(cursor);
496      cursor *= 1.1;
497    }
498  });
499
500  /**
501   * Tests zigzag conversions.
502   */
503  it('can encode and decode zigzag 64', function() {
504    function stringToHiLoPair(str) {
505      jspb.utils.splitDecimalString(str);
506      return {
507        lo: jspb.utils.split64Low >>> 0,
508        hi: jspb.utils.split64High >>> 0
509      };
510    }
511    function makeHiLoPair(lo, hi) {
512      return {lo: lo >>> 0, hi: hi >>> 0};
513    }
514    // Test cases directly from the protobuf dev guide.
515    // https://engdoc.corp.google.com/eng/howto/protocolbuffers/developerguide/encoding.shtml?cl=head#types
516    var testCases = [
517      {original: stringToHiLoPair('0'), zigzag: stringToHiLoPair('0')},
518      {original: stringToHiLoPair('-1'), zigzag: stringToHiLoPair('1')},
519      {original: stringToHiLoPair('1'), zigzag: stringToHiLoPair('2')},
520      {original: stringToHiLoPair('-2'), zigzag: stringToHiLoPair('3')},
521      {
522        original: stringToHiLoPair('2147483647'),
523        zigzag: stringToHiLoPair('4294967294')
524      },
525      {
526        original: stringToHiLoPair('-2147483648'),
527        zigzag: stringToHiLoPair('4294967295')
528      },
529      // 64-bit extremes
530      {
531        original: stringToHiLoPair('9223372036854775807'),
532        zigzag: stringToHiLoPair('18446744073709551614')
533      },
534      {
535        original: stringToHiLoPair('-9223372036854775808'),
536        zigzag: stringToHiLoPair('18446744073709551615')
537      },
538    ];
539    for (const c of testCases) {
540      expect(jspb.utils.toZigzag64(c.original.lo, c.original.hi, makeHiLoPair))
541          .toEqual(c.zigzag);
542      expect(jspb.utils.fromZigzag64(c.zigzag.lo, c.zigzag.hi, makeHiLoPair))
543          .toEqual(c.original);
544    }
545  });
546
547
548  /**
549   * Tests counting packed varints.
550   */
551  it('testCountVarints', function() {
552    var values = [];
553    for (var i = 1; i < 1000000000; i *= 1.1) {
554      values.push(Math.floor(i));
555    }
556
557    var writer = new jspb.BinaryWriter();
558    writer.writePackedUint64(1, values);
559
560    var buffer = new Uint8Array(writer.getResultBuffer());
561
562    // We should have two more varints than we started with - one for the field
563    // tag, one for the packed length.
564    expect(jspb.utils.countVarints(buffer, 0, buffer.length))
565        .toEqual(values.length + 2);
566  });
567
568
569  /**
570   * Tests counting matching varint fields.
571   */
572  it('testCountVarintFields', function() {
573    var writer = new jspb.BinaryWriter();
574
575    var count = 0;
576    for (var i = 1; i < 1000000000; i *= 1.1) {
577      writer.writeUint64(1, Math.floor(i));
578      count++;
579    }
580    writer.writeString(2, 'terminator');
581
582    var buffer = new Uint8Array(writer.getResultBuffer());
583    expect(jspb.utils.countVarintFields(buffer, 0, buffer.length, 1))
584        .toEqual(count);
585
586    writer = new jspb.BinaryWriter();
587
588    count = 0;
589    for (var i = 1; i < 1000000000; i *= 1.1) {
590      writer.writeUint64(123456789, Math.floor(i));
591      count++;
592    }
593    writer.writeString(2, 'terminator');
594
595    buffer = new Uint8Array(writer.getResultBuffer());
596    expect(jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789))
597        .toEqual(count);
598  });
599
600
601  /**
602   * Tests counting matching fixed32 fields.
603   */
604  it('testCountFixed32Fields', function() {
605    var writer = new jspb.BinaryWriter();
606
607    var count = 0;
608    for (var i = 1; i < 1000000000; i *= 1.1) {
609      writer.writeFixed32(1, Math.floor(i));
610      count++;
611    }
612    writer.writeString(2, 'terminator');
613
614    var buffer = new Uint8Array(writer.getResultBuffer());
615    expect(jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1))
616        .toEqual(count);
617
618    writer = new jspb.BinaryWriter();
619
620    count = 0;
621    for (var i = 1; i < 1000000000; i *= 1.1) {
622      writer.writeFixed32(123456789, Math.floor(i));
623      count++;
624    }
625    writer.writeString(2, 'terminator');
626
627    buffer = new Uint8Array(writer.getResultBuffer());
628    expect(jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789))
629        .toEqual(count);
630  });
631
632
633  /**
634   * Tests counting matching fixed64 fields.
635   */
636  it('testCountFixed64Fields', function() {
637    var writer = new jspb.BinaryWriter();
638
639    var count = 0;
640    for (var i = 1; i < 1000000000; i *= 1.1) {
641      writer.writeDouble(1, i);
642      count++;
643    }
644    writer.writeString(2, 'terminator');
645
646    var buffer = new Uint8Array(writer.getResultBuffer());
647    expect(jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1))
648        .toEqual(count);
649
650    writer = new jspb.BinaryWriter();
651
652    count = 0;
653    for (var i = 1; i < 1000000000; i *= 1.1) {
654      writer.writeDouble(123456789, i);
655      count++;
656    }
657    writer.writeString(2, 'terminator');
658
659    buffer = new Uint8Array(writer.getResultBuffer());
660    expect(jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789))
661        .toEqual(count);
662  });
663
664
665  /**
666   * Tests counting matching delimited fields.
667   */
668  it('testCountDelimitedFields', function() {
669    var writer = new jspb.BinaryWriter();
670
671    var count = 0;
672    for (var i = 1; i < 1000; i *= 1.1) {
673      writer.writeBytes(1, [Math.floor(i)]);
674      count++;
675    }
676    writer.writeString(2, 'terminator');
677
678    var buffer = new Uint8Array(writer.getResultBuffer());
679    expect(jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1))
680        .toEqual(count);
681
682    writer = new jspb.BinaryWriter();
683
684    count = 0;
685    for (var i = 1; i < 1000; i *= 1.1) {
686      writer.writeBytes(123456789, [Math.floor(i)]);
687      count++;
688    }
689    writer.writeString(2, 'terminator');
690
691    buffer = new Uint8Array(writer.getResultBuffer());
692    expect(jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789))
693        .toEqual(count);
694  });
695
696
697  /**
698   * Tests byte format for debug strings.
699   */
700  it('testDebugBytesToTextFormat', function() {
701    expect(jspb.utils.debugBytesToTextFormat(null)).toEqual('""');
702    expect(jspb.utils.debugBytesToTextFormat([
703      0, 16, 255
704    ])).toEqual('"\\x00\\x10\\xff"');
705  });
706
707
708  /**
709   * Tests converting byte blob sources into byte blobs.
710   */
711  it('testByteSourceToUint8Array', function() {
712    var convert = jspb.utils.byteSourceToUint8Array;
713
714    var sourceData = [];
715    for (var i = 0; i < 256; i++) {
716      sourceData.push(i);
717    }
718
719    var sourceBytes = new Uint8Array(sourceData);
720    var sourceBuffer = sourceBytes.buffer;
721    var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
722    var sourceString = goog.crypt.byteArrayToString(sourceData);
723
724    function check(result) {
725      expect(result.constructor).toEqual(Uint8Array);
726      expect(result.length).toEqual(sourceData.length);
727      for (var i = 0; i < result.length; i++) {
728        expect(result[i]).toEqual(sourceData[i]);
729      }
730    }
731
732    // Converting Uint8Arrays into Uint8Arrays should be a no-op.
733    expect(convert(sourceBytes)).toEqual(sourceBytes);
734
735    // Converting Array<numbers> into Uint8Arrays should work.
736    check(convert(sourceData));
737
738    // Converting ArrayBuffers into Uint8Arrays should work.
739    check(convert(sourceBuffer));
740
741    // Converting base64-encoded strings into Uint8Arrays should work.
742    check(convert(sourceBase64));
743  });
744});
745