mirror of
https://github.com/protobufjs/protobuf.js.git
synced 2025-12-08 20:58:55 +00:00
504 lines
14 KiB
JavaScript
504 lines
14 KiB
JavaScript
"use strict";
|
|
module.exports = Reader;
|
|
|
|
var util = require("./util/runtime");
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Creates a new reader using the specified buffer.
|
|
* @function
|
|
* @param {Uint8Array} buffer Buffer to read from
|
|
* @returns {BufferReader|Reader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader}
|
|
*/
|
|
Reader.create = util.Buffer
|
|
? function create_buffer_setup(buffer) {
|
|
if (!BufferReader)
|
|
BufferReader = require("./reader_buffer");
|
|
return (Reader.create = function create_buffer(buffer) {
|
|
return new BufferReader(buffer);
|
|
})(buffer);
|
|
}
|
|
: function create_array(buffer) {
|
|
return new Reader(buffer);
|
|
};
|
|
|
|
/** @alias Reader.prototype */
|
|
var ReaderPrototype = Reader.prototype;
|
|
|
|
ReaderPrototype._slice = util.Array.prototype.subarray || util.Array.prototype.slice;
|
|
|
|
/**
|
|
* Reads a varint as an unsigned 32 bit value.
|
|
* @function
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.uint32 = (function read_uint32_setup() { // eslint-disable-line wrap-iife
|
|
var value = 0xffffffff >>> 0; // 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 next */
|
|
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
|
|
*/
|
|
ReaderPrototype.int32 = function read_int32() {
|
|
return this.uint32() | 0;
|
|
};
|
|
|
|
/**
|
|
* Reads a zig-zag encoded varint as a signed 32 bit value.
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.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, 0 >>> 0);
|
|
var i = 0;
|
|
if (this.len - this.pos > 4) { // fast route (lo)
|
|
for (i = 0; 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;
|
|
} else {
|
|
for (i = 0; i < 4; ++i) {
|
|
/* istanbul ignore next */
|
|
if (this.pos >= this.len)
|
|
throw indexOutOfRange(this);
|
|
// 1st..4th
|
|
bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;
|
|
if (this.buf[this.pos++] < 128)
|
|
return bits;
|
|
}
|
|
/* istanbul ignore next */
|
|
if (this.pos >= this.len)
|
|
throw indexOutOfRange(this);
|
|
// 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;
|
|
}
|
|
if (this.len - this.pos > 4) { // fast route (hi)
|
|
for (i = 0; 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 = 0; i < 5; ++i) {
|
|
/* istanbul ignore next */
|
|
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;
|
|
}
|
|
}
|
|
throw Error("invalid varint encoding");
|
|
}
|
|
|
|
function read_int64_long() {
|
|
return readLongVarint.call(this).toLong();
|
|
}
|
|
|
|
function read_int64_number() {
|
|
return readLongVarint.call(this).toNumber();
|
|
}
|
|
|
|
function read_uint64_long() {
|
|
return readLongVarint.call(this).toLong(true);
|
|
}
|
|
|
|
function read_uint64_number() {
|
|
return readLongVarint.call(this).toNumber(true);
|
|
}
|
|
|
|
function read_sint64_long() {
|
|
return readLongVarint.call(this).zzDecode().toLong();
|
|
}
|
|
|
|
function read_sint64_number() {
|
|
return readLongVarint.call(this).zzDecode().toNumber();
|
|
}
|
|
|
|
/* eslint-enable no-invalid-this */
|
|
|
|
/**
|
|
* Reads a varint as a signed 64 bit value.
|
|
* @name Reader#int64
|
|
* @function
|
|
* @returns {Long|number} Value read
|
|
*/
|
|
|
|
/**
|
|
* Reads a varint as an unsigned 64 bit value.
|
|
* @name Reader#uint64
|
|
* @function
|
|
* @returns {Long|number} Value read
|
|
*/
|
|
|
|
/**
|
|
* Reads a zig-zag encoded varint as a signed 64 bit value.
|
|
* @name Reader#sint64
|
|
* @function
|
|
* @returns {Long|number} Value read
|
|
*/
|
|
|
|
/**
|
|
* Reads a varint as a boolean.
|
|
* @returns {boolean} Value read
|
|
*/
|
|
ReaderPrototype.bool = function read_bool() {
|
|
return this.uint32() !== 0;
|
|
};
|
|
|
|
function readFixed32(buf, end) {
|
|
return (buf[end - 4]
|
|
| buf[end - 3] << 8
|
|
| buf[end - 2] << 16
|
|
| buf[end - 1] << 24) >>> 0;
|
|
}
|
|
|
|
/**
|
|
* Reads fixed 32 bits as a number.
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.fixed32 = function read_fixed32() {
|
|
|
|
/* istanbul ignore next */
|
|
if (this.pos + 4 > this.len)
|
|
throw indexOutOfRange(this, 4);
|
|
|
|
return readFixed32(this.buf, this.pos += 4);
|
|
};
|
|
|
|
/**
|
|
* Reads zig-zag encoded fixed 32 bits as a number.
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.sfixed32 = function read_sfixed32() {
|
|
var value = this.fixed32();
|
|
return value >>> 1 ^ -(value & 1);
|
|
};
|
|
|
|
/* eslint-disable no-invalid-this */
|
|
|
|
function readFixed64(/* this: Reader */) {
|
|
|
|
/* istanbul ignore next */
|
|
if (this.pos + 8 > this.len)
|
|
throw indexOutOfRange(this, 8);
|
|
|
|
return new LongBits(readFixed32(this.buf, this.pos += 4), readFixed32(this.buf, this.pos += 4));
|
|
}
|
|
|
|
function read_fixed64_long() {
|
|
return readFixed64.call(this).toLong(true);
|
|
}
|
|
|
|
function read_fixed64_number() {
|
|
return readFixed64.call(this).toNumber(true);
|
|
}
|
|
|
|
function read_sfixed64_long() {
|
|
return readFixed64.call(this).zzDecode().toLong();
|
|
}
|
|
|
|
function read_sfixed64_number() {
|
|
return readFixed64.call(this).zzDecode().toNumber();
|
|
}
|
|
|
|
/* eslint-enable no-invalid-this */
|
|
|
|
/**
|
|
* Reads fixed 64 bits.
|
|
* @name Reader#fixed64
|
|
* @function
|
|
* @returns {Long|number} Value read
|
|
*/
|
|
|
|
/**
|
|
* Reads zig-zag encoded fixed 64 bits.
|
|
* @name Reader#sfixed64
|
|
* @function
|
|
* @returns {Long|number} Value read
|
|
*/
|
|
|
|
var readFloat = typeof Float32Array !== "undefined"
|
|
? (function() { // eslint-disable-line wrap-iife
|
|
var f32 = new Float32Array(1),
|
|
f8b = new Uint8Array(f32.buffer);
|
|
f32[0] = -0;
|
|
return f8b[3] // already le?
|
|
? function readFloat_f32(buf, pos) {
|
|
f8b[0] = buf[pos ];
|
|
f8b[1] = buf[pos + 1];
|
|
f8b[2] = buf[pos + 2];
|
|
f8b[3] = buf[pos + 3];
|
|
return f32[0];
|
|
}
|
|
: function readFloat_f32_le(buf, pos) {
|
|
f8b[3] = buf[pos ];
|
|
f8b[2] = buf[pos + 1];
|
|
f8b[1] = buf[pos + 2];
|
|
f8b[0] = buf[pos + 3];
|
|
return f32[0];
|
|
};
|
|
})()
|
|
: function readFloat_ieee754(buf, pos) {
|
|
var uint = readFixed32(buf, pos + 4),
|
|
sign = (uint >> 31) * 2 + 1,
|
|
exponent = uint >>> 23 & 255,
|
|
mantissa = uint & 8388607;
|
|
return exponent === 255
|
|
? mantissa
|
|
? NaN
|
|
: sign * Infinity
|
|
: exponent === 0 // denormal
|
|
? sign * 1.401298464324817e-45 * mantissa
|
|
: sign * Math.pow(2, exponent - 150) * (mantissa + 8388608);
|
|
};
|
|
|
|
/**
|
|
* Reads a float (32 bit) as a number.
|
|
* @function
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.float = function read_float() {
|
|
|
|
/* istanbul ignore next */
|
|
if (this.pos + 4 > this.len)
|
|
throw indexOutOfRange(this, 4);
|
|
|
|
var value = readFloat(this.buf, this.pos);
|
|
this.pos += 4;
|
|
return value;
|
|
};
|
|
|
|
var readDouble = typeof Float64Array !== "undefined"
|
|
? (function() { // eslint-disable-line wrap-iife
|
|
var f64 = new Float64Array(1),
|
|
f8b = new Uint8Array(f64.buffer);
|
|
f64[0] = -0;
|
|
return f8b[7] // already le?
|
|
? function readDouble_f64(buf, pos) {
|
|
f8b[0] = buf[pos ];
|
|
f8b[1] = buf[pos + 1];
|
|
f8b[2] = buf[pos + 2];
|
|
f8b[3] = buf[pos + 3];
|
|
f8b[4] = buf[pos + 4];
|
|
f8b[5] = buf[pos + 5];
|
|
f8b[6] = buf[pos + 6];
|
|
f8b[7] = buf[pos + 7];
|
|
return f64[0];
|
|
}
|
|
: function readDouble_f64_le(buf, pos) {
|
|
f8b[7] = buf[pos ];
|
|
f8b[6] = buf[pos + 1];
|
|
f8b[5] = buf[pos + 2];
|
|
f8b[4] = buf[pos + 3];
|
|
f8b[3] = buf[pos + 4];
|
|
f8b[2] = buf[pos + 5];
|
|
f8b[1] = buf[pos + 6];
|
|
f8b[0] = buf[pos + 7];
|
|
return f64[0];
|
|
};
|
|
})()
|
|
: function readDouble_ieee754(buf, pos) {
|
|
var lo = readFixed32(buf, pos + 4),
|
|
hi = readFixed32(buf, pos + 8);
|
|
var sign = (hi >> 31) * 2 + 1,
|
|
exponent = hi >>> 20 & 2047,
|
|
mantissa = 4294967296 * (hi & 1048575) + lo;
|
|
return exponent === 2047
|
|
? mantissa
|
|
? NaN
|
|
: sign * Infinity
|
|
: exponent === 0 // denormal
|
|
? sign * 5e-324 * mantissa
|
|
: sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496);
|
|
};
|
|
|
|
/**
|
|
* Reads a double (64 bit float) as a number.
|
|
* @function
|
|
* @returns {number} Value read
|
|
*/
|
|
ReaderPrototype.double = function read_double() {
|
|
|
|
/* istanbul ignore next */
|
|
if (this.pos + 8 > this.len)
|
|
throw indexOutOfRange(this, 4);
|
|
|
|
var value = readDouble(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
|
|
*/
|
|
ReaderPrototype.bytes = function read_bytes() {
|
|
var length = this.uint32(),
|
|
start = this.pos,
|
|
end = this.pos + length;
|
|
|
|
/* istanbul ignore next */
|
|
if (end > this.len)
|
|
throw indexOutOfRange(this, length);
|
|
|
|
this.pos += length;
|
|
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
|
|
*/
|
|
ReaderPrototype.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`
|
|
*/
|
|
ReaderPrototype.skip = function skip(length) {
|
|
if (typeof length === "number") {
|
|
/* istanbul ignore next */
|
|
if (this.pos + length > this.len)
|
|
throw indexOutOfRange(this, length);
|
|
this.pos += length;
|
|
} else {
|
|
do {
|
|
/* istanbul ignore next */
|
|
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`
|
|
*/
|
|
ReaderPrototype.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:
|
|
do { // eslint-disable-line no-constant-condition
|
|
if ((wireType = this.uint32() & 7) === 4)
|
|
break;
|
|
this.skipType(wireType);
|
|
} while (true);
|
|
break;
|
|
case 5:
|
|
this.skip(4);
|
|
break;
|
|
|
|
/* istanbul ignore next */
|
|
default:
|
|
throw Error("invalid wire type: " + wireType);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function configure() {
|
|
if (util.Long) {
|
|
ReaderPrototype.int64 = read_int64_long;
|
|
ReaderPrototype.uint64 = read_uint64_long;
|
|
ReaderPrototype.sint64 = read_sint64_long;
|
|
ReaderPrototype.fixed64 = read_fixed64_long;
|
|
ReaderPrototype.sfixed64 = read_sfixed64_long;
|
|
} else {
|
|
ReaderPrototype.int64 = read_int64_number;
|
|
ReaderPrototype.uint64 = read_uint64_number;
|
|
ReaderPrototype.sint64 = read_sint64_number;
|
|
ReaderPrototype.fixed64 = read_fixed64_number;
|
|
ReaderPrototype.sfixed64 = read_sfixed64_number;
|
|
}
|
|
}
|
|
|
|
Reader._configure = configure;
|
|
|
|
configure();
|