1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2008 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/ 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using System; 34*1b3f573fSAndroid Build Coastguard Worker using System.Buffers; 35*1b3f573fSAndroid Build Coastguard Worker using System.Collections.Generic; 36*1b3f573fSAndroid Build Coastguard Worker using System.IO; 37*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.CompilerServices; 38*1b3f573fSAndroid Build Coastguard Worker using System.Security; 39*1b3f573fSAndroid Build Coastguard Worker using Google.Protobuf.Collections; 40*1b3f573fSAndroid Build Coastguard Worker 41*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf 42*1b3f573fSAndroid Build Coastguard Worker { 43*1b3f573fSAndroid Build Coastguard Worker /// <summary> 44*1b3f573fSAndroid Build Coastguard Worker /// Reading and skipping messages / groups 45*1b3f573fSAndroid Build Coastguard Worker /// </summary> 46*1b3f573fSAndroid Build Coastguard Worker [SecuritySafeCritical] 47*1b3f573fSAndroid Build Coastguard Worker internal static class ParsingPrimitivesMessages 48*1b3f573fSAndroid Build Coastguard Worker { 49*1b3f573fSAndroid Build Coastguard Worker private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 }; 50*1b3f573fSAndroid Build Coastguard Worker SkipLastField(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)51*1b3f573fSAndroid Build Coastguard Worker public static void SkipLastField(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 52*1b3f573fSAndroid Build Coastguard Worker { 53*1b3f573fSAndroid Build Coastguard Worker if (state.lastTag == 0) 54*1b3f573fSAndroid Build Coastguard Worker { 55*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream"); 56*1b3f573fSAndroid Build Coastguard Worker } 57*1b3f573fSAndroid Build Coastguard Worker switch (WireFormat.GetTagWireType(state.lastTag)) 58*1b3f573fSAndroid Build Coastguard Worker { 59*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.StartGroup: 60*1b3f573fSAndroid Build Coastguard Worker SkipGroup(ref buffer, ref state, state.lastTag); 61*1b3f573fSAndroid Build Coastguard Worker break; 62*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.EndGroup: 63*1b3f573fSAndroid Build Coastguard Worker throw new InvalidProtocolBufferException( 64*1b3f573fSAndroid Build Coastguard Worker "SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing"); 65*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.Fixed32: 66*1b3f573fSAndroid Build Coastguard Worker ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 67*1b3f573fSAndroid Build Coastguard Worker break; 68*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.Fixed64: 69*1b3f573fSAndroid Build Coastguard Worker ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 70*1b3f573fSAndroid Build Coastguard Worker break; 71*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.LengthDelimited: 72*1b3f573fSAndroid Build Coastguard Worker var length = ParsingPrimitives.ParseLength(ref buffer, ref state); 73*1b3f573fSAndroid Build Coastguard Worker ParsingPrimitives.SkipRawBytes(ref buffer, ref state, length); 74*1b3f573fSAndroid Build Coastguard Worker break; 75*1b3f573fSAndroid Build Coastguard Worker case WireFormat.WireType.Varint: 76*1b3f573fSAndroid Build Coastguard Worker ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 77*1b3f573fSAndroid Build Coastguard Worker break; 78*1b3f573fSAndroid Build Coastguard Worker } 79*1b3f573fSAndroid Build Coastguard Worker } 80*1b3f573fSAndroid Build Coastguard Worker 81*1b3f573fSAndroid Build Coastguard Worker /// <summary> 82*1b3f573fSAndroid Build Coastguard Worker /// Skip a group. 83*1b3f573fSAndroid Build Coastguard Worker /// </summary> SkipGroup(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint startGroupTag)84*1b3f573fSAndroid Build Coastguard Worker public static void SkipGroup(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint startGroupTag) 85*1b3f573fSAndroid Build Coastguard Worker { 86*1b3f573fSAndroid Build Coastguard Worker // Note: Currently we expect this to be the way that groups are read. We could put the recursion 87*1b3f573fSAndroid Build Coastguard Worker // depth changes into the ReadTag method instead, potentially... 88*1b3f573fSAndroid Build Coastguard Worker state.recursionDepth++; 89*1b3f573fSAndroid Build Coastguard Worker if (state.recursionDepth >= state.recursionLimit) 90*1b3f573fSAndroid Build Coastguard Worker { 91*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.RecursionLimitExceeded(); 92*1b3f573fSAndroid Build Coastguard Worker } 93*1b3f573fSAndroid Build Coastguard Worker uint tag; 94*1b3f573fSAndroid Build Coastguard Worker while (true) 95*1b3f573fSAndroid Build Coastguard Worker { 96*1b3f573fSAndroid Build Coastguard Worker tag = ParsingPrimitives.ParseTag(ref buffer, ref state); 97*1b3f573fSAndroid Build Coastguard Worker if (tag == 0) 98*1b3f573fSAndroid Build Coastguard Worker { 99*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.TruncatedMessage(); 100*1b3f573fSAndroid Build Coastguard Worker } 101*1b3f573fSAndroid Build Coastguard Worker // Can't call SkipLastField for this case- that would throw. 102*1b3f573fSAndroid Build Coastguard Worker if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup) 103*1b3f573fSAndroid Build Coastguard Worker { 104*1b3f573fSAndroid Build Coastguard Worker break; 105*1b3f573fSAndroid Build Coastguard Worker } 106*1b3f573fSAndroid Build Coastguard Worker // This recursion will allow us to handle nested groups. 107*1b3f573fSAndroid Build Coastguard Worker SkipLastField(ref buffer, ref state); 108*1b3f573fSAndroid Build Coastguard Worker } 109*1b3f573fSAndroid Build Coastguard Worker int startField = WireFormat.GetTagFieldNumber(startGroupTag); 110*1b3f573fSAndroid Build Coastguard Worker int endField = WireFormat.GetTagFieldNumber(tag); 111*1b3f573fSAndroid Build Coastguard Worker if (startField != endField) 112*1b3f573fSAndroid Build Coastguard Worker { 113*1b3f573fSAndroid Build Coastguard Worker throw new InvalidProtocolBufferException( 114*1b3f573fSAndroid Build Coastguard Worker $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}"); 115*1b3f573fSAndroid Build Coastguard Worker } 116*1b3f573fSAndroid Build Coastguard Worker state.recursionDepth--; 117*1b3f573fSAndroid Build Coastguard Worker } 118*1b3f573fSAndroid Build Coastguard Worker ReadMessage(ref ParseContext ctx, IMessage message)119*1b3f573fSAndroid Build Coastguard Worker public static void ReadMessage(ref ParseContext ctx, IMessage message) 120*1b3f573fSAndroid Build Coastguard Worker { 121*1b3f573fSAndroid Build Coastguard Worker int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state); 122*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 123*1b3f573fSAndroid Build Coastguard Worker { 124*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.RecursionLimitExceeded(); 125*1b3f573fSAndroid Build Coastguard Worker } 126*1b3f573fSAndroid Build Coastguard Worker int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length); 127*1b3f573fSAndroid Build Coastguard Worker ++ctx.state.recursionDepth; 128*1b3f573fSAndroid Build Coastguard Worker 129*1b3f573fSAndroid Build Coastguard Worker ReadRawMessage(ref ctx, message); 130*1b3f573fSAndroid Build Coastguard Worker 131*1b3f573fSAndroid Build Coastguard Worker CheckReadEndOfStreamTag(ref ctx.state); 132*1b3f573fSAndroid Build Coastguard Worker // Check that we've read exactly as much data as expected. 133*1b3f573fSAndroid Build Coastguard Worker if (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state)) 134*1b3f573fSAndroid Build Coastguard Worker { 135*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.TruncatedMessage(); 136*1b3f573fSAndroid Build Coastguard Worker } 137*1b3f573fSAndroid Build Coastguard Worker --ctx.state.recursionDepth; 138*1b3f573fSAndroid Build Coastguard Worker SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit); 139*1b3f573fSAndroid Build Coastguard Worker } 140*1b3f573fSAndroid Build Coastguard Worker ReadMapEntry(ref ParseContext ctx, MapField<TKey, TValue>.Codec codec)141*1b3f573fSAndroid Build Coastguard Worker public static KeyValuePair<TKey, TValue> ReadMapEntry<TKey, TValue>(ref ParseContext ctx, MapField<TKey, TValue>.Codec codec) 142*1b3f573fSAndroid Build Coastguard Worker { 143*1b3f573fSAndroid Build Coastguard Worker int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state); 144*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 145*1b3f573fSAndroid Build Coastguard Worker { 146*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.RecursionLimitExceeded(); 147*1b3f573fSAndroid Build Coastguard Worker } 148*1b3f573fSAndroid Build Coastguard Worker int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length); 149*1b3f573fSAndroid Build Coastguard Worker ++ctx.state.recursionDepth; 150*1b3f573fSAndroid Build Coastguard Worker 151*1b3f573fSAndroid Build Coastguard Worker TKey key = codec.KeyCodec.DefaultValue; 152*1b3f573fSAndroid Build Coastguard Worker TValue value = codec.ValueCodec.DefaultValue; 153*1b3f573fSAndroid Build Coastguard Worker 154*1b3f573fSAndroid Build Coastguard Worker uint tag; 155*1b3f573fSAndroid Build Coastguard Worker while ((tag = ctx.ReadTag()) != 0) 156*1b3f573fSAndroid Build Coastguard Worker { 157*1b3f573fSAndroid Build Coastguard Worker if (tag == codec.KeyCodec.Tag) 158*1b3f573fSAndroid Build Coastguard Worker { 159*1b3f573fSAndroid Build Coastguard Worker key = codec.KeyCodec.Read(ref ctx); 160*1b3f573fSAndroid Build Coastguard Worker } 161*1b3f573fSAndroid Build Coastguard Worker else if (tag == codec.ValueCodec.Tag) 162*1b3f573fSAndroid Build Coastguard Worker { 163*1b3f573fSAndroid Build Coastguard Worker value = codec.ValueCodec.Read(ref ctx); 164*1b3f573fSAndroid Build Coastguard Worker } 165*1b3f573fSAndroid Build Coastguard Worker else 166*1b3f573fSAndroid Build Coastguard Worker { 167*1b3f573fSAndroid Build Coastguard Worker SkipLastField(ref ctx.buffer, ref ctx.state); 168*1b3f573fSAndroid Build Coastguard Worker } 169*1b3f573fSAndroid Build Coastguard Worker } 170*1b3f573fSAndroid Build Coastguard Worker 171*1b3f573fSAndroid Build Coastguard Worker // Corner case: a map entry with a key but no value, where the value type is a message. 172*1b3f573fSAndroid Build Coastguard Worker // Read it as if we'd seen input with no data (i.e. create a "default" message). 173*1b3f573fSAndroid Build Coastguard Worker if (value == null) 174*1b3f573fSAndroid Build Coastguard Worker { 175*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.CodedInputStream != null) 176*1b3f573fSAndroid Build Coastguard Worker { 177*1b3f573fSAndroid Build Coastguard Worker // the decoded message might not support parsing from ParseContext, so 178*1b3f573fSAndroid Build Coastguard Worker // we need to allow fallback to the legacy MergeFrom(CodedInputStream) parsing. 179*1b3f573fSAndroid Build Coastguard Worker value = codec.ValueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData)); 180*1b3f573fSAndroid Build Coastguard Worker } 181*1b3f573fSAndroid Build Coastguard Worker else 182*1b3f573fSAndroid Build Coastguard Worker { 183*1b3f573fSAndroid Build Coastguard Worker ParseContext.Initialize(new ReadOnlySequence<byte>(ZeroLengthMessageStreamData), out ParseContext zeroLengthCtx); 184*1b3f573fSAndroid Build Coastguard Worker value = codec.ValueCodec.Read(ref zeroLengthCtx); 185*1b3f573fSAndroid Build Coastguard Worker } 186*1b3f573fSAndroid Build Coastguard Worker } 187*1b3f573fSAndroid Build Coastguard Worker 188*1b3f573fSAndroid Build Coastguard Worker CheckReadEndOfStreamTag(ref ctx.state); 189*1b3f573fSAndroid Build Coastguard Worker // Check that we've read exactly as much data as expected. 190*1b3f573fSAndroid Build Coastguard Worker if (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state)) 191*1b3f573fSAndroid Build Coastguard Worker { 192*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.TruncatedMessage(); 193*1b3f573fSAndroid Build Coastguard Worker } 194*1b3f573fSAndroid Build Coastguard Worker --ctx.state.recursionDepth; 195*1b3f573fSAndroid Build Coastguard Worker SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit); 196*1b3f573fSAndroid Build Coastguard Worker 197*1b3f573fSAndroid Build Coastguard Worker return new KeyValuePair<TKey, TValue>(key, value); 198*1b3f573fSAndroid Build Coastguard Worker } 199*1b3f573fSAndroid Build Coastguard Worker ReadGroup(ref ParseContext ctx, IMessage message)200*1b3f573fSAndroid Build Coastguard Worker public static void ReadGroup(ref ParseContext ctx, IMessage message) 201*1b3f573fSAndroid Build Coastguard Worker { 202*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 203*1b3f573fSAndroid Build Coastguard Worker { 204*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.RecursionLimitExceeded(); 205*1b3f573fSAndroid Build Coastguard Worker } 206*1b3f573fSAndroid Build Coastguard Worker ++ctx.state.recursionDepth; 207*1b3f573fSAndroid Build Coastguard Worker 208*1b3f573fSAndroid Build Coastguard Worker uint tag = ctx.state.lastTag; 209*1b3f573fSAndroid Build Coastguard Worker int fieldNumber = WireFormat.GetTagFieldNumber(tag); 210*1b3f573fSAndroid Build Coastguard Worker ReadRawMessage(ref ctx, message); 211*1b3f573fSAndroid Build Coastguard Worker CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); 212*1b3f573fSAndroid Build Coastguard Worker 213*1b3f573fSAndroid Build Coastguard Worker --ctx.state.recursionDepth; 214*1b3f573fSAndroid Build Coastguard Worker } 215*1b3f573fSAndroid Build Coastguard Worker ReadGroup(ref ParseContext ctx, int fieldNumber, UnknownFieldSet set)216*1b3f573fSAndroid Build Coastguard Worker public static void ReadGroup(ref ParseContext ctx, int fieldNumber, UnknownFieldSet set) 217*1b3f573fSAndroid Build Coastguard Worker { 218*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 219*1b3f573fSAndroid Build Coastguard Worker { 220*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.RecursionLimitExceeded(); 221*1b3f573fSAndroid Build Coastguard Worker } 222*1b3f573fSAndroid Build Coastguard Worker ++ctx.state.recursionDepth; 223*1b3f573fSAndroid Build Coastguard Worker 224*1b3f573fSAndroid Build Coastguard Worker set.MergeGroupFrom(ref ctx); 225*1b3f573fSAndroid Build Coastguard Worker CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); 226*1b3f573fSAndroid Build Coastguard Worker 227*1b3f573fSAndroid Build Coastguard Worker --ctx.state.recursionDepth; 228*1b3f573fSAndroid Build Coastguard Worker } 229*1b3f573fSAndroid Build Coastguard Worker ReadRawMessage(ref ParseContext ctx, IMessage message)230*1b3f573fSAndroid Build Coastguard Worker public static void ReadRawMessage(ref ParseContext ctx, IMessage message) 231*1b3f573fSAndroid Build Coastguard Worker { 232*1b3f573fSAndroid Build Coastguard Worker if (message is IBufferMessage bufferMessage) 233*1b3f573fSAndroid Build Coastguard Worker { 234*1b3f573fSAndroid Build Coastguard Worker bufferMessage.InternalMergeFrom(ref ctx); 235*1b3f573fSAndroid Build Coastguard Worker } 236*1b3f573fSAndroid Build Coastguard Worker else 237*1b3f573fSAndroid Build Coastguard Worker { 238*1b3f573fSAndroid Build Coastguard Worker // If we reached here, it means we've ran into a nested message with older generated code 239*1b3f573fSAndroid Build Coastguard Worker // which doesn't provide the InternalMergeFrom method that takes a ParseContext. 240*1b3f573fSAndroid Build Coastguard Worker // With a slight performance overhead, we can still parse this message just fine, 241*1b3f573fSAndroid Build Coastguard Worker // but we need to find the original CodedInputStream instance that initiated this 242*1b3f573fSAndroid Build Coastguard Worker // parsing process and make sure its internal state is up to date. 243*1b3f573fSAndroid Build Coastguard Worker // Note that this performance overhead is not very high (basically copying contents of a struct) 244*1b3f573fSAndroid Build Coastguard Worker // and it will only be incurred in case the application mixes older and newer generated code. 245*1b3f573fSAndroid Build Coastguard Worker // Regenerating the code from .proto files will remove this overhead because it will 246*1b3f573fSAndroid Build Coastguard Worker // generate the InternalMergeFrom method we need. 247*1b3f573fSAndroid Build Coastguard Worker 248*1b3f573fSAndroid Build Coastguard Worker if (ctx.state.CodedInputStream == null) 249*1b3f573fSAndroid Build Coastguard Worker { 250*1b3f573fSAndroid Build Coastguard Worker // This can only happen when the parsing started without providing a CodedInputStream instance 251*1b3f573fSAndroid Build Coastguard Worker // (e.g. ParseContext was created directly from a ReadOnlySequence). 252*1b3f573fSAndroid Build Coastguard Worker // That also means that one of the new parsing APIs was used at the top level 253*1b3f573fSAndroid Build Coastguard Worker // and in such case it is reasonable to require that all the nested message provide 254*1b3f573fSAndroid Build Coastguard Worker // up-to-date generated code with ParseContext support (and fail otherwise). 255*1b3f573fSAndroid Build Coastguard Worker throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code."); 256*1b3f573fSAndroid Build Coastguard Worker } 257*1b3f573fSAndroid Build Coastguard Worker 258*1b3f573fSAndroid Build Coastguard Worker ctx.CopyStateTo(ctx.state.CodedInputStream); 259*1b3f573fSAndroid Build Coastguard Worker try 260*1b3f573fSAndroid Build Coastguard Worker { 261*1b3f573fSAndroid Build Coastguard Worker // fallback parse using the CodedInputStream that started current parsing tree 262*1b3f573fSAndroid Build Coastguard Worker message.MergeFrom(ctx.state.CodedInputStream); 263*1b3f573fSAndroid Build Coastguard Worker } 264*1b3f573fSAndroid Build Coastguard Worker finally 265*1b3f573fSAndroid Build Coastguard Worker { 266*1b3f573fSAndroid Build Coastguard Worker ctx.LoadStateFrom(ctx.state.CodedInputStream); 267*1b3f573fSAndroid Build Coastguard Worker } 268*1b3f573fSAndroid Build Coastguard Worker } 269*1b3f573fSAndroid Build Coastguard Worker } 270*1b3f573fSAndroid Build Coastguard Worker 271*1b3f573fSAndroid Build Coastguard Worker /// <summary> 272*1b3f573fSAndroid Build Coastguard Worker /// Verifies that the last call to ReadTag() returned tag 0 - in other words, 273*1b3f573fSAndroid Build Coastguard Worker /// we've reached the end of the stream when we expected to. 274*1b3f573fSAndroid Build Coastguard Worker /// </summary> 275*1b3f573fSAndroid Build Coastguard Worker /// <exception cref="InvalidProtocolBufferException">The 276*1b3f573fSAndroid Build Coastguard Worker /// tag read was not the one specified</exception> CheckReadEndOfStreamTag(ref ParserInternalState state)277*1b3f573fSAndroid Build Coastguard Worker public static void CheckReadEndOfStreamTag(ref ParserInternalState state) 278*1b3f573fSAndroid Build Coastguard Worker { 279*1b3f573fSAndroid Build Coastguard Worker if (state.lastTag != 0) 280*1b3f573fSAndroid Build Coastguard Worker { 281*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.MoreDataAvailable(); 282*1b3f573fSAndroid Build Coastguard Worker } 283*1b3f573fSAndroid Build Coastguard Worker } 284*1b3f573fSAndroid Build Coastguard Worker CheckLastTagWas(ref ParserInternalState state, uint expectedTag)285*1b3f573fSAndroid Build Coastguard Worker private static void CheckLastTagWas(ref ParserInternalState state, uint expectedTag) 286*1b3f573fSAndroid Build Coastguard Worker { 287*1b3f573fSAndroid Build Coastguard Worker if (state.lastTag != expectedTag) { 288*1b3f573fSAndroid Build Coastguard Worker throw InvalidProtocolBufferException.InvalidEndTag(); 289*1b3f573fSAndroid Build Coastguard Worker } 290*1b3f573fSAndroid Build Coastguard Worker } 291*1b3f573fSAndroid Build Coastguard Worker } 292*1b3f573fSAndroid Build Coastguard Worker }