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