• 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 
38 namespace Google.Protobuf
39 {
40     /// <summary>
41     /// Reads and decodes protocol message fields.
42     /// </summary>
43     /// <remarks>
44     /// <para>
45     /// This class is generally used by generated code to read appropriate
46     /// primitives from the stream. It effectively encapsulates the lowest
47     /// levels of protocol buffer format.
48     /// </para>
49     /// <para>
50     /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>
51     /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
52     /// </para>
53     /// </remarks>
54     public sealed class CodedInputStream : IDisposable
55     {
56         /// <summary>
57         /// Whether to leave the underlying stream open when disposing of this stream.
58         /// This is always true when there's no stream.
59         /// </summary>
60         private readonly bool leaveOpen;
61 
62         /// <summary>
63         /// Buffer of data read from the stream or provided at construction time.
64         /// </summary>
65         private readonly byte[] buffer;
66 
67         /// <summary>
68         /// The index of the buffer at which we need to refill from the stream (if there is one).
69         /// </summary>
70         private int bufferSize;
71 
72         private int bufferSizeAfterLimit = 0;
73         /// <summary>
74         /// The position within the current buffer (i.e. the next byte to read)
75         /// </summary>
76         private int bufferPos = 0;
77 
78         /// <summary>
79         /// The stream to read further input from, or null if the byte array buffer was provided
80         /// directly on construction, with no further data available.
81         /// </summary>
82         private readonly Stream input;
83 
84         /// <summary>
85         /// The last tag we read. 0 indicates we've read to the end of the stream
86         /// (or haven't read anything yet).
87         /// </summary>
88         private uint lastTag = 0;
89 
90         /// <summary>
91         /// The next tag, used to store the value read by PeekTag.
92         /// </summary>
93         private uint nextTag = 0;
94         private bool hasNextTag = false;
95 
96         internal const int DefaultRecursionLimit = 64;
97         internal const int DefaultSizeLimit = 64 << 20; // 64MB
98         internal const int BufferSize = 4096;
99 
100         /// <summary>
101         /// The total number of bytes read before the current buffer. The
102         /// total bytes read up to the current position can be computed as
103         /// totalBytesRetired + bufferPos.
104         /// </summary>
105         private int totalBytesRetired = 0;
106 
107         /// <summary>
108         /// The absolute position of the end of the current message.
109         /// </summary>
110         private int currentLimit = int.MaxValue;
111 
112         private int recursionDepth = 0;
113 
114         private readonly int recursionLimit;
115         private readonly int sizeLimit;
116 
117         #region Construction
118         // Note that the checks are performed such that we don't end up checking obviously-valid things
119         // like non-null references for arrays we've just created.
120 
121         /// <summary>
122         /// Creates a new CodedInputStream reading data from the given byte array.
123         /// </summary>
CodedInputStream(byte[] buffer)124         public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length)
125         {
126         }
127 
128         /// <summary>
129         /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice.
130         /// </summary>
CodedInputStream(byte[] buffer, int offset, int length)131         public CodedInputStream(byte[] buffer, int offset, int length)
132             : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length)
133         {
134             if (offset < 0 || offset > buffer.Length)
135             {
136                 throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
137             }
138             if (length < 0 || offset + length > buffer.Length)
139             {
140                 throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
141             }
142         }
143 
144         /// <summary>
145         /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed
146         /// when the returned object is disposed.
147         /// </summary>
148         /// <param name="input">The stream to read from.</param>
CodedInputStream(Stream input)149         public CodedInputStream(Stream input) : this(input, false)
150         {
151         }
152 
153         /// <summary>
154         /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream.
155         /// </summary>
156         /// <param name="input">The stream to read from.</param>
157         /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/> open when the returned
158         /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of the given stream when the
159         /// returned object is disposed.</param>
CodedInputStream(Stream input, bool leaveOpen)160         public CodedInputStream(Stream input, bool leaveOpen)
161             : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0)
162         {
163             this.leaveOpen = leaveOpen;
164         }
165 
166         /// <summary>
167         /// Creates a new CodedInputStream reading data from the given
168         /// stream and buffer, using the default limits.
169         /// </summary>
CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize)170         internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize)
171         {
172             this.input = input;
173             this.buffer = buffer;
174             this.bufferPos = bufferPos;
175             this.bufferSize = bufferSize;
176             this.sizeLimit = DefaultSizeLimit;
177             this.recursionLimit = DefaultRecursionLimit;
178         }
179 
180         /// <summary>
181         /// Creates a new CodedInputStream reading data from the given
182         /// stream and buffer, using the specified limits.
183         /// </summary>
184         /// <remarks>
185         /// This chains to the version with the default limits instead of vice versa to avoid
186         /// having to check that the default values are valid every time.
187         /// </remarks>
CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit)188         internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit)
189             : this(input, buffer, bufferPos, bufferSize)
190         {
191             if (sizeLimit <= 0)
192             {
193                 throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");
194             }
195             if (recursionLimit <= 0)
196             {
197                 throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");
198             }
199             this.sizeLimit = sizeLimit;
200             this.recursionLimit = recursionLimit;
201         }
202         #endregion
203 
204         /// <summary>
205         /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading
206         /// from an input stream.
207         /// </summary>
208         /// <remarks>
209         /// This method exists separately from the constructor to reduce the number of constructor overloads.
210         /// It is likely to be used considerably less frequently than the constructors, as the default limits
211         /// are suitable for most use cases.
212         /// </remarks>
213         /// <param name="input">The input stream to read from</param>
214         /// <param name="sizeLimit">The total limit of data to read from the stream.</param>
215         /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param>
216         /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size
217         /// and recursion limits.</returns>
CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)218         public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)
219         {
220             return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit);
221         }
222 
223         /// <summary>
224         /// Returns the current position in the input stream, or the position in the input buffer
225         /// </summary>
226         public long Position
227         {
228             get
229             {
230                 if (input != null)
231                 {
232                     return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos);
233                 }
234                 return bufferPos;
235             }
236         }
237 
238         /// <summary>
239         /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
240         /// the end of the stream.
241         /// </summary>
242         internal uint LastTag { get { return lastTag; } }
243 
244         /// <summary>
245         /// Returns the size limit for this stream.
246         /// </summary>
247         /// <remarks>
248         /// This limit is applied when reading from the underlying stream, as a sanity check. It is
249         /// not applied when reading from a byte array data source without an underlying stream.
250         /// The default value is 64MB.
251         /// </remarks>
252         /// <value>
253         /// The size limit.
254         /// </value>
255         public int SizeLimit { get { return sizeLimit; } }
256 
257         /// <summary>
258         /// Returns the recursion limit for this stream. This limit is applied whilst reading messages,
259         /// to avoid maliciously-recursive data.
260         /// </summary>
261         /// <remarks>
262         /// The default limit is 64.
263         /// </remarks>
264         /// <value>
265         /// The recursion limit for this stream.
266         /// </value>
267         public int RecursionLimit { get { return recursionLimit; } }
268 
269         /// <summary>
270         /// Disposes of this instance, potentially closing any underlying stream.
271         /// </summary>
272         /// <remarks>
273         /// As there is no flushing to perform here, disposing of a <see cref="CodedInputStream"/> which
274         /// was constructed with the <c>leaveOpen</c> option parameter set to <c>true</c> (or one which
275         /// was constructed to read from a byte array) has no effect.
276         /// </remarks>
Dispose()277         public void Dispose()
278         {
279             if (!leaveOpen)
280             {
281                 input.Dispose();
282             }
283         }
284 
285         #region Validation
286         /// <summary>
287         /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
288         /// we've reached the end of the stream when we expected to.
289         /// </summary>
290         /// <exception cref="InvalidProtocolBufferException">The
291         /// tag read was not the one specified</exception>
CheckReadEndOfStreamTag()292         internal void CheckReadEndOfStreamTag()
293         {
294             if (lastTag != 0)
295             {
296                 throw InvalidProtocolBufferException.MoreDataAvailable();
297             }
298         }
299         #endregion
300 
301         #region Reading of tags etc
302 
303         /// <summary>
304         /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the
305         /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
306         /// same value.)
307         /// </summary>
PeekTag()308         public uint PeekTag()
309         {
310             if (hasNextTag)
311             {
312                 return nextTag;
313             }
314 
315             uint savedLast = lastTag;
316             nextTag = ReadTag();
317             hasNextTag = true;
318             lastTag = savedLast; // Undo the side effect of ReadTag
319             return nextTag;
320         }
321 
322         /// <summary>
323         /// Reads a field tag, returning the tag of 0 for "end of stream".
324         /// </summary>
325         /// <remarks>
326         /// If this method returns 0, it doesn't necessarily mean the end of all
327         /// the data in this CodedInputStream; it may be the end of the logical stream
328         /// for an embedded message, for example.
329         /// </remarks>
330         /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>
ReadTag()331         public uint ReadTag()
332         {
333             if (hasNextTag)
334             {
335                 lastTag = nextTag;
336                 hasNextTag = false;
337                 return lastTag;
338             }
339 
340             // Optimize for the incredibly common case of having at least two bytes left in the buffer,
341             // and those two bytes being enough to get the tag. This will be true for fields up to 4095.
342             if (bufferPos + 2 <= bufferSize)
343             {
344                 int tmp = buffer[bufferPos++];
345                 if (tmp < 128)
346                 {
347                     lastTag = (uint)tmp;
348                 }
349                 else
350                 {
351                     int result = tmp & 0x7f;
352                     if ((tmp = buffer[bufferPos++]) < 128)
353                     {
354                         result |= tmp << 7;
355                         lastTag = (uint) result;
356                     }
357                     else
358                     {
359                         // Nope, rewind and go the potentially slow route.
360                         bufferPos -= 2;
361                         lastTag = ReadRawVarint32();
362                     }
363                 }
364             }
365             else
366             {
367                 if (IsAtEnd)
368                 {
369                     lastTag = 0;
370                     return 0; // This is the only case in which we return 0.
371                 }
372 
373                 lastTag = ReadRawVarint32();
374             }
375             if (lastTag == 0)
376             {
377                 // If we actually read zero, that's not a valid tag.
378                 throw InvalidProtocolBufferException.InvalidTag();
379             }
380             return lastTag;
381         }
382 
383         /// <summary>
384         /// Skips the data for the field with the tag we've just read.
385         /// This should be called directly after <see cref="ReadTag"/>, when
386         /// the caller wishes to skip an unknown field.
387         /// </summary>
388         /// <remarks>
389         /// This method throws <see cref="InvalidProtocolBufferException"/> if the last-read tag was an end-group tag.
390         /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the
391         /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly
392         /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag.
393         /// </remarks>
394         /// <exception cref="InvalidProtocolBufferException">The last tag was an end-group tag</exception>
395         /// <exception cref="InvalidOperationException">The last read operation read to the end of the logical stream</exception>
SkipLastField()396         public void SkipLastField()
397         {
398             if (lastTag == 0)
399             {
400                 throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream");
401             }
402             switch (WireFormat.GetTagWireType(lastTag))
403             {
404                 case WireFormat.WireType.StartGroup:
405                     SkipGroup(lastTag);
406                     break;
407                 case WireFormat.WireType.EndGroup:
408                     throw new InvalidProtocolBufferException(
409                         "SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing");
410                 case WireFormat.WireType.Fixed32:
411                     ReadFixed32();
412                     break;
413                 case WireFormat.WireType.Fixed64:
414                     ReadFixed64();
415                     break;
416                 case WireFormat.WireType.LengthDelimited:
417                     var length = ReadLength();
418                     SkipRawBytes(length);
419                     break;
420                 case WireFormat.WireType.Varint:
421                     ReadRawVarint32();
422                     break;
423             }
424         }
425 
SkipGroup(uint startGroupTag)426         private void SkipGroup(uint startGroupTag)
427         {
428             // Note: Currently we expect this to be the way that groups are read. We could put the recursion
429             // depth changes into the ReadTag method instead, potentially...
430             recursionDepth++;
431             if (recursionDepth >= recursionLimit)
432             {
433                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
434             }
435             uint tag;
436             while (true)
437             {
438                 tag = ReadTag();
439                 if (tag == 0)
440                 {
441                     throw InvalidProtocolBufferException.TruncatedMessage();
442                 }
443                 // Can't call SkipLastField for this case- that would throw.
444                 if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup)
445                 {
446                     break;
447                 }
448                 // This recursion will allow us to handle nested groups.
449                 SkipLastField();
450             }
451             int startField = WireFormat.GetTagFieldNumber(startGroupTag);
452             int endField = WireFormat.GetTagFieldNumber(tag);
453             if (startField != endField)
454             {
455                 throw new InvalidProtocolBufferException(
456                     $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}");
457             }
458             recursionDepth--;
459         }
460 
461         /// <summary>
462         /// Reads a double field from the stream.
463         /// </summary>
ReadDouble()464         public double ReadDouble()
465         {
466             return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
467         }
468 
469         /// <summary>
470         /// Reads a float field from the stream.
471         /// </summary>
ReadFloat()472         public float ReadFloat()
473         {
474             if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
475             {
476                 float ret = BitConverter.ToSingle(buffer, bufferPos);
477                 bufferPos += 4;
478                 return ret;
479             }
480             else
481             {
482                 byte[] rawBytes = ReadRawBytes(4);
483                 if (!BitConverter.IsLittleEndian)
484                 {
485                     ByteArray.Reverse(rawBytes);
486                 }
487                 return BitConverter.ToSingle(rawBytes, 0);
488             }
489         }
490 
491         /// <summary>
492         /// Reads a uint64 field from the stream.
493         /// </summary>
ReadUInt64()494         public ulong ReadUInt64()
495         {
496             return ReadRawVarint64();
497         }
498 
499         /// <summary>
500         /// Reads an int64 field from the stream.
501         /// </summary>
ReadInt64()502         public long ReadInt64()
503         {
504             return (long) ReadRawVarint64();
505         }
506 
507         /// <summary>
508         /// Reads an int32 field from the stream.
509         /// </summary>
ReadInt32()510         public int ReadInt32()
511         {
512             return (int) ReadRawVarint32();
513         }
514 
515         /// <summary>
516         /// Reads a fixed64 field from the stream.
517         /// </summary>
ReadFixed64()518         public ulong ReadFixed64()
519         {
520             return ReadRawLittleEndian64();
521         }
522 
523         /// <summary>
524         /// Reads a fixed32 field from the stream.
525         /// </summary>
ReadFixed32()526         public uint ReadFixed32()
527         {
528             return ReadRawLittleEndian32();
529         }
530 
531         /// <summary>
532         /// Reads a bool field from the stream.
533         /// </summary>
ReadBool()534         public bool ReadBool()
535         {
536             return ReadRawVarint32() != 0;
537         }
538 
539         /// <summary>
540         /// Reads a string field from the stream.
541         /// </summary>
ReadString()542         public string ReadString()
543         {
544             int length = ReadLength();
545             // No need to read any data for an empty string.
546             if (length == 0)
547             {
548                 return "";
549             }
550             if (length <= bufferSize - bufferPos)
551             {
552                 // Fast path:  We already have the bytes in a contiguous buffer, so
553                 //   just copy directly from it.
554                 String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
555                 bufferPos += length;
556                 return result;
557             }
558             // Slow path: Build a byte array first then copy it.
559             return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
560         }
561 
562         /// <summary>
563         /// Reads an embedded message field value from the stream.
564         /// </summary>
ReadMessage(IMessage builder)565         public void ReadMessage(IMessage builder)
566         {
567             int length = ReadLength();
568             if (recursionDepth >= recursionLimit)
569             {
570                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
571             }
572             int oldLimit = PushLimit(length);
573             ++recursionDepth;
574             builder.MergeFrom(this);
575             CheckReadEndOfStreamTag();
576             // Check that we've read exactly as much data as expected.
577             if (!ReachedLimit)
578             {
579                 throw InvalidProtocolBufferException.TruncatedMessage();
580             }
581             --recursionDepth;
582             PopLimit(oldLimit);
583         }
584 
585         /// <summary>
586         /// Reads a bytes field value from the stream.
587         /// </summary>
ReadBytes()588         public ByteString ReadBytes()
589         {
590             int length = ReadLength();
591             if (length <= bufferSize - bufferPos && length > 0)
592             {
593                 // Fast path:  We already have the bytes in a contiguous buffer, so
594                 //   just copy directly from it.
595                 ByteString result = ByteString.CopyFrom(buffer, bufferPos, length);
596                 bufferPos += length;
597                 return result;
598             }
599             else
600             {
601                 // Slow path:  Build a byte array and attach it to a new ByteString.
602                 return ByteString.AttachBytes(ReadRawBytes(length));
603             }
604         }
605 
606         /// <summary>
607         /// Reads a uint32 field value from the stream.
608         /// </summary>
ReadUInt32()609         public uint ReadUInt32()
610         {
611             return ReadRawVarint32();
612         }
613 
614         /// <summary>
615         /// Reads an enum field value from the stream.
616         /// </summary>
ReadEnum()617         public int ReadEnum()
618         {
619             // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
620             return (int) ReadRawVarint32();
621         }
622 
623         /// <summary>
624         /// Reads an sfixed32 field value from the stream.
625         /// </summary>
ReadSFixed32()626         public int ReadSFixed32()
627         {
628             return (int) ReadRawLittleEndian32();
629         }
630 
631         /// <summary>
632         /// Reads an sfixed64 field value from the stream.
633         /// </summary>
ReadSFixed64()634         public long ReadSFixed64()
635         {
636             return (long) ReadRawLittleEndian64();
637         }
638 
639         /// <summary>
640         /// Reads an sint32 field value from the stream.
641         /// </summary>
ReadSInt32()642         public int ReadSInt32()
643         {
644             return DecodeZigZag32(ReadRawVarint32());
645         }
646 
647         /// <summary>
648         /// Reads an sint64 field value from the stream.
649         /// </summary>
ReadSInt64()650         public long ReadSInt64()
651         {
652             return DecodeZigZag64(ReadRawVarint64());
653         }
654 
655         /// <summary>
656         /// Reads a length for length-delimited data.
657         /// </summary>
658         /// <remarks>
659         /// This is internally just reading a varint, but this method exists
660         /// to make the calling code clearer.
661         /// </remarks>
ReadLength()662         public int ReadLength()
663         {
664             return (int) ReadRawVarint32();
665         }
666 
667         /// <summary>
668         /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
669         /// the tag is consumed and the method returns <c>true</c>; otherwise, the
670         /// stream is left in the original position and the method returns <c>false</c>.
671         /// </summary>
MaybeConsumeTag(uint tag)672         public bool MaybeConsumeTag(uint tag)
673         {
674             if (PeekTag() == tag)
675             {
676                 hasNextTag = false;
677                 return true;
678             }
679             return false;
680         }
681 
682         #endregion
683 
684         #region Underlying reading primitives
685 
686         /// <summary>
687         /// Same code as ReadRawVarint32, but read each byte individually, checking for
688         /// buffer overflow.
689         /// </summary>
SlowReadRawVarint32()690         private uint SlowReadRawVarint32()
691         {
692             int tmp = ReadRawByte();
693             if (tmp < 128)
694             {
695                 return (uint) tmp;
696             }
697             int result = tmp & 0x7f;
698             if ((tmp = ReadRawByte()) < 128)
699             {
700                 result |= tmp << 7;
701             }
702             else
703             {
704                 result |= (tmp & 0x7f) << 7;
705                 if ((tmp = ReadRawByte()) < 128)
706                 {
707                     result |= tmp << 14;
708                 }
709                 else
710                 {
711                     result |= (tmp & 0x7f) << 14;
712                     if ((tmp = ReadRawByte()) < 128)
713                     {
714                         result |= tmp << 21;
715                     }
716                     else
717                     {
718                         result |= (tmp & 0x7f) << 21;
719                         result |= (tmp = ReadRawByte()) << 28;
720                         if (tmp >= 128)
721                         {
722                             // Discard upper 32 bits.
723                             for (int i = 0; i < 5; i++)
724                             {
725                                 if (ReadRawByte() < 128)
726                                 {
727                                     return (uint) result;
728                                 }
729                             }
730                             throw InvalidProtocolBufferException.MalformedVarint();
731                         }
732                     }
733                 }
734             }
735             return (uint) result;
736         }
737 
738         /// <summary>
739         /// Reads a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
740         /// This method is optimised for the case where we've got lots of data in the buffer.
741         /// That means we can check the size just once, then just read directly from the buffer
742         /// without constant rechecking of the buffer length.
743         /// </summary>
ReadRawVarint32()744         internal uint ReadRawVarint32()
745         {
746             if (bufferPos + 5 > bufferSize)
747             {
748                 return SlowReadRawVarint32();
749             }
750 
751             int tmp = buffer[bufferPos++];
752             if (tmp < 128)
753             {
754                 return (uint) tmp;
755             }
756             int result = tmp & 0x7f;
757             if ((tmp = buffer[bufferPos++]) < 128)
758             {
759                 result |= tmp << 7;
760             }
761             else
762             {
763                 result |= (tmp & 0x7f) << 7;
764                 if ((tmp = buffer[bufferPos++]) < 128)
765                 {
766                     result |= tmp << 14;
767                 }
768                 else
769                 {
770                     result |= (tmp & 0x7f) << 14;
771                     if ((tmp = buffer[bufferPos++]) < 128)
772                     {
773                         result |= tmp << 21;
774                     }
775                     else
776                     {
777                         result |= (tmp & 0x7f) << 21;
778                         result |= (tmp = buffer[bufferPos++]) << 28;
779                         if (tmp >= 128)
780                         {
781                             // Discard upper 32 bits.
782                             // Note that this has to use ReadRawByte() as we only ensure we've
783                             // got at least 5 bytes at the start of the method. This lets us
784                             // use the fast path in more cases, and we rarely hit this section of code.
785                             for (int i = 0; i < 5; i++)
786                             {
787                                 if (ReadRawByte() < 128)
788                                 {
789                                     return (uint) result;
790                                 }
791                             }
792                             throw InvalidProtocolBufferException.MalformedVarint();
793                         }
794                     }
795                 }
796             }
797             return (uint) result;
798         }
799 
800         /// <summary>
801         /// Reads a varint from the input one byte at a time, so that it does not
802         /// read any bytes after the end of the varint. If you simply wrapped the
803         /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
804         /// then you would probably end up reading past the end of the varint since
805         /// CodedInputStream buffers its input.
806         /// </summary>
807         /// <param name="input"></param>
808         /// <returns></returns>
ReadRawVarint32(Stream input)809         internal static uint ReadRawVarint32(Stream input)
810         {
811             int result = 0;
812             int offset = 0;
813             for (; offset < 32; offset += 7)
814             {
815                 int b = input.ReadByte();
816                 if (b == -1)
817                 {
818                     throw InvalidProtocolBufferException.TruncatedMessage();
819                 }
820                 result |= (b & 0x7f) << offset;
821                 if ((b & 0x80) == 0)
822                 {
823                     return (uint) result;
824                 }
825             }
826             // Keep reading up to 64 bits.
827             for (; offset < 64; offset += 7)
828             {
829                 int b = input.ReadByte();
830                 if (b == -1)
831                 {
832                     throw InvalidProtocolBufferException.TruncatedMessage();
833                 }
834                 if ((b & 0x80) == 0)
835                 {
836                     return (uint) result;
837                 }
838             }
839             throw InvalidProtocolBufferException.MalformedVarint();
840         }
841 
842         /// <summary>
843         /// Reads a raw varint from the stream.
844         /// </summary>
ReadRawVarint64()845         internal ulong ReadRawVarint64()
846         {
847             int shift = 0;
848             ulong result = 0;
849             while (shift < 64)
850             {
851                 byte b = ReadRawByte();
852                 result |= (ulong) (b & 0x7F) << shift;
853                 if ((b & 0x80) == 0)
854                 {
855                     return result;
856                 }
857                 shift += 7;
858             }
859             throw InvalidProtocolBufferException.MalformedVarint();
860         }
861 
862         /// <summary>
863         /// Reads a 32-bit little-endian integer from the stream.
864         /// </summary>
ReadRawLittleEndian32()865         internal uint ReadRawLittleEndian32()
866         {
867             uint b1 = ReadRawByte();
868             uint b2 = ReadRawByte();
869             uint b3 = ReadRawByte();
870             uint b4 = ReadRawByte();
871             return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
872         }
873 
874         /// <summary>
875         /// Reads a 64-bit little-endian integer from the stream.
876         /// </summary>
ReadRawLittleEndian64()877         internal ulong ReadRawLittleEndian64()
878         {
879             ulong b1 = ReadRawByte();
880             ulong b2 = ReadRawByte();
881             ulong b3 = ReadRawByte();
882             ulong b4 = ReadRawByte();
883             ulong b5 = ReadRawByte();
884             ulong b6 = ReadRawByte();
885             ulong b7 = ReadRawByte();
886             ulong b8 = ReadRawByte();
887             return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
888                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
889         }
890 
891         /// <summary>
892         /// Decode a 32-bit value with ZigZag encoding.
893         /// </summary>
894         /// <remarks>
895         /// ZigZag encodes signed integers into values that can be efficiently
896         /// encoded with varint.  (Otherwise, negative values must be
897         /// sign-extended to 64 bits to be varint encoded, thus always taking
898         /// 10 bytes on the wire.)
899         /// </remarks>
DecodeZigZag32(uint n)900         internal static int DecodeZigZag32(uint n)
901         {
902             return (int)(n >> 1) ^ -(int)(n & 1);
903         }
904 
905         /// <summary>
906         /// Decode a 32-bit value with ZigZag encoding.
907         /// </summary>
908         /// <remarks>
909         /// ZigZag encodes signed integers into values that can be efficiently
910         /// encoded with varint.  (Otherwise, negative values must be
911         /// sign-extended to 64 bits to be varint encoded, thus always taking
912         /// 10 bytes on the wire.)
913         /// </remarks>
DecodeZigZag64(ulong n)914         internal static long DecodeZigZag64(ulong n)
915         {
916             return (long)(n >> 1) ^ -(long)(n & 1);
917         }
918         #endregion
919 
920         #region Internal reading and buffer management
921 
922         /// <summary>
923         /// Sets currentLimit to (current position) + byteLimit. This is called
924         /// when descending into a length-delimited embedded message. The previous
925         /// limit is returned.
926         /// </summary>
927         /// <returns>The old limit.</returns>
PushLimit(int byteLimit)928         internal int PushLimit(int byteLimit)
929         {
930             if (byteLimit < 0)
931             {
932                 throw InvalidProtocolBufferException.NegativeSize();
933             }
934             byteLimit += totalBytesRetired + bufferPos;
935             int oldLimit = currentLimit;
936             if (byteLimit > oldLimit)
937             {
938                 throw InvalidProtocolBufferException.TruncatedMessage();
939             }
940             currentLimit = byteLimit;
941 
942             RecomputeBufferSizeAfterLimit();
943 
944             return oldLimit;
945         }
946 
RecomputeBufferSizeAfterLimit()947         private void RecomputeBufferSizeAfterLimit()
948         {
949             bufferSize += bufferSizeAfterLimit;
950             int bufferEnd = totalBytesRetired + bufferSize;
951             if (bufferEnd > currentLimit)
952             {
953                 // Limit is in current buffer.
954                 bufferSizeAfterLimit = bufferEnd - currentLimit;
955                 bufferSize -= bufferSizeAfterLimit;
956             }
957             else
958             {
959                 bufferSizeAfterLimit = 0;
960             }
961         }
962 
963         /// <summary>
964         /// Discards the current limit, returning the previous limit.
965         /// </summary>
PopLimit(int oldLimit)966         internal void PopLimit(int oldLimit)
967         {
968             currentLimit = oldLimit;
969             RecomputeBufferSizeAfterLimit();
970         }
971 
972         /// <summary>
973         /// Returns whether or not all the data before the limit has been read.
974         /// </summary>
975         /// <returns></returns>
976         internal bool ReachedLimit
977         {
978             get
979             {
980                 if (currentLimit == int.MaxValue)
981                 {
982                     return false;
983                 }
984                 int currentAbsolutePosition = totalBytesRetired + bufferPos;
985                 return currentAbsolutePosition >= currentLimit;
986             }
987         }
988 
989         /// <summary>
990         /// Returns true if the stream has reached the end of the input. This is the
991         /// case if either the end of the underlying input source has been reached or
992         /// the stream has reached a limit created using PushLimit.
993         /// </summary>
994         public bool IsAtEnd
995         {
996             get { return bufferPos == bufferSize && !RefillBuffer(false); }
997         }
998 
999         /// <summary>
1000         /// Called when buffer is empty to read more bytes from the
1001         /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1002         /// either there will be at least one byte in the buffer when it returns
1003         /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,
1004         /// RefillBuffer() returns false if no more bytes were available.
1005         /// </summary>
1006         /// <param name="mustSucceed"></param>
1007         /// <returns></returns>
RefillBuffer(bool mustSucceed)1008         private bool RefillBuffer(bool mustSucceed)
1009         {
1010             if (bufferPos < bufferSize)
1011             {
1012                 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1013             }
1014 
1015             if (totalBytesRetired + bufferSize == currentLimit)
1016             {
1017                 // Oops, we hit a limit.
1018                 if (mustSucceed)
1019                 {
1020                     throw InvalidProtocolBufferException.TruncatedMessage();
1021                 }
1022                 else
1023                 {
1024                     return false;
1025                 }
1026             }
1027 
1028             totalBytesRetired += bufferSize;
1029 
1030             bufferPos = 0;
1031             bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1032             if (bufferSize < 0)
1033             {
1034                 throw new InvalidOperationException("Stream.Read returned a negative count");
1035             }
1036             if (bufferSize == 0)
1037             {
1038                 if (mustSucceed)
1039                 {
1040                     throw InvalidProtocolBufferException.TruncatedMessage();
1041                 }
1042                 else
1043                 {
1044                     return false;
1045                 }
1046             }
1047             else
1048             {
1049                 RecomputeBufferSizeAfterLimit();
1050                 int totalBytesRead =
1051                     totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1052                 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1053                 {
1054                     throw InvalidProtocolBufferException.SizeLimitExceeded();
1055                 }
1056                 return true;
1057             }
1058         }
1059 
1060         /// <summary>
1061         /// Read one byte from the input.
1062         /// </summary>
1063         /// <exception cref="InvalidProtocolBufferException">
1064         /// the end of the stream or the current limit was reached
1065         /// </exception>
ReadRawByte()1066         internal byte ReadRawByte()
1067         {
1068             if (bufferPos == bufferSize)
1069             {
1070                 RefillBuffer(true);
1071             }
1072             return buffer[bufferPos++];
1073         }
1074 
1075         /// <summary>
1076         /// Reads a fixed size of bytes from the input.
1077         /// </summary>
1078         /// <exception cref="InvalidProtocolBufferException">
1079         /// the end of the stream or the current limit was reached
1080         /// </exception>
ReadRawBytes(int size)1081         internal byte[] ReadRawBytes(int size)
1082         {
1083             if (size < 0)
1084             {
1085                 throw InvalidProtocolBufferException.NegativeSize();
1086             }
1087 
1088             if (totalBytesRetired + bufferPos + size > currentLimit)
1089             {
1090                 // Read to the end of the stream (up to the current limit) anyway.
1091                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1092                 // Then fail.
1093                 throw InvalidProtocolBufferException.TruncatedMessage();
1094             }
1095 
1096             if (size <= bufferSize - bufferPos)
1097             {
1098                 // We have all the bytes we need already.
1099                 byte[] bytes = new byte[size];
1100                 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
1101                 bufferPos += size;
1102                 return bytes;
1103             }
1104             else if (size < buffer.Length)
1105             {
1106                 // Reading more bytes than are in the buffer, but not an excessive number
1107                 // of bytes.  We can safely allocate the resulting array ahead of time.
1108 
1109                 // First copy what we have.
1110                 byte[] bytes = new byte[size];
1111                 int pos = bufferSize - bufferPos;
1112                 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
1113                 bufferPos = bufferSize;
1114 
1115                 // We want to use RefillBuffer() and then copy from the buffer into our
1116                 // byte array rather than reading directly into our byte array because
1117                 // the input may be unbuffered.
1118                 RefillBuffer(true);
1119 
1120                 while (size - pos > bufferSize)
1121                 {
1122                     Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
1123                     pos += bufferSize;
1124                     bufferPos = bufferSize;
1125                     RefillBuffer(true);
1126                 }
1127 
1128                 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
1129                 bufferPos = size - pos;
1130 
1131                 return bytes;
1132             }
1133             else
1134             {
1135                 // The size is very large.  For security reasons, we can't allocate the
1136                 // entire byte array yet.  The size comes directly from the input, so a
1137                 // maliciously-crafted message could provide a bogus very large size in
1138                 // order to trick the app into allocating a lot of memory.  We avoid this
1139                 // by allocating and reading only a small chunk at a time, so that the
1140                 // malicious message must actually *be* extremely large to cause
1141                 // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
1142 
1143                 // Remember the buffer markers since we'll have to copy the bytes out of
1144                 // it later.
1145                 int originalBufferPos = bufferPos;
1146                 int originalBufferSize = bufferSize;
1147 
1148                 // Mark the current buffer consumed.
1149                 totalBytesRetired += bufferSize;
1150                 bufferPos = 0;
1151                 bufferSize = 0;
1152 
1153                 // Read all the rest of the bytes we need.
1154                 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1155                 List<byte[]> chunks = new List<byte[]>();
1156 
1157                 while (sizeLeft > 0)
1158                 {
1159                     byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
1160                     int pos = 0;
1161                     while (pos < chunk.Length)
1162                     {
1163                         int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1164                         if (n <= 0)
1165                         {
1166                             throw InvalidProtocolBufferException.TruncatedMessage();
1167                         }
1168                         totalBytesRetired += n;
1169                         pos += n;
1170                     }
1171                     sizeLeft -= chunk.Length;
1172                     chunks.Add(chunk);
1173                 }
1174 
1175                 // OK, got everything.  Now concatenate it all into one buffer.
1176                 byte[] bytes = new byte[size];
1177 
1178                 // Start by copying the leftover bytes from this.buffer.
1179                 int newPos = originalBufferSize - originalBufferPos;
1180                 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
1181 
1182                 // And now all the chunks.
1183                 foreach (byte[] chunk in chunks)
1184                 {
1185                     Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
1186                     newPos += chunk.Length;
1187                 }
1188 
1189                 // Done.
1190                 return bytes;
1191             }
1192         }
1193 
1194         /// <summary>
1195         /// Reads and discards <paramref name="size"/> bytes.
1196         /// </summary>
1197         /// <exception cref="InvalidProtocolBufferException">the end of the stream
1198         /// or the current limit was reached</exception>
SkipRawBytes(int size)1199         private void SkipRawBytes(int size)
1200         {
1201             if (size < 0)
1202             {
1203                 throw InvalidProtocolBufferException.NegativeSize();
1204             }
1205 
1206             if (totalBytesRetired + bufferPos + size > currentLimit)
1207             {
1208                 // Read to the end of the stream anyway.
1209                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1210                 // Then fail.
1211                 throw InvalidProtocolBufferException.TruncatedMessage();
1212             }
1213 
1214             if (size <= bufferSize - bufferPos)
1215             {
1216                 // We have all the bytes we need already.
1217                 bufferPos += size;
1218             }
1219             else
1220             {
1221                 // Skipping more bytes than are in the buffer.  First skip what we have.
1222                 int pos = bufferSize - bufferPos;
1223 
1224                 // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize)
1225                 // totalBytesRetired += pos;
1226                 totalBytesRetired += bufferSize;
1227 
1228                 bufferPos = 0;
1229                 bufferSize = 0;
1230 
1231                 // Then skip directly from the InputStream for the rest.
1232                 if (pos < size)
1233                 {
1234                     if (input == null)
1235                     {
1236                         throw InvalidProtocolBufferException.TruncatedMessage();
1237                     }
1238                     SkipImpl(size - pos);
1239                     totalBytesRetired += size - pos;
1240                 }
1241             }
1242         }
1243 
1244         /// <summary>
1245         /// Abstraction of skipping to cope with streams which can't really skip.
1246         /// </summary>
SkipImpl(int amountToSkip)1247         private void SkipImpl(int amountToSkip)
1248         {
1249             if (input.CanSeek)
1250             {
1251                 long previousPosition = input.Position;
1252                 input.Position += amountToSkip;
1253                 if (input.Position != previousPosition + amountToSkip)
1254                 {
1255                     throw InvalidProtocolBufferException.TruncatedMessage();
1256                 }
1257             }
1258             else
1259             {
1260                 byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)];
1261                 while (amountToSkip > 0)
1262                 {
1263                     int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip));
1264                     if (bytesRead <= 0)
1265                     {
1266                         throw InvalidProtocolBufferException.TruncatedMessage();
1267                     }
1268                     amountToSkip -= bytesRead;
1269                 }
1270             }
1271         }
1272 
1273         #endregion
1274     }
1275 }