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