Home | History | Annotate | Download | only in binary
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 /**
     32  * @fileoverview This file contains helper code used by jspb.BinaryReader
     33  * and BinaryWriter.
     34  *
     35  * @author aappleby (a] google.com (Austin Appleby)
     36  */
     37 
     38 goog.provide('jspb.utils');
     39 
     40 goog.require('goog.asserts');
     41 goog.require('goog.crypt.base64');
     42 goog.require('goog.string');
     43 goog.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  */
     58 jspb.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  */
     66 jspb.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  */
     74 jspb.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  */
     90 jspb.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  */
    123 jspb.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  */
    158 jspb.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  */
    215 jspb.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  */
    275 jspb.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  */
    297 jspb.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  */
    309 jspb.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  */
    332 jspb.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  */
    358 jspb.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  */
    388 jspb.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  */
    417 jspb.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  */
    435 jspb.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  */
    448 jspb.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  */
    520 jspb.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  */
    543 jspb.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  */
    561 jspb.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  */
    576 jspb.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  */
    625 jspb.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  */
    646 jspb.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  */
    672 jspb.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  */
    687 jspb.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  */
    701 jspb.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  */
    724 jspb.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  */
    780 jspb.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  */
    827 jspb.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  */
    842 jspb.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  */
    857 jspb.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  */
    898 jspb.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  */
    917 jspb.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  */
    933 jspb.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  */
    953 jspb.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