/** * @fileoverview Helper methods for reading data from the binary wire format. */ goog.module('protobuf.binary.reader'); const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); const ByteString = goog.require('protobuf.ByteString'); const Int64 = goog.require('protobuf.Int64'); /****************************************************************************** * OPTIONAL FUNCTIONS ******************************************************************************/ /** * Reads a boolean from the binary bytes. * Also returns the first position after the boolean. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{value: boolean, nextCursor: number}} */ function readBoolValue(bufferDecoder, index) { const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); return {value: lowBits !== 0 || highBits !== 0, nextCursor: dataStart}; } /** * Reads a boolean value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {boolean} * @package */ function readBool(bufferDecoder, start) { return readBoolValue(bufferDecoder, start).value; } /** * Reads a double value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!ByteString} * @package */ function readBytes(bufferDecoder, start) { return readDelimited(bufferDecoder, start).asByteString(); } /** * Reads a int32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{value: number, nextCursor: number}} * @package */ function readInt32Value(bufferDecoder, index) { const {lowBits, dataStart} = bufferDecoder.getVarint(index); // Negative 32 bit integers are encoded with 64 bit values. // Clients are expected to truncate back to 32 bits. // This is why we are dropping the upper bytes here. return {value: lowBits | 0, nextCursor: dataStart}; } /** * Reads a int32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readInt32(bufferDecoder, start) { return readInt32Value(bufferDecoder, start).value; } /** * Reads a int32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{ value: !Int64, nextCursor: number}} * @package */ function readInt64Value(bufferDecoder, index) { const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); return {value: Int64.fromBits(lowBits, highBits), nextCursor: dataStart}; } /** * Reads a int32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Int64} * @package */ function readInt64(bufferDecoder, start) { return readInt64Value(bufferDecoder, start).value; } /** * Reads a fixed int32 value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readFixed32(bufferDecoder, start) { return bufferDecoder.getUint32(start); } /** * Reads a float value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readFloat(bufferDecoder, start) { return bufferDecoder.getFloat32(start); } /** * Reads a fixed int64 value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Int64} * @package */ function readSfixed64(bufferDecoder, start) { const lowBits = bufferDecoder.getInt32(start); const highBits = bufferDecoder.getInt32(start + 4); return Int64.fromBits(lowBits, highBits); } /** * Reads a sint32 value from the binary bytes encoded as varint. * Also returns the first position after the boolean. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{value: number, nextCursor: number}} */ function readSint32Value(bufferDecoder, index) { const {lowBits, dataStart} = bufferDecoder.getVarint(index); // Truncate upper bits and convert from zig zag to signd int return {value: (lowBits >>> 1) ^ -(lowBits & 0x01), nextCursor: dataStart}; } /** * Reads a sint32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readSint32(bufferDecoder, start) { return readSint32Value(bufferDecoder, start).value; } /** * Reads a sint64 value from the binary bytes encoded as varint. * Also returns the first position after the value. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{value: !Int64, nextCursor: number}} * @package */ function readSint64Value(bufferDecoder, index) { const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index); const sign = -(lowBits & 0x01); const decodedLowerBits = ((lowBits >>> 1) | (highBits & 0x01) << 31) ^ sign; const decodedUpperBits = (highBits >>> 1) ^ sign; return { value: Int64.fromBits(decodedLowerBits, decodedUpperBits), nextCursor: dataStart }; } /** * Reads a sint64 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Int64} * @package */ function readSint64(bufferDecoder, start) { return readSint64Value(bufferDecoder, start).value; } /** * Read a subarray of bytes representing a length delimited field. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!BufferDecoder} * @package */ function readDelimited(bufferDecoder, start) { const {lowBits, dataStart} = bufferDecoder.getVarint(start); const unsignedLength = lowBits >>> 0; return bufferDecoder.subBufferDecoder(dataStart, unsignedLength); } /** * Reads a string value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {string} * @package */ function readString(bufferDecoder, start) { return readDelimited(bufferDecoder, start).asString(); } /** * Reads a uint32 value from the binary bytes encoded as varint. * Also returns the first position after the value. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} index Start of the data. * @return {{value: number, nextCursor: number}} */ function readUint32Value(bufferDecoder, index) { const {lowBits, dataStart} = bufferDecoder.getVarint(index); return {value: lowBits >>> 0, nextCursor: dataStart}; } /** * Reads a uint32 value from the binary bytes encoded as varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readUint32(bufferDecoder, start) { return readUint32Value(bufferDecoder, start).value; } /** * Reads a double value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readDouble(bufferDecoder, start) { return bufferDecoder.getFloat64(start); } /** * Reads a fixed int32 value from the binary bytes. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {number} * @package */ function readSfixed32(bufferDecoder, start) { return bufferDecoder.getInt32(start); } /****************************************************************************** * REPEATED FUNCTIONS ******************************************************************************/ /** * Reads a packed bool field, which consists of a length header and a list of * unsigned varints. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedBool(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readBoolValue); } /** * Reads a packed double field, which consists of a length header and a list of * fixed64. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedDouble(bufferDecoder, start) { return readPackedFixed(bufferDecoder, start, 8, readDouble); } /** * Reads a packed fixed32 field, which consists of a length header and a list of * fixed32. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedFixed32(bufferDecoder, start) { return readPackedFixed(bufferDecoder, start, 4, readFixed32); } /** * Reads a packed float field, which consists of a length header and a list of * fixed64. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedFloat(bufferDecoder, start) { return readPackedFixed(bufferDecoder, start, 4, readFloat); } /** * Reads a packed int32 field, which consists of a length header and a list of * varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedInt32(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readInt32Value); } /** * Reads a packed int64 field, which consists of a length header and a list * of int64. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedInt64(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readInt64Value); } /** * Reads a packed sfixed32 field, which consists of a length header and a list * of sfixed32. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedSfixed32(bufferDecoder, start) { return readPackedFixed(bufferDecoder, start, 4, readSfixed32); } /** * Reads a packed sfixed64 field, which consists of a length header and a list * of sfixed64. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedSfixed64(bufferDecoder, start) { return readPackedFixed(bufferDecoder, start, 8, readSfixed64); } /** * Reads a packed sint32 field, which consists of a length header and a list of * varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedSint32(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readSint32Value); } /** * Reads a packed sint64 field, which consists of a length header and a list * of sint64. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedSint64(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readSint64Value); } /** * Reads a packed uint32 field, which consists of a length header and a list of * varint. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @return {!Array} * @package */ function readPackedUint32(bufferDecoder, start) { return readPackedVariableLength(bufferDecoder, start, readUint32Value); } /** * Read packed variable length values. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @param {function(!BufferDecoder, number):{value:T, nextCursor: number}} * valueFunction * @return {!Array} * @package * @template T */ function readPackedVariableLength(bufferDecoder, start, valueFunction) { const /** !Array */ result = []; const {lowBits, dataStart} = bufferDecoder.getVarint(start); let cursor = dataStart; const unsignedLength = lowBits >>> 0; while (cursor < dataStart + unsignedLength) { const {value, nextCursor} = valueFunction(bufferDecoder, cursor); cursor = nextCursor; result.push(value); } return result; } /** * Read a packed fixed values. * @param {!BufferDecoder} bufferDecoder Binary format encoded bytes. * @param {number} start Start of the data. * @param {number} size End of the data. * @param {function(!BufferDecoder, number):T} valueFunction * @return {!Array} * @package * @template T */ function readPackedFixed(bufferDecoder, start, size, valueFunction) { const {lowBits, dataStart} = bufferDecoder.getVarint(start); const unsignedLength = lowBits >>> 0; const noOfEntries = unsignedLength / size; const /** !Array */ result = new Array(noOfEntries); let cursor = dataStart; for (let i = 0; i < noOfEntries; i++) { result[i] = valueFunction(bufferDecoder, cursor); cursor += size; } return result; } exports = { readBool, readBytes, readDelimited, readDouble, readFixed32, readFloat, readInt32, readInt64, readSint32, readSint64, readSfixed32, readSfixed64, readString, readUint32, readPackedBool, readPackedDouble, readPackedFixed32, readPackedFloat, readPackedInt32, readPackedInt64, readPackedSfixed32, readPackedSfixed64, readPackedSint32, readPackedSint64, readPackedUint32, };