Protocol Buffers - Google's data interchange format (grpc依赖) https://developers.google.com/protocol-buffers/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

192 lines
5.3 KiB

/**
* @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 {checkCriticalPositionIndex, 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;
}
/**
* An Indexer that indexes a given binary protobuf by fieldnumber.
*/
class Indexer {
/**
* @param {!BufferDecoder} bufferDecoder
* @private
*/
constructor(bufferDecoder) {
/** @private @const {!BufferDecoder} */
this.bufferDecoder_ = bufferDecoder;
/** @private {number} */
this.cursor_ = bufferDecoder.startIndex();
}
/**
* @param {number|undefined} pivot
* @return {!Storage<!Field>}
*/
index(pivot) {
const storage = new Storage(pivot);
while (this.hasNextByte_()) {
const tag = this.readVarInt32_();
const wireType = tagToWireType(tag);
const fieldNumber = tagToFieldNumber(tag);
checkCriticalState(
fieldNumber > 0, `Invalid field number ${fieldNumber}`);
addIndexEntry(storage, fieldNumber, wireType, this.cursor_);
checkCriticalState(
!this.skipField_(wireType, fieldNumber),
'Found unmatched stop group.');
}
return storage;
}
/**
* Skips over fields until the next field of the message.
* @param {!WireType} wireType
* @param {number} fieldNumber
* @return {boolean} Whether the field we skipped over was a stop group.
* @private
*/
skipField_(wireType, fieldNumber) {
switch (wireType) {
case WireType.VARINT:
this.cursor_ = this.bufferDecoder_.skipVarint(this.cursor_);
return false;
case WireType.FIXED64:
this.skip_(8);
return false;
case WireType.DELIMITED:
const length = this.readVarInt32_();
this.skip_(length);
return false;
case WireType.START_GROUP:
checkCriticalState(this.skipGroup_(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:
this.skip_(4);
return false;
default:
throw new Error(`Invalid wire type: ${wireType}`);
}
}
/**
* Seeks forward by the given amount.
* @param {number} skipAmount
* @private
*/
skip_(skipAmount) {
this.cursor_ += skipAmount;
checkCriticalPositionIndex(this.cursor_, this.bufferDecoder_.endIndex());
}
/**
* Skips over fields until it finds the end of a given group.
* @param {number} groupFieldNumber
* @return {boolean} Returns true if an end was found.
* @private
*/
skipGroup_(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 (this.hasNextByte_()) {
const tag = this.readVarInt32_();
const wireType = tagToWireType(tag);
const fieldNumber = tagToFieldNumber(tag);
if (this.skipField_(wireType, fieldNumber)) {
checkCriticalState(
groupFieldNumber === fieldNumber,
`Expected stop group for fieldnumber ${
groupFieldNumber} not found.`);
return true;
}
}
return false;
}
/**
* Returns a JS number for a 32 bit var int.
* @return {number}
* @private
*/
readVarInt32_() {
const {lowBits, dataStart} = this.bufferDecoder_.getVarint(this.cursor_);
this.cursor_ = dataStart;
return lowBits;
}
/**
* Returns true if there are more bytes to read in the array.
* @return {boolean}
* @private
*/
hasNextByte_() {
return this.cursor_ < this.bufferDecoder_.endIndex();
}
}
/**
* 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) {
return new Indexer(bufferDecoder).index(pivot);
}
exports = {
buildIndex,
};