Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/2.4.1/ProtobufCS/src/ProtocolBuffers/CodedInputStream.cs @ 11211

Last change on this file since 11211 was 8295, checked in by abeham, 12 years ago

#1897:

  • Removed protocol buffers 0.9.1
  • Added protocol buffers 2.4.1
  • Updated proto processing command
File size: 64.0 KB
Line 
1#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13//     * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//     * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19//     * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System;
38using System.Collections.Generic;
39using System.IO;
40using System.Text;
41using Google.ProtocolBuffers.Descriptors;
42
43namespace Google.ProtocolBuffers
44{
45    /// <summary>
46    /// Readings and decodes protocol message fields.
47    /// </summary>
48    /// <remarks>
49    /// This class contains two kinds of methods:  methods that read specific
50    /// protocol message constructs and field types (e.g. ReadTag and
51    /// ReadInt32) and methods that read low-level values (e.g.
52    /// ReadRawVarint32 and ReadRawBytes).  If you are reading encoded protocol
53    /// messages, you should use the former methods, but if you are reading some
54    /// other format of your own design, use the latter. The names of the former
55    /// methods are taken from the protocol buffer type names, not .NET types.
56    /// (Hence ReadFloat instead of ReadSingle, and ReadBool instead of ReadBoolean.)
57    ///
58    /// TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly,
59    /// set at construction time.
60    /// </remarks>
61    public sealed class CodedInputStream : ICodedInputStream
62    {
63        private readonly byte[] buffer;
64        private int bufferSize;
65        private int bufferSizeAfterLimit = 0;
66        private int bufferPos = 0;
67        private readonly Stream input;
68        private uint lastTag = 0;
69
70        private uint nextTag = 0;
71        private bool hasNextTag = false;
72
73        internal const int DefaultRecursionLimit = 64;
74        internal const int DefaultSizeLimit = 64 << 20; // 64MB
75        public const int BufferSize = 4096;
76
77        /// <summary>
78        /// The total number of bytes read before the current buffer. The
79        /// total bytes read up to the current position can be computed as
80        /// totalBytesRetired + bufferPos.
81        /// </summary>
82        private int totalBytesRetired = 0;
83
84        /// <summary>
85        /// The absolute position of the end of the current message.
86        /// </summary>
87        private int currentLimit = int.MaxValue;
88
89        /// <summary>
90        /// <see cref="SetRecursionLimit"/>
91        /// </summary>
92        private int recursionDepth = 0;
93
94        private int recursionLimit = DefaultRecursionLimit;
95
96        /// <summary>
97        /// <see cref="SetSizeLimit"/>
98        /// </summary>
99        private int sizeLimit = DefaultSizeLimit;
100
101        #region Construction
102
103        /// <summary>
104        /// Creates a new CodedInputStream reading data from the given
105        /// stream.
106        /// </summary>
107        public static CodedInputStream CreateInstance(Stream input)
108        {
109            return new CodedInputStream(input);
110        }
111
112        /// <summary>
113        /// Creates a new CodedInputStream reading data from the given
114        /// byte array.
115        /// </summary>
116        public static CodedInputStream CreateInstance(byte[] buf)
117        {
118            return new CodedInputStream(buf, 0, buf.Length);
119        }
120
121        /// <summary>
122        /// Creates a new CodedInputStream that reads from the given
123        /// byte array slice.
124        /// </summary>
125        public static CodedInputStream CreateInstance(byte[] buf, int offset, int length)
126        {
127            return new CodedInputStream(buf, offset, length);
128        }
129
130        private CodedInputStream(byte[] buffer, int offset, int length)
131        {
132            this.buffer = buffer;
133            this.bufferPos = offset;
134            this.bufferSize = offset + length;
135            this.input = null;
136        }
137
138        private CodedInputStream(Stream input)
139        {
140            this.buffer = new byte[BufferSize];
141            this.bufferSize = 0;
142            this.input = input;
143        }
144
145        #endregion
146
147        void ICodedInputStream.ReadMessageStart() { }
148        void ICodedInputStream.ReadMessageEnd() { }
149
150        #region Validation
151
152        /// <summary>
153        /// Verifies that the last call to ReadTag() returned the given tag value.
154        /// This is used to verify that a nested group ended with the correct
155        /// end tag.
156        /// </summary>
157        /// <exception cref="InvalidProtocolBufferException">The last
158        /// tag read was not the one specified</exception>
159        [CLSCompliant(false)]
160        public void CheckLastTagWas(uint value)
161        {
162            if (lastTag != value)
163            {
164                throw InvalidProtocolBufferException.InvalidEndTag();
165            }
166        }
167
168        #endregion
169
170        #region Reading of tags etc
171
172        /// <summary>
173        /// Attempt to peek at the next field tag.
174        /// </summary>
175        [CLSCompliant(false)]
176        public bool PeekNextTag(out uint fieldTag, out string fieldName)
177        {
178            if (hasNextTag)
179            {
180                fieldName = null;
181                fieldTag = nextTag;
182                return true;
183            }
184
185            uint savedLast = lastTag;
186            hasNextTag = ReadTag(out nextTag, out fieldName);
187            lastTag = savedLast;
188            fieldTag = nextTag;
189            return hasNextTag;
190        }
191
192        /// <summary>
193        /// Attempt to read a field tag, returning false if we have reached the end
194        /// of the input data.
195        /// </summary>
196        /// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
197        /// <param name="fieldName">Not Supported - For protobuffer streams, this parameter is always null</param>
198        /// <returns>true if the next fieldTag was read</returns>
199        [CLSCompliant(false)]
200        public bool ReadTag(out uint fieldTag, out string fieldName)
201        {
202            fieldName = null;
203
204            if (hasNextTag)
205            {
206                fieldTag = nextTag;
207                lastTag = fieldTag;
208                hasNextTag = false;
209                return true;
210            }
211
212            if (IsAtEnd)
213            {
214                fieldTag = 0;
215                lastTag = fieldTag;
216                return false;
217            }
218
219            fieldTag = ReadRawVarint32();
220            lastTag = fieldTag;
221            if (lastTag == 0)
222            {
223                // If we actually read zero, that's not a valid tag.
224                throw InvalidProtocolBufferException.InvalidTag();
225            }
226            return true;
227        }
228
229        /// <summary>
230        /// Read a double field from the stream.
231        /// </summary>
232        public bool ReadDouble(ref double value)
233        {
234#if SILVERLIGHT || COMPACT_FRAMEWORK_35
235            if (BitConverter.IsLittleEndian && 8 <= bufferSize - bufferPos)
236            {
237                value = BitConverter.ToDouble(buffer, bufferPos);
238                bufferPos += 8;
239            }
240            else
241            {
242                byte[] rawBytes = ReadRawBytes(8);
243                if (!BitConverter.IsLittleEndian)
244                    ByteArray.Reverse(rawBytes);
245                value = BitConverter.ToDouble(rawBytes, 0);
246            }
247#else
248            value = BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
249#endif
250            return true;
251        }
252
253        /// <summary>
254        /// Read a float field from the stream.
255        /// </summary>
256        public bool ReadFloat(ref float value)
257        {
258            if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
259            {
260                value = BitConverter.ToSingle(buffer, bufferPos);
261                bufferPos += 4;
262            }
263            else
264            {
265                byte[] rawBytes = ReadRawBytes(4);
266                if (!BitConverter.IsLittleEndian)
267                {
268                    ByteArray.Reverse(rawBytes);
269                }
270                value = BitConverter.ToSingle(rawBytes, 0);
271            }
272            return true;
273        }
274
275        /// <summary>
276        /// Read a uint64 field from the stream.
277        /// </summary>
278        [CLSCompliant(false)]
279        public bool ReadUInt64(ref ulong value)
280        {
281            value = ReadRawVarint64();
282            return true;
283        }
284
285        /// <summary>
286        /// Read an int64 field from the stream.
287        /// </summary>
288        public bool ReadInt64(ref long value)
289        {
290            value = (long) ReadRawVarint64();
291            return true;
292        }
293
294        /// <summary>
295        /// Read an int32 field from the stream.
296        /// </summary>
297        public bool ReadInt32(ref int value)
298        {
299            value = (int) ReadRawVarint32();
300            return true;
301        }
302
303        /// <summary>
304        /// Read a fixed64 field from the stream.
305        /// </summary>
306        [CLSCompliant(false)]
307        public bool ReadFixed64(ref ulong value)
308        {
309            value = ReadRawLittleEndian64();
310            return true;
311        }
312
313        /// <summary>
314        /// Read a fixed32 field from the stream.
315        /// </summary>
316        [CLSCompliant(false)]
317        public bool ReadFixed32(ref uint value)
318        {
319            value = ReadRawLittleEndian32();
320            return true;
321        }
322
323        /// <summary>
324        /// Read a bool field from the stream.
325        /// </summary>
326        public bool ReadBool(ref bool value)
327        {
328            value = ReadRawVarint32() != 0;
329            return true;
330        }
331
332        /// <summary>
333        /// Reads a string field from the stream.
334        /// </summary>
335        public bool ReadString(ref string value)
336        {
337            int size = (int) ReadRawVarint32();
338            // No need to read any data for an empty string.
339            if (size == 0)
340            {
341                value = "";
342                return true;
343            }
344            if (size <= bufferSize - bufferPos)
345            {
346                // Fast path:  We already have the bytes in a contiguous buffer, so
347                //   just copy directly from it.
348                String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
349                bufferPos += size;
350                value = result;
351                return true;
352            }
353            // Slow path: Build a byte array first then copy it.
354            value = Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
355            return true;
356        }
357
358        /// <summary>
359        /// Reads a group field value from the stream.
360        /// </summary>   
361        public void ReadGroup(int fieldNumber, IBuilderLite builder,
362                              ExtensionRegistry extensionRegistry)
363        {
364            if (recursionDepth >= recursionLimit)
365            {
366                throw InvalidProtocolBufferException.RecursionLimitExceeded();
367            }
368            ++recursionDepth;
369            builder.WeakMergeFrom(this, extensionRegistry);
370            CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
371            --recursionDepth;
372        }
373
374        /// <summary>
375        /// Reads a group field value from the stream and merges it into the given
376        /// UnknownFieldSet.
377        /// </summary>   
378        [Obsolete]
379        public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
380        {
381            if (recursionDepth >= recursionLimit)
382            {
383                throw InvalidProtocolBufferException.RecursionLimitExceeded();
384            }
385            ++recursionDepth;
386            builder.WeakMergeFrom(this);
387            CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
388            --recursionDepth;
389        }
390
391        /// <summary>
392        /// Reads an embedded message field value from the stream.
393        /// </summary>   
394        public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
395        {
396            int length = (int) ReadRawVarint32();
397            if (recursionDepth >= recursionLimit)
398            {
399                throw InvalidProtocolBufferException.RecursionLimitExceeded();
400            }
401            int oldLimit = PushLimit(length);
402            ++recursionDepth;
403            builder.WeakMergeFrom(this, extensionRegistry);
404            CheckLastTagWas(0);
405            --recursionDepth;
406            PopLimit(oldLimit);
407        }
408
409        /// <summary>
410        /// Reads a bytes field value from the stream.
411        /// </summary>   
412        public bool ReadBytes(ref ByteString value)
413        {
414            int size = (int) ReadRawVarint32();
415            if (size < bufferSize - bufferPos && size > 0)
416            {
417                // Fast path:  We already have the bytes in a contiguous buffer, so
418                //   just copy directly from it.
419                ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
420                bufferPos += size;
421                value = result;
422                return true;
423            }
424            else
425            {
426                // Slow path:  Build a byte array and attach it to a new ByteString.
427                value = ByteString.AttachBytes(ReadRawBytes(size));
428                return true;
429            }
430        }
431
432        /// <summary>
433        /// Reads a uint32 field value from the stream.
434        /// </summary>   
435        [CLSCompliant(false)]
436        public bool ReadUInt32(ref uint value)
437        {
438            value = ReadRawVarint32();
439            return true;
440        }
441
442        /// <summary>
443        /// Reads an enum field value from the stream. The caller is responsible
444        /// for converting the numeric value to an actual enum.
445        /// </summary>   
446        public bool ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
447        {
448            int rawValue = (int) ReadRawVarint32();
449
450            value = mapping.FindValueByNumber(rawValue);
451            if (value != null)
452            {
453                unknown = null;
454                return true;
455            }
456            unknown = rawValue;
457            return false;
458        }
459
460        /// <summary>
461        /// Reads an enum field value from the stream. If the enum is valid for type T,
462        /// then the ref value is set and it returns true.  Otherwise the unkown output
463        /// value is set and this method returns false.
464        /// </summary>   
465        [CLSCompliant(false)]
466        public bool ReadEnum<T>(ref T value, out object unknown)
467            where T : struct, IComparable, IFormattable, IConvertible
468        {
469            int number = (int) ReadRawVarint32();
470            if (Enum.IsDefined(typeof (T), number))
471            {
472                unknown = null;
473                value = (T) (object) number;
474                return true;
475            }
476            unknown = number;
477            return false;
478        }
479
480        /// <summary>
481        /// Reads an sfixed32 field value from the stream.
482        /// </summary>   
483        public bool ReadSFixed32(ref int value)
484        {
485            value = (int) ReadRawLittleEndian32();
486            return true;
487        }
488
489        /// <summary>
490        /// Reads an sfixed64 field value from the stream.
491        /// </summary>   
492        public bool ReadSFixed64(ref long value)
493        {
494            value = (long) ReadRawLittleEndian64();
495            return true;
496        }
497
498        /// <summary>
499        /// Reads an sint32 field value from the stream.
500        /// </summary>   
501        public bool ReadSInt32(ref int value)
502        {
503            value = DecodeZigZag32(ReadRawVarint32());
504            return true;
505        }
506
507        /// <summary>
508        /// Reads an sint64 field value from the stream.
509        /// </summary>   
510        public bool ReadSInt64(ref long value)
511        {
512            value = DecodeZigZag64(ReadRawVarint64());
513            return true;
514        }
515
516        private bool BeginArray(uint fieldTag, out bool isPacked, out int oldLimit)
517        {
518            isPacked = WireFormat.GetTagWireType(fieldTag) == WireFormat.WireType.LengthDelimited;
519
520            if (isPacked)
521            {
522                int length = (int) (ReadRawVarint32() & int.MaxValue);
523                if (length > 0)
524                {
525                    oldLimit = PushLimit(length);
526                    return true;
527                }
528                oldLimit = -1;
529                return false; //packed but empty
530            }
531
532            oldLimit = -1;
533            return true;
534        }
535
536        /// <summary>
537        /// Returns true if the next tag is also part of the same unpacked array.
538        /// </summary>
539        private bool ContinueArray(uint currentTag)
540        {
541            string ignore;
542            uint next;
543            if (PeekNextTag(out next, out ignore))
544            {
545                if (next == currentTag)
546                {
547                    hasNextTag = false;
548                    return true;
549                }
550            }
551            return false;
552        }
553
554        /// <summary>
555        /// Returns true if the next tag is also part of the same array, which may or may not be packed.
556        /// </summary>
557        private bool ContinueArray(uint currentTag, bool packed, int oldLimit)
558        {
559            if (packed)
560            {
561                if (ReachedLimit)
562                {
563                    PopLimit(oldLimit);
564                    return false;
565                }
566                return true;
567            }
568
569            string ignore;
570            uint next;
571            if (PeekNextTag(out next, out ignore))
572            {
573                if (next == currentTag)
574                {
575                    hasNextTag = false;
576                    return true;
577                }
578            }
579            return false;
580        }
581
582        [CLSCompliant(false)]
583        public void ReadPrimitiveArray(FieldType fieldType, uint fieldTag, string fieldName, ICollection<object> list)
584        {
585            WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
586            WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
587
588            // 2.3 allows packed form even if the field is not declared packed.
589            if (normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
590            {
591                int length = (int) (ReadRawVarint32() & int.MaxValue);
592                int limit = PushLimit(length);
593                while (!ReachedLimit)
594                {
595                    Object value = null;
596                    if (ReadPrimitiveField(fieldType, ref value))
597                    {
598                        list.Add(value);
599                    }
600                }
601                PopLimit(limit);
602            }
603            else
604            {
605                Object value = null;
606                do
607                {
608                    if (ReadPrimitiveField(fieldType, ref value))
609                    {
610                        list.Add(value);
611                    }
612                } while (ContinueArray(fieldTag));
613            }
614        }
615
616        [CLSCompliant(false)]
617        public void ReadStringArray(uint fieldTag, string fieldName, ICollection<string> list)
618        {
619            string tmp = null;
620            do
621            {
622                ReadString(ref tmp);
623                list.Add(tmp);
624            } while (ContinueArray(fieldTag));
625        }
626
627        [CLSCompliant(false)]
628        public void ReadBytesArray(uint fieldTag, string fieldName, ICollection<ByteString> list)
629        {
630            ByteString tmp = null;
631            do
632            {
633                ReadBytes(ref tmp);
634                list.Add(tmp);
635            } while (ContinueArray(fieldTag));
636        }
637
638        [CLSCompliant(false)]
639        public void ReadBoolArray(uint fieldTag, string fieldName, ICollection<bool> list)
640        {
641            bool isPacked;
642            int holdLimit;
643            if (BeginArray(fieldTag, out isPacked, out holdLimit))
644            {
645                bool tmp = false;
646                do
647                {
648                    ReadBool(ref tmp);
649                    list.Add(tmp);
650                } while (ContinueArray(fieldTag, isPacked, holdLimit));
651            }
652        }
653
654        [CLSCompliant(false)]
655        public void ReadInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
656        {
657            bool isPacked;
658            int holdLimit;
659            if (BeginArray(fieldTag, out isPacked, out holdLimit))
660            {
661                int tmp = 0;
662                do
663                {
664                    ReadInt32(ref tmp);
665                    list.Add(tmp);
666                } while (ContinueArray(fieldTag, isPacked, holdLimit));
667            }
668        }
669
670        [CLSCompliant(false)]
671        public void ReadSInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
672        {
673            bool isPacked;
674            int holdLimit;
675            if (BeginArray(fieldTag, out isPacked, out holdLimit))
676            {
677                int tmp = 0;
678                do
679                {
680                    ReadSInt32(ref tmp);
681                    list.Add(tmp);
682                } while (ContinueArray(fieldTag, isPacked, holdLimit));
683            }
684        }
685
686        [CLSCompliant(false)]
687        public void ReadUInt32Array(uint fieldTag, string fieldName, ICollection<uint> list)
688        {
689            bool isPacked;
690            int holdLimit;
691            if (BeginArray(fieldTag, out isPacked, out holdLimit))
692            {
693                uint tmp = 0;
694                do
695                {
696                    ReadUInt32(ref tmp);
697                    list.Add(tmp);
698                } while (ContinueArray(fieldTag, isPacked, holdLimit));
699            }
700        }
701
702        [CLSCompliant(false)]
703        public void ReadFixed32Array(uint fieldTag, string fieldName, ICollection<uint> list)
704        {
705            bool isPacked;
706            int holdLimit;
707            if (BeginArray(fieldTag, out isPacked, out holdLimit))
708            {
709                uint tmp = 0;
710                do
711                {
712                    ReadFixed32(ref tmp);
713                    list.Add(tmp);
714                } while (ContinueArray(fieldTag, isPacked, holdLimit));
715            }
716        }
717
718        [CLSCompliant(false)]
719        public void ReadSFixed32Array(uint fieldTag, string fieldName, ICollection<int> list)
720        {
721            bool isPacked;
722            int holdLimit;
723            if (BeginArray(fieldTag, out isPacked, out holdLimit))
724            {
725                int tmp = 0;
726                do
727                {
728                    ReadSFixed32(ref tmp);
729                    list.Add(tmp);
730                } while (ContinueArray(fieldTag, isPacked, holdLimit));
731            }
732        }
733
734        [CLSCompliant(false)]
735        public void ReadInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
736        {
737            bool isPacked;
738            int holdLimit;
739            if (BeginArray(fieldTag, out isPacked, out holdLimit))
740            {
741                long tmp = 0;
742                do
743                {
744                    ReadInt64(ref tmp);
745                    list.Add(tmp);
746                } while (ContinueArray(fieldTag, isPacked, holdLimit));
747            }
748        }
749
750        [CLSCompliant(false)]
751        public void ReadSInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
752        {
753            bool isPacked;
754            int holdLimit;
755            if (BeginArray(fieldTag, out isPacked, out holdLimit))
756            {
757                long tmp = 0;
758                do
759                {
760                    ReadSInt64(ref tmp);
761                    list.Add(tmp);
762                } while (ContinueArray(fieldTag, isPacked, holdLimit));
763            }
764        }
765
766        [CLSCompliant(false)]
767        public void ReadUInt64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
768        {
769            bool isPacked;
770            int holdLimit;
771            if (BeginArray(fieldTag, out isPacked, out holdLimit))
772            {
773                ulong tmp = 0;
774                do
775                {
776                    ReadUInt64(ref tmp);
777                    list.Add(tmp);
778                } while (ContinueArray(fieldTag, isPacked, holdLimit));
779            }
780        }
781
782        [CLSCompliant(false)]
783        public void ReadFixed64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
784        {
785            bool isPacked;
786            int holdLimit;
787            if (BeginArray(fieldTag, out isPacked, out holdLimit))
788            {
789                ulong tmp = 0;
790                do
791                {
792                    ReadFixed64(ref tmp);
793                    list.Add(tmp);
794                } while (ContinueArray(fieldTag, isPacked, holdLimit));
795            }
796        }
797
798        [CLSCompliant(false)]
799        public void ReadSFixed64Array(uint fieldTag, string fieldName, ICollection<long> list)
800        {
801            bool isPacked;
802            int holdLimit;
803            if (BeginArray(fieldTag, out isPacked, out holdLimit))
804            {
805                long tmp = 0;
806                do
807                {
808                    ReadSFixed64(ref tmp);
809                    list.Add(tmp);
810                } while (ContinueArray(fieldTag, isPacked, holdLimit));
811            }
812        }
813
814        [CLSCompliant(false)]
815        public void ReadDoubleArray(uint fieldTag, string fieldName, ICollection<double> list)
816        {
817            bool isPacked;
818            int holdLimit;
819            if (BeginArray(fieldTag, out isPacked, out holdLimit))
820            {
821                double tmp = 0;
822                do
823                {
824                    ReadDouble(ref tmp);
825                    list.Add(tmp);
826                } while (ContinueArray(fieldTag, isPacked, holdLimit));
827            }
828        }
829
830        [CLSCompliant(false)]
831        public void ReadFloatArray(uint fieldTag, string fieldName, ICollection<float> list)
832        {
833            bool isPacked;
834            int holdLimit;
835            if (BeginArray(fieldTag, out isPacked, out holdLimit))
836            {
837                float tmp = 0;
838                do
839                {
840                    ReadFloat(ref tmp);
841                    list.Add(tmp);
842                } while (ContinueArray(fieldTag, isPacked, holdLimit));
843            }
844        }
845
846        [CLSCompliant(false)]
847        public void ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list,
848                                  out ICollection<object> unknown, IEnumLiteMap mapping)
849        {
850            unknown = null;
851            object unkval;
852            IEnumLite value = null;
853            WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
854
855            // 2.3 allows packed form even if the field is not declared packed.
856            if (wformat == WireFormat.WireType.LengthDelimited)
857            {
858                int length = (int) (ReadRawVarint32() & int.MaxValue);
859                int limit = PushLimit(length);
860                while (!ReachedLimit)
861                {
862                    if (ReadEnum(ref value, out unkval, mapping))
863                    {
864                        list.Add(value);
865                    }
866                    else
867                    {
868                        if (unknown == null)
869                        {
870                            unknown = new List<object>();
871                        }
872                        unknown.Add(unkval);
873                    }
874                }
875                PopLimit(limit);
876            }
877            else
878            {
879                do
880                {
881                    if (ReadEnum(ref value, out unkval, mapping))
882                    {
883                        list.Add(value);
884                    }
885                    else
886                    {
887                        if (unknown == null)
888                        {
889                            unknown = new List<object>();
890                        }
891                        unknown.Add(unkval);
892                    }
893                } while (ContinueArray(fieldTag));
894            }
895        }
896
897        [CLSCompliant(false)]
898        public void ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list,
899                                     out ICollection<object> unknown)
900            where T : struct, IComparable, IFormattable, IConvertible
901        {
902            unknown = null;
903            object unkval;
904            T value = default(T);
905            WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
906
907            // 2.3 allows packed form even if the field is not declared packed.
908            if (wformat == WireFormat.WireType.LengthDelimited)
909            {
910                int length = (int) (ReadRawVarint32() & int.MaxValue);
911                int limit = PushLimit(length);
912                while (!ReachedLimit)
913                {
914                    if (ReadEnum<T>(ref value, out unkval))
915                    {
916                        list.Add(value);
917                    }
918                    else
919                    {
920                        if (unknown == null)
921                        {
922                            unknown = new List<object>();
923                        }
924                        unknown.Add(unkval);
925                    }
926                }
927                PopLimit(limit);
928            }
929            else
930            {
931                do
932                {
933                    if (ReadEnum(ref value, out unkval))
934                    {
935                        list.Add(value);
936                    }
937                    else
938                    {
939                        if (unknown == null)
940                        {
941                            unknown = new List<object>();
942                        }
943                        unknown.Add(unkval);
944                    }
945                } while (ContinueArray(fieldTag));
946            }
947        }
948
949        [CLSCompliant(false)]
950        public void ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType,
951                                        ExtensionRegistry registry) where T : IMessageLite
952        {
953            do
954            {
955                IBuilderLite builder = messageType.WeakCreateBuilderForType();
956                ReadMessage(builder, registry);
957                list.Add((T) builder.WeakBuildPartial());
958            } while (ContinueArray(fieldTag));
959        }
960
961        [CLSCompliant(false)]
962        public void ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType,
963                                      ExtensionRegistry registry) where T : IMessageLite
964        {
965            do
966            {
967                IBuilderLite builder = messageType.WeakCreateBuilderForType();
968                ReadGroup(WireFormat.GetTagFieldNumber(fieldTag), builder, registry);
969                list.Add((T) builder.WeakBuildPartial());
970            } while (ContinueArray(fieldTag));
971        }
972
973        /// <summary>
974        /// Reads a field of any primitive type. Enums, groups and embedded
975        /// messages are not handled by this method.
976        /// </summary>
977        public bool ReadPrimitiveField(FieldType fieldType, ref object value)
978        {
979            switch (fieldType)
980            {
981                case FieldType.Double:
982                    {
983                        double tmp = 0;
984                        if (ReadDouble(ref tmp))
985                        {
986                            value = tmp;
987                            return true;
988                        }
989                        return false;
990                    }
991                case FieldType.Float:
992                    {
993                        float tmp = 0;
994                        if (ReadFloat(ref tmp))
995                        {
996                            value = tmp;
997                            return true;
998                        }
999                        return false;
1000                    }
1001                case FieldType.Int64:
1002                    {
1003                        long tmp = 0;
1004                        if (ReadInt64(ref tmp))
1005                        {
1006                            value = tmp;
1007                            return true;
1008                        }
1009                        return false;
1010                    }
1011                case FieldType.UInt64:
1012                    {
1013                        ulong tmp = 0;
1014                        if (ReadUInt64(ref tmp))
1015                        {
1016                            value = tmp;
1017                            return true;
1018                        }
1019                        return false;
1020                    }
1021                case FieldType.Int32:
1022                    {
1023                        int tmp = 0;
1024                        if (ReadInt32(ref tmp))
1025                        {
1026                            value = tmp;
1027                            return true;
1028                        }
1029                        return false;
1030                    }
1031                case FieldType.Fixed64:
1032                    {
1033                        ulong tmp = 0;
1034                        if (ReadFixed64(ref tmp))
1035                        {
1036                            value = tmp;
1037                            return true;
1038                        }
1039                        return false;
1040                    }
1041                case FieldType.Fixed32:
1042                    {
1043                        uint tmp = 0;
1044                        if (ReadFixed32(ref tmp))
1045                        {
1046                            value = tmp;
1047                            return true;
1048                        }
1049                        return false;
1050                    }
1051                case FieldType.Bool:
1052                    {
1053                        bool tmp = false;
1054                        if (ReadBool(ref tmp))
1055                        {
1056                            value = tmp;
1057                            return true;
1058                        }
1059                        return false;
1060                    }
1061                case FieldType.String:
1062                    {
1063                        string tmp = null;
1064                        if (ReadString(ref tmp))
1065                        {
1066                            value = tmp;
1067                            return true;
1068                        }
1069                        return false;
1070                    }
1071                case FieldType.Bytes:
1072                    {
1073                        ByteString tmp = null;
1074                        if (ReadBytes(ref tmp))
1075                        {
1076                            value = tmp;
1077                            return true;
1078                        }
1079                        return false;
1080                    }
1081                case FieldType.UInt32:
1082                    {
1083                        uint tmp = 0;
1084                        if (ReadUInt32(ref tmp))
1085                        {
1086                            value = tmp;
1087                            return true;
1088                        }
1089                        return false;
1090                    }
1091                case FieldType.SFixed32:
1092                    {
1093                        int tmp = 0;
1094                        if (ReadSFixed32(ref tmp))
1095                        {
1096                            value = tmp;
1097                            return true;
1098                        }
1099                        return false;
1100                    }
1101                case FieldType.SFixed64:
1102                    {
1103                        long tmp = 0;
1104                        if (ReadSFixed64(ref tmp))
1105                        {
1106                            value = tmp;
1107                            return true;
1108                        }
1109                        return false;
1110                    }
1111                case FieldType.SInt32:
1112                    {
1113                        int tmp = 0;
1114                        if (ReadSInt32(ref tmp))
1115                        {
1116                            value = tmp;
1117                            return true;
1118                        }
1119                        return false;
1120                    }
1121                case FieldType.SInt64:
1122                    {
1123                        long tmp = 0;
1124                        if (ReadSInt64(ref tmp))
1125                        {
1126                            value = tmp;
1127                            return true;
1128                        }
1129                        return false;
1130                    }
1131                case FieldType.Group:
1132                    throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
1133                case FieldType.Message:
1134                    throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
1135                    // We don't handle enums because we don't know what to do if the
1136                    // value is not recognized.
1137                case FieldType.Enum:
1138                    throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
1139                default:
1140                    throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1141            }
1142        }
1143
1144        #endregion
1145
1146        #region Underlying reading primitives
1147
1148        /// <summary>
1149        /// Same code as ReadRawVarint32, but read each byte individually, checking for
1150        /// buffer overflow.
1151        /// </summary>
1152        private uint SlowReadRawVarint32()
1153        {
1154            int tmp = ReadRawByte();
1155            if (tmp < 128)
1156            {
1157                return (uint) tmp;
1158            }
1159            int result = tmp & 0x7f;
1160            if ((tmp = ReadRawByte()) < 128)
1161            {
1162                result |= tmp << 7;
1163            }
1164            else
1165            {
1166                result |= (tmp & 0x7f) << 7;
1167                if ((tmp = ReadRawByte()) < 128)
1168                {
1169                    result |= tmp << 14;
1170                }
1171                else
1172                {
1173                    result |= (tmp & 0x7f) << 14;
1174                    if ((tmp = ReadRawByte()) < 128)
1175                    {
1176                        result |= tmp << 21;
1177                    }
1178                    else
1179                    {
1180                        result |= (tmp & 0x7f) << 21;
1181                        result |= (tmp = ReadRawByte()) << 28;
1182                        if (tmp >= 128)
1183                        {
1184                            // Discard upper 32 bits.
1185                            for (int i = 0; i < 5; i++)
1186                            {
1187                                if (ReadRawByte() < 128)
1188                                {
1189                                    return (uint) result;
1190                                }
1191                            }
1192                            throw InvalidProtocolBufferException.MalformedVarint();
1193                        }
1194                    }
1195                }
1196            }
1197            return (uint) result;
1198        }
1199
1200        /// <summary>
1201        /// Read a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
1202        /// This method is optimised for the case where we've got lots of data in the buffer.
1203        /// That means we can check the size just once, then just read directly from the buffer
1204        /// without constant rechecking of the buffer length.
1205        /// </summary>
1206        [CLSCompliant(false)]
1207        public uint ReadRawVarint32()
1208        {
1209            if (bufferPos + 5 > bufferSize)
1210            {
1211                return SlowReadRawVarint32();
1212            }
1213
1214            int tmp = buffer[bufferPos++];
1215            if (tmp < 128)
1216            {
1217                return (uint) tmp;
1218            }
1219            int result = tmp & 0x7f;
1220            if ((tmp = buffer[bufferPos++]) < 128)
1221            {
1222                result |= tmp << 7;
1223            }
1224            else
1225            {
1226                result |= (tmp & 0x7f) << 7;
1227                if ((tmp = buffer[bufferPos++]) < 128)
1228                {
1229                    result |= tmp << 14;
1230                }
1231                else
1232                {
1233                    result |= (tmp & 0x7f) << 14;
1234                    if ((tmp = buffer[bufferPos++]) < 128)
1235                    {
1236                        result |= tmp << 21;
1237                    }
1238                    else
1239                    {
1240                        result |= (tmp & 0x7f) << 21;
1241                        result |= (tmp = buffer[bufferPos++]) << 28;
1242                        if (tmp >= 128)
1243                        {
1244                            // Discard upper 32 bits.
1245                            // Note that this has to use ReadRawByte() as we only ensure we've
1246                            // got at least 5 bytes at the start of the method. This lets us
1247                            // use the fast path in more cases, and we rarely hit this section of code.
1248                            for (int i = 0; i < 5; i++)
1249                            {
1250                                if (ReadRawByte() < 128)
1251                                {
1252                                    return (uint) result;
1253                                }
1254                            }
1255                            throw InvalidProtocolBufferException.MalformedVarint();
1256                        }
1257                    }
1258                }
1259            }
1260            return (uint) result;
1261        }
1262
1263        /// <summary>
1264        /// Reads a varint from the input one byte at a time, so that it does not
1265        /// read any bytes after the end of the varint. If you simply wrapped the
1266        /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
1267        /// then you would probably end up reading past the end of the varint since
1268        /// CodedInputStream buffers its input.
1269        /// </summary>
1270        /// <param name="input"></param>
1271        /// <returns></returns>
1272        [CLSCompliant(false)]
1273        public static uint ReadRawVarint32(Stream input)
1274        {
1275            int result = 0;
1276            int offset = 0;
1277            for (; offset < 32; offset += 7)
1278            {
1279                int b = input.ReadByte();
1280                if (b == -1)
1281                {
1282                    throw InvalidProtocolBufferException.TruncatedMessage();
1283                }
1284                result |= (b & 0x7f) << offset;
1285                if ((b & 0x80) == 0)
1286                {
1287                    return (uint) result;
1288                }
1289            }
1290            // Keep reading up to 64 bits.
1291            for (; offset < 64; offset += 7)
1292            {
1293                int b = input.ReadByte();
1294                if (b == -1)
1295                {
1296                    throw InvalidProtocolBufferException.TruncatedMessage();
1297                }
1298                if ((b & 0x80) == 0)
1299                {
1300                    return (uint) result;
1301                }
1302            }
1303            throw InvalidProtocolBufferException.MalformedVarint();
1304        }
1305
1306        /// <summary>
1307        /// Read a raw varint from the stream.
1308        /// </summary>
1309        [CLSCompliant(false)]
1310        public ulong ReadRawVarint64()
1311        {
1312            int shift = 0;
1313            ulong result = 0;
1314            while (shift < 64)
1315            {
1316                byte b = ReadRawByte();
1317                result |= (ulong) (b & 0x7F) << shift;
1318                if ((b & 0x80) == 0)
1319                {
1320                    return result;
1321                }
1322                shift += 7;
1323            }
1324            throw InvalidProtocolBufferException.MalformedVarint();
1325        }
1326
1327        /// <summary>
1328        /// Read a 32-bit little-endian integer from the stream.
1329        /// </summary>
1330        [CLSCompliant(false)]
1331        public uint ReadRawLittleEndian32()
1332        {
1333            uint b1 = ReadRawByte();
1334            uint b2 = ReadRawByte();
1335            uint b3 = ReadRawByte();
1336            uint b4 = ReadRawByte();
1337            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
1338        }
1339
1340        /// <summary>
1341        /// Read a 64-bit little-endian integer from the stream.
1342        /// </summary>
1343        [CLSCompliant(false)]
1344        public ulong ReadRawLittleEndian64()
1345        {
1346            ulong b1 = ReadRawByte();
1347            ulong b2 = ReadRawByte();
1348            ulong b3 = ReadRawByte();
1349            ulong b4 = ReadRawByte();
1350            ulong b5 = ReadRawByte();
1351            ulong b6 = ReadRawByte();
1352            ulong b7 = ReadRawByte();
1353            ulong b8 = ReadRawByte();
1354            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
1355                   | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
1356        }
1357
1358        #endregion
1359
1360        /// <summary>
1361        /// Decode a 32-bit value with ZigZag encoding.
1362        /// </summary>
1363        /// <remarks>
1364        /// ZigZag encodes signed integers into values that can be efficiently
1365        /// encoded with varint.  (Otherwise, negative values must be
1366        /// sign-extended to 64 bits to be varint encoded, thus always taking
1367        /// 10 bytes on the wire.)
1368        /// </remarks>
1369        [CLSCompliant(false)]
1370        public static int DecodeZigZag32(uint n)
1371        {
1372            return (int) (n >> 1) ^ -(int) (n & 1);
1373        }
1374
1375        /// <summary>
1376        /// Decode a 32-bit value with ZigZag encoding.
1377        /// </summary>
1378        /// <remarks>
1379        /// ZigZag encodes signed integers into values that can be efficiently
1380        /// encoded with varint.  (Otherwise, negative values must be
1381        /// sign-extended to 64 bits to be varint encoded, thus always taking
1382        /// 10 bytes on the wire.)
1383        /// </remarks>
1384        [CLSCompliant(false)]
1385        public static long DecodeZigZag64(ulong n)
1386        {
1387            return (long) (n >> 1) ^ -(long) (n & 1);
1388        }
1389
1390        /// <summary>
1391        /// Set the maximum message recursion depth.
1392        /// </summary>
1393        /// <remarks>
1394        /// In order to prevent malicious
1395        /// messages from causing stack overflows, CodedInputStream limits
1396        /// how deeply messages may be nested.  The default limit is 64.
1397        /// </remarks>
1398        public int SetRecursionLimit(int limit)
1399        {
1400            if (limit < 0)
1401            {
1402                throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
1403            }
1404            int oldLimit = recursionLimit;
1405            recursionLimit = limit;
1406            return oldLimit;
1407        }
1408
1409        /// <summary>
1410        /// Set the maximum message size.
1411        /// </summary>
1412        /// <remarks>
1413        /// In order to prevent malicious messages from exhausting memory or
1414        /// causing integer overflows, CodedInputStream limits how large a message may be.
1415        /// The default limit is 64MB.  You should set this limit as small
1416        /// as you can without harming your app's functionality.  Note that
1417        /// size limits only apply when reading from an InputStream, not
1418        /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
1419        /// If you want to read several messages from a single CodedInputStream, you
1420        /// can call ResetSizeCounter() after each message to avoid hitting the
1421        /// size limit.
1422        /// </remarks>
1423        public int SetSizeLimit(int limit)
1424        {
1425            if (limit < 0)
1426            {
1427                throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
1428            }
1429            int oldLimit = sizeLimit;
1430            sizeLimit = limit;
1431            return oldLimit;
1432        }
1433
1434        #region Internal reading and buffer management
1435
1436        /// <summary>
1437        /// Resets the current size counter to zero (see SetSizeLimit).
1438        /// </summary>
1439        public void ResetSizeCounter()
1440        {
1441            totalBytesRetired = 0;
1442        }
1443
1444        /// <summary>
1445        /// Sets currentLimit to (current position) + byteLimit. This is called
1446        /// when descending into a length-delimited embedded message. The previous
1447        /// limit is returned.
1448        /// </summary>
1449        /// <returns>The old limit.</returns>
1450        public int PushLimit(int byteLimit)
1451        {
1452            if (byteLimit < 0)
1453            {
1454                throw InvalidProtocolBufferException.NegativeSize();
1455            }
1456            byteLimit += totalBytesRetired + bufferPos;
1457            int oldLimit = currentLimit;
1458            if (byteLimit > oldLimit)
1459            {
1460                throw InvalidProtocolBufferException.TruncatedMessage();
1461            }
1462            currentLimit = byteLimit;
1463
1464            RecomputeBufferSizeAfterLimit();
1465
1466            return oldLimit;
1467        }
1468
1469        private void RecomputeBufferSizeAfterLimit()
1470        {
1471            bufferSize += bufferSizeAfterLimit;
1472            int bufferEnd = totalBytesRetired + bufferSize;
1473            if (bufferEnd > currentLimit)
1474            {
1475                // Limit is in current buffer.
1476                bufferSizeAfterLimit = bufferEnd - currentLimit;
1477                bufferSize -= bufferSizeAfterLimit;
1478            }
1479            else
1480            {
1481                bufferSizeAfterLimit = 0;
1482            }
1483        }
1484
1485        /// <summary>
1486        /// Discards the current limit, returning the previous limit.
1487        /// </summary>
1488        public void PopLimit(int oldLimit)
1489        {
1490            currentLimit = oldLimit;
1491            RecomputeBufferSizeAfterLimit();
1492        }
1493
1494        /// <summary>
1495        /// Returns whether or not all the data before the limit has been read.
1496        /// </summary>
1497        /// <returns></returns>
1498        public bool ReachedLimit
1499        {
1500            get
1501            {
1502                if (currentLimit == int.MaxValue)
1503                {
1504                    return false;
1505                }
1506                int currentAbsolutePosition = totalBytesRetired + bufferPos;
1507                return currentAbsolutePosition >= currentLimit;
1508            }
1509        }
1510
1511        /// <summary>
1512        /// Returns true if the stream has reached the end of the input. This is the
1513        /// case if either the end of the underlying input source has been reached or
1514        /// the stream has reached a limit created using PushLimit.
1515        /// </summary>
1516        public bool IsAtEnd
1517        {
1518            get { return bufferPos == bufferSize && !RefillBuffer(false); }
1519        }
1520
1521        /// <summary>
1522        /// Called when buffer is empty to read more bytes from the
1523        /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1524        /// either there will be at least one byte in the buffer when it returns
1525        /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,
1526        /// RefillBuffer() returns false if no more bytes were available.
1527        /// </summary>
1528        /// <param name="mustSucceed"></param>
1529        /// <returns></returns>
1530        private bool RefillBuffer(bool mustSucceed)
1531        {
1532            if (bufferPos < bufferSize)
1533            {
1534                throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1535            }
1536
1537            if (totalBytesRetired + bufferSize == currentLimit)
1538            {
1539                // Oops, we hit a limit.
1540                if (mustSucceed)
1541                {
1542                    throw InvalidProtocolBufferException.TruncatedMessage();
1543                }
1544                else
1545                {
1546                    return false;
1547                }
1548            }
1549
1550            totalBytesRetired += bufferSize;
1551
1552            bufferPos = 0;
1553            bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1554            if (bufferSize < 0)
1555            {
1556                throw new InvalidOperationException("Stream.Read returned a negative count");
1557            }
1558            if (bufferSize == 0)
1559            {
1560                if (mustSucceed)
1561                {
1562                    throw InvalidProtocolBufferException.TruncatedMessage();
1563                }
1564                else
1565                {
1566                    return false;
1567                }
1568            }
1569            else
1570            {
1571                RecomputeBufferSizeAfterLimit();
1572                int totalBytesRead =
1573                    totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1574                if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1575                {
1576                    throw InvalidProtocolBufferException.SizeLimitExceeded();
1577                }
1578                return true;
1579            }
1580        }
1581
1582        /// <summary>
1583        /// Read one byte from the input.
1584        /// </summary>
1585        /// <exception cref="InvalidProtocolBufferException">
1586        /// the end of the stream or the current limit was reached
1587        /// </exception>
1588        public byte ReadRawByte()
1589        {
1590            if (bufferPos == bufferSize)
1591            {
1592                RefillBuffer(true);
1593            }
1594            return buffer[bufferPos++];
1595        }
1596
1597        /// <summary>
1598        /// Read a fixed size of bytes from the input.
1599        /// </summary>
1600        /// <exception cref="InvalidProtocolBufferException">
1601        /// the end of the stream or the current limit was reached
1602        /// </exception>
1603        public byte[] ReadRawBytes(int size)
1604        {
1605            if (size < 0)
1606            {
1607                throw InvalidProtocolBufferException.NegativeSize();
1608            }
1609
1610            if (totalBytesRetired + bufferPos + size > currentLimit)
1611            {
1612                // Read to the end of the stream anyway.
1613                SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1614                // Then fail.
1615                throw InvalidProtocolBufferException.TruncatedMessage();
1616            }
1617
1618            if (size <= bufferSize - bufferPos)
1619            {
1620                // We have all the bytes we need already.
1621                byte[] bytes = new byte[size];
1622                ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
1623                bufferPos += size;
1624                return bytes;
1625            }
1626            else if (size < BufferSize)
1627            {
1628                // Reading more bytes than are in the buffer, but not an excessive number
1629                // of bytes.  We can safely allocate the resulting array ahead of time.
1630
1631                // First copy what we have.
1632                byte[] bytes = new byte[size];
1633                int pos = bufferSize - bufferPos;
1634                ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
1635                bufferPos = bufferSize;
1636
1637                // We want to use RefillBuffer() and then copy from the buffer into our
1638                // byte array rather than reading directly into our byte array because
1639                // the input may be unbuffered.
1640                RefillBuffer(true);
1641
1642                while (size - pos > bufferSize)
1643                {
1644                    Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
1645                    pos += bufferSize;
1646                    bufferPos = bufferSize;
1647                    RefillBuffer(true);
1648                }
1649
1650                ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
1651                bufferPos = size - pos;
1652
1653                return bytes;
1654            }
1655            else
1656            {
1657                // The size is very large.  For security reasons, we can't allocate the
1658                // entire byte array yet.  The size comes directly from the input, so a
1659                // maliciously-crafted message could provide a bogus very large size in
1660                // order to trick the app into allocating a lot of memory.  We avoid this
1661                // by allocating and reading only a small chunk at a time, so that the
1662                // malicious message must actually *be* extremely large to cause
1663                // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
1664
1665                // Remember the buffer markers since we'll have to copy the bytes out of
1666                // it later.
1667                int originalBufferPos = bufferPos;
1668                int originalBufferSize = bufferSize;
1669
1670                // Mark the current buffer consumed.
1671                totalBytesRetired += bufferSize;
1672                bufferPos = 0;
1673                bufferSize = 0;
1674
1675                // Read all the rest of the bytes we need.
1676                int sizeLeft = size - (originalBufferSize - originalBufferPos);
1677                List<byte[]> chunks = new List<byte[]>();
1678
1679                while (sizeLeft > 0)
1680                {
1681                    byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
1682                    int pos = 0;
1683                    while (pos < chunk.Length)
1684                    {
1685                        int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1686                        if (n <= 0)
1687                        {
1688                            throw InvalidProtocolBufferException.TruncatedMessage();
1689                        }
1690                        totalBytesRetired += n;
1691                        pos += n;
1692                    }
1693                    sizeLeft -= chunk.Length;
1694                    chunks.Add(chunk);
1695                }
1696
1697                // OK, got everything.  Now concatenate it all into one buffer.
1698                byte[] bytes = new byte[size];
1699
1700                // Start by copying the leftover bytes from this.buffer.
1701                int newPos = originalBufferSize - originalBufferPos;
1702                ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
1703
1704                // And now all the chunks.
1705                foreach (byte[] chunk in chunks)
1706                {
1707                    Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
1708                    newPos += chunk.Length;
1709                }
1710
1711                // Done.
1712                return bytes;
1713            }
1714        }
1715
1716        /// <summary>
1717        /// Reads and discards a single field, given its tag value.
1718        /// </summary>
1719        /// <returns>false if the tag is an end-group tag, in which case
1720        /// nothing is skipped. Otherwise, returns true.</returns>
1721        [CLSCompliant(false)]
1722        public bool SkipField()
1723        {
1724            uint tag = lastTag;
1725            switch (WireFormat.GetTagWireType(tag))
1726            {
1727                case WireFormat.WireType.Varint:
1728                    ReadRawVarint64();
1729                    return true;
1730                case WireFormat.WireType.Fixed64:
1731                    ReadRawLittleEndian64();
1732                    return true;
1733                case WireFormat.WireType.LengthDelimited:
1734                    SkipRawBytes((int) ReadRawVarint32());
1735                    return true;
1736                case WireFormat.WireType.StartGroup:
1737                    SkipMessage();
1738                    CheckLastTagWas(
1739                        WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
1740                                           WireFormat.WireType.EndGroup));
1741                    return true;
1742                case WireFormat.WireType.EndGroup:
1743                    return false;
1744                case WireFormat.WireType.Fixed32:
1745                    ReadRawLittleEndian32();
1746                    return true;
1747                default:
1748                    throw InvalidProtocolBufferException.InvalidWireType();
1749            }
1750        }
1751
1752        /// <summary>
1753        /// Reads and discards an entire message.  This will read either until EOF
1754        /// or until an endgroup tag, whichever comes first.
1755        /// </summary>
1756        public void SkipMessage()
1757        {
1758            uint tag;
1759            string name;
1760            while (ReadTag(out tag, out name))
1761            {
1762                if (!SkipField())
1763                {
1764                    return;
1765                }
1766            }
1767        }
1768
1769        /// <summary>
1770        /// Reads and discards <paramref name="size"/> bytes.
1771        /// </summary>
1772        /// <exception cref="InvalidProtocolBufferException">the end of the stream
1773        /// or the current limit was reached</exception>
1774        public void SkipRawBytes(int size)
1775        {
1776            if (size < 0)
1777            {
1778                throw InvalidProtocolBufferException.NegativeSize();
1779            }
1780
1781            if (totalBytesRetired + bufferPos + size > currentLimit)
1782            {
1783                // Read to the end of the stream anyway.
1784                SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1785                // Then fail.
1786                throw InvalidProtocolBufferException.TruncatedMessage();
1787            }
1788
1789            if (size <= bufferSize - bufferPos)
1790            {
1791                // We have all the bytes we need already.
1792                bufferPos += size;
1793            }
1794            else
1795            {
1796                // Skipping more bytes than are in the buffer.  First skip what we have.
1797                int pos = bufferSize - bufferPos;
1798                totalBytesRetired += pos;
1799                bufferPos = 0;
1800                bufferSize = 0;
1801
1802                // Then skip directly from the InputStream for the rest.
1803                if (pos < size)
1804                {
1805                    if (input == null)
1806                    {
1807                        throw InvalidProtocolBufferException.TruncatedMessage();
1808                    }
1809                    SkipImpl(size - pos);
1810                    totalBytesRetired += size - pos;
1811                }
1812            }
1813        }
1814
1815        /// <summary>
1816        /// Abstraction of skipping to cope with streams which can't really skip.
1817        /// </summary>
1818        private void SkipImpl(int amountToSkip)
1819        {
1820            if (input.CanSeek)
1821            {
1822                long previousPosition = input.Position;
1823                input.Position += amountToSkip;
1824                if (input.Position != previousPosition + amountToSkip)
1825                {
1826                    throw InvalidProtocolBufferException.TruncatedMessage();
1827                }
1828            }
1829            else
1830            {
1831                byte[] skipBuffer = new byte[1024];
1832                while (amountToSkip > 0)
1833                {
1834                    int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
1835                    if (bytesRead <= 0)
1836                    {
1837                        throw InvalidProtocolBufferException.TruncatedMessage();
1838                    }
1839                    amountToSkip -= bytesRead;
1840                }
1841            }
1842        }
1843
1844        #endregion
1845    }
1846}
Note: See TracBrowser for help on using the repository browser.