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 utilities for decoding primitive values 33 * (signed and unsigned integers, varints, booleans, enums, hashes, strings, 34 * and raw bytes) embedded in Uint8Arrays into their corresponding Javascript 35 * types. 36 * 37 * Major caveat - Javascript is unable to accurately represent integers larger 38 * than 2^53 due to its use of a double-precision floating point format or all 39 * numbers. If you need to guarantee that 64-bit values survive with all bits 40 * intact, you _must_ read them using one of the Hash64 methods, which return 41 * an 8-character string. 42 * 43 * @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed 44 * @author aappleby@google.com (Austin Appleby) 45 */ 46 47goog.provide('jspb.BinaryDecoder'); 48 49goog.require('goog.asserts'); 50goog.require('goog.crypt'); 51goog.require('jspb.utils'); 52 53 54 55/** 56 * BinaryDecoder implements the decoders for all the wire types specified in 57 * https://developers.google.com/protocol-buffers/docs/encoding. 58 * 59 * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from. 60 * @param {number=} opt_start The optional offset to start reading at. 61 * @param {number=} opt_length The optional length of the block to read - 62 * we'll throw an assertion if we go off the end of the block. 63 * @constructor 64 * @struct 65 */ 66jspb.BinaryDecoder = function(opt_bytes, opt_start, opt_length) { 67 /** 68 * Typed byte-wise view of the source buffer. 69 * @private {?Uint8Array} 70 */ 71 this.bytes_ = null; 72 73 /** 74 * Start point of the block to read. 75 * @private {number} 76 */ 77 this.start_ = 0; 78 79 /** 80 * End point of the block to read. 81 * @private {number} 82 */ 83 this.end_ = 0; 84 85 /** 86 * Current read location in bytes_. 87 * @private {number} 88 */ 89 this.cursor_ = 0; 90 91 /** 92 * Set to true if this decoder encountered an error due to corrupt data. 93 * @private {boolean} 94 */ 95 this.error_ = false; 96 97 if (opt_bytes) { 98 this.setBlock(opt_bytes, opt_start, opt_length); 99 } 100}; 101 102 103/** 104 * Global pool of BinaryDecoder instances. 105 * @private {!Array<!jspb.BinaryDecoder>} 106 */ 107jspb.BinaryDecoder.instanceCache_ = []; 108 109 110/** 111 * Pops an instance off the instance cache, or creates one if the cache is 112 * empty. 113 * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from. 114 * @param {number=} opt_start The optional offset to start reading at. 115 * @param {number=} opt_length The optional length of the block to read - 116 * we'll throw an assertion if we go off the end of the block. 117 * @return {!jspb.BinaryDecoder} 118 */ 119jspb.BinaryDecoder.alloc = function(opt_bytes, opt_start, opt_length) { 120 if (jspb.BinaryDecoder.instanceCache_.length) { 121 var newDecoder = jspb.BinaryDecoder.instanceCache_.pop(); 122 if (opt_bytes) { 123 newDecoder.setBlock(opt_bytes, opt_start, opt_length); 124 } 125 return newDecoder; 126 } else { 127 return new jspb.BinaryDecoder(opt_bytes, opt_start, opt_length); 128 } 129}; 130 131 132/** 133 * Puts this instance back in the instance cache. 134 */ 135jspb.BinaryDecoder.prototype.free = function() { 136 this.clear(); 137 if (jspb.BinaryDecoder.instanceCache_.length < 100) { 138 jspb.BinaryDecoder.instanceCache_.push(this); 139 } 140}; 141 142 143/** 144 * Makes a copy of this decoder. 145 * @return {!jspb.BinaryDecoder} 146 */ 147jspb.BinaryDecoder.prototype.clone = function() { 148 return jspb.BinaryDecoder.alloc(this.bytes_, 149 this.start_, this.end_ - this.start_); 150}; 151 152 153/** 154 * Clears the decoder. 155 */ 156jspb.BinaryDecoder.prototype.clear = function() { 157 this.bytes_ = null; 158 this.start_ = 0; 159 this.end_ = 0; 160 this.cursor_ = 0; 161 this.error_ = false; 162}; 163 164 165/** 166 * Returns the raw buffer. 167 * @return {?Uint8Array} The raw buffer. 168 */ 169jspb.BinaryDecoder.prototype.getBuffer = function() { 170 return this.bytes_; 171}; 172 173 174/** 175 * Changes the block of bytes we're decoding. 176 * @param {!jspb.ByteSource} data The bytes we're reading from. 177 * @param {number=} opt_start The optional offset to start reading at. 178 * @param {number=} opt_length The optional length of the block to read - 179 * we'll throw an assertion if we go off the end of the block. 180 */ 181jspb.BinaryDecoder.prototype.setBlock = 182 function(data, opt_start, opt_length) { 183 this.bytes_ = jspb.utils.byteSourceToUint8Array(data); 184 this.start_ = (opt_start !== undefined) ? opt_start : 0; 185 this.end_ = (opt_length !== undefined) ? this.start_ + opt_length : 186 this.bytes_.length; 187 this.cursor_ = this.start_; 188}; 189 190 191/** 192 * @return {number} 193 */ 194jspb.BinaryDecoder.prototype.getEnd = function() { 195 return this.end_; 196}; 197 198 199/** 200 * @param {number} end 201 */ 202jspb.BinaryDecoder.prototype.setEnd = function(end) { 203 this.end_ = end; 204}; 205 206 207/** 208 * Moves the read cursor back to the start of the block. 209 */ 210jspb.BinaryDecoder.prototype.reset = function() { 211 this.cursor_ = this.start_; 212}; 213 214 215/** 216 * Returns the internal read cursor. 217 * @return {number} The internal read cursor. 218 */ 219jspb.BinaryDecoder.prototype.getCursor = function() { 220 return this.cursor_; 221}; 222 223 224/** 225 * Returns the internal read cursor. 226 * @param {number} cursor The new cursor. 227 */ 228jspb.BinaryDecoder.prototype.setCursor = function(cursor) { 229 this.cursor_ = cursor; 230}; 231 232 233/** 234 * Advances the stream cursor by the given number of bytes. 235 * @param {number} count The number of bytes to advance by. 236 */ 237jspb.BinaryDecoder.prototype.advance = function(count) { 238 this.cursor_ += count; 239 goog.asserts.assert(this.cursor_ <= this.end_); 240}; 241 242 243/** 244 * Returns true if this decoder is at the end of the block. 245 * @return {boolean} 246 */ 247jspb.BinaryDecoder.prototype.atEnd = function() { 248 return this.cursor_ == this.end_; 249}; 250 251 252/** 253 * Returns true if this decoder is at the end of the block. 254 * @return {boolean} 255 */ 256jspb.BinaryDecoder.prototype.pastEnd = function() { 257 return this.cursor_ > this.end_; 258}; 259 260 261/** 262 * Returns true if this decoder encountered an error due to corrupt data. 263 * @return {boolean} 264 */ 265jspb.BinaryDecoder.prototype.getError = function() { 266 return this.error_ || 267 (this.cursor_ < 0) || 268 (this.cursor_ > this.end_); 269}; 270 271 272/** 273 * Reads an unsigned varint from the binary stream and invokes the conversion 274 * function with the value in two signed 32 bit integers to produce the result. 275 * Since this does not convert the value to a number, no precision is lost. 276 * 277 * It's possible for an unsigned varint to be incorrectly encoded - more than 278 * 64 bits' worth of data could be present. If this happens, this method will 279 * throw an error. 280 * 281 * Decoding varints requires doing some funny base-128 math - for more 282 * details on the format, see 283 * https://developers.google.com/protocol-buffers/docs/encoding 284 * 285 * @param {function(number, number): T} convert Conversion function to produce 286 * the result value, takes parameters (lowBits, highBits). 287 * @return {T} 288 * @template T 289 */ 290jspb.BinaryDecoder.prototype.readSplitVarint64 = function(convert) { 291 var temp = 128; 292 var lowBits = 0; 293 var highBits = 0; 294 295 // Read the first four bytes of the varint, stopping at the terminator if we 296 // see it. 297 for (var i = 0; i < 4 && temp >= 128; i++) { 298 temp = this.bytes_[this.cursor_++]; 299 lowBits |= (temp & 0x7F) << (i * 7); 300 } 301 302 if (temp >= 128) { 303 // Read the fifth byte, which straddles the low and high dwords. 304 temp = this.bytes_[this.cursor_++]; 305 lowBits |= (temp & 0x7F) << 28; 306 highBits |= (temp & 0x7F) >> 4; 307 } 308 309 if (temp >= 128) { 310 // Read the sixth through tenth byte. 311 for (var i = 0; i < 5 && temp >= 128; i++) { 312 temp = this.bytes_[this.cursor_++]; 313 highBits |= (temp & 0x7F) << (i * 7 + 3); 314 } 315 } 316 317 if (temp < 128) { 318 return convert(lowBits >>> 0, highBits >>> 0); 319 } 320 321 // If we did not see the terminator, the encoding was invalid. 322 goog.asserts.fail('Failed to read varint, encoding is invalid.'); 323 this.error_ = true; 324}; 325 326 327/** 328 * Reads a signed zigzag encoded varint from the binary stream and invokes 329 * the conversion function with the value in two signed 32 bit integers to 330 * produce the result. Since this does not convert the value to a number, no 331 * precision is lost. 332 * 333 * It's possible for an unsigned varint to be incorrectly encoded - more than 334 * 64 bits' worth of data could be present. If this happens, this method will 335 * throw an error. 336 * 337 * Zigzag encoding is a modification of varint encoding that reduces the 338 * storage overhead for small negative integers - for more details on the 339 * format, see https://developers.google.com/protocol-buffers/docs/encoding 340 * 341 * @param {function(number, number): T} convert Conversion function to produce 342 * the result value, takes parameters (lowBits, highBits). 343 * @return {T} 344 * @template T 345 */ 346jspb.BinaryDecoder.prototype.readSplitZigzagVarint64 = function(convert) { 347 return this.readSplitVarint64(function(low, high) { 348 return jspb.utils.fromZigzag64(low, high, convert); 349 }); 350}; 351 352 353/** 354 * Reads a 64-bit fixed-width value from the stream and invokes the conversion 355 * function with the value in two signed 32 bit integers to produce the result. 356 * Since this does not convert the value to a number, no precision is lost. 357 * 358 * @param {function(number, number): T} convert Conversion function to produce 359 * the result value, takes parameters (lowBits, highBits). 360 * @return {T} 361 * @template T 362 */ 363jspb.BinaryDecoder.prototype.readSplitFixed64 = function(convert) { 364 var bytes = this.bytes_; 365 var cursor = this.cursor_; 366 this.cursor_ += 8; 367 var lowBits = 0; 368 var highBits = 0; 369 for (var i = cursor + 7; i >= cursor; i--) { 370 lowBits = (lowBits << 8) | bytes[i]; 371 highBits = (highBits << 8) | bytes[i + 4]; 372 } 373 return convert(lowBits, highBits); 374}; 375 376 377/** 378 * Skips over a varint in the block without decoding it. 379 */ 380jspb.BinaryDecoder.prototype.skipVarint = function() { 381 while (this.bytes_[this.cursor_] & 0x80) { 382 this.cursor_++; 383 } 384 this.cursor_++; 385}; 386 387 388/** 389 * Skips backwards over a varint in the block - to do this correctly, we have 390 * to know the value we're skipping backwards over or things are ambiguous. 391 * @param {number} value The varint value to unskip. 392 */ 393jspb.BinaryDecoder.prototype.unskipVarint = function(value) { 394 while (value > 128) { 395 this.cursor_--; 396 value = value >>> 7; 397 } 398 this.cursor_--; 399}; 400 401 402/** 403 * Reads a 32-bit varint from the binary stream. Due to a quirk of the encoding 404 * format and Javascript's handling of bitwise math, this actually works 405 * correctly for both signed and unsigned 32-bit varints. 406 * 407 * This function is called vastly more frequently than any other in 408 * BinaryDecoder, so it has been unrolled and tweaked for performance. 409 * 410 * If there are more than 32 bits of data in the varint, it _must_ be due to 411 * sign-extension. If we're in debug mode and the high 32 bits don't match the 412 * expected sign extension, this method will throw an error. 413 * 414 * Decoding varints requires doing some funny base-128 math - for more 415 * details on the format, see 416 * https://developers.google.com/protocol-buffers/docs/encoding 417 * 418 * @return {number} The decoded unsigned 32-bit varint. 419 */ 420jspb.BinaryDecoder.prototype.readUnsignedVarint32 = function() { 421 var temp; 422 var bytes = this.bytes_; 423 424 temp = bytes[this.cursor_ + 0]; 425 var x = (temp & 0x7F); 426 if (temp < 128) { 427 this.cursor_ += 1; 428 goog.asserts.assert(this.cursor_ <= this.end_); 429 return x; 430 } 431 432 temp = bytes[this.cursor_ + 1]; 433 x |= (temp & 0x7F) << 7; 434 if (temp < 128) { 435 this.cursor_ += 2; 436 goog.asserts.assert(this.cursor_ <= this.end_); 437 return x; 438 } 439 440 temp = bytes[this.cursor_ + 2]; 441 x |= (temp & 0x7F) << 14; 442 if (temp < 128) { 443 this.cursor_ += 3; 444 goog.asserts.assert(this.cursor_ <= this.end_); 445 return x; 446 } 447 448 temp = bytes[this.cursor_ + 3]; 449 x |= (temp & 0x7F) << 21; 450 if (temp < 128) { 451 this.cursor_ += 4; 452 goog.asserts.assert(this.cursor_ <= this.end_); 453 return x; 454 } 455 456 temp = bytes[this.cursor_ + 4]; 457 x |= (temp & 0x0F) << 28; 458 if (temp < 128) { 459 // We're reading the high bits of an unsigned varint. The byte we just read 460 // also contains bits 33 through 35, which we're going to discard. 461 this.cursor_ += 5; 462 goog.asserts.assert(this.cursor_ <= this.end_); 463 return x >>> 0; 464 } 465 466 // If we get here, we need to truncate coming bytes. However we need to make 467 // sure cursor place is correct. 468 this.cursor_ += 5; 469 if (bytes[this.cursor_++] >= 128 && 470 bytes[this.cursor_++] >= 128 && 471 bytes[this.cursor_++] >= 128 && 472 bytes[this.cursor_++] >= 128 && 473 bytes[this.cursor_++] >= 128) { 474 // If we get here, the varint is too long. 475 goog.asserts.assert(false); 476 } 477 478 goog.asserts.assert(this.cursor_ <= this.end_); 479 return x; 480}; 481 482 483/** 484 * The readUnsignedVarint32 above deals with signed 32-bit varints correctly, 485 * so this is just an alias. 486 * 487 * @return {number} The decoded signed 32-bit varint. 488 */ 489jspb.BinaryDecoder.prototype.readSignedVarint32 = 490 jspb.BinaryDecoder.prototype.readUnsignedVarint32; 491 492 493/** 494 * Reads a 32-bit unsigned variant and returns its value as a string. 495 * 496 * @return {string} The decoded unsigned 32-bit varint as a string. 497 */ 498jspb.BinaryDecoder.prototype.readUnsignedVarint32String = function() { 499 // 32-bit integers fit in JavaScript numbers without loss of precision, so 500 // string variants of 32-bit varint readers can simply delegate then convert 501 // to string. 502 var value = this.readUnsignedVarint32(); 503 return value.toString(); 504}; 505 506 507/** 508 * Reads a 32-bit signed variant and returns its value as a string. 509 * 510 * @return {string} The decoded signed 32-bit varint as a string. 511 */ 512jspb.BinaryDecoder.prototype.readSignedVarint32String = function() { 513 // 32-bit integers fit in JavaScript numbers without loss of precision, so 514 // string variants of 32-bit varint readers can simply delegate then convert 515 // to string. 516 var value = this.readSignedVarint32(); 517 return value.toString(); 518}; 519 520 521/** 522 * Reads a signed, zigzag-encoded 32-bit varint from the binary stream. 523 * 524 * Zigzag encoding is a modification of varint encoding that reduces the 525 * storage overhead for small negative integers - for more details on the 526 * format, see https://developers.google.com/protocol-buffers/docs/encoding 527 * 528 * @return {number} The decoded signed, zigzag-encoded 32-bit varint. 529 */ 530jspb.BinaryDecoder.prototype.readZigzagVarint32 = function() { 531 var result = this.readUnsignedVarint32(); 532 return (result >>> 1) ^ - (result & 1); 533}; 534 535 536/** 537 * Reads an unsigned 64-bit varint from the binary stream. Note that since 538 * Javascript represents all numbers as double-precision floats, there will be 539 * precision lost if the absolute value of the varint is larger than 2^53. 540 * 541 * @return {number} The decoded unsigned varint. Precision will be lost if the 542 * integer exceeds 2^53. 543 */ 544jspb.BinaryDecoder.prototype.readUnsignedVarint64 = function() { 545 return this.readSplitVarint64(jspb.utils.joinUint64); 546}; 547 548 549/** 550 * Reads an unsigned 64-bit varint from the binary stream and returns the value 551 * as a decimal string. 552 * 553 * @return {string} The decoded unsigned varint as a decimal string. 554 */ 555jspb.BinaryDecoder.prototype.readUnsignedVarint64String = function() { 556 return this.readSplitVarint64(jspb.utils.joinUnsignedDecimalString); 557}; 558 559 560/** 561 * Reads a signed 64-bit varint from the binary stream. Note that since 562 * Javascript represents all numbers as double-precision floats, there will be 563 * precision lost if the absolute value of the varint is larger than 2^53. 564 * 565 * @return {number} The decoded signed varint. Precision will be lost if the 566 * integer exceeds 2^53. 567 */ 568jspb.BinaryDecoder.prototype.readSignedVarint64 = function() { 569 return this.readSplitVarint64(jspb.utils.joinInt64); 570}; 571 572 573/** 574 * Reads an signed 64-bit varint from the binary stream and returns the value 575 * as a decimal string. 576 * 577 * @return {string} The decoded signed varint as a decimal string. 578 */ 579jspb.BinaryDecoder.prototype.readSignedVarint64String = function() { 580 return this.readSplitVarint64(jspb.utils.joinSignedDecimalString); 581}; 582 583 584/** 585 * Reads a signed, zigzag-encoded 64-bit varint from the binary stream. Note 586 * that since Javascript represents all numbers as double-precision floats, 587 * there will be precision lost if the absolute value of the varint is larger 588 * than 2^53. 589 * 590 * Zigzag encoding is a modification of varint encoding that reduces the 591 * storage overhead for small negative integers - for more details on the 592 * format, see https://developers.google.com/protocol-buffers/docs/encoding 593 * 594 * @return {number} The decoded zigzag varint. Precision will be lost if the 595 * integer exceeds 2^53. 596 */ 597jspb.BinaryDecoder.prototype.readZigzagVarint64 = function() { 598 return this.readSplitVarint64(jspb.utils.joinZigzag64); 599}; 600 601 602/** 603 * Reads a signed, zigzag-encoded 64-bit varint from the binary stream 604 * losslessly and returns it as an 8-character Unicode string for use as a hash 605 * table key. 606 * 607 * Zigzag encoding is a modification of varint encoding that reduces the 608 * storage overhead for small negative integers - for more details on the 609 * format, see https://developers.google.com/protocol-buffers/docs/encoding 610 * 611 * @return {string} The decoded zigzag varint in hash64 format. 612 */ 613jspb.BinaryDecoder.prototype.readZigzagVarintHash64 = function() { 614 return this.readSplitZigzagVarint64(jspb.utils.joinHash64); 615}; 616 617 618/** 619 * Reads a signed, zigzag-encoded 64-bit varint from the binary stream and 620 * returns its value as a string. 621 * 622 * Zigzag encoding is a modification of varint encoding that reduces the 623 * storage overhead for small negative integers - for more details on the 624 * format, see https://developers.google.com/protocol-buffers/docs/encoding 625 * 626 * @return {string} The decoded signed, zigzag-encoded 64-bit varint as a 627 * string. 628 */ 629jspb.BinaryDecoder.prototype.readZigzagVarint64String = function() { 630 return this.readSplitZigzagVarint64(jspb.utils.joinSignedDecimalString); 631}; 632 633 634/** 635 * Reads a raw unsigned 8-bit integer from the binary stream. 636 * 637 * @return {number} The unsigned 8-bit integer read from the binary stream. 638 */ 639jspb.BinaryDecoder.prototype.readUint8 = function() { 640 var a = this.bytes_[this.cursor_ + 0]; 641 this.cursor_ += 1; 642 goog.asserts.assert(this.cursor_ <= this.end_); 643 return a; 644}; 645 646 647/** 648 * Reads a raw unsigned 16-bit integer from the binary stream. 649 * 650 * @return {number} The unsigned 16-bit integer read from the binary stream. 651 */ 652jspb.BinaryDecoder.prototype.readUint16 = function() { 653 var a = this.bytes_[this.cursor_ + 0]; 654 var b = this.bytes_[this.cursor_ + 1]; 655 this.cursor_ += 2; 656 goog.asserts.assert(this.cursor_ <= this.end_); 657 return (a << 0) | (b << 8); 658}; 659 660 661/** 662 * Reads a raw unsigned 32-bit integer from the binary stream. 663 * 664 * @return {number} The unsigned 32-bit integer read from the binary stream. 665 */ 666jspb.BinaryDecoder.prototype.readUint32 = function() { 667 var a = this.bytes_[this.cursor_ + 0]; 668 var b = this.bytes_[this.cursor_ + 1]; 669 var c = this.bytes_[this.cursor_ + 2]; 670 var d = this.bytes_[this.cursor_ + 3]; 671 this.cursor_ += 4; 672 goog.asserts.assert(this.cursor_ <= this.end_); 673 return ((a << 0) | (b << 8) | (c << 16) | (d << 24)) >>> 0; 674}; 675 676 677/** 678 * Reads a raw unsigned 64-bit integer from the binary stream. Note that since 679 * Javascript represents all numbers as double-precision floats, there will be 680 * precision lost if the absolute value of the integer is larger than 2^53. 681 * 682 * @return {number} The unsigned 64-bit integer read from the binary stream. 683 * Precision will be lost if the integer exceeds 2^53. 684 */ 685jspb.BinaryDecoder.prototype.readUint64 = function() { 686 var bitsLow = this.readUint32(); 687 var bitsHigh = this.readUint32(); 688 return jspb.utils.joinUint64(bitsLow, bitsHigh); 689}; 690 691 692/** 693 * Reads a raw unsigned 64-bit integer from the binary stream. Note that since 694 * Javascript represents all numbers as double-precision floats, there will be 695 * precision lost if the absolute value of the integer is larger than 2^53. 696 * 697 * @return {string} The unsigned 64-bit integer read from the binary stream. 698 */ 699jspb.BinaryDecoder.prototype.readUint64String = function() { 700 var bitsLow = this.readUint32(); 701 var bitsHigh = this.readUint32(); 702 return jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh); 703}; 704 705 706/** 707 * Reads a raw signed 8-bit integer from the binary stream. 708 * 709 * @return {number} The signed 8-bit integer read from the binary stream. 710 */ 711jspb.BinaryDecoder.prototype.readInt8 = function() { 712 var a = this.bytes_[this.cursor_ + 0]; 713 this.cursor_ += 1; 714 goog.asserts.assert(this.cursor_ <= this.end_); 715 return (a << 24) >> 24; 716}; 717 718 719/** 720 * Reads a raw signed 16-bit integer from the binary stream. 721 * 722 * @return {number} The signed 16-bit integer read from the binary stream. 723 */ 724jspb.BinaryDecoder.prototype.readInt16 = function() { 725 var a = this.bytes_[this.cursor_ + 0]; 726 var b = this.bytes_[this.cursor_ + 1]; 727 this.cursor_ += 2; 728 goog.asserts.assert(this.cursor_ <= this.end_); 729 return (((a << 0) | (b << 8)) << 16) >> 16; 730}; 731 732 733/** 734 * Reads a raw signed 32-bit integer from the binary stream. 735 * 736 * @return {number} The signed 32-bit integer read from the binary stream. 737 */ 738jspb.BinaryDecoder.prototype.readInt32 = function() { 739 var a = this.bytes_[this.cursor_ + 0]; 740 var b = this.bytes_[this.cursor_ + 1]; 741 var c = this.bytes_[this.cursor_ + 2]; 742 var d = this.bytes_[this.cursor_ + 3]; 743 this.cursor_ += 4; 744 goog.asserts.assert(this.cursor_ <= this.end_); 745 return (a << 0) | (b << 8) | (c << 16) | (d << 24); 746}; 747 748 749/** 750 * Reads a raw signed 64-bit integer from the binary stream. Note that since 751 * Javascript represents all numbers as double-precision floats, there will be 752 * precision lost if the absolute value of the integer is larger than 2^53. 753 * 754 * @return {number} The signed 64-bit integer read from the binary stream. 755 * Precision will be lost if the integer exceeds 2^53. 756 */ 757jspb.BinaryDecoder.prototype.readInt64 = function() { 758 var bitsLow = this.readUint32(); 759 var bitsHigh = this.readUint32(); 760 return jspb.utils.joinInt64(bitsLow, bitsHigh); 761}; 762 763 764/** 765 * Reads a raw signed 64-bit integer from the binary stream and returns it as a 766 * string. 767 * 768 * @return {string} The signed 64-bit integer read from the binary stream. 769 * Precision will be lost if the integer exceeds 2^53. 770 */ 771jspb.BinaryDecoder.prototype.readInt64String = function() { 772 var bitsLow = this.readUint32(); 773 var bitsHigh = this.readUint32(); 774 return jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh); 775}; 776 777 778/** 779 * Reads a 32-bit floating-point number from the binary stream, using the 780 * temporary buffer to realign the data. 781 * 782 * @return {number} The float read from the binary stream. 783 */ 784jspb.BinaryDecoder.prototype.readFloat = function() { 785 var bitsLow = this.readUint32(); 786 var bitsHigh = 0; 787 return jspb.utils.joinFloat32(bitsLow, bitsHigh); 788}; 789 790 791/** 792 * Reads a 64-bit floating-point number from the binary stream, using the 793 * temporary buffer to realign the data. 794 * 795 * @return {number} The double read from the binary stream. 796 */ 797jspb.BinaryDecoder.prototype.readDouble = function() { 798 var bitsLow = this.readUint32(); 799 var bitsHigh = this.readUint32(); 800 return jspb.utils.joinFloat64(bitsLow, bitsHigh); 801}; 802 803 804/** 805 * Reads a boolean value from the binary stream. 806 * @return {boolean} The boolean read from the binary stream. 807 */ 808jspb.BinaryDecoder.prototype.readBool = function() { 809 return !!this.bytes_[this.cursor_++]; 810}; 811 812 813/** 814 * Reads an enum value from the binary stream, which are always encoded as 815 * signed varints. 816 * @return {number} The enum value read from the binary stream. 817 */ 818jspb.BinaryDecoder.prototype.readEnum = function() { 819 return this.readSignedVarint32(); 820}; 821 822 823/** 824 * Reads and parses a UTF-8 encoded unicode string from the stream. 825 * The code is inspired by maps.vectortown.parse.StreamedDataViewReader. 826 * Supports codepoints from U+0000 up to U+10FFFF. 827 * (http://en.wikipedia.org/wiki/UTF-8). 828 * @param {number} length The length of the string to read. 829 * @return {string} The decoded string. 830 */ 831jspb.BinaryDecoder.prototype.readString = function(length) { 832 var bytes = this.bytes_; 833 var cursor = this.cursor_; 834 var end = cursor + length; 835 var codeUnits = []; 836 837 var result = ''; 838 while (cursor < end) { 839 var c = bytes[cursor++]; 840 if (c < 128) { // Regular 7-bit ASCII. 841 codeUnits.push(c); 842 } else if (c < 192) { 843 // UTF-8 continuation mark. We are out of sync. This 844 // might happen if we attempted to read a character 845 // with more than four bytes. 846 continue; 847 } else if (c < 224) { // UTF-8 with two bytes. 848 var c2 = bytes[cursor++]; 849 codeUnits.push(((c & 31) << 6) | (c2 & 63)); 850 } else if (c < 240) { // UTF-8 with three bytes. 851 var c2 = bytes[cursor++]; 852 var c3 = bytes[cursor++]; 853 codeUnits.push(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 854 } else if (c < 248) { // UTF-8 with 4 bytes. 855 var c2 = bytes[cursor++]; 856 var c3 = bytes[cursor++]; 857 var c4 = bytes[cursor++]; 858 // Characters written on 4 bytes have 21 bits for a codepoint. 859 // We can't fit that on 16bit characters, so we use surrogates. 860 var codepoint = ((c & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63); 861 // Surrogates formula from wikipedia. 862 // 1. Subtract 0x10000 from codepoint 863 codepoint -= 0x10000; 864 // 2. Split this into the high 10-bit value and the low 10-bit value 865 // 3. Add 0xD800 to the high value to form the high surrogate 866 // 4. Add 0xDC00 to the low value to form the low surrogate: 867 var low = (codepoint & 1023) + 0xDC00; 868 var high = ((codepoint >> 10) & 1023) + 0xD800; 869 codeUnits.push(high, low); 870 } 871 872 // Avoid exceeding the maximum stack size when calling `apply`. 873 if (codeUnits.length >= 8192) { 874 result += String.fromCharCode.apply(null, codeUnits); 875 codeUnits.length = 0; 876 } 877 } 878 result += goog.crypt.byteArrayToString(codeUnits); 879 this.cursor_ = cursor; 880 return result; 881}; 882 883 884/** 885 * Reads and parses a UTF-8 encoded unicode string (with length prefix) from 886 * the stream. 887 * @return {string} The decoded string. 888 */ 889jspb.BinaryDecoder.prototype.readStringWithLength = function() { 890 var length = this.readUnsignedVarint32(); 891 return this.readString(length); 892}; 893 894 895/** 896 * Reads a block of raw bytes from the binary stream. 897 * 898 * @param {number} length The number of bytes to read. 899 * @return {!Uint8Array} The decoded block of bytes, or an empty block if the 900 * length was invalid. 901 */ 902jspb.BinaryDecoder.prototype.readBytes = function(length) { 903 if (length < 0 || 904 this.cursor_ + length > this.bytes_.length) { 905 this.error_ = true; 906 goog.asserts.fail('Invalid byte length!'); 907 return new Uint8Array(0); 908 } 909 910 var result = this.bytes_.subarray(this.cursor_, this.cursor_ + length); 911 912 this.cursor_ += length; 913 goog.asserts.assert(this.cursor_ <= this.end_); 914 return result; 915}; 916 917 918/** 919 * Reads a 64-bit varint from the stream and returns it as an 8-character 920 * Unicode string for use as a hash table key. 921 * 922 * @return {string} The hash value. 923 */ 924jspb.BinaryDecoder.prototype.readVarintHash64 = function() { 925 return this.readSplitVarint64(jspb.utils.joinHash64); 926}; 927 928 929/** 930 * Reads a 64-bit fixed-width value from the stream and returns it as an 931 * 8-character Unicode string for use as a hash table key. 932 * 933 * @return {string} The hash value. 934 */ 935jspb.BinaryDecoder.prototype.readFixedHash64 = function() { 936 var bytes = this.bytes_; 937 var cursor = this.cursor_; 938 939 var a = bytes[cursor + 0]; 940 var b = bytes[cursor + 1]; 941 var c = bytes[cursor + 2]; 942 var d = bytes[cursor + 3]; 943 var e = bytes[cursor + 4]; 944 var f = bytes[cursor + 5]; 945 var g = bytes[cursor + 6]; 946 var h = bytes[cursor + 7]; 947 948 this.cursor_ += 8; 949 950 return String.fromCharCode(a, b, c, d, e, f, g, h); 951}; 952