/// <summary> /// Tries to merge a field from the coded input, returning true if the field was merged. /// If the set is null or the field was not otherwise merged, this returns false. /// </summary> public static bool TryMergeFieldFrom <TTarget>(ref ExtensionSet <TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage <TTarget> { Extension extension; int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag); IExtensionValue extensionValue; if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue)) { extensionValue.MergeFrom(ref ctx); return(true); } else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension)) { IExtensionValue value = extension.CreateValue(); value.MergeFrom(ref ctx); set = (set ?? new ExtensionSet <TTarget>()); set.ValuesByNumber.Add(extension.FieldNumber, value); return(true); } else { return(false); } }
/// <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> /// Parse a single field from <paramref name="ctx"/> and merge it /// into this set. /// </summary> /// <param name="ctx">The parse context from which to read the field</param> /// <returns>false if the tag is an "end group" tag, true otherwise</returns> private bool MergeFieldFrom(ref ParseContext ctx) { uint tag = ctx.LastTag; int number = WireFormat.GetTagFieldNumber(tag); switch (WireFormat.GetTagWireType(tag)) { case WireFormat.WireType.Varint: { ulong uint64 = ctx.ReadUInt64(); GetOrAddField(number).AddVarint(uint64); return(true); } case WireFormat.WireType.Fixed32: { uint uint32 = ctx.ReadFixed32(); GetOrAddField(number).AddFixed32(uint32); return(true); } case WireFormat.WireType.Fixed64: { ulong uint64 = ctx.ReadFixed64(); GetOrAddField(number).AddFixed64(uint64); return(true); } case WireFormat.WireType.LengthDelimited: { ByteString bytes = ctx.ReadBytes(); GetOrAddField(number).AddLengthDelimited(bytes); return(true); } case WireFormat.WireType.StartGroup: { UnknownFieldSet set = new UnknownFieldSet(); ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set); GetOrAddField(number).AddGroup(set); return(true); } case WireFormat.WireType.EndGroup: { return(false); } default: throw InvalidProtocolBufferException.InvalidWireType(); } }
public static void ReadGroup(ref ParseContext ctx, IMessage message) { if (ctx.state.recursionDepth >= ctx.state.recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); } ++ctx.state.recursionDepth; uint tag = ctx.state.lastTag; int fieldNumber = WireFormat.GetTagFieldNumber(tag); ReadRawMessage(ref ctx, message); CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); --ctx.state.recursionDepth; }
/// <summary> /// Skip a group. /// </summary> public static void SkipGroup(ref ReadOnlySpan <byte> buffer, ref ParserInternalState state, uint startGroupTag) { // Note: Currently we expect this to be the way that groups are read. We could put the recursion // depth changes into the ReadTag method instead, potentially... state.recursionDepth++; if (state.recursionDepth >= state.recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); } uint tag; while (true) { tag = ParsingPrimitives.ParseTag(ref buffer, ref state); if (tag == 0) { throw InvalidProtocolBufferException.TruncatedMessage(); } // Can't call SkipLastField for this case- that would throw. if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup) { break; } // This recursion will allow us to handle nested groups. SkipLastField(ref buffer, ref state); } int startField = WireFormat.GetTagFieldNumber(startGroupTag); int endField = WireFormat.GetTagFieldNumber(tag); if (startField != endField) { throw new InvalidProtocolBufferException( $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}"); } state.recursionDepth--; }
internal bool ContainsInputField(uint lastTag, Type target, out Extension extension) { return(extensions.TryGetValue(new ObjectIntPair <Type>(target, WireFormat.GetTagFieldNumber(lastTag)), out extension)); }