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