123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /**
- * @fileoverview Utilities to index a binary proto by fieldnumbers without
- * relying on strutural proto information.
- */
- goog.module('protobuf.binary.indexer');
- const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
- const Storage = goog.require('protobuf.binary.Storage');
- const WireType = goog.require('protobuf.binary.WireType');
- const {Field} = goog.require('protobuf.binary.field');
- const {checkCriticalElementIndex, checkCriticalState} = goog.require('protobuf.internal.checks');
- /**
- * Appends a new entry in the index array for the given field number.
- * @param {!Storage<!Field>} storage
- * @param {number} fieldNumber
- * @param {!WireType} wireType
- * @param {number} startIndex
- */
- function addIndexEntry(storage, fieldNumber, wireType, startIndex) {
- const field = storage.get(fieldNumber);
- if (field !== undefined) {
- field.addIndexEntry(wireType, startIndex);
- } else {
- storage.set(fieldNumber, Field.fromFirstIndexEntry(wireType, startIndex));
- }
- }
- /**
- * Returns wire type stored in a tag.
- * Protos store the wire type as the first 3 bit of a tag.
- * @param {number} tag
- * @return {!WireType}
- */
- function tagToWireType(tag) {
- return /** @type {!WireType} */ (tag & 0x07);
- }
- /**
- * Returns the field number stored in a tag.
- * Protos store the field number in the upper 29 bits of a 32 bit number.
- * @param {number} tag
- * @return {number}
- */
- function tagToFieldNumber(tag) {
- return tag >>> 3;
- }
- /**
- * Creates an index of field locations in a given binary protobuf.
- * @param {!BufferDecoder} bufferDecoder
- * @param {number|undefined} pivot
- * @return {!Storage<!Field>}
- * @package
- */
- function buildIndex(bufferDecoder, pivot) {
- bufferDecoder.setCursor(bufferDecoder.startIndex());
- const storage = new Storage(pivot);
- while (bufferDecoder.hasNext()) {
- const tag = bufferDecoder.getUnsignedVarint32();
- const wireType = tagToWireType(tag);
- const fieldNumber = tagToFieldNumber(tag);
- checkCriticalState(fieldNumber > 0, `Invalid field number ${fieldNumber}`);
- addIndexEntry(storage, fieldNumber, wireType, bufferDecoder.cursor());
- checkCriticalState(
- !skipField_(bufferDecoder, wireType, fieldNumber),
- 'Found unmatched stop group.');
- }
- return storage;
- }
- /**
- * Skips over fields until the next field of the message.
- * @param {!BufferDecoder} bufferDecoder
- * @param {!WireType} wireType
- * @param {number} fieldNumber
- * @return {boolean} Whether the field we skipped over was a stop group.
- * @private
- */
- function skipField_(bufferDecoder, wireType, fieldNumber) {
- switch (wireType) {
- case WireType.VARINT:
- checkCriticalElementIndex(
- bufferDecoder.cursor(), bufferDecoder.endIndex());
- bufferDecoder.skipVarint(bufferDecoder.cursor());
- return false;
- case WireType.FIXED64:
- bufferDecoder.skip(8);
- return false;
- case WireType.DELIMITED:
- checkCriticalElementIndex(
- bufferDecoder.cursor(), bufferDecoder.endIndex());
- const length = bufferDecoder.getUnsignedVarint32();
- bufferDecoder.skip(length);
- return false;
- case WireType.START_GROUP:
- checkCriticalState(
- skipGroup_(bufferDecoder, fieldNumber), 'No end group found.');
- return false;
- case WireType.END_GROUP:
- // Signal that we found a stop group to the caller
- return true;
- case WireType.FIXED32:
- bufferDecoder.skip(4);
- return false;
- default:
- throw new Error(`Invalid wire type: ${wireType}`);
- }
- }
- /**
- * Skips over fields until it finds the end of a given group.
- * @param {!BufferDecoder} bufferDecoder
- * @param {number} groupFieldNumber
- * @return {boolean} Returns true if an end was found.
- * @private
- */
- function skipGroup_(bufferDecoder, groupFieldNumber) {
- // On a start group we need to keep skipping fields until we find a
- // corresponding stop group
- // Note: Since we are calling skipField from here nested groups will be
- // handled by recursion of this method and thus we will not see a nested
- // STOP GROUP here unless there is something wrong with the input data.
- while (bufferDecoder.hasNext()) {
- const tag = bufferDecoder.getUnsignedVarint32();
- const wireType = tagToWireType(tag);
- const fieldNumber = tagToFieldNumber(tag);
- if (skipField_(bufferDecoder, wireType, fieldNumber)) {
- checkCriticalState(
- groupFieldNumber === fieldNumber,
- `Expected stop group for fieldnumber ${groupFieldNumber} not found.`);
- return true;
- }
- }
- return false;
- }
- exports = {
- buildIndex,
- };
|