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