|
|
|
@ -47,118 +47,96 @@ function tagToFieldNumber(tag) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* An Indexer that indexes a given binary protobuf by fieldnumber. |
|
|
|
|
* Creates an index of field locations in a given binary protobuf. |
|
|
|
|
* @param {!BufferDecoder} bufferDecoder |
|
|
|
|
* @param {number|undefined} pivot |
|
|
|
|
* @return {!Storage<!Field>} |
|
|
|
|
* @package |
|
|
|
|
*/ |
|
|
|
|
class Indexer { |
|
|
|
|
/** |
|
|
|
|
* @param {!BufferDecoder} bufferDecoder |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
constructor(bufferDecoder) { |
|
|
|
|
/** @private @const {!BufferDecoder} */ |
|
|
|
|
this.bufferDecoder_ = bufferDecoder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param {number|undefined} pivot |
|
|
|
|
* @return {!Storage<!Field>} |
|
|
|
|
*/ |
|
|
|
|
index(pivot) { |
|
|
|
|
this.bufferDecoder_.setCursor(this.bufferDecoder_.startIndex()); |
|
|
|
|
|
|
|
|
|
const storage = new Storage(pivot); |
|
|
|
|
while (this.bufferDecoder_.hasNext()) { |
|
|
|
|
const tag = this.bufferDecoder_.getUnsignedVarint32(); |
|
|
|
|
const wireType = tagToWireType(tag); |
|
|
|
|
const fieldNumber = tagToFieldNumber(tag); |
|
|
|
|
checkCriticalState( |
|
|
|
|
fieldNumber > 0, `Invalid field number ${fieldNumber}`); |
|
|
|
|
function buildIndex(bufferDecoder, pivot) { |
|
|
|
|
bufferDecoder.setCursor(bufferDecoder.startIndex()); |
|
|
|
|
|
|
|
|
|
addIndexEntry( |
|
|
|
|
storage, fieldNumber, wireType, this.bufferDecoder_.cursor()); |
|
|
|
|
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}`); |
|
|
|
|
|
|
|
|
|
checkCriticalState( |
|
|
|
|
!this.skipField_(wireType, fieldNumber), |
|
|
|
|
'Found unmatched stop group.'); |
|
|
|
|
} |
|
|
|
|
return storage; |
|
|
|
|
} |
|
|
|
|
addIndexEntry(storage, fieldNumber, wireType, bufferDecoder.cursor()); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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: |
|
|
|
|
checkCriticalElementIndex( |
|
|
|
|
this.bufferDecoder_.cursor(), this.bufferDecoder_.endIndex()); |
|
|
|
|
this.bufferDecoder_.skipVarint(this.bufferDecoder_.cursor()); |
|
|
|
|
return false; |
|
|
|
|
case WireType.FIXED64: |
|
|
|
|
this.bufferDecoder_.skip(8); |
|
|
|
|
return false; |
|
|
|
|
case WireType.DELIMITED: |
|
|
|
|
checkCriticalElementIndex( |
|
|
|
|
this.bufferDecoder_.cursor(), this.bufferDecoder_.endIndex()); |
|
|
|
|
const length = this.bufferDecoder_.getUnsignedVarint32(); |
|
|
|
|
this.bufferDecoder_.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.bufferDecoder_.skip(4); |
|
|
|
|
return false; |
|
|
|
|
default: |
|
|
|
|
throw new Error(`Invalid wire type: ${wireType}`); |
|
|
|
|
} |
|
|
|
|
checkCriticalState( |
|
|
|
|
!skipField_(bufferDecoder, wireType, fieldNumber), |
|
|
|
|
'Found unmatched stop group.'); |
|
|
|
|
} |
|
|
|
|
return storage; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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.bufferDecoder_.hasNext()) { |
|
|
|
|
const tag = this.bufferDecoder_.getUnsignedVarint32(); |
|
|
|
|
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; |
|
|
|
|
/** |
|
|
|
|
* 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}`); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates an index of field locations in a given binary protobuf. |
|
|
|
|
* Skips over fields until it finds the end of a given group. |
|
|
|
|
* @param {!BufferDecoder} bufferDecoder |
|
|
|
|
* @param {number|undefined} pivot |
|
|
|
|
* @return {!Storage<!Field>} |
|
|
|
|
* @package |
|
|
|
|
* @param {number} groupFieldNumber |
|
|
|
|
* @return {boolean} Returns true if an end was found. |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
function buildIndex(bufferDecoder, pivot) { |
|
|
|
|
return new Indexer(bufferDecoder).index(pivot); |
|
|
|
|
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 = { |
|
|
|
|