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