• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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