• 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 This file contains helper code used by jspb.BinaryReader
33 * and BinaryWriter.
34 *
35 * @author aappleby@google.com (Austin Appleby)
36 */
37
38goog.provide('jspb.utils');
39
40goog.require('goog.asserts');
41goog.require('goog.crypt.base64');
42goog.require('goog.string');
43goog.require('jspb.BinaryConstants');
44
45
46/**
47 * Javascript can't natively handle 64-bit data types, so to manipulate them we
48 * have to split them into two 32-bit halves and do the math manually.
49 *
50 * Instead of instantiating and passing small structures around to do this, we
51 * instead just use two global temporary values. This one stores the low 32
52 * bits of a split value - for example, if the original value was a 64-bit
53 * integer, this temporary value will contain the low 32 bits of that integer.
54 * If the original value was a double, this temporary value will contain the
55 * low 32 bits of the binary representation of that double, etcetera.
56 * @type {number}
57 */
58jspb.utils.split64Low = 0;
59
60
61/**
62 * And correspondingly, this temporary variable will contain the high 32 bits
63 * of whatever value was split.
64 * @type {number}
65 */
66jspb.utils.split64High = 0;
67
68
69/**
70 * Splits an unsigned Javascript integer into two 32-bit halves and stores it
71 * in the temp values above.
72 * @param {number} value The number to split.
73 */
74jspb.utils.splitUint64 = function(value) {
75  // Extract low 32 bits and high 32 bits as unsigned integers.
76  var lowBits = value >>> 0;
77  var highBits = Math.floor((value - lowBits) /
78                            jspb.BinaryConstants.TWO_TO_32) >>> 0;
79
80  jspb.utils.split64Low = lowBits;
81  jspb.utils.split64High = highBits;
82};
83
84
85/**
86 * Splits a signed Javascript integer into two 32-bit halves and stores it in
87 * the temp values above.
88 * @param {number} value The number to split.
89 */
90jspb.utils.splitInt64 = function(value) {
91  // Convert to sign-magnitude representation.
92  var sign = (value < 0);
93  value = Math.abs(value);
94
95  // Extract low 32 bits and high 32 bits as unsigned integers.
96  var lowBits = value >>> 0;
97  var highBits = Math.floor((value - lowBits) /
98                            jspb.BinaryConstants.TWO_TO_32);
99  highBits = highBits >>> 0;
100
101  // Perform two's complement conversion if the sign bit was set.
102  if (sign) {
103    highBits = ~highBits >>> 0;
104    lowBits = ~lowBits >>> 0;
105    lowBits += 1;
106    if (lowBits > 0xFFFFFFFF) {
107      lowBits = 0;
108      highBits++;
109      if (highBits > 0xFFFFFFFF) highBits = 0;
110    }
111  }
112
113  jspb.utils.split64Low = lowBits;
114  jspb.utils.split64High = highBits;
115};
116
117
118/**
119 * Convers a signed Javascript integer into zigzag format, splits it into two
120 * 32-bit halves, and stores it in the temp values above.
121 * @param {number} value The number to split.
122 */
123jspb.utils.splitZigzag64 = function(value) {
124  // Convert to sign-magnitude and scale by 2 before we split the value.
125  var sign = (value < 0);
126  value = Math.abs(value) * 2;
127
128  jspb.utils.splitUint64(value);
129  var lowBits = jspb.utils.split64Low;
130  var highBits = jspb.utils.split64High;
131
132  // If the value is negative, subtract 1 from the split representation so we
133  // don't lose the sign bit due to precision issues.
134  if (sign) {
135    if (lowBits == 0) {
136      if (highBits == 0) {
137        lowBits = 0xFFFFFFFF;
138        highBits = 0xFFFFFFFF;
139      } else {
140        highBits--;
141        lowBits = 0xFFFFFFFF;
142      }
143    } else {
144      lowBits--;
145    }
146  }
147
148  jspb.utils.split64Low = lowBits;
149  jspb.utils.split64High = highBits;
150};
151
152
153/**
154 * Converts a floating-point number into 32-bit IEEE representation and stores
155 * it in the temp values above.
156 * @param {number} value
157 */
158jspb.utils.splitFloat32 = function(value) {
159  var sign = (value < 0) ? 1 : 0;
160  value = sign ? -value : value;
161  var exp;
162  var mant;
163
164  // Handle zeros.
165  if (value === 0) {
166    if ((1 / value) > 0) {
167      // Positive zero.
168      jspb.utils.split64High = 0;
169      jspb.utils.split64Low = 0x00000000;
170    } else {
171      // Negative zero.
172      jspb.utils.split64High = 0;
173      jspb.utils.split64Low = 0x80000000;
174    }
175    return;
176  }
177
178  // Handle nans.
179  if (isNaN(value)) {
180    jspb.utils.split64High = 0;
181    jspb.utils.split64Low = 0x7FFFFFFF;
182    return;
183  }
184
185  // Handle infinities.
186  if (value > jspb.BinaryConstants.FLOAT32_MAX) {
187    jspb.utils.split64High = 0;
188    jspb.utils.split64Low = ((sign << 31) | (0x7F800000)) >>> 0;
189    return;
190  }
191
192  // Handle denormals.
193  if (value < jspb.BinaryConstants.FLOAT32_MIN) {
194    // Number is a denormal.
195    mant = Math.round(value / Math.pow(2, -149));
196    jspb.utils.split64High = 0;
197    jspb.utils.split64Low = ((sign << 31) | mant) >>> 0;
198    return;
199  }
200
201  exp = Math.floor(Math.log(value) / Math.LN2);
202  mant = value * Math.pow(2, -exp);
203  mant = Math.round(mant * jspb.BinaryConstants.TWO_TO_23) & 0x7FFFFF;
204
205  jspb.utils.split64High = 0;
206  jspb.utils.split64Low = ((sign << 31) | ((exp + 127) << 23) | mant) >>> 0;
207};
208
209
210/**
211 * Converts a floating-point number into 64-bit IEEE representation and stores
212 * it in the temp values above.
213 * @param {number} value
214 */
215jspb.utils.splitFloat64 = function(value) {
216  var sign = (value < 0) ? 1 : 0;
217  value = sign ? -value : value;
218
219  // Handle zeros.
220  if (value === 0) {
221    if ((1 / value) > 0) {
222      // Positive zero.
223      jspb.utils.split64High = 0x00000000;
224      jspb.utils.split64Low = 0x00000000;
225    } else {
226      // Negative zero.
227      jspb.utils.split64High = 0x80000000;
228      jspb.utils.split64Low = 0x00000000;
229    }
230    return;
231  }
232
233  // Handle nans.
234  if (isNaN(value)) {
235    jspb.utils.split64High = 0x7FFFFFFF;
236    jspb.utils.split64Low = 0xFFFFFFFF;
237    return;
238  }
239
240  // Handle infinities.
241  if (value > jspb.BinaryConstants.FLOAT64_MAX) {
242    jspb.utils.split64High = ((sign << 31) | (0x7FF00000)) >>> 0;
243    jspb.utils.split64Low = 0;
244    return;
245  }
246
247  // Handle denormals.
248  if (value < jspb.BinaryConstants.FLOAT64_MIN) {
249    // Number is a denormal.
250    var mant = value / Math.pow(2, -1074);
251    var mantHigh = (mant / jspb.BinaryConstants.TWO_TO_32);
252    jspb.utils.split64High = ((sign << 31) | mantHigh) >>> 0;
253    jspb.utils.split64Low = (mant >>> 0);
254    return;
255  }
256
257  var exp = Math.floor(Math.log(value) / Math.LN2);
258  if (exp == 1024) exp = 1023;
259  var mant = value * Math.pow(2, -exp);
260
261  var mantHigh = (mant * jspb.BinaryConstants.TWO_TO_20) & 0xFFFFF;
262  var mantLow = (mant * jspb.BinaryConstants.TWO_TO_52) >>> 0;
263
264  jspb.utils.split64High =
265      ((sign << 31) | ((exp + 1023) << 20) | mantHigh) >>> 0;
266  jspb.utils.split64Low = mantLow;
267};
268
269
270/**
271 * Converts an 8-character hash string into two 32-bit numbers and stores them
272 * in the temp values above.
273 * @param {string} hash
274 */
275jspb.utils.splitHash64 = function(hash) {
276  var a = hash.charCodeAt(0);
277  var b = hash.charCodeAt(1);
278  var c = hash.charCodeAt(2);
279  var d = hash.charCodeAt(3);
280  var e = hash.charCodeAt(4);
281  var f = hash.charCodeAt(5);
282  var g = hash.charCodeAt(6);
283  var h = hash.charCodeAt(7);
284
285  jspb.utils.split64Low = (a + (b << 8) + (c << 16) + (d << 24)) >>> 0;
286  jspb.utils.split64High = (e + (f << 8) + (g << 16) + (h << 24)) >>> 0;
287};
288
289
290/**
291 * Joins two 32-bit values into a 64-bit unsigned integer. Precision will be
292 * lost if the result is greater than 2^52.
293 * @param {number} bitsLow
294 * @param {number} bitsHigh
295 * @return {number}
296 */
297jspb.utils.joinUint64 = function(bitsLow, bitsHigh) {
298  return bitsHigh * jspb.BinaryConstants.TWO_TO_32 + bitsLow;
299};
300
301
302/**
303 * Joins two 32-bit values into a 64-bit signed integer. Precision will be lost
304 * if the result is greater than 2^52.
305 * @param {number} bitsLow
306 * @param {number} bitsHigh
307 * @return {number}
308 */
309jspb.utils.joinInt64 = function(bitsLow, bitsHigh) {
310  // If the high bit is set, do a manual two's complement conversion.
311  var sign = (bitsHigh & 0x80000000);
312  if (sign) {
313    bitsLow = (~bitsLow + 1) >>> 0;
314    bitsHigh = ~bitsHigh >>> 0;
315    if (bitsLow == 0) {
316      bitsHigh = (bitsHigh + 1) >>> 0;
317    }
318  }
319
320  var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
321  return sign ? -result : result;
322};
323
324
325/**
326 * Joins two 32-bit values into a 64-bit unsigned integer and applies zigzag
327 * decoding. Precision will be lost if the result is greater than 2^52.
328 * @param {number} bitsLow
329 * @param {number} bitsHigh
330 * @return {number}
331 */
332jspb.utils.joinZigzag64 = function(bitsLow, bitsHigh) {
333  // Extract the sign bit and shift right by one.
334  var sign = bitsLow & 1;
335  bitsLow = ((bitsLow >>> 1) | (bitsHigh << 31)) >>> 0;
336  bitsHigh = bitsHigh >>> 1;
337
338  // Increment the split value if the sign bit was set.
339  if (sign) {
340    bitsLow = (bitsLow + 1) >>> 0;
341    if (bitsLow == 0) {
342      bitsHigh = (bitsHigh + 1) >>> 0;
343    }
344  }
345
346  var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
347  return sign ? -result : result;
348};
349
350
351/**
352 * Joins two 32-bit values into a 32-bit IEEE floating point number and
353 * converts it back into a Javascript number.
354 * @param {number} bitsLow The low 32 bits of the binary number;
355 * @param {number} bitsHigh The high 32 bits of the binary number.
356 * @return {number}
357 */
358jspb.utils.joinFloat32 = function(bitsLow, bitsHigh) {
359  var sign = ((bitsLow >> 31) * 2 + 1);
360  var exp = (bitsLow >>> 23) & 0xFF;
361  var mant = bitsLow & 0x7FFFFF;
362
363  if (exp == 0xFF) {
364    if (mant) {
365      return NaN;
366    } else {
367      return sign * Infinity;
368    }
369  }
370
371  if (exp == 0) {
372    // Denormal.
373    return sign * Math.pow(2, -149) * mant;
374  } else {
375    return sign * Math.pow(2, exp - 150) *
376           (mant + Math.pow(2, 23));
377  }
378};
379
380
381/**
382 * Joins two 32-bit values into a 64-bit IEEE floating point number and
383 * converts it back into a Javascript number.
384 * @param {number} bitsLow The low 32 bits of the binary number;
385 * @param {number} bitsHigh The high 32 bits of the binary number.
386 * @return {number}
387 */
388jspb.utils.joinFloat64 = function(bitsLow, bitsHigh) {
389  var sign = ((bitsHigh >> 31) * 2 + 1);
390  var exp = (bitsHigh >>> 20) & 0x7FF;
391  var mant = jspb.BinaryConstants.TWO_TO_32 * (bitsHigh & 0xFFFFF) + bitsLow;
392
393  if (exp == 0x7FF) {
394    if (mant) {
395      return NaN;
396    } else {
397      return sign * Infinity;
398    }
399  }
400
401  if (exp == 0) {
402    // Denormal.
403    return sign * Math.pow(2, -1074) * mant;
404  } else {
405    return sign * Math.pow(2, exp - 1075) *
406           (mant + jspb.BinaryConstants.TWO_TO_52);
407  }
408};
409
410
411/**
412 * Joins two 32-bit values into an 8-character hash string.
413 * @param {number} bitsLow
414 * @param {number} bitsHigh
415 * @return {string}
416 */
417jspb.utils.joinHash64 = function(bitsLow, bitsHigh) {
418  var a = (bitsLow >>> 0) & 0xFF;
419  var b = (bitsLow >>> 8) & 0xFF;
420  var c = (bitsLow >>> 16) & 0xFF;
421  var d = (bitsLow >>> 24) & 0xFF;
422  var e = (bitsHigh >>> 0) & 0xFF;
423  var f = (bitsHigh >>> 8) & 0xFF;
424  var g = (bitsHigh >>> 16) & 0xFF;
425  var h = (bitsHigh >>> 24) & 0xFF;
426
427  return String.fromCharCode(a, b, c, d, e, f, g, h);
428};
429
430
431/**
432 * Individual digits for number->string conversion.
433 * @const {!Array.<number>}
434 */
435jspb.utils.DIGITS = [
436  '0', '1', '2', '3', '4', '5', '6', '7',
437  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
438];
439
440
441/**
442 * Losslessly converts a 64-bit unsigned integer in 32:32 split representation
443 * into a decimal string.
444 * @param {number} bitsLow The low 32 bits of the binary number;
445 * @param {number} bitsHigh The high 32 bits of the binary number.
446 * @return {string} The binary number represented as a string.
447 */
448jspb.utils.joinUnsignedDecimalString = function(bitsLow, bitsHigh) {
449  // Skip the expensive conversion if the number is small enough to use the
450  // built-in conversions.
451  if (bitsHigh <= 0x1FFFFF) {
452    return '' + (jspb.BinaryConstants.TWO_TO_32 * bitsHigh + bitsLow);
453  }
454
455  // What this code is doing is essentially converting the input number from
456  // base-2 to base-1e7, which allows us to represent the 64-bit range with
457  // only 3 (very large) digits. Those digits are then trivial to convert to
458  // a base-10 string.
459
460  // The magic numbers used here are -
461  // 2^24 = 16777216 = (1,6777216) in base-1e7.
462  // 2^48 = 281474976710656 = (2,8147497,6710656) in base-1e7.
463
464  // Split 32:32 representation into 16:24:24 representation so our
465  // intermediate digits don't overflow.
466  var low = bitsLow & 0xFFFFFF;
467  var mid = (((bitsLow >>> 24) | (bitsHigh << 8)) >>> 0) & 0xFFFFFF;
468  var high = (bitsHigh >> 16) & 0xFFFF;
469
470  // Assemble our three base-1e7 digits, ignoring carries. The maximum
471  // value in a digit at this step is representable as a 48-bit integer, which
472  // can be stored in a 64-bit floating point number.
473  var digitA = low + (mid * 6777216) + (high * 6710656);
474  var digitB = mid + (high * 8147497);
475  var digitC = (high * 2);
476
477  // Apply carries from A to B and from B to C.
478  var base = 10000000;
479  if (digitA >= base) {
480    digitB += Math.floor(digitA / base);
481    digitA %= base;
482  }
483
484  if (digitB >= base) {
485    digitC += Math.floor(digitB / base);
486    digitB %= base;
487  }
488
489  // Convert base-1e7 digits to base-10, omitting leading zeroes.
490  var table = jspb.utils.DIGITS;
491  var start = false;
492  var result = '';
493
494  function emit(digit) {
495    var temp = base;
496    for (var i = 0; i < 7; i++) {
497      temp /= 10;
498      var decimalDigit = ((digit / temp) % 10) >>> 0;
499      if ((decimalDigit == 0) && !start) continue;
500      start = true;
501      result += table[decimalDigit];
502    }
503  }
504
505  if (digitC || start) emit(digitC);
506  if (digitB || start) emit(digitB);
507  if (digitA || start) emit(digitA);
508
509  return result;
510};
511
512
513/**
514 * Losslessly converts a 64-bit signed integer in 32:32 split representation
515 * into a decimal string.
516 * @param {number} bitsLow The low 32 bits of the binary number;
517 * @param {number} bitsHigh The high 32 bits of the binary number.
518 * @return {string} The binary number represented as a string.
519 */
520jspb.utils.joinSignedDecimalString = function(bitsLow, bitsHigh) {
521  // If we're treating the input as a signed value and the high bit is set, do
522  // a manual two's complement conversion before the decimal conversion.
523  var negative = (bitsHigh & 0x80000000);
524  if (negative) {
525    bitsLow = (~bitsLow + 1) >>> 0;
526    var carry = (bitsLow == 0) ? 1 : 0;
527    bitsHigh = (~bitsHigh + carry) >>> 0;
528  }
529
530  var result = jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
531  return negative ? '-' + result : result;
532};
533
534
535/**
536 * Convert an 8-character hash string representing either a signed or unsigned
537 * 64-bit integer into its decimal representation without losing accuracy.
538 * @param {string} hash The hash string to convert.
539 * @param {boolean} signed True if we should treat the hash string as encoding
540 *     a signed integer.
541 * @return {string}
542 */
543jspb.utils.hash64ToDecimalString = function(hash, signed) {
544  jspb.utils.splitHash64(hash);
545  var bitsLow = jspb.utils.split64Low;
546  var bitsHigh = jspb.utils.split64High;
547  return signed ?
548      jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh) :
549      jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
550};
551
552
553/**
554 * Converts an array of 8-character hash strings into their decimal
555 * representations.
556 * @param {!Array.<string>} hashes The array of hash strings to convert.
557 * @param {boolean} signed True if we should treat the hash string as encoding
558 *     a signed integer.
559 * @return {!Array.<string>}
560 */
561jspb.utils.hash64ArrayToDecimalStrings = function(hashes, signed) {
562  var result = new Array(hashes.length);
563  for (var i = 0; i < hashes.length; i++) {
564    result[i] = jspb.utils.hash64ToDecimalString(hashes[i], signed);
565  }
566  return result;
567};
568
569
570/**
571 * Converts a signed or unsigned decimal string into its hash string
572 * representation.
573 * @param {string} dec
574 * @return {string}
575 */
576jspb.utils.decimalStringToHash64 = function(dec) {
577  goog.asserts.assert(dec.length > 0);
578
579  // Check for minus sign.
580  var minus = false;
581  if (dec[0] === '-') {
582    minus = true;
583    dec = dec.slice(1);
584  }
585
586  // Store result as a byte array.
587  var resultBytes = [0, 0, 0, 0, 0, 0, 0, 0];
588
589  // Set result to m*result + c.
590  function muladd(m, c) {
591    for (var i = 0; i < 8 && (m !== 1 || c > 0); i++) {
592      var r = m * resultBytes[i] + c;
593      resultBytes[i] = r & 0xFF;
594      c = r >>> 8;
595    }
596  }
597
598  // Negate the result bits.
599  function neg() {
600    for (var i = 0; i < 8; i++) {
601      resultBytes[i] = (~resultBytes[i]) & 0xFF;
602    }
603  }
604
605  // For each decimal digit, set result to 10*result + digit.
606  for (var i = 0; i < dec.length; i++) {
607    muladd(10, jspb.utils.DIGITS.indexOf(dec[i]));
608  }
609
610  // If there's a minus sign, convert into two's complement.
611  if (minus) {
612    neg();
613    muladd(1, 1);
614  }
615
616  return String.fromCharCode.apply(null, resultBytes);
617};
618
619
620/**
621 * Converts an 8-character hash string into its hexadecimal representation.
622 * @param {string} hash
623 * @return {string}
624 */
625jspb.utils.hash64ToHexString = function(hash) {
626  var temp = new Array(18);
627  temp[0] = '0';
628  temp[1] = 'x';
629
630  for (var i = 0; i < 8; i++) {
631    var c = hash.charCodeAt(7 - i);
632    temp[i * 2 + 2] = jspb.utils.DIGITS[c >> 4];
633    temp[i * 2 + 3] = jspb.utils.DIGITS[c & 0xF];
634  }
635
636  var result = temp.join('');
637  return result;
638};
639
640
641/**
642 * Converts a '0x<16 digits>' hex string into its hash string representation.
643 * @param {string} hex
644 * @return {string}
645 */
646jspb.utils.hexStringToHash64 = function(hex) {
647  hex = hex.toLowerCase();
648  goog.asserts.assert(hex.length == 18);
649  goog.asserts.assert(hex[0] == '0');
650  goog.asserts.assert(hex[1] == 'x');
651
652  var result = '';
653  for (var i = 0; i < 8; i++) {
654    var hi = jspb.utils.DIGITS.indexOf(hex[i * 2 + 2]);
655    var lo = jspb.utils.DIGITS.indexOf(hex[i * 2 + 3]);
656    result = String.fromCharCode(hi * 16 + lo) + result;
657  }
658
659  return result;
660};
661
662
663/**
664 * Convert an 8-character hash string representing either a signed or unsigned
665 * 64-bit integer into a Javascript number. Will lose accuracy if the result is
666 * larger than 2^52.
667 * @param {string} hash The hash string to convert.
668 * @param {boolean} signed True if the has should be interpreted as a signed
669 *     number.
670 * @return {number}
671 */
672jspb.utils.hash64ToNumber = function(hash, signed) {
673  jspb.utils.splitHash64(hash);
674  var bitsLow = jspb.utils.split64Low;
675  var bitsHigh = jspb.utils.split64High;
676  return signed ? jspb.utils.joinInt64(bitsLow, bitsHigh) :
677                  jspb.utils.joinUint64(bitsLow, bitsHigh);
678};
679
680
681/**
682 * Convert a Javascript number into an 8-character hash string. Will lose
683 * precision if the value is non-integral or greater than 2^64.
684 * @param {number} value The integer to convert.
685 * @return {string}
686 */
687jspb.utils.numberToHash64 = function(value) {
688  jspb.utils.splitInt64(value);
689  return jspb.utils.joinHash64(jspb.utils.split64Low,
690                                  jspb.utils.split64High);
691};
692
693
694/**
695 * Counts the number of contiguous varints in a buffer.
696 * @param {!Uint8Array} buffer The buffer to scan.
697 * @param {number} start The starting point in the buffer to scan.
698 * @param {number} end The end point in the buffer to scan.
699 * @return {number} The number of varints in the buffer.
700 */
701jspb.utils.countVarints = function(buffer, start, end) {
702  // Count how many high bits of each byte were set in the buffer.
703  var count = 0;
704  for (var i = start; i < end; i++) {
705    count += buffer[i] >> 7;
706  }
707
708  // The number of varints in the buffer equals the size of the buffer minus
709  // the number of non-terminal bytes in the buffer (those with the high bit
710  // set).
711  return (end - start) - count;
712};
713
714
715/**
716 * Counts the number of contiguous varint fields with the given field number in
717 * the buffer.
718 * @param {!Uint8Array} buffer The buffer to scan.
719 * @param {number} start The starting point in the buffer to scan.
720 * @param {number} end The end point in the buffer to scan.
721 * @param {number} field The field number to count.
722 * @return {number} The number of matching fields in the buffer.
723 */
724jspb.utils.countVarintFields = function(buffer, start, end, field) {
725  var count = 0;
726  var cursor = start;
727  var tag = field * 8 + jspb.BinaryConstants.WireType.VARINT;
728
729  if (tag < 128) {
730    // Single-byte field tag, we can use a slightly quicker count.
731    while (cursor < end) {
732      // Skip the field tag, or exit if we find a non-matching tag.
733      if (buffer[cursor++] != tag) return count;
734
735      // Field tag matches, we've found a valid field.
736      count++;
737
738      // Skip the varint.
739      while (1) {
740        var x = buffer[cursor++];
741        if ((x & 0x80) == 0) break;
742      }
743    }
744  } else {
745    while (cursor < end) {
746      // Skip the field tag, or exit if we find a non-matching tag.
747      var temp = tag;
748      while (temp > 128) {
749        if (buffer[cursor] != ((temp & 0x7F) | 0x80)) return count;
750        cursor++;
751        temp >>= 7;
752      }
753      if (buffer[cursor++] != temp) return count;
754
755      // Field tag matches, we've found a valid field.
756      count++;
757
758      // Skip the varint.
759      while (1) {
760        var x = buffer[cursor++];
761        if ((x & 0x80) == 0) break;
762      }
763    }
764  }
765  return count;
766};
767
768
769/**
770 * Counts the number of contiguous fixed32 fields with the given tag in the
771 * buffer.
772 * @param {!Uint8Array} buffer The buffer to scan.
773 * @param {number} start The starting point in the buffer to scan.
774 * @param {number} end The end point in the buffer to scan.
775 * @param {number} tag The tag value to count.
776 * @param {number} stride The number of bytes to skip per field.
777 * @return {number} The number of fields with a matching tag in the buffer.
778 * @private
779 */
780jspb.utils.countFixedFields_ =
781    function(buffer, start, end, tag, stride) {
782  var count = 0;
783  var cursor = start;
784
785  if (tag < 128) {
786    // Single-byte field tag, we can use a slightly quicker count.
787    while (cursor < end) {
788      // Skip the field tag, or exit if we find a non-matching tag.
789      if (buffer[cursor++] != tag) return count;
790
791      // Field tag matches, we've found a valid field.
792      count++;
793
794      // Skip the value.
795      cursor += stride;
796    }
797  } else {
798    while (cursor < end) {
799      // Skip the field tag, or exit if we find a non-matching tag.
800      var temp = tag;
801      while (temp > 128) {
802        if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
803        temp >>= 7;
804      }
805      if (buffer[cursor++] != temp) return count;
806
807      // Field tag matches, we've found a valid field.
808      count++;
809
810      // Skip the value.
811      cursor += stride;
812    }
813  }
814  return count;
815};
816
817
818/**
819 * Counts the number of contiguous fixed32 fields with the given field number
820 * in the buffer.
821 * @param {!Uint8Array} buffer The buffer to scan.
822 * @param {number} start The starting point in the buffer to scan.
823 * @param {number} end The end point in the buffer to scan.
824 * @param {number} field The field number to count.
825 * @return {number} The number of matching fields in the buffer.
826 */
827jspb.utils.countFixed32Fields = function(buffer, start, end, field) {
828  var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED32;
829  return jspb.utils.countFixedFields_(buffer, start, end, tag, 4);
830};
831
832
833/**
834 * Counts the number of contiguous fixed64 fields with the given field number
835 * in the buffer.
836 * @param {!Uint8Array} buffer The buffer to scan.
837 * @param {number} start The starting point in the buffer to scan.
838 * @param {number} end The end point in the buffer to scan.
839 * @param {number} field The field number to count
840 * @return {number} The number of matching fields in the buffer.
841 */
842jspb.utils.countFixed64Fields = function(buffer, start, end, field) {
843  var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED64;
844  return jspb.utils.countFixedFields_(buffer, start, end, tag, 8);
845};
846
847
848/**
849 * Counts the number of contiguous delimited fields with the given field number
850 * in the buffer.
851 * @param {!Uint8Array} buffer The buffer to scan.
852 * @param {number} start The starting point in the buffer to scan.
853 * @param {number} end The end point in the buffer to scan.
854 * @param {number} field The field number to count.
855 * @return {number} The number of matching fields in the buffer.
856 */
857jspb.utils.countDelimitedFields = function(buffer, start, end, field) {
858  var count = 0;
859  var cursor = start;
860  var tag = field * 8 + jspb.BinaryConstants.WireType.DELIMITED;
861
862  while (cursor < end) {
863    // Skip the field tag, or exit if we find a non-matching tag.
864    var temp = tag;
865    while (temp > 128) {
866      if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
867      temp >>= 7;
868    }
869    if (buffer[cursor++] != temp) return count;
870
871    // Field tag matches, we've found a valid field.
872    count++;
873
874    // Decode the length prefix.
875    var length = 0;
876    var shift = 1;
877    while (1) {
878      temp = buffer[cursor++];
879      length += (temp & 0x7f) * shift;
880      shift *= 128;
881      if ((temp & 0x80) == 0) break;
882    }
883
884    // Advance the cursor past the blob.
885    cursor += length;
886  }
887  return count;
888};
889
890
891/**
892 * String-ify bytes for text format. Should be optimized away in non-debug.
893 * The returned string uses \xXX escapes for all values and is itself quoted.
894 * [1, 31] serializes to '"\x01\x1f"'.
895 * @param {jspb.ByteSource} byteSource The bytes to serialize.
896 * @return {string} Stringified bytes for text format.
897 */
898jspb.utils.debugBytesToTextFormat = function(byteSource) {
899  var s = '"';
900  if (byteSource) {
901    var bytes = jspb.utils.byteSourceToUint8Array(byteSource);
902    for (var i = 0; i < bytes.length; i++) {
903      s += '\\x';
904      if (bytes[i] < 16) s += '0';
905      s += bytes[i].toString(16);
906    }
907  }
908  return s + '"';
909};
910
911
912/**
913 * String-ify a scalar for text format. Should be optimized away in non-debug.
914 * @param {string|number|boolean} scalar The scalar to stringify.
915 * @return {string} Stringified scalar for text format.
916 */
917jspb.utils.debugScalarToTextFormat = function(scalar) {
918  if (goog.isString(scalar)) {
919    return goog.string.quote(scalar);
920  } else {
921    return scalar.toString();
922  }
923};
924
925
926/**
927 * Utility function: convert a string with codepoints 0--255 inclusive to a
928 * Uint8Array. If any codepoints greater than 255 exist in the string, throws an
929 * exception.
930 * @param {string} str
931 * @return {!Uint8Array}
932 */
933jspb.utils.stringToByteArray = function(str) {
934  var arr = new Uint8Array(str.length);
935  for (var i = 0; i < str.length; i++) {
936    var codepoint = str.charCodeAt(i);
937    if (codepoint > 255) {
938      throw new Error('Conversion error: string contains codepoint ' +
939                      'outside of byte range');
940    }
941    arr[i] = codepoint;
942  }
943  return arr;
944};
945
946
947/**
948 * Converts any type defined in jspb.ByteSource into a Uint8Array.
949 * @param {!jspb.ByteSource} data
950 * @return {!Uint8Array}
951 * @suppress {invalidCasts}
952 */
953jspb.utils.byteSourceToUint8Array = function(data) {
954  if (data.constructor === Uint8Array) {
955    return /** @type {!Uint8Array} */(data);
956  }
957
958  if (data.constructor === ArrayBuffer) {
959    data = /** @type {!ArrayBuffer} */(data);
960    return /** @type {!Uint8Array} */(new Uint8Array(data));
961  }
962
963  if (data.constructor === Array) {
964    data = /** @type {!Array.<number>} */(data);
965    return /** @type {!Uint8Array} */(new Uint8Array(data));
966  }
967
968  if (data.constructor === String) {
969    data = /** @type {string} */(data);
970    return goog.crypt.base64.decodeStringToUint8Array(data);
971  }
972
973  goog.asserts.fail('Type not convertible to Uint8Array.');
974  return /** @type {!Uint8Array} */(new Uint8Array(0));
975};
976