1goog.module('protobuf.binary.tag'); 2 3const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); 4const WireType = goog.require('protobuf.binary.WireType'); 5const {checkCriticalElementIndex, checkCriticalState} = goog.require('protobuf.internal.checks'); 6 7/** 8 * Returns wire type stored in a tag. 9 * Protos store the wire type as the first 3 bit of a tag. 10 * @param {number} tag 11 * @return {!WireType} 12 */ 13function tagToWireType(tag) { 14 return /** @type {!WireType} */ (tag & 0x07); 15} 16 17/** 18 * Returns the field number stored in a tag. 19 * Protos store the field number in the upper 29 bits of a 32 bit number. 20 * @param {number} tag 21 * @return {number} 22 */ 23function tagToFieldNumber(tag) { 24 return tag >>> 3; 25} 26 27/** 28 * Combines wireType and fieldNumber into a tag. 29 * @param {!WireType} wireType 30 * @param {number} fieldNumber 31 * @return {number} 32 */ 33function createTag(wireType, fieldNumber) { 34 return (fieldNumber << 3 | wireType) >>> 0; 35} 36 37/** 38 * Returns the length, in bytes, of the field in the tag stream, less the tag 39 * itself. 40 * Note: This moves the cursor in the bufferDecoder. 41 * @param {!BufferDecoder} bufferDecoder 42 * @param {number} start 43 * @param {!WireType} wireType 44 * @param {number} fieldNumber 45 * @return {number} 46 * @private 47 */ 48function getTagLength(bufferDecoder, start, wireType, fieldNumber) { 49 bufferDecoder.setCursor(start); 50 skipField(bufferDecoder, wireType, fieldNumber); 51 return bufferDecoder.cursor() - start; 52} 53 54/** 55 * @param {number} value 56 * @return {number} 57 */ 58function get32BitVarintLength(value) { 59 if (value < 0) { 60 return 5; 61 } 62 let size = 1; 63 while (value >= 128) { 64 size++; 65 value >>>= 7; 66 } 67 return size; 68} 69 70/** 71 * Skips over a field. 72 * Note: If the field is a start group the entire group will be skipped, placing 73 * the cursor onto the next field. 74 * @param {!BufferDecoder} bufferDecoder 75 * @param {!WireType} wireType 76 * @param {number} fieldNumber 77 */ 78function skipField(bufferDecoder, wireType, fieldNumber) { 79 switch (wireType) { 80 case WireType.VARINT: 81 checkCriticalElementIndex( 82 bufferDecoder.cursor(), bufferDecoder.endIndex()); 83 bufferDecoder.skipVarint(); 84 return; 85 case WireType.FIXED64: 86 bufferDecoder.skip(8); 87 return; 88 case WireType.DELIMITED: 89 checkCriticalElementIndex( 90 bufferDecoder.cursor(), bufferDecoder.endIndex()); 91 const length = bufferDecoder.getUnsignedVarint32(); 92 bufferDecoder.skip(length); 93 return; 94 case WireType.START_GROUP: 95 const foundGroup = skipGroup_(bufferDecoder, fieldNumber); 96 checkCriticalState(foundGroup, 'No end group found.'); 97 return; 98 case WireType.FIXED32: 99 bufferDecoder.skip(4); 100 return; 101 default: 102 throw new Error(`Unexpected wire type: ${wireType}`); 103 } 104} 105 106/** 107 * Skips over fields until it finds the end of a given group consuming the stop 108 * group tag. 109 * @param {!BufferDecoder} bufferDecoder 110 * @param {number} groupFieldNumber 111 * @return {boolean} Whether the end group tag was found. 112 * @private 113 */ 114function skipGroup_(bufferDecoder, groupFieldNumber) { 115 // On a start group we need to keep skipping fields until we find a 116 // corresponding stop group 117 // Note: Since we are calling skipField from here nested groups will be 118 // handled by recursion of this method and thus we will not see a nested 119 // STOP GROUP here unless there is something wrong with the input data. 120 while (bufferDecoder.hasNext()) { 121 const tag = bufferDecoder.getUnsignedVarint32(); 122 const wireType = tagToWireType(tag); 123 const fieldNumber = tagToFieldNumber(tag); 124 125 if (wireType === WireType.END_GROUP) { 126 checkCriticalState( 127 groupFieldNumber === fieldNumber, 128 `Expected stop group for fieldnumber ${groupFieldNumber} not found.`); 129 return true; 130 } else { 131 skipField(bufferDecoder, wireType, fieldNumber); 132 } 133 } 134 return false; 135} 136 137exports = { 138 createTag, 139 get32BitVarintLength, 140 getTagLength, 141 skipField, 142 tagToWireType, 143 tagToFieldNumber, 144}; 145