• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 using Google.Protobuf.Collections;
34 using System;
35 using System.Collections.Generic;
36 using System.IO;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39 using System.Security;
40 
41 namespace 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>
CodedInputStream(byte[] buffer)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>
CodedInputStream(byte[] buffer, int offset, int length)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>
CodedInputStream(Stream input)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>
CodedInputStream(Stream input, bool leaveOpen)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>
CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)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>
CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)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>
CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)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>
Dispose()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>
CheckReadEndOfStreamTag()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>
PeekTag()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>
ReadTag()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>
SkipLastField()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>
SkipGroup(uint startGroupTag)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>
ReadDouble()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>
ReadFloat()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>
ReadUInt64()373         public ulong ReadUInt64()
374         {
375             return ReadRawVarint64();
376         }
377 
378         /// <summary>
379         /// Reads an int64 field from the stream.
380         /// </summary>
ReadInt64()381         public long ReadInt64()
382         {
383             return (long) ReadRawVarint64();
384         }
385 
386         /// <summary>
387         /// Reads an int32 field from the stream.
388         /// </summary>
ReadInt32()389         public int ReadInt32()
390         {
391             return (int) ReadRawVarint32();
392         }
393 
394         /// <summary>
395         /// Reads a fixed64 field from the stream.
396         /// </summary>
ReadFixed64()397         public ulong ReadFixed64()
398         {
399             return ReadRawLittleEndian64();
400         }
401 
402         /// <summary>
403         /// Reads a fixed32 field from the stream.
404         /// </summary>
ReadFixed32()405         public uint ReadFixed32()
406         {
407             return ReadRawLittleEndian32();
408         }
409 
410         /// <summary>
411         /// Reads a bool field from the stream.
412         /// </summary>
ReadBool()413         public bool ReadBool()
414         {
415             return ReadRawVarint64() != 0;
416         }
417 
418         /// <summary>
419         /// Reads a string field from the stream.
420         /// </summary>
ReadString()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>
ReadMessage(IMessage builder)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>
ReadGroup(IMessage builder)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>
ReadBytes()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>
ReadUInt32()478         public uint ReadUInt32()
479         {
480             return ReadRawVarint32();
481         }
482 
483         /// <summary>
484         /// Reads an enum field value from the stream.
485         /// </summary>
ReadEnum()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>
ReadSFixed32()495         public int ReadSFixed32()
496         {
497             return (int) ReadRawLittleEndian32();
498         }
499 
500         /// <summary>
501         /// Reads an sfixed64 field value from the stream.
502         /// </summary>
ReadSFixed64()503         public long ReadSFixed64()
504         {
505             return (long) ReadRawLittleEndian64();
506         }
507 
508         /// <summary>
509         /// Reads an sint32 field value from the stream.
510         /// </summary>
ReadSInt32()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>
ReadSInt64()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>
ReadLength()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>
MaybeConsumeTag(uint tag)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>
ReadRawVarint32()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>
ReadRawVarint32(Stream input)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>
ReadRawVarint64()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>
ReadRawLittleEndian32()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>
ReadRawLittleEndian64()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>
PushLimit(int byteLimit)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>
PopLimit(int oldLimit)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>
RefillBuffer(bool mustSucceed)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>
ReadRawBytes(int size)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>
ReadRawMessage(IMessage message)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