• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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