1#region Copyright notice and license 2// Protocol Buffers - Google's data interchange format 3// Copyright 2008 Google Inc. All rights reserved. 4// https://developers.google.com/protocol-buffers/ 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are 8// met: 9// 10// * Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above 13// copyright notice, this list of conditions and the following disclaimer 14// in the documentation and/or other materials provided with the 15// distribution. 16// * Neither the name of Google Inc. nor the names of its 17// contributors may be used to endorse or promote products derived from 18// this software without specific prior written permission. 19// 20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31#endregion 32 33using Google.Protobuf.Collections; 34using System; 35using System.Collections.Generic; 36using System.IO; 37using System.Runtime.CompilerServices; 38using System.Runtime.InteropServices; 39using System.Security; 40 41namespace Google.Protobuf 42{ 43 /// <summary> 44 /// Reads and decodes protocol message fields. 45 /// </summary> 46 /// <remarks> 47 /// <para> 48 /// This class is generally used by generated code to read appropriate 49 /// primitives from the stream. It effectively encapsulates the lowest 50 /// levels of protocol buffer format. 51 /// </para> 52 /// <para> 53 /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/> 54 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields. 55 /// </para> 56 /// </remarks> 57 [SecuritySafeCritical] 58 public sealed class CodedInputStream : IDisposable 59 { 60 /// <summary> 61 /// Whether to leave the underlying stream open when disposing of this stream. 62 /// This is always true when there's no stream. 63 /// </summary> 64 private readonly bool leaveOpen; 65 66 /// <summary> 67 /// Buffer of data read from the stream or provided at construction time. 68 /// </summary> 69 private readonly byte[] buffer; 70 71 /// <summary> 72 /// The stream to read further input from, or null if the byte array buffer was provided 73 /// directly on construction, with no further data available. 74 /// </summary> 75 private readonly Stream input; 76 77 /// <summary> 78 /// The parser state is kept separately so that other parse implementations can reuse the same 79 /// parsing primitives. 80 /// </summary> 81 private ParserInternalState state; 82 83 internal const int DefaultRecursionLimit = 100; 84 internal const int DefaultSizeLimit = Int32.MaxValue; 85 internal const int BufferSize = 4096; 86 87 #region Construction 88 // Note that the checks are performed such that we don't end up checking obviously-valid things 89 // like non-null references for arrays we've just created. 90 91 /// <summary> 92 /// Creates a new CodedInputStream reading data from the given byte array. 93 /// </summary> 94 public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true) 95 { 96 } 97 98 /// <summary> 99 /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice. 100 /// </summary> 101 public CodedInputStream(byte[] buffer, int offset, int length) 102 : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true) 103 { 104 if (offset < 0 || offset > buffer.Length) 105 { 106 throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer"); 107 } 108 if (length < 0 || offset + length > buffer.Length) 109 { 110 throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer"); 111 } 112 } 113 114 /// <summary> 115 /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed 116 /// when the returned object is disposed. 117 /// </summary> 118 /// <param name="input">The stream to read from.</param> 119 public CodedInputStream(Stream input) : this(input, false) 120 { 121 } 122 123 /// <summary> 124 /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream. 125 /// </summary> 126 /// <param name="input">The stream to read from.</param> 127 /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/> open when the returned 128 /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of the given stream when the 129 /// returned object is disposed.</param> 130 public CodedInputStream(Stream input, bool leaveOpen) 131 : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen) 132 { 133 } 134 135 /// <summary> 136 /// Creates a new CodedInputStream reading data from the given 137 /// stream and buffer, using the default limits. 138 /// </summary> 139 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen) 140 { 141 this.input = input; 142 this.buffer = buffer; 143 this.state.bufferPos = bufferPos; 144 this.state.bufferSize = bufferSize; 145 this.state.sizeLimit = DefaultSizeLimit; 146 this.state.recursionLimit = DefaultRecursionLimit; 147 SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper); 148 this.leaveOpen = leaveOpen; 149 150 this.state.currentLimit = int.MaxValue; 151 } 152 153 /// <summary> 154 /// Creates a new CodedInputStream reading data from the given 155 /// stream and buffer, using the specified limits. 156 /// </summary> 157 /// <remarks> 158 /// This chains to the version with the default limits instead of vice versa to avoid 159 /// having to check that the default values are valid every time. 160 /// </remarks> 161 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen) 162 : this(input, buffer, bufferPos, bufferSize, leaveOpen) 163 { 164 if (sizeLimit <= 0) 165 { 166 throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive"); 167 } 168 if (recursionLimit <= 0) 169 { 170 throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive"); 171 } 172 this.state.sizeLimit = sizeLimit; 173 this.state.recursionLimit = recursionLimit; 174 } 175 #endregion 176 177 /// <summary> 178 /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading 179 /// from an input stream. 180 /// </summary> 181 /// <remarks> 182 /// This method exists separately from the constructor to reduce the number of constructor overloads. 183 /// It is likely to be used considerably less frequently than the constructors, as the default limits 184 /// are suitable for most use cases. 185 /// </remarks> 186 /// <param name="input">The input stream to read from</param> 187 /// <param name="sizeLimit">The total limit of data to read from the stream.</param> 188 /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param> 189 /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size 190 /// and recursion limits.</returns> 191 public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit) 192 { 193 // Note: we may want an overload accepting leaveOpen 194 return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit, false); 195 } 196 197 /// <summary> 198 /// Returns the current position in the input stream, or the position in the input buffer 199 /// </summary> 200 public long Position 201 { 202 get 203 { 204 if (input != null) 205 { 206 return input.Position - ((state.bufferSize + state.bufferSizeAfterLimit) - state.bufferPos); 207 } 208 return state.bufferPos; 209 } 210 } 211 212 /// <summary> 213 /// Returns the last tag read, or 0 if no tags have been read or we've read beyond 214 /// the end of the stream. 215 /// </summary> 216 internal uint LastTag { get { return state.lastTag; } } 217 218 /// <summary> 219 /// Returns the size limit for this stream. 220 /// </summary> 221 /// <remarks> 222 /// This limit is applied when reading from the underlying stream, as a sanity check. It is 223 /// not applied when reading from a byte array data source without an underlying stream. 224 /// The default value is Int32.MaxValue. 225 /// </remarks> 226 /// <value> 227 /// The size limit. 228 /// </value> 229 public int SizeLimit { get { return state.sizeLimit; } } 230 231 /// <summary> 232 /// Returns the recursion limit for this stream. This limit is applied whilst reading messages, 233 /// to avoid maliciously-recursive data. 234 /// </summary> 235 /// <remarks> 236 /// The default limit is 100. 237 /// </remarks> 238 /// <value> 239 /// The recursion limit for this stream. 240 /// </value> 241 public int RecursionLimit { get { return state.recursionLimit; } } 242 243 /// <summary> 244 /// Internal-only property; when set to true, unknown fields will be discarded while parsing. 245 /// </summary> 246 internal bool DiscardUnknownFields 247 { 248 get { return state.DiscardUnknownFields; } 249 set { state.DiscardUnknownFields = value; } 250 } 251 252 /// <summary> 253 /// Internal-only property; provides extension identifiers to compatible messages while parsing. 254 /// </summary> 255 internal ExtensionRegistry ExtensionRegistry 256 { 257 get { return state.ExtensionRegistry; } 258 set { state.ExtensionRegistry = value; } 259 } 260 261 internal byte[] InternalBuffer => buffer; 262 263 internal Stream InternalInputStream => input; 264 265 internal ref ParserInternalState InternalState => ref state; 266 267 /// <summary> 268 /// Disposes of this instance, potentially closing any underlying stream. 269 /// </summary> 270 /// <remarks> 271 /// As there is no flushing to perform here, disposing of a <see cref="CodedInputStream"/> which 272 /// was constructed with the <c>leaveOpen</c> option parameter set to <c>true</c> (or one which 273 /// was constructed to read from a byte array) has no effect. 274 /// </remarks> 275 public void Dispose() 276 { 277 if (!leaveOpen) 278 { 279 input.Dispose(); 280 } 281 } 282 283 #region Validation 284 /// <summary> 285 /// Verifies that the last call to ReadTag() returned tag 0 - in other words, 286 /// we've reached the end of the stream when we expected to. 287 /// </summary> 288 /// <exception cref="InvalidProtocolBufferException">The 289 /// tag read was not the one specified</exception> 290 internal void CheckReadEndOfStreamTag() 291 { 292 ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref state); 293 } 294 #endregion 295 296 #region Reading of tags etc 297 298 /// <summary> 299 /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the 300 /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the 301 /// same value.) 302 /// </summary> 303 public uint PeekTag() 304 { 305 var span = new ReadOnlySpan<byte>(buffer); 306 return ParsingPrimitives.PeekTag(ref span, ref state); 307 } 308 309 /// <summary> 310 /// Reads a field tag, returning the tag of 0 for "end of stream". 311 /// </summary> 312 /// <remarks> 313 /// If this method returns 0, it doesn't necessarily mean the end of all 314 /// the data in this CodedInputStream; it may be the end of the logical stream 315 /// for an embedded message, for example. 316 /// </remarks> 317 /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns> 318 public uint ReadTag() 319 { 320 var span = new ReadOnlySpan<byte>(buffer); 321 return ParsingPrimitives.ParseTag(ref span, ref state); 322 } 323 324 /// <summary> 325 /// Skips the data for the field with the tag we've just read. 326 /// This should be called directly after <see cref="ReadTag"/>, when 327 /// the caller wishes to skip an unknown field. 328 /// </summary> 329 /// <remarks> 330 /// This method throws <see cref="InvalidProtocolBufferException"/> if the last-read tag was an end-group tag. 331 /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the 332 /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly 333 /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag. 334 /// </remarks> 335 /// <exception cref="InvalidProtocolBufferException">The last tag was an end-group tag</exception> 336 /// <exception cref="InvalidOperationException">The last read operation read to the end of the logical stream</exception> 337 public void SkipLastField() 338 { 339 var span = new ReadOnlySpan<byte>(buffer); 340 ParsingPrimitivesMessages.SkipLastField(ref span, ref state); 341 } 342 343 /// <summary> 344 /// Skip a group. 345 /// </summary> 346 internal void SkipGroup(uint startGroupTag) 347 { 348 var span = new ReadOnlySpan<byte>(buffer); 349 ParsingPrimitivesMessages.SkipGroup(ref span, ref state, startGroupTag); 350 } 351 352 /// <summary> 353 /// Reads a double field from the stream. 354 /// </summary> 355 public double ReadDouble() 356 { 357 var span = new ReadOnlySpan<byte>(buffer); 358 return ParsingPrimitives.ParseDouble(ref span, ref state); 359 } 360 361 /// <summary> 362 /// Reads a float field from the stream. 363 /// </summary> 364 public float ReadFloat() 365 { 366 var span = new ReadOnlySpan<byte>(buffer); 367 return ParsingPrimitives.ParseFloat(ref span, ref state); 368 } 369 370 /// <summary> 371 /// Reads a uint64 field from the stream. 372 /// </summary> 373 public ulong ReadUInt64() 374 { 375 return ReadRawVarint64(); 376 } 377 378 /// <summary> 379 /// Reads an int64 field from the stream. 380 /// </summary> 381 public long ReadInt64() 382 { 383 return (long) ReadRawVarint64(); 384 } 385 386 /// <summary> 387 /// Reads an int32 field from the stream. 388 /// </summary> 389 public int ReadInt32() 390 { 391 return (int) ReadRawVarint32(); 392 } 393 394 /// <summary> 395 /// Reads a fixed64 field from the stream. 396 /// </summary> 397 public ulong ReadFixed64() 398 { 399 return ReadRawLittleEndian64(); 400 } 401 402 /// <summary> 403 /// Reads a fixed32 field from the stream. 404 /// </summary> 405 public uint ReadFixed32() 406 { 407 return ReadRawLittleEndian32(); 408 } 409 410 /// <summary> 411 /// Reads a bool field from the stream. 412 /// </summary> 413 public bool ReadBool() 414 { 415 return ReadRawVarint64() != 0; 416 } 417 418 /// <summary> 419 /// Reads a string field from the stream. 420 /// </summary> 421 public string ReadString() 422 { 423 var span = new ReadOnlySpan<byte>(buffer); 424 return ParsingPrimitives.ReadString(ref span, ref state); 425 } 426 427 /// <summary> 428 /// Reads an embedded message field value from the stream. 429 /// </summary> 430 public void ReadMessage(IMessage builder) 431 { 432 // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalMergeFrom method), 433 // what we're doing here works fine, but could be more efficient. 434 // What happends is that we first initialize a ParseContext from the current coded input stream only to parse the length of the message, at which point 435 // we will need to switch back again to CodedInputStream-based parsing (which involves copying and storing the state) to be able to 436 // invoke the legacy MergeFrom(CodedInputStream) method. 437 // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it). 438 var span = new ReadOnlySpan<byte>(buffer); 439 ParseContext.Initialize(ref span, ref state, out ParseContext ctx); 440 try 441 { 442 ParsingPrimitivesMessages.ReadMessage(ref ctx, builder); 443 } 444 finally 445 { 446 ctx.CopyStateTo(this); 447 } 448 } 449 450 /// <summary> 451 /// Reads an embedded group field from the stream. 452 /// </summary> 453 public void ReadGroup(IMessage builder) 454 { 455 ParseContext.Initialize(this, out ParseContext ctx); 456 try 457 { 458 ParsingPrimitivesMessages.ReadGroup(ref ctx, builder); 459 } 460 finally 461 { 462 ctx.CopyStateTo(this); 463 } 464 } 465 466 /// <summary> 467 /// Reads a bytes field value from the stream. 468 /// </summary> 469 public ByteString ReadBytes() 470 { 471 var span = new ReadOnlySpan<byte>(buffer); 472 return ParsingPrimitives.ReadBytes(ref span, ref state); 473 } 474 475 /// <summary> 476 /// Reads a uint32 field value from the stream. 477 /// </summary> 478 public uint ReadUInt32() 479 { 480 return ReadRawVarint32(); 481 } 482 483 /// <summary> 484 /// Reads an enum field value from the stream. 485 /// </summary> 486 public int ReadEnum() 487 { 488 // Currently just a pass-through, but it's nice to separate it logically from WriteInt32. 489 return (int) ReadRawVarint32(); 490 } 491 492 /// <summary> 493 /// Reads an sfixed32 field value from the stream. 494 /// </summary> 495 public int ReadSFixed32() 496 { 497 return (int) ReadRawLittleEndian32(); 498 } 499 500 /// <summary> 501 /// Reads an sfixed64 field value from the stream. 502 /// </summary> 503 public long ReadSFixed64() 504 { 505 return (long) ReadRawLittleEndian64(); 506 } 507 508 /// <summary> 509 /// Reads an sint32 field value from the stream. 510 /// </summary> 511 public int ReadSInt32() 512 { 513 return ParsingPrimitives.DecodeZigZag32(ReadRawVarint32()); 514 } 515 516 /// <summary> 517 /// Reads an sint64 field value from the stream. 518 /// </summary> 519 public long ReadSInt64() 520 { 521 return ParsingPrimitives.DecodeZigZag64(ReadRawVarint64()); 522 } 523 524 /// <summary> 525 /// Reads a length for length-delimited data. 526 /// </summary> 527 /// <remarks> 528 /// This is internally just reading a varint, but this method exists 529 /// to make the calling code clearer. 530 /// </remarks> 531 public int ReadLength() 532 { 533 var span = new ReadOnlySpan<byte>(buffer); 534 return ParsingPrimitives.ParseLength(ref span, ref state); 535 } 536 537 /// <summary> 538 /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>, 539 /// the tag is consumed and the method returns <c>true</c>; otherwise, the 540 /// stream is left in the original position and the method returns <c>false</c>. 541 /// </summary> 542 public bool MaybeConsumeTag(uint tag) 543 { 544 var span = new ReadOnlySpan<byte>(buffer); 545 return ParsingPrimitives.MaybeConsumeTag(ref span, ref state, tag); 546 } 547 548#endregion 549 550 #region Underlying reading primitives 551 552 /// <summary> 553 /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits. 554 /// This method is optimised for the case where we've got lots of data in the buffer. 555 /// That means we can check the size just once, then just read directly from the buffer 556 /// without constant rechecking of the buffer length. 557 /// </summary> 558 internal uint ReadRawVarint32() 559 { 560 var span = new ReadOnlySpan<byte>(buffer); 561 return ParsingPrimitives.ParseRawVarint32(ref span, ref state); 562 } 563 564 /// <summary> 565 /// Reads a varint from the input one byte at a time, so that it does not 566 /// read any bytes after the end of the varint. If you simply wrapped the 567 /// stream in a CodedInputStream and used ReadRawVarint32(Stream) 568 /// then you would probably end up reading past the end of the varint since 569 /// CodedInputStream buffers its input. 570 /// </summary> 571 /// <param name="input"></param> 572 /// <returns></returns> 573 internal static uint ReadRawVarint32(Stream input) 574 { 575 return ParsingPrimitives.ReadRawVarint32(input); 576 } 577 578 /// <summary> 579 /// Reads a raw varint from the stream. 580 /// </summary> 581 internal ulong ReadRawVarint64() 582 { 583 var span = new ReadOnlySpan<byte>(buffer); 584 return ParsingPrimitives.ParseRawVarint64(ref span, ref state); 585 } 586 587 /// <summary> 588 /// Reads a 32-bit little-endian integer from the stream. 589 /// </summary> 590 internal uint ReadRawLittleEndian32() 591 { 592 var span = new ReadOnlySpan<byte>(buffer); 593 return ParsingPrimitives.ParseRawLittleEndian32(ref span, ref state); 594 } 595 596 /// <summary> 597 /// Reads a 64-bit little-endian integer from the stream. 598 /// </summary> 599 internal ulong ReadRawLittleEndian64() 600 { 601 var span = new ReadOnlySpan<byte>(buffer); 602 return ParsingPrimitives.ParseRawLittleEndian64(ref span, ref state); 603 } 604 #endregion 605 606 #region Internal reading and buffer management 607 608 /// <summary> 609 /// Sets currentLimit to (current position) + byteLimit. This is called 610 /// when descending into a length-delimited embedded message. The previous 611 /// limit is returned. 612 /// </summary> 613 /// <returns>The old limit.</returns> 614 internal int PushLimit(int byteLimit) 615 { 616 return SegmentedBufferHelper.PushLimit(ref state, byteLimit); 617 } 618 619 /// <summary> 620 /// Discards the current limit, returning the previous limit. 621 /// </summary> 622 internal void PopLimit(int oldLimit) 623 { 624 SegmentedBufferHelper.PopLimit(ref state, oldLimit); 625 } 626 627 /// <summary> 628 /// Returns whether or not all the data before the limit has been read. 629 /// </summary> 630 /// <returns></returns> 631 internal bool ReachedLimit 632 { 633 get 634 { 635 return SegmentedBufferHelper.IsReachedLimit(ref state); 636 } 637 } 638 639 /// <summary> 640 /// Returns true if the stream has reached the end of the input. This is the 641 /// case if either the end of the underlying input source has been reached or 642 /// the stream has reached a limit created using PushLimit. 643 /// </summary> 644 public bool IsAtEnd 645 { 646 get 647 { 648 var span = new ReadOnlySpan<byte>(buffer); 649 return SegmentedBufferHelper.IsAtEnd(ref span, ref state); 650 } 651 } 652 653 /// <summary> 654 /// Called when buffer is empty to read more bytes from the 655 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that 656 /// either there will be at least one byte in the buffer when it returns 657 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false, 658 /// RefillBuffer() returns false if no more bytes were available. 659 /// </summary> 660 /// <param name="mustSucceed"></param> 661 /// <returns></returns> 662 private bool RefillBuffer(bool mustSucceed) 663 { 664 var span = new ReadOnlySpan<byte>(buffer); 665 return state.segmentedBufferHelper.RefillBuffer(ref span, ref state, mustSucceed); 666 } 667 668 /// <summary> 669 /// Reads a fixed size of bytes from the input. 670 /// </summary> 671 /// <exception cref="InvalidProtocolBufferException"> 672 /// the end of the stream or the current limit was reached 673 /// </exception> 674 internal byte[] ReadRawBytes(int size) 675 { 676 var span = new ReadOnlySpan<byte>(buffer); 677 return ParsingPrimitives.ReadRawBytes(ref span, ref state, size); 678 } 679 680 /// <summary> 681 /// Reads a top-level message or a nested message after the limits for this message have been pushed. 682 /// (parser will proceed until the end of the current limit) 683 /// NOTE: this method needs to be public because it's invoked by the generated code - e.g. msg.MergeFrom(CodedInputStream input) method 684 /// </summary> 685 public void ReadRawMessage(IMessage message) 686 { 687 ParseContext.Initialize(this, out ParseContext ctx); 688 try 689 { 690 ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message); 691 } 692 finally 693 { 694 ctx.CopyStateTo(this); 695 } 696 } 697#endregion 698 } 699} 700