/// <summary> /// Reads a field tag, returning the tag of 0 for "end of stream". /// </summary> /// <remarks> /// If this method returns 0, it doesn't necessarily mean the end of all /// the data in this CodedInputStream; it may be the end of the logical stream /// for an embedded message, for example. /// </remarks> /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns> public uint ReadTag() { if (hasNextTag) { lastTag = nextTag; hasNextTag = false; return(lastTag); } // Optimize for the incredibly common case of having at least two bytes left in the buffer, // and those two bytes being enough to get the tag. This will be true for fields up to 4095. if (bufferPos + 2 <= bufferSize) { int tmp = buffer[bufferPos++]; if (tmp < 128) { lastTag = (uint)tmp; } else { int result = tmp & 0x7f; if ((tmp = buffer[bufferPos++]) < 128) { result |= tmp << 7; lastTag = (uint)result; } else { // Nope, rewind and go the potentially slow route. bufferPos -= 2; lastTag = ReadRawVarint32(); } } } else { if (IsAtEnd) { lastTag = 0; return(0); } lastTag = ReadRawVarint32(); } if (WireFormat.GetTagFieldNumber(lastTag) == 0) { // If we actually read a tag with a field of 0, that's not a valid tag. throw InvalidProtocolBufferException.InvalidTag(); } if (ReachedLimit) { return(0); } return(lastTag); }
/// <summary> /// Parses the next tag. /// If the end of logical stream was reached, an invalid tag of 0 is returned. /// </summary> public static uint ParseTag(ref ReadOnlySpan <byte> buffer, ref ParserInternalState state) { // The "nextTag" logic is there only as an optimization for reading non-packed repeated / map // fields and is strictly speaking not necessary. // TODO(jtattermusch): look into simplifying the ParseTag logic. if (state.hasNextTag) { state.lastTag = state.nextTag; state.hasNextTag = false; return(state.lastTag); } // Optimize for the incredibly common case of having at least two bytes left in the buffer, // and those two bytes being enough to get the tag. This will be true for fields up to 4095. if (state.bufferPos + 2 <= state.bufferSize) { int tmp = buffer[state.bufferPos++]; if (tmp < 128) { state.lastTag = (uint)tmp; } else { int result = tmp & 0x7f; if ((tmp = buffer[state.bufferPos++]) < 128) { result |= tmp << 7; state.lastTag = (uint)result; } else { // Nope, rewind and go the potentially slow route. state.bufferPos -= 2; state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); } } } else { if (SegmentedBufferHelper.IsAtEnd(ref buffer, ref state)) { state.lastTag = 0; return(0); } state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); } if (WireFormat.GetTagFieldNumber(state.lastTag) == 0) { // If we actually read a tag with a field of 0, that's not a valid tag. throw InvalidProtocolBufferException.InvalidTag(); } return(state.lastTag); }
/// <summary> /// Attempts to read a field tag, returning false if we have reached the end /// of the input data. /// </summary> /// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param> /// <returns>true if the next fieldTag was read</returns> public bool ReadTag(out uint fieldTag) { if (hasNextTag) { fieldTag = nextTag; lastTag = fieldTag; hasNextTag = false; return(true); } // Optimize for the incredibly common case of having at least two bytes left in the buffer, // and those two bytes being enough to get the tag. This will be true for fields up to 4095. if (bufferPos + 2 <= bufferSize) { int tmp = buffer[bufferPos++]; if (tmp < 128) { fieldTag = (uint)tmp; } else { int result = tmp & 0x7f; if ((tmp = buffer[bufferPos++]) < 128) { result |= tmp << 7; fieldTag = (uint)result; } else { // Nope, rewind and go the potentially slow route. bufferPos -= 2; fieldTag = ReadRawVarint32(); } } } else { if (IsAtEnd) { fieldTag = 0; lastTag = fieldTag; return(false); } fieldTag = ReadRawVarint32(); } lastTag = fieldTag; if (lastTag == 0) { // If we actually read zero, that's not a valid tag. throw InvalidProtocolBufferException.InvalidTag(); } return(true); }