| "use strict"; |
| module.exports = Reader; |
| |
| var util = require("./util/minimal"); |
| |
| var BufferReader; // cyclic |
| |
| var LongBits = util.LongBits, |
| utf8 = util.utf8; |
| |
| /* istanbul ignore next */ |
| function indexOutOfRange(reader, writeLength) { |
| return RangeError("index out of range: " + reader.pos + " + " + (writeLength || 1) + " > " + reader.len); |
| } |
| |
| /** |
| * Constructs a new reader instance using the specified buffer. |
| * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`. |
| * @constructor |
| * @param {Uint8Array} buffer Buffer to read from |
| */ |
| function Reader(buffer) { |
| |
| /** |
| * Read buffer. |
| * @type {Uint8Array} |
| */ |
| this.buf = buffer; |
| |
| /** |
| * Read buffer position. |
| * @type {number} |
| */ |
| this.pos = 0; |
| |
| /** |
| * Read buffer length. |
| * @type {number} |
| */ |
| this.len = buffer.length; |
| } |
| |
| var create_array = typeof Uint8Array !== "undefined" |
| ? function create_typed_array(buffer) { |
| if (buffer instanceof Uint8Array || Array.isArray(buffer)) |
| return new Reader(buffer); |
| throw Error("illegal buffer"); |
| } |
| /* istanbul ignore next */ |
| : function create_array(buffer) { |
| if (Array.isArray(buffer)) |
| return new Reader(buffer); |
| throw Error("illegal buffer"); |
| }; |
| |
| var create = function create() { |
| return util.Buffer |
| ? function create_buffer_setup(buffer) { |
| return (Reader.create = function create_buffer(buffer) { |
| return util.Buffer.isBuffer(buffer) |
| ? new BufferReader(buffer) |
| /* istanbul ignore next */ |
| : create_array(buffer); |
| })(buffer); |
| } |
| /* istanbul ignore next */ |
| : create_array; |
| }; |
| |
| /** |
| * Creates a new reader using the specified buffer. |
| * @function |
| * @param {Uint8Array|Buffer} buffer Buffer to read from |
| * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader} |
| * @throws {Error} If `buffer` is not a valid buffer |
| */ |
| Reader.create = create(); |
| |
| Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice; |
| |
| /** |
| * Reads a varint as an unsigned 32 bit value. |
| * @function |
| * @returns {number} Value read |
| */ |
| Reader.prototype.uint32 = (function read_uint32_setup() { |
| var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!) |
| return function read_uint32() { |
| value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value; |
| value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value; |
| value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value; |
| value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value; |
| value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value; |
| |
| /* istanbul ignore if */ |
| if ((this.pos += 5) > this.len) { |
| this.pos = this.len; |
| throw indexOutOfRange(this, 10); |
| } |
| return value; |
| }; |
| })(); |
| |
| /** |
| * Reads a varint as a signed 32 bit value. |
| * @returns {number} Value read |
| */ |
| Reader.prototype.int32 = function read_int32() { |
| return this.uint32() | 0; |
| }; |
| |
| /** |
| * Reads a zig-zag encoded varint as a signed 32 bit value. |
| * @returns {number} Value read |
| */ |
| Reader.prototype.sint32 = function read_sint32() { |
| var value = this.uint32(); |
| return value >>> 1 ^ -(value & 1) | 0; |
| }; |
| |
| /* eslint-disable no-invalid-this */ |
| |
| function readLongVarint() { |
| // tends to deopt with local vars for octet etc. |
| var bits = new LongBits(0, 0); |
| var i = 0; |
| if (this.len - this.pos > 4) { // fast route (lo) |
| for (; i < 4; ++i) { |
| // 1st..4th |
| bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; |
| if (this.buf[this.pos++] < 128) |
| return bits; |
| } |
| // 5th |
| bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0; |
| bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0; |
| if (this.buf[this.pos++] < 128) |
| return bits; |
| i = 0; |
| } else { |
| for (; i < 3; ++i) { |
| /* istanbul ignore if */ |
| if (this.pos >= this.len) |
| throw indexOutOfRange(this); |
| // 1st..3th |
| bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; |
| if (this.buf[this.pos++] < 128) |
| return bits; |
| } |
| // 4th |
| bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0; |
| return bits; |
| } |
| if (this.len - this.pos > 4) { // fast route (hi) |
| for (; i < 5; ++i) { |
| // 6th..10th |
| bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; |
| if (this.buf[this.pos++] < 128) |
| return bits; |
| } |
| } else { |
| for (; i < 5; ++i) { |
| /* istanbul ignore if */ |
| if (this.pos >= this.len) |
| throw indexOutOfRange(this); |
| // 6th..10th |
| bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; |
| if (this.buf[this.pos++] < 128) |
| return bits; |
| } |
| } |
| /* istanbul ignore next */ |
| throw Error("invalid varint encoding"); |
| } |
| |
| /* eslint-enable no-invalid-this */ |
| |
| /** |
| * Reads a varint as a signed 64 bit value. |
| * @name Reader#int64 |
| * @function |
| * @returns {Long} Value read |
| */ |
| |
| /** |
| * Reads a varint as an unsigned 64 bit value. |
| * @name Reader#uint64 |
| * @function |
| * @returns {Long} Value read |
| */ |
| |
| /** |
| * Reads a zig-zag encoded varint as a signed 64 bit value. |
| * @name Reader#sint64 |
| * @function |
| * @returns {Long} Value read |
| */ |
| |
| /** |
| * Reads a varint as a boolean. |
| * @returns {boolean} Value read |
| */ |
| Reader.prototype.bool = function read_bool() { |
| return this.uint32() !== 0; |
| }; |
| |
| function readFixed32_end(buf, end) { // note that this uses `end`, not `pos` |
| return (buf[end - 4] |
| | buf[end - 3] << 8 |
| | buf[end - 2] << 16 |
| | buf[end - 1] << 24) >>> 0; |
| } |
| |
| /** |
| * Reads fixed 32 bits as an unsigned 32 bit integer. |
| * @returns {number} Value read |
| */ |
| Reader.prototype.fixed32 = function read_fixed32() { |
| |
| /* istanbul ignore if */ |
| if (this.pos + 4 > this.len) |
| throw indexOutOfRange(this, 4); |
| |
| return readFixed32_end(this.buf, this.pos += 4); |
| }; |
| |
| /** |
| * Reads fixed 32 bits as a signed 32 bit integer. |
| * @returns {number} Value read |
| */ |
| Reader.prototype.sfixed32 = function read_sfixed32() { |
| |
| /* istanbul ignore if */ |
| if (this.pos + 4 > this.len) |
| throw indexOutOfRange(this, 4); |
| |
| return readFixed32_end(this.buf, this.pos += 4) | 0; |
| }; |
| |
| /* eslint-disable no-invalid-this */ |
| |
| function readFixed64(/* this: Reader */) { |
| |
| /* istanbul ignore if */ |
| if (this.pos + 8 > this.len) |
| throw indexOutOfRange(this, 8); |
| |
| return new LongBits(readFixed32_end(this.buf, this.pos += 4), readFixed32_end(this.buf, this.pos += 4)); |
| } |
| |
| /* eslint-enable no-invalid-this */ |
| |
| /** |
| * Reads fixed 64 bits. |
| * @name Reader#fixed64 |
| * @function |
| * @returns {Long} Value read |
| */ |
| |
| /** |
| * Reads zig-zag encoded fixed 64 bits. |
| * @name Reader#sfixed64 |
| * @function |
| * @returns {Long} Value read |
| */ |
| |
| /** |
| * Reads a float (32 bit) as a number. |
| * @function |
| * @returns {number} Value read |
| */ |
| Reader.prototype.float = function read_float() { |
| |
| /* istanbul ignore if */ |
| if (this.pos + 4 > this.len) |
| throw indexOutOfRange(this, 4); |
| |
| var value = util.float.readFloatLE(this.buf, this.pos); |
| this.pos += 4; |
| return value; |
| }; |
| |
| /** |
| * Reads a double (64 bit float) as a number. |
| * @function |
| * @returns {number} Value read |
| */ |
| Reader.prototype.double = function read_double() { |
| |
| /* istanbul ignore if */ |
| if (this.pos + 8 > this.len) |
| throw indexOutOfRange(this, 4); |
| |
| var value = util.float.readDoubleLE(this.buf, this.pos); |
| this.pos += 8; |
| return value; |
| }; |
| |
| /** |
| * Reads a sequence of bytes preceeded by its length as a varint. |
| * @returns {Uint8Array} Value read |
| */ |
| Reader.prototype.bytes = function read_bytes() { |
| var length = this.uint32(), |
| start = this.pos, |
| end = this.pos + length; |
| |
| /* istanbul ignore if */ |
| if (end > this.len) |
| throw indexOutOfRange(this, length); |
| |
| this.pos += length; |
| if (Array.isArray(this.buf)) // plain array |
| return this.buf.slice(start, end); |
| return start === end // fix for IE 10/Win8 and others' subarray returning array of size 1 |
| ? new this.buf.constructor(0) |
| : this._slice.call(this.buf, start, end); |
| }; |
| |
| /** |
| * Reads a string preceeded by its byte length as a varint. |
| * @returns {string} Value read |
| */ |
| Reader.prototype.string = function read_string() { |
| var bytes = this.bytes(); |
| return utf8.read(bytes, 0, bytes.length); |
| }; |
| |
| /** |
| * Skips the specified number of bytes if specified, otherwise skips a varint. |
| * @param {number} [length] Length if known, otherwise a varint is assumed |
| * @returns {Reader} `this` |
| */ |
| Reader.prototype.skip = function skip(length) { |
| if (typeof length === "number") { |
| /* istanbul ignore if */ |
| if (this.pos + length > this.len) |
| throw indexOutOfRange(this, length); |
| this.pos += length; |
| } else { |
| do { |
| /* istanbul ignore if */ |
| if (this.pos >= this.len) |
| throw indexOutOfRange(this); |
| } while (this.buf[this.pos++] & 128); |
| } |
| return this; |
| }; |
| |
| /** |
| * Skips the next element of the specified wire type. |
| * @param {number} wireType Wire type received |
| * @returns {Reader} `this` |
| */ |
| Reader.prototype.skipType = function(wireType) { |
| switch (wireType) { |
| case 0: |
| this.skip(); |
| break; |
| case 1: |
| this.skip(8); |
| break; |
| case 2: |
| this.skip(this.uint32()); |
| break; |
| case 3: |
| while ((wireType = this.uint32() & 7) !== 4) { |
| this.skipType(wireType); |
| } |
| break; |
| case 5: |
| this.skip(4); |
| break; |
| |
| /* istanbul ignore next */ |
| default: |
| throw Error("invalid wire type " + wireType + " at offset " + this.pos); |
| } |
| return this; |
| }; |
| |
| Reader._configure = function(BufferReader_) { |
| BufferReader = BufferReader_; |
| Reader.create = create(); |
| BufferReader._configure(); |
| |
| var fn = util.Long ? "toLong" : /* istanbul ignore next */ "toNumber"; |
| util.merge(Reader.prototype, { |
| |
| int64: function read_int64() { |
| return readLongVarint.call(this)[fn](false); |
| }, |
| |
| uint64: function read_uint64() { |
| return readLongVarint.call(this)[fn](true); |
| }, |
| |
| sint64: function read_sint64() { |
| return readLongVarint.call(this).zzDecode()[fn](false); |
| }, |
| |
| fixed64: function read_fixed64() { |
| return readFixed64.call(this)[fn](true); |
| }, |
| |
| sfixed64: function read_sfixed64() { |
| return readFixed64.call(this)[fn](false); |
| } |
| |
| }); |
| }; |