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