• 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. If the enum is valid for type T,
616         /// then the ref value is set and it returns true.  Otherwise the unknown output
617         /// value is set and this method returns false.
618         /// </summary>
ReadEnum()619         public int ReadEnum()
620         {
621             // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
622             return (int) ReadRawVarint32();
623         }
624 
625         /// <summary>
626         /// Reads an sfixed32 field value from the stream.
627         /// </summary>
ReadSFixed32()628         public int ReadSFixed32()
629         {
630             return (int) ReadRawLittleEndian32();
631         }
632 
633         /// <summary>
634         /// Reads an sfixed64 field value from the stream.
635         /// </summary>
ReadSFixed64()636         public long ReadSFixed64()
637         {
638             return (long) ReadRawLittleEndian64();
639         }
640 
641         /// <summary>
642         /// Reads an sint32 field value from the stream.
643         /// </summary>
ReadSInt32()644         public int ReadSInt32()
645         {
646             return DecodeZigZag32(ReadRawVarint32());
647         }
648 
649         /// <summary>
650         /// Reads an sint64 field value from the stream.
651         /// </summary>
ReadSInt64()652         public long ReadSInt64()
653         {
654             return DecodeZigZag64(ReadRawVarint64());
655         }
656 
657         /// <summary>
658         /// Reads a length for length-delimited data.
659         /// </summary>
660         /// <remarks>
661         /// This is internally just reading a varint, but this method exists
662         /// to make the calling code clearer.
663         /// </remarks>
ReadLength()664         public int ReadLength()
665         {
666             return (int) ReadRawVarint32();
667         }
668 
669         /// <summary>
670         /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
671         /// the tag is consumed and the method returns <c>true</c>; otherwise, the
672         /// stream is left in the original position and the method returns <c>false</c>.
673         /// </summary>
MaybeConsumeTag(uint tag)674         public bool MaybeConsumeTag(uint tag)
675         {
676             if (PeekTag() == tag)
677             {
678                 hasNextTag = false;
679                 return true;
680             }
681             return false;
682         }
683 
684         #endregion
685 
686         #region Underlying reading primitives
687 
688         /// <summary>
689         /// Same code as ReadRawVarint32, but read each byte individually, checking for
690         /// buffer overflow.
691         /// </summary>
SlowReadRawVarint32()692         private uint SlowReadRawVarint32()
693         {
694             int tmp = ReadRawByte();
695             if (tmp < 128)
696             {
697                 return (uint) tmp;
698             }
699             int result = tmp & 0x7f;
700             if ((tmp = ReadRawByte()) < 128)
701             {
702                 result |= tmp << 7;
703             }
704             else
705             {
706                 result |= (tmp & 0x7f) << 7;
707                 if ((tmp = ReadRawByte()) < 128)
708                 {
709                     result |= tmp << 14;
710                 }
711                 else
712                 {
713                     result |= (tmp & 0x7f) << 14;
714                     if ((tmp = ReadRawByte()) < 128)
715                     {
716                         result |= tmp << 21;
717                     }
718                     else
719                     {
720                         result |= (tmp & 0x7f) << 21;
721                         result |= (tmp = ReadRawByte()) << 28;
722                         if (tmp >= 128)
723                         {
724                             // Discard upper 32 bits.
725                             for (int i = 0; i < 5; i++)
726                             {
727                                 if (ReadRawByte() < 128)
728                                 {
729                                     return (uint) result;
730                                 }
731                             }
732                             throw InvalidProtocolBufferException.MalformedVarint();
733                         }
734                     }
735                 }
736             }
737             return (uint) result;
738         }
739 
740         /// <summary>
741         /// Reads a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
742         /// This method is optimised for the case where we've got lots of data in the buffer.
743         /// That means we can check the size just once, then just read directly from the buffer
744         /// without constant rechecking of the buffer length.
745         /// </summary>
ReadRawVarint32()746         internal uint ReadRawVarint32()
747         {
748             if (bufferPos + 5 > bufferSize)
749             {
750                 return SlowReadRawVarint32();
751             }
752 
753             int tmp = buffer[bufferPos++];
754             if (tmp < 128)
755             {
756                 return (uint) tmp;
757             }
758             int result = tmp & 0x7f;
759             if ((tmp = buffer[bufferPos++]) < 128)
760             {
761                 result |= tmp << 7;
762             }
763             else
764             {
765                 result |= (tmp & 0x7f) << 7;
766                 if ((tmp = buffer[bufferPos++]) < 128)
767                 {
768                     result |= tmp << 14;
769                 }
770                 else
771                 {
772                     result |= (tmp & 0x7f) << 14;
773                     if ((tmp = buffer[bufferPos++]) < 128)
774                     {
775                         result |= tmp << 21;
776                     }
777                     else
778                     {
779                         result |= (tmp & 0x7f) << 21;
780                         result |= (tmp = buffer[bufferPos++]) << 28;
781                         if (tmp >= 128)
782                         {
783                             // Discard upper 32 bits.
784                             // Note that this has to use ReadRawByte() as we only ensure we've
785                             // got at least 5 bytes at the start of the method. This lets us
786                             // use the fast path in more cases, and we rarely hit this section of code.
787                             for (int i = 0; i < 5; i++)
788                             {
789                                 if (ReadRawByte() < 128)
790                                 {
791                                     return (uint) result;
792                                 }
793                             }
794                             throw InvalidProtocolBufferException.MalformedVarint();
795                         }
796                     }
797                 }
798             }
799             return (uint) result;
800         }
801 
802         /// <summary>
803         /// Reads a varint from the input one byte at a time, so that it does not
804         /// read any bytes after the end of the varint. If you simply wrapped the
805         /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
806         /// then you would probably end up reading past the end of the varint since
807         /// CodedInputStream buffers its input.
808         /// </summary>
809         /// <param name="input"></param>
810         /// <returns></returns>
ReadRawVarint32(Stream input)811         internal static uint ReadRawVarint32(Stream input)
812         {
813             int result = 0;
814             int offset = 0;
815             for (; offset < 32; offset += 7)
816             {
817                 int b = input.ReadByte();
818                 if (b == -1)
819                 {
820                     throw InvalidProtocolBufferException.TruncatedMessage();
821                 }
822                 result |= (b & 0x7f) << offset;
823                 if ((b & 0x80) == 0)
824                 {
825                     return (uint) result;
826                 }
827             }
828             // Keep reading up to 64 bits.
829             for (; offset < 64; offset += 7)
830             {
831                 int b = input.ReadByte();
832                 if (b == -1)
833                 {
834                     throw InvalidProtocolBufferException.TruncatedMessage();
835                 }
836                 if ((b & 0x80) == 0)
837                 {
838                     return (uint) result;
839                 }
840             }
841             throw InvalidProtocolBufferException.MalformedVarint();
842         }
843 
844         /// <summary>
845         /// Reads a raw varint from the stream.
846         /// </summary>
ReadRawVarint64()847         internal ulong ReadRawVarint64()
848         {
849             int shift = 0;
850             ulong result = 0;
851             while (shift < 64)
852             {
853                 byte b = ReadRawByte();
854                 result |= (ulong) (b & 0x7F) << shift;
855                 if ((b & 0x80) == 0)
856                 {
857                     return result;
858                 }
859                 shift += 7;
860             }
861             throw InvalidProtocolBufferException.MalformedVarint();
862         }
863 
864         /// <summary>
865         /// Reads a 32-bit little-endian integer from the stream.
866         /// </summary>
ReadRawLittleEndian32()867         internal uint ReadRawLittleEndian32()
868         {
869             uint b1 = ReadRawByte();
870             uint b2 = ReadRawByte();
871             uint b3 = ReadRawByte();
872             uint b4 = ReadRawByte();
873             return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
874         }
875 
876         /// <summary>
877         /// Reads a 64-bit little-endian integer from the stream.
878         /// </summary>
ReadRawLittleEndian64()879         internal ulong ReadRawLittleEndian64()
880         {
881             ulong b1 = ReadRawByte();
882             ulong b2 = ReadRawByte();
883             ulong b3 = ReadRawByte();
884             ulong b4 = ReadRawByte();
885             ulong b5 = ReadRawByte();
886             ulong b6 = ReadRawByte();
887             ulong b7 = ReadRawByte();
888             ulong b8 = ReadRawByte();
889             return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
890                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
891         }
892 
893         /// <summary>
894         /// Decode a 32-bit value with ZigZag encoding.
895         /// </summary>
896         /// <remarks>
897         /// ZigZag encodes signed integers into values that can be efficiently
898         /// encoded with varint.  (Otherwise, negative values must be
899         /// sign-extended to 64 bits to be varint encoded, thus always taking
900         /// 10 bytes on the wire.)
901         /// </remarks>
DecodeZigZag32(uint n)902         internal static int DecodeZigZag32(uint n)
903         {
904             return (int)(n >> 1) ^ -(int)(n & 1);
905         }
906 
907         /// <summary>
908         /// Decode a 32-bit value with ZigZag encoding.
909         /// </summary>
910         /// <remarks>
911         /// ZigZag encodes signed integers into values that can be efficiently
912         /// encoded with varint.  (Otherwise, negative values must be
913         /// sign-extended to 64 bits to be varint encoded, thus always taking
914         /// 10 bytes on the wire.)
915         /// </remarks>
DecodeZigZag64(ulong n)916         internal static long DecodeZigZag64(ulong n)
917         {
918             return (long)(n >> 1) ^ -(long)(n & 1);
919         }
920         #endregion
921 
922         #region Internal reading and buffer management
923 
924         /// <summary>
925         /// Sets currentLimit to (current position) + byteLimit. This is called
926         /// when descending into a length-delimited embedded message. The previous
927         /// limit is returned.
928         /// </summary>
929         /// <returns>The old limit.</returns>
PushLimit(int byteLimit)930         internal int PushLimit(int byteLimit)
931         {
932             if (byteLimit < 0)
933             {
934                 throw InvalidProtocolBufferException.NegativeSize();
935             }
936             byteLimit += totalBytesRetired + bufferPos;
937             int oldLimit = currentLimit;
938             if (byteLimit > oldLimit)
939             {
940                 throw InvalidProtocolBufferException.TruncatedMessage();
941             }
942             currentLimit = byteLimit;
943 
944             RecomputeBufferSizeAfterLimit();
945 
946             return oldLimit;
947         }
948 
RecomputeBufferSizeAfterLimit()949         private void RecomputeBufferSizeAfterLimit()
950         {
951             bufferSize += bufferSizeAfterLimit;
952             int bufferEnd = totalBytesRetired + bufferSize;
953             if (bufferEnd > currentLimit)
954             {
955                 // Limit is in current buffer.
956                 bufferSizeAfterLimit = bufferEnd - currentLimit;
957                 bufferSize -= bufferSizeAfterLimit;
958             }
959             else
960             {
961                 bufferSizeAfterLimit = 0;
962             }
963         }
964 
965         /// <summary>
966         /// Discards the current limit, returning the previous limit.
967         /// </summary>
PopLimit(int oldLimit)968         internal void PopLimit(int oldLimit)
969         {
970             currentLimit = oldLimit;
971             RecomputeBufferSizeAfterLimit();
972         }
973 
974         /// <summary>
975         /// Returns whether or not all the data before the limit has been read.
976         /// </summary>
977         /// <returns></returns>
978         internal bool ReachedLimit
979         {
980             get
981             {
982                 if (currentLimit == int.MaxValue)
983                 {
984                     return false;
985                 }
986                 int currentAbsolutePosition = totalBytesRetired + bufferPos;
987                 return currentAbsolutePosition >= currentLimit;
988             }
989         }
990 
991         /// <summary>
992         /// Returns true if the stream has reached the end of the input. This is the
993         /// case if either the end of the underlying input source has been reached or
994         /// the stream has reached a limit created using PushLimit.
995         /// </summary>
996         public bool IsAtEnd
997         {
998             get { return bufferPos == bufferSize && !RefillBuffer(false); }
999         }
1000 
1001         /// <summary>
1002         /// Called when buffer is empty to read more bytes from the
1003         /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1004         /// either there will be at least one byte in the buffer when it returns
1005         /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,
1006         /// RefillBuffer() returns false if no more bytes were available.
1007         /// </summary>
1008         /// <param name="mustSucceed"></param>
1009         /// <returns></returns>
RefillBuffer(bool mustSucceed)1010         private bool RefillBuffer(bool mustSucceed)
1011         {
1012             if (bufferPos < bufferSize)
1013             {
1014                 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1015             }
1016 
1017             if (totalBytesRetired + bufferSize == currentLimit)
1018             {
1019                 // Oops, we hit a limit.
1020                 if (mustSucceed)
1021                 {
1022                     throw InvalidProtocolBufferException.TruncatedMessage();
1023                 }
1024                 else
1025                 {
1026                     return false;
1027                 }
1028             }
1029 
1030             totalBytesRetired += bufferSize;
1031 
1032             bufferPos = 0;
1033             bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1034             if (bufferSize < 0)
1035             {
1036                 throw new InvalidOperationException("Stream.Read returned a negative count");
1037             }
1038             if (bufferSize == 0)
1039             {
1040                 if (mustSucceed)
1041                 {
1042                     throw InvalidProtocolBufferException.TruncatedMessage();
1043                 }
1044                 else
1045                 {
1046                     return false;
1047                 }
1048             }
1049             else
1050             {
1051                 RecomputeBufferSizeAfterLimit();
1052                 int totalBytesRead =
1053                     totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1054                 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1055                 {
1056                     throw InvalidProtocolBufferException.SizeLimitExceeded();
1057                 }
1058                 return true;
1059             }
1060         }
1061 
1062         /// <summary>
1063         /// Read one byte from the input.
1064         /// </summary>
1065         /// <exception cref="InvalidProtocolBufferException">
1066         /// the end of the stream or the current limit was reached
1067         /// </exception>
ReadRawByte()1068         internal byte ReadRawByte()
1069         {
1070             if (bufferPos == bufferSize)
1071             {
1072                 RefillBuffer(true);
1073             }
1074             return buffer[bufferPos++];
1075         }
1076 
1077         /// <summary>
1078         /// Reads a fixed size of bytes from the input.
1079         /// </summary>
1080         /// <exception cref="InvalidProtocolBufferException">
1081         /// the end of the stream or the current limit was reached
1082         /// </exception>
ReadRawBytes(int size)1083         internal byte[] ReadRawBytes(int size)
1084         {
1085             if (size < 0)
1086             {
1087                 throw InvalidProtocolBufferException.NegativeSize();
1088             }
1089 
1090             if (totalBytesRetired + bufferPos + size > currentLimit)
1091             {
1092                 // Read to the end of the stream (up to the current limit) anyway.
1093                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1094                 // Then fail.
1095                 throw InvalidProtocolBufferException.TruncatedMessage();
1096             }
1097 
1098             if (size <= bufferSize - bufferPos)
1099             {
1100                 // We have all the bytes we need already.
1101                 byte[] bytes = new byte[size];
1102                 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
1103                 bufferPos += size;
1104                 return bytes;
1105             }
1106             else if (size < buffer.Length)
1107             {
1108                 // Reading more bytes than are in the buffer, but not an excessive number
1109                 // of bytes.  We can safely allocate the resulting array ahead of time.
1110 
1111                 // First copy what we have.
1112                 byte[] bytes = new byte[size];
1113                 int pos = bufferSize - bufferPos;
1114                 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
1115                 bufferPos = bufferSize;
1116 
1117                 // We want to use RefillBuffer() and then copy from the buffer into our
1118                 // byte array rather than reading directly into our byte array because
1119                 // the input may be unbuffered.
1120                 RefillBuffer(true);
1121 
1122                 while (size - pos > bufferSize)
1123                 {
1124                     Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
1125                     pos += bufferSize;
1126                     bufferPos = bufferSize;
1127                     RefillBuffer(true);
1128                 }
1129 
1130                 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
1131                 bufferPos = size - pos;
1132 
1133                 return bytes;
1134             }
1135             else
1136             {
1137                 // The size is very large.  For security reasons, we can't allocate the
1138                 // entire byte array yet.  The size comes directly from the input, so a
1139                 // maliciously-crafted message could provide a bogus very large size in
1140                 // order to trick the app into allocating a lot of memory.  We avoid this
1141                 // by allocating and reading only a small chunk at a time, so that the
1142                 // malicious message must actually *be* extremely large to cause
1143                 // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
1144 
1145                 // Remember the buffer markers since we'll have to copy the bytes out of
1146                 // it later.
1147                 int originalBufferPos = bufferPos;
1148                 int originalBufferSize = bufferSize;
1149 
1150                 // Mark the current buffer consumed.
1151                 totalBytesRetired += bufferSize;
1152                 bufferPos = 0;
1153                 bufferSize = 0;
1154 
1155                 // Read all the rest of the bytes we need.
1156                 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1157                 List<byte[]> chunks = new List<byte[]>();
1158 
1159                 while (sizeLeft > 0)
1160                 {
1161                     byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
1162                     int pos = 0;
1163                     while (pos < chunk.Length)
1164                     {
1165                         int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1166                         if (n <= 0)
1167                         {
1168                             throw InvalidProtocolBufferException.TruncatedMessage();
1169                         }
1170                         totalBytesRetired += n;
1171                         pos += n;
1172                     }
1173                     sizeLeft -= chunk.Length;
1174                     chunks.Add(chunk);
1175                 }
1176 
1177                 // OK, got everything.  Now concatenate it all into one buffer.
1178                 byte[] bytes = new byte[size];
1179 
1180                 // Start by copying the leftover bytes from this.buffer.
1181                 int newPos = originalBufferSize - originalBufferPos;
1182                 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
1183 
1184                 // And now all the chunks.
1185                 foreach (byte[] chunk in chunks)
1186                 {
1187                     Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
1188                     newPos += chunk.Length;
1189                 }
1190 
1191                 // Done.
1192                 return bytes;
1193             }
1194         }
1195 
1196         /// <summary>
1197         /// Reads and discards <paramref name="size"/> bytes.
1198         /// </summary>
1199         /// <exception cref="InvalidProtocolBufferException">the end of the stream
1200         /// or the current limit was reached</exception>
SkipRawBytes(int size)1201         private void SkipRawBytes(int size)
1202         {
1203             if (size < 0)
1204             {
1205                 throw InvalidProtocolBufferException.NegativeSize();
1206             }
1207 
1208             if (totalBytesRetired + bufferPos + size > currentLimit)
1209             {
1210                 // Read to the end of the stream anyway.
1211                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1212                 // Then fail.
1213                 throw InvalidProtocolBufferException.TruncatedMessage();
1214             }
1215 
1216             if (size <= bufferSize - bufferPos)
1217             {
1218                 // We have all the bytes we need already.
1219                 bufferPos += size;
1220             }
1221             else
1222             {
1223                 // Skipping more bytes than are in the buffer.  First skip what we have.
1224                 int pos = bufferSize - bufferPos;
1225 
1226                 // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize)
1227                 // totalBytesRetired += pos;
1228                 totalBytesRetired += bufferSize;
1229 
1230                 bufferPos = 0;
1231                 bufferSize = 0;
1232 
1233                 // Then skip directly from the InputStream for the rest.
1234                 if (pos < size)
1235                 {
1236                     if (input == null)
1237                     {
1238                         throw InvalidProtocolBufferException.TruncatedMessage();
1239                     }
1240                     SkipImpl(size - pos);
1241                     totalBytesRetired += size - pos;
1242                 }
1243             }
1244         }
1245 
1246         /// <summary>
1247         /// Abstraction of skipping to cope with streams which can't really skip.
1248         /// </summary>
SkipImpl(int amountToSkip)1249         private void SkipImpl(int amountToSkip)
1250         {
1251             if (input.CanSeek)
1252             {
1253                 long previousPosition = input.Position;
1254                 input.Position += amountToSkip;
1255                 if (input.Position != previousPosition + amountToSkip)
1256                 {
1257                     throw InvalidProtocolBufferException.TruncatedMessage();
1258                 }
1259             }
1260             else
1261             {
1262                 byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)];
1263                 while (amountToSkip > 0)
1264                 {
1265                     int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip));
1266                     if (bytesRead <= 0)
1267                     {
1268                         throw InvalidProtocolBufferException.TruncatedMessage();
1269                     }
1270                     amountToSkip -= bytesRead;
1271                 }
1272             }
1273         }
1274 
1275         #endregion
1276     }
1277 }