blob: 74fa94758f97e12c2e95875088e11f04421c771f [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#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>
csharptest17699c22011-06-03 21:57:15 -050061 public sealed partial class CodedInputStream : ICodedInputStream
csharptest71f662c2011-05-20 15:15:34 -050062 {
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 internal const int DefaultRecursionLimit = 64;
71 internal const int DefaultSizeLimit = 64 << 20; // 64MB
72 public const int BufferSize = 4096;
73
74 /// <summary>
75 /// The total number of bytes read before the current buffer. The
76 /// total bytes read up to the current position can be computed as
77 /// totalBytesRetired + bufferPos.
78 /// </summary>
79 private int totalBytesRetired = 0;
80
81 /// <summary>
82 /// The absolute position of the end of the current message.
83 /// </summary>
84 private int currentLimit = int.MaxValue;
85
86 /// <summary>
87 /// <see cref="SetRecursionLimit"/>
88 /// </summary>
89 private int recursionDepth = 0;
90
91 private int recursionLimit = DefaultRecursionLimit;
92
93 /// <summary>
94 /// <see cref="SetSizeLimit"/>
95 /// </summary>
96 private int sizeLimit = DefaultSizeLimit;
97
98 #region Construction
99
100 /// <summary>
101 /// Creates a new CodedInputStream reading data from the given
102 /// stream.
103 /// </summary>
104 public static CodedInputStream CreateInstance(Stream input)
105 {
106 return new CodedInputStream(input);
107 }
108
109 /// <summary>
110 /// Creates a new CodedInputStream reading data from the given
111 /// byte array.
112 /// </summary>
113 public static CodedInputStream CreateInstance(byte[] buf)
114 {
115 return new CodedInputStream(buf, 0, buf.Length);
116 }
117
118 /// <summary>
119 /// Creates a new CodedInputStream that reads from the given
120 /// byte array slice.
121 /// </summary>
122 public static CodedInputStream CreateInstance(byte[] buf, int offset, int length)
123 {
124 return new CodedInputStream(buf, offset, length);
125 }
126
127 private CodedInputStream(byte[] buffer, int offset, int length)
128 {
129 this.buffer = buffer;
130 this.bufferPos = offset;
131 this.bufferSize = offset + length;
132 this.input = null;
133 }
134
135 private CodedInputStream(Stream input)
136 {
137 this.buffer = new byte[BufferSize];
138 this.bufferSize = 0;
139 this.input = input;
140 }
141
142 #endregion
143
144 #region Validation
145
146 /// <summary>
147 /// Verifies that the last call to ReadTag() returned the given tag value.
148 /// This is used to verify that a nested group ended with the correct
149 /// end tag.
150 /// </summary>
151 /// <exception cref="InvalidProtocolBufferException">The last
152 /// tag read was not the one specified</exception>
153 [CLSCompliant(false)]
154 public void CheckLastTagWas(uint value)
155 {
156 if (lastTag != value)
157 {
158 throw InvalidProtocolBufferException.InvalidEndTag();
159 }
160 }
161
162 #endregion
163
164 #region Reading of tags etc
csharptest123e5342011-06-03 14:15:21 -0500165
csharptest71f662c2011-05-20 15:15:34 -0500166 /// <summary>
csharptest123e5342011-06-03 14:15:21 -0500167 /// Attempt to read a field tag, returning false if we have reached the end
168 /// of the input data.
csharptest71f662c2011-05-20 15:15:34 -0500169 /// </summary>
csharptest123e5342011-06-03 14:15:21 -0500170 /// <remarks>
171 /// <para>
172 /// If fieldTag is non-zero and ReadTag returns true then the value in fieldName
173 /// may or may not be populated. However, if fieldTag is zero and ReadTag returns
174 /// true, then fieldName should be populated with a non-null field name.
175 /// </para><para>
176 /// In other words if ReadTag returns true then either fieldTag will be non-zero OR
177 /// fieldName will be non-zero. In some cases both may be populated, however the
178 /// builders will always prefer the fieldTag over fieldName.
179 /// </para>
180 /// </remarks>
csharptest71f662c2011-05-20 15:15:34 -0500181 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -0500182 public bool ReadTag(out uint fieldTag, out string fieldName)
csharptest71f662c2011-05-20 15:15:34 -0500183 {
csharptest123e5342011-06-03 14:15:21 -0500184 fieldName = null;
185
csharptest71f662c2011-05-20 15:15:34 -0500186 if (IsAtEnd)
187 {
csharptest123e5342011-06-03 14:15:21 -0500188 lastTag = fieldTag = 0;
189 return false;
csharptest71f662c2011-05-20 15:15:34 -0500190 }
191
csharptest123e5342011-06-03 14:15:21 -0500192 lastTag = fieldTag = ReadRawVarint32();
csharptest71f662c2011-05-20 15:15:34 -0500193 if (lastTag == 0)
194 {
195 // If we actually read zero, that's not a valid tag.
196 throw InvalidProtocolBufferException.InvalidTag();
197 }
csharptest123e5342011-06-03 14:15:21 -0500198 return true;
csharptest71f662c2011-05-20 15:15:34 -0500199 }
200
201 /// <summary>
202 /// Read a double field from the stream.
203 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500204 public bool ReadDouble(ref double value)
csharptest71f662c2011-05-20 15:15:34 -0500205 {
206#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
csharptestd2af9e92011-06-03 21:35:02 -0500207 byte[] rawBytes = ReadRawBytes(8);
208 if (!BitConverter.IsLittleEndian)
209 Array.Reverse(rawBytes);
210 value = BitConverter.ToDouble(rawBytes, 0);
211 return true;
csharptest71f662c2011-05-20 15:15:34 -0500212#else
csharptestd2af9e92011-06-03 21:35:02 -0500213 value = BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
214 return true;
csharptest71f662c2011-05-20 15:15:34 -0500215#endif
216 }
217
218 /// <summary>
219 /// Read a float field from the stream.
220 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500221 public bool ReadFloat(ref float value)
csharptest71f662c2011-05-20 15:15:34 -0500222 {
csharptestd2af9e92011-06-03 21:35:02 -0500223 byte[] rawBytes = ReadRawBytes(4);
224 if (!BitConverter.IsLittleEndian)
225 Array.Reverse(rawBytes);
226 value = BitConverter.ToSingle(rawBytes, 0);
227 return true;
csharptest71f662c2011-05-20 15:15:34 -0500228 }
229
230 /// <summary>
231 /// Read a uint64 field from the stream.
232 /// </summary>
233 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500234 public bool ReadUInt64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500235 {
csharptestd2af9e92011-06-03 21:35:02 -0500236 value = ReadRawVarint64();
237 return true;
csharptest71f662c2011-05-20 15:15:34 -0500238 }
239
240 /// <summary>
241 /// Read an int64 field from the stream.
242 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500243 public bool ReadInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500244 {
csharptestd2af9e92011-06-03 21:35:02 -0500245 value = (long) ReadRawVarint64();
246 return true;
csharptest71f662c2011-05-20 15:15:34 -0500247 }
248
249 /// <summary>
250 /// Read an int32 field from the stream.
251 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500252 public bool ReadInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500253 {
csharptestd2af9e92011-06-03 21:35:02 -0500254 value = (int)ReadRawVarint32();
255 return true;
csharptest71f662c2011-05-20 15:15:34 -0500256 }
257
258 /// <summary>
259 /// Read a fixed64 field from the stream.
260 /// </summary>
261 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500262 public bool ReadFixed64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500263 {
csharptestd2af9e92011-06-03 21:35:02 -0500264 value = ReadRawLittleEndian64();
265 return true;
csharptest71f662c2011-05-20 15:15:34 -0500266 }
267
268 /// <summary>
269 /// Read a fixed32 field from the stream.
270 /// </summary>
271 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500272 public bool ReadFixed32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500273 {
csharptestd2af9e92011-06-03 21:35:02 -0500274 value = ReadRawLittleEndian32();
275 return true;
csharptest71f662c2011-05-20 15:15:34 -0500276 }
277
278 /// <summary>
279 /// Read a bool field from the stream.
280 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500281 public bool ReadBool(ref bool value)
csharptest71f662c2011-05-20 15:15:34 -0500282 {
csharptestd2af9e92011-06-03 21:35:02 -0500283 value = ReadRawVarint32() != 0;
284 return true;
csharptest71f662c2011-05-20 15:15:34 -0500285 }
286
287 /// <summary>
288 /// Reads a string field from the stream.
289 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500290 public bool ReadString(ref string value)
csharptest71f662c2011-05-20 15:15:34 -0500291 {
292 int size = (int) ReadRawVarint32();
293 // No need to read any data for an empty string.
294 if (size == 0)
295 {
csharptestd2af9e92011-06-03 21:35:02 -0500296 value = "";
297 return true;
csharptest71f662c2011-05-20 15:15:34 -0500298 }
299 if (size <= bufferSize - bufferPos)
300 {
301 // Fast path: We already have the bytes in a contiguous buffer, so
302 // just copy directly from it.
303 String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
304 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500305 value = result;
306 return true;
csharptest71f662c2011-05-20 15:15:34 -0500307 }
308 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500309 value = Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
310 return true;
csharptest71f662c2011-05-20 15:15:34 -0500311 }
312
313 /// <summary>
314 /// Reads a group field value from the stream.
315 /// </summary>
316 public void ReadGroup(int fieldNumber, IBuilderLite builder,
317 ExtensionRegistry extensionRegistry)
318 {
319 if (recursionDepth >= recursionLimit)
320 {
321 throw InvalidProtocolBufferException.RecursionLimitExceeded();
322 }
323 ++recursionDepth;
324 builder.WeakMergeFrom(this, extensionRegistry);
325 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
326 --recursionDepth;
327 }
328
329 /// <summary>
330 /// Reads a group field value from the stream and merges it into the given
331 /// UnknownFieldSet.
332 /// </summary>
333 [Obsolete]
334 public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
335 {
336 if (recursionDepth >= recursionLimit)
337 {
338 throw InvalidProtocolBufferException.RecursionLimitExceeded();
339 }
340 ++recursionDepth;
341 builder.WeakMergeFrom(this);
342 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
343 --recursionDepth;
344 }
345
346 /// <summary>
347 /// Reads an embedded message field value from the stream.
348 /// </summary>
349 public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
350 {
351 int length = (int) ReadRawVarint32();
352 if (recursionDepth >= recursionLimit)
353 {
354 throw InvalidProtocolBufferException.RecursionLimitExceeded();
355 }
356 int oldLimit = PushLimit(length);
357 ++recursionDepth;
358 builder.WeakMergeFrom(this, extensionRegistry);
359 CheckLastTagWas(0);
360 --recursionDepth;
361 PopLimit(oldLimit);
362 }
363
364 /// <summary>
365 /// Reads a bytes field value from the stream.
366 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500367 public bool ReadBytes(ref ByteString value)
csharptest71f662c2011-05-20 15:15:34 -0500368 {
369 int size = (int) ReadRawVarint32();
370 if (size < bufferSize - bufferPos && size > 0)
371 {
372 // Fast path: We already have the bytes in a contiguous buffer, so
373 // just copy directly from it.
374 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
375 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500376 value = result;
377 return true;
csharptest71f662c2011-05-20 15:15:34 -0500378 }
379 else
380 {
381 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500382 value = ByteString.AttachBytes(ReadRawBytes(size));
383 return true;
csharptest71f662c2011-05-20 15:15:34 -0500384 }
385 }
386
387 /// <summary>
388 /// Reads a uint32 field value from the stream.
389 /// </summary>
390 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500391 public bool ReadUInt32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500392 {
csharptestd2af9e92011-06-03 21:35:02 -0500393 value = ReadRawVarint32();
394 return true;
csharptest71f662c2011-05-20 15:15:34 -0500395 }
396
397 /// <summary>
398 /// Reads an enum field value from the stream. The caller is responsible
399 /// for converting the numeric value to an actual enum.
400 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500401 public bool ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
csharptest71f662c2011-05-20 15:15:34 -0500402 {
csharptestd2af9e92011-06-03 21:35:02 -0500403 int rawValue = (int)ReadRawVarint32();
404
405 value = mapping.FindValueByNumber(rawValue);
406 if (value != null)
407 {
408 unknown = null;
409 return true;
410 }
411 unknown = rawValue;
412 return false;
413 }
414
415 /// <summary>
416 /// Reads an enum field value from the stream. If the enum is valid for type T,
417 /// then the ref value is set and it returns true. Otherwise the unkown output
418 /// value is set and this method returns false.
419 /// </summary>
420 [CLSCompliant(false)]
421 public bool ReadEnum<T>(ref T value, out object unknown)
422 where T : struct, IComparable, IFormattable, IConvertible
423 {
424 int number = (int)ReadRawVarint32();
425 if (Enum.IsDefined(typeof(T), number))
426 {
427 unknown = null;
428 value = (T)(object)number;
429 return true;
430 }
431 unknown = number;
432 return false;
csharptest71f662c2011-05-20 15:15:34 -0500433 }
434
435 /// <summary>
436 /// Reads an sfixed32 field value from the stream.
437 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500438 public bool ReadSFixed32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500439 {
csharptestd2af9e92011-06-03 21:35:02 -0500440 value = (int)ReadRawLittleEndian32();
441 return true;
csharptest71f662c2011-05-20 15:15:34 -0500442 }
443
444 /// <summary>
445 /// Reads an sfixed64 field value from the stream.
446 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500447 public bool ReadSFixed64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500448 {
csharptestd2af9e92011-06-03 21:35:02 -0500449 value = (long)ReadRawLittleEndian64();
450 return true;
csharptest71f662c2011-05-20 15:15:34 -0500451 }
452
453 /// <summary>
454 /// Reads an sint32 field value from the stream.
455 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500456 public bool ReadSInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500457 {
csharptestd2af9e92011-06-03 21:35:02 -0500458 value = DecodeZigZag32(ReadRawVarint32());
459 return true;
csharptest71f662c2011-05-20 15:15:34 -0500460 }
461
462 /// <summary>
463 /// Reads an sint64 field value from the stream.
464 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500465 public bool ReadSInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500466 {
csharptestd2af9e92011-06-03 21:35:02 -0500467 value = DecodeZigZag64(ReadRawVarint64());
468 return true;
469 }
470
471 [CLSCompliant(false)]
472 public void ReadPrimitiveArray<T>(FieldType fieldType, uint fieldTag, string fieldName, ICollection<T> list)
473 {
474 WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
475 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
476
477 // 2.3 allows packed form even if the field is not declared packed.
478 if(normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
479 {
480 int length = (int)(ReadRawVarint32() & int.MaxValue);
481 int limit = PushLimit(length);
482 while (!ReachedLimit)
483 {
484 Object value = null;
485 if(ReadPrimitiveField(fieldType, ref value))
486 list.Add((T)value);
487 }
488 PopLimit(limit);
489 }
490 else
491 {
492 Object value = null;
493 if (ReadPrimitiveField(fieldType, ref value))
494 list.Add((T)value);
495 }
496 }
497
498 [CLSCompliant(false)]
499 public void ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list, out ICollection<object> unknown, IEnumLiteMap mapping)
500 {
501 unknown = null;
502 object unkval;
503 IEnumLite value = null;
504 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
505
506 // 2.3 allows packed form even if the field is not declared packed.
507 if (wformat == WireFormat.WireType.LengthDelimited)
508 {
509 int length = (int)(ReadRawVarint32() & int.MaxValue);
510 int limit = PushLimit(length);
511 while (!ReachedLimit)
512 {
513 if (ReadEnum(ref value, out unkval, mapping))
514 list.Add(value);
515 else
516 {
517 if (unknown == null)
518 unknown = new List<object>();
519 unknown.Add(unkval);
520 }
521 }
522 PopLimit(limit);
523 }
524 else
525 {
526 if (ReadEnum(ref value, out unkval, mapping))
527 list.Add(value);
528 else
529 unknown = new object[] { unkval };
530 }
531 }
532
533 [CLSCompliant(false)]
534 public void ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list, out ICollection<object> unknown)
535 where T : struct, IComparable, IFormattable, IConvertible
536 {
537 unknown = null;
538 object unkval;
539 T value = default(T);
540 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
541
542 // 2.3 allows packed form even if the field is not declared packed.
543 if (wformat == WireFormat.WireType.LengthDelimited)
544 {
545 int length = (int)(ReadRawVarint32() & int.MaxValue);
546 int limit = PushLimit(length);
547 while (!ReachedLimit)
548 {
549 if (ReadEnum<T>(ref value, out unkval))
550 list.Add(value);
551 else
552 {
553 if (unknown == null)
554 unknown = new List<object>();
555 unknown.Add(unkval);
556 }
557 }
558 PopLimit(limit);
559 }
560 else
561 {
562 if (ReadEnum(ref value, out unkval))
563 list.Add(value);
564 else
565 unknown = new object[] { unkval };
566 }
567 }
568
569 [CLSCompliant(false)]
570 public void ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
571 {
572 IBuilderLite builder = messageType.WeakCreateBuilderForType();
573 ReadMessage(builder, registry);
574 list.Add((T)builder.WeakBuildPartial());
575 }
576
577 [CLSCompliant(false)]
578 public void ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
579 {
580 IBuilderLite builder = messageType.WeakCreateBuilderForType();
581 ReadGroup(WireFormat.GetTagFieldNumber(fieldTag), builder, registry);
582 list.Add((T)builder.WeakBuildPartial());
csharptest71f662c2011-05-20 15:15:34 -0500583 }
584
585 /// <summary>
586 /// Reads a field of any primitive type. Enums, groups and embedded
587 /// messages are not handled by this method.
588 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500589 public bool ReadPrimitiveField(FieldType fieldType, ref object value)
csharptest71f662c2011-05-20 15:15:34 -0500590 {
591 switch (fieldType)
592 {
593 case FieldType.Double:
csharptestd2af9e92011-06-03 21:35:02 -0500594 {
595 double tmp = 0;
596 if (ReadDouble(ref tmp))
597 {
598 value = tmp;
599 return true;
600 }
601 return false;
602 }
csharptest71f662c2011-05-20 15:15:34 -0500603 case FieldType.Float:
csharptestd2af9e92011-06-03 21:35:02 -0500604 {
605 float tmp = 0;
606 if (ReadFloat(ref tmp))
607 {
608 value = tmp;
609 return true;
610 }
611 return false;
612 }
csharptest71f662c2011-05-20 15:15:34 -0500613 case FieldType.Int64:
csharptestd2af9e92011-06-03 21:35:02 -0500614 {
615 long tmp = 0;
616 if (ReadInt64(ref tmp))
617 {
618 value = tmp;
619 return true;
620 }
621 return false;
622 }
csharptest71f662c2011-05-20 15:15:34 -0500623 case FieldType.UInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500624 {
625 ulong tmp = 0;
626 if (ReadUInt64(ref tmp))
627 {
628 value = tmp;
629 return true;
630 }
631 return false;
632 }
csharptest71f662c2011-05-20 15:15:34 -0500633 case FieldType.Int32:
csharptestd2af9e92011-06-03 21:35:02 -0500634 {
635 int tmp = 0;
636 if (ReadInt32(ref tmp))
637 {
638 value = tmp;
639 return true;
640 }
641 return false;
642 }
csharptest71f662c2011-05-20 15:15:34 -0500643 case FieldType.Fixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500644 {
645 ulong tmp = 0;
646 if (ReadFixed64(ref tmp))
647 {
648 value = tmp;
649 return true;
650 }
651 return false;
652 }
csharptest71f662c2011-05-20 15:15:34 -0500653 case FieldType.Fixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500654 {
655 uint tmp = 0;
656 if (ReadFixed32(ref tmp))
657 {
658 value = tmp;
659 return true;
660 }
661 return false;
662 }
csharptest71f662c2011-05-20 15:15:34 -0500663 case FieldType.Bool:
csharptestd2af9e92011-06-03 21:35:02 -0500664 {
665 bool tmp = false;
666 if (ReadBool(ref tmp))
667 {
668 value = tmp;
669 return true;
670 }
671 return false;
672 }
csharptest71f662c2011-05-20 15:15:34 -0500673 case FieldType.String:
csharptestd2af9e92011-06-03 21:35:02 -0500674 {
675 string tmp = null;
676 if (ReadString(ref tmp))
677 {
678 value = tmp;
679 return true;
680 }
681 return false;
682 }
csharptest71f662c2011-05-20 15:15:34 -0500683 case FieldType.Bytes:
csharptestd2af9e92011-06-03 21:35:02 -0500684 {
685 ByteString tmp = null;
686 if (ReadBytes(ref tmp))
687 {
688 value = tmp;
689 return true;
690 }
691 return false;
692 }
csharptest71f662c2011-05-20 15:15:34 -0500693 case FieldType.UInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500694 {
695 uint tmp = 0;
696 if (ReadUInt32(ref tmp))
697 {
698 value = tmp;
699 return true;
700 }
701 return false;
702 }
csharptest71f662c2011-05-20 15:15:34 -0500703 case FieldType.SFixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500704 {
705 int tmp = 0;
706 if (ReadSFixed32(ref tmp))
707 {
708 value = tmp;
709 return true;
710 }
711 return false;
712 }
csharptest71f662c2011-05-20 15:15:34 -0500713 case FieldType.SFixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500714 {
715 long tmp = 0;
716 if (ReadSFixed64(ref tmp))
717 {
718 value = tmp;
719 return true;
720 }
721 return false;
722 }
csharptest71f662c2011-05-20 15:15:34 -0500723 case FieldType.SInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500724 {
725 int tmp = 0;
726 if (ReadSInt32(ref tmp))
727 {
728 value = tmp;
729 return true;
730 }
731 return false;
732 }
csharptest71f662c2011-05-20 15:15:34 -0500733 case FieldType.SInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500734 {
735 long tmp = 0;
736 if (ReadSInt64(ref tmp))
737 {
738 value = tmp;
739 return true;
740 }
741 return false;
742 }
csharptest71f662c2011-05-20 15:15:34 -0500743 case FieldType.Group:
744 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
745 case FieldType.Message:
746 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
747 // We don't handle enums because we don't know what to do if the
748 // value is not recognized.
749 case FieldType.Enum:
750 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
751 default:
752 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
753 }
754 }
755
756 #endregion
757
758 #region Underlying reading primitives
759
760 /// <summary>
761 /// Same code as ReadRawVarint32, but read each byte individually, checking for
762 /// buffer overflow.
763 /// </summary>
764 private uint SlowReadRawVarint32()
765 {
766 int tmp = ReadRawByte();
767 if (tmp < 128)
768 {
769 return (uint) tmp;
770 }
771 int result = tmp & 0x7f;
772 if ((tmp = ReadRawByte()) < 128)
773 {
774 result |= tmp << 7;
775 }
776 else
777 {
778 result |= (tmp & 0x7f) << 7;
779 if ((tmp = ReadRawByte()) < 128)
780 {
781 result |= tmp << 14;
782 }
783 else
784 {
785 result |= (tmp & 0x7f) << 14;
786 if ((tmp = ReadRawByte()) < 128)
787 {
788 result |= tmp << 21;
789 }
790 else
791 {
792 result |= (tmp & 0x7f) << 21;
793 result |= (tmp = ReadRawByte()) << 28;
794 if (tmp >= 128)
795 {
796 // Discard upper 32 bits.
797 for (int i = 0; i < 5; i++)
798 {
799 if (ReadRawByte() < 128) return (uint) result;
800 }
801 throw InvalidProtocolBufferException.MalformedVarint();
802 }
803 }
804 }
805 }
806 return (uint) result;
807 }
808
809 /// <summary>
810 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
811 /// This method is optimised for the case where we've got lots of data in the buffer.
812 /// That means we can check the size just once, then just read directly from the buffer
813 /// without constant rechecking of the buffer length.
814 /// </summary>
815 [CLSCompliant(false)]
816 public uint ReadRawVarint32()
817 {
818 if (bufferPos + 5 > bufferSize)
819 {
820 return SlowReadRawVarint32();
821 }
822
823 int tmp = buffer[bufferPos++];
824 if (tmp < 128)
825 {
826 return (uint) tmp;
827 }
828 int result = tmp & 0x7f;
829 if ((tmp = buffer[bufferPos++]) < 128)
830 {
831 result |= tmp << 7;
832 }
833 else
834 {
835 result |= (tmp & 0x7f) << 7;
836 if ((tmp = buffer[bufferPos++]) < 128)
837 {
838 result |= tmp << 14;
839 }
840 else
841 {
842 result |= (tmp & 0x7f) << 14;
843 if ((tmp = buffer[bufferPos++]) < 128)
844 {
845 result |= tmp << 21;
846 }
847 else
848 {
849 result |= (tmp & 0x7f) << 21;
850 result |= (tmp = buffer[bufferPos++]) << 28;
851 if (tmp >= 128)
852 {
853 // Discard upper 32 bits.
854 // Note that this has to use ReadRawByte() as we only ensure we've
855 // got at least 5 bytes at the start of the method. This lets us
856 // use the fast path in more cases, and we rarely hit this section of code.
857 for (int i = 0; i < 5; i++)
858 {
859 if (ReadRawByte() < 128) return (uint) result;
860 }
861 throw InvalidProtocolBufferException.MalformedVarint();
862 }
863 }
864 }
865 }
866 return (uint) result;
867 }
868
869 /// <summary>
870 /// Reads a varint from the input one byte at a time, so that it does not
871 /// read any bytes after the end of the varint. If you simply wrapped the
872 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
873 /// then you would probably end up reading past the end of the varint since
874 /// CodedInputStream buffers its input.
875 /// </summary>
876 /// <param name="input"></param>
877 /// <returns></returns>
878 [CLSCompliant(false)]
879 public static uint ReadRawVarint32(Stream input)
880 {
881 int result = 0;
882 int offset = 0;
883 for (; offset < 32; offset += 7)
884 {
885 int b = input.ReadByte();
886 if (b == -1)
887 {
888 throw InvalidProtocolBufferException.TruncatedMessage();
889 }
890 result |= (b & 0x7f) << offset;
891 if ((b & 0x80) == 0)
892 {
893 return (uint) result;
894 }
895 }
896 // Keep reading up to 64 bits.
897 for (; offset < 64; offset += 7)
898 {
899 int b = input.ReadByte();
900 if (b == -1)
901 {
902 throw InvalidProtocolBufferException.TruncatedMessage();
903 }
904 if ((b & 0x80) == 0)
905 {
906 return (uint) result;
907 }
908 }
909 throw InvalidProtocolBufferException.MalformedVarint();
910 }
911
912 /// <summary>
913 /// Read a raw varint from the stream.
914 /// </summary>
915 [CLSCompliant(false)]
916 public ulong ReadRawVarint64()
917 {
918 int shift = 0;
919 ulong result = 0;
920 while (shift < 64)
921 {
922 byte b = ReadRawByte();
923 result |= (ulong) (b & 0x7F) << shift;
924 if ((b & 0x80) == 0)
925 {
926 return result;
927 }
928 shift += 7;
929 }
930 throw InvalidProtocolBufferException.MalformedVarint();
931 }
932
933 /// <summary>
934 /// Read a 32-bit little-endian integer from the stream.
935 /// </summary>
936 [CLSCompliant(false)]
937 public uint ReadRawLittleEndian32()
938 {
939 uint b1 = ReadRawByte();
940 uint b2 = ReadRawByte();
941 uint b3 = ReadRawByte();
942 uint b4 = ReadRawByte();
943 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
944 }
945
946 /// <summary>
947 /// Read a 64-bit little-endian integer from the stream.
948 /// </summary>
949 [CLSCompliant(false)]
950 public ulong ReadRawLittleEndian64()
951 {
952 ulong b1 = ReadRawByte();
953 ulong b2 = ReadRawByte();
954 ulong b3 = ReadRawByte();
955 ulong b4 = ReadRawByte();
956 ulong b5 = ReadRawByte();
957 ulong b6 = ReadRawByte();
958 ulong b7 = ReadRawByte();
959 ulong b8 = ReadRawByte();
960 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
961 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
962 }
963
964 #endregion
965
966 /// <summary>
967 /// Decode a 32-bit value with ZigZag encoding.
968 /// </summary>
969 /// <remarks>
970 /// ZigZag encodes signed integers into values that can be efficiently
971 /// encoded with varint. (Otherwise, negative values must be
972 /// sign-extended to 64 bits to be varint encoded, thus always taking
973 /// 10 bytes on the wire.)
974 /// </remarks>
975 [CLSCompliant(false)]
976 public static int DecodeZigZag32(uint n)
977 {
978 return (int) (n >> 1) ^ -(int) (n & 1);
979 }
980
981 /// <summary>
982 /// Decode a 32-bit value with ZigZag encoding.
983 /// </summary>
984 /// <remarks>
985 /// ZigZag encodes signed integers into values that can be efficiently
986 /// encoded with varint. (Otherwise, negative values must be
987 /// sign-extended to 64 bits to be varint encoded, thus always taking
988 /// 10 bytes on the wire.)
989 /// </remarks>
990 [CLSCompliant(false)]
991 public static long DecodeZigZag64(ulong n)
992 {
993 return (long) (n >> 1) ^ -(long) (n & 1);
994 }
995
996 /// <summary>
997 /// Set the maximum message recursion depth.
998 /// </summary>
999 /// <remarks>
1000 /// In order to prevent malicious
1001 /// messages from causing stack overflows, CodedInputStream limits
1002 /// how deeply messages may be nested. The default limit is 64.
1003 /// </remarks>
1004 public int SetRecursionLimit(int limit)
1005 {
1006 if (limit < 0)
1007 {
1008 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
1009 }
1010 int oldLimit = recursionLimit;
1011 recursionLimit = limit;
1012 return oldLimit;
1013 }
1014
1015 /// <summary>
1016 /// Set the maximum message size.
1017 /// </summary>
1018 /// <remarks>
1019 /// In order to prevent malicious messages from exhausting memory or
1020 /// causing integer overflows, CodedInputStream limits how large a message may be.
1021 /// The default limit is 64MB. You should set this limit as small
1022 /// as you can without harming your app's functionality. Note that
1023 /// size limits only apply when reading from an InputStream, not
1024 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
1025 /// If you want to read several messages from a single CodedInputStream, you
1026 /// can call ResetSizeCounter() after each message to avoid hitting the
1027 /// size limit.
1028 /// </remarks>
1029 public int SetSizeLimit(int limit)
1030 {
1031 if (limit < 0)
1032 {
1033 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
1034 }
1035 int oldLimit = sizeLimit;
1036 sizeLimit = limit;
1037 return oldLimit;
1038 }
1039
1040 #region Internal reading and buffer management
1041
1042 /// <summary>
1043 /// Resets the current size counter to zero (see SetSizeLimit).
1044 /// </summary>
1045 public void ResetSizeCounter()
1046 {
1047 totalBytesRetired = 0;
1048 }
1049
1050 /// <summary>
1051 /// Sets currentLimit to (current position) + byteLimit. This is called
1052 /// when descending into a length-delimited embedded message. The previous
1053 /// limit is returned.
1054 /// </summary>
1055 /// <returns>The old limit.</returns>
1056 public int PushLimit(int byteLimit)
1057 {
1058 if (byteLimit < 0)
1059 {
1060 throw InvalidProtocolBufferException.NegativeSize();
1061 }
1062 byteLimit += totalBytesRetired + bufferPos;
1063 int oldLimit = currentLimit;
1064 if (byteLimit > oldLimit)
1065 {
1066 throw InvalidProtocolBufferException.TruncatedMessage();
1067 }
1068 currentLimit = byteLimit;
1069
1070 RecomputeBufferSizeAfterLimit();
1071
1072 return oldLimit;
1073 }
1074
1075 private void RecomputeBufferSizeAfterLimit()
1076 {
1077 bufferSize += bufferSizeAfterLimit;
1078 int bufferEnd = totalBytesRetired + bufferSize;
1079 if (bufferEnd > currentLimit)
1080 {
1081 // Limit is in current buffer.
1082 bufferSizeAfterLimit = bufferEnd - currentLimit;
1083 bufferSize -= bufferSizeAfterLimit;
1084 }
1085 else
1086 {
1087 bufferSizeAfterLimit = 0;
1088 }
1089 }
1090
1091 /// <summary>
1092 /// Discards the current limit, returning the previous limit.
1093 /// </summary>
1094 public void PopLimit(int oldLimit)
1095 {
1096 currentLimit = oldLimit;
1097 RecomputeBufferSizeAfterLimit();
1098 }
1099
1100 /// <summary>
1101 /// Returns whether or not all the data before the limit has been read.
1102 /// </summary>
1103 /// <returns></returns>
1104 public bool ReachedLimit
1105 {
1106 get
1107 {
1108 if (currentLimit == int.MaxValue)
1109 {
1110 return false;
1111 }
1112 int currentAbsolutePosition = totalBytesRetired + bufferPos;
1113 return currentAbsolutePosition >= currentLimit;
1114 }
1115 }
1116
1117 /// <summary>
1118 /// Returns true if the stream has reached the end of the input. This is the
1119 /// case if either the end of the underlying input source has been reached or
1120 /// the stream has reached a limit created using PushLimit.
1121 /// </summary>
1122 public bool IsAtEnd
1123 {
1124 get { return bufferPos == bufferSize && !RefillBuffer(false); }
1125 }
1126
1127 /// <summary>
1128 /// Called when buffer is empty to read more bytes from the
1129 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1130 /// either there will be at least one byte in the buffer when it returns
1131 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
1132 /// RefillBuffer() returns false if no more bytes were available.
1133 /// </summary>
1134 /// <param name="mustSucceed"></param>
1135 /// <returns></returns>
1136 private bool RefillBuffer(bool mustSucceed)
1137 {
1138 if (bufferPos < bufferSize)
1139 {
1140 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1141 }
1142
1143 if (totalBytesRetired + bufferSize == currentLimit)
1144 {
1145 // Oops, we hit a limit.
1146 if (mustSucceed)
1147 {
1148 throw InvalidProtocolBufferException.TruncatedMessage();
1149 }
1150 else
1151 {
1152 return false;
1153 }
1154 }
1155
1156 totalBytesRetired += bufferSize;
1157
1158 bufferPos = 0;
1159 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1160 if (bufferSize < 0)
1161 {
1162 throw new InvalidOperationException("Stream.Read returned a negative count");
1163 }
1164 if (bufferSize == 0)
1165 {
1166 if (mustSucceed)
1167 {
1168 throw InvalidProtocolBufferException.TruncatedMessage();
1169 }
1170 else
1171 {
1172 return false;
1173 }
1174 }
1175 else
1176 {
1177 RecomputeBufferSizeAfterLimit();
1178 int totalBytesRead =
1179 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1180 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1181 {
1182 throw InvalidProtocolBufferException.SizeLimitExceeded();
1183 }
1184 return true;
1185 }
1186 }
1187
1188 /// <summary>
1189 /// Read one byte from the input.
1190 /// </summary>
1191 /// <exception cref="InvalidProtocolBufferException">
1192 /// the end of the stream or the current limit was reached
1193 /// </exception>
1194 public byte ReadRawByte()
1195 {
1196 if (bufferPos == bufferSize)
1197 {
1198 RefillBuffer(true);
1199 }
1200 return buffer[bufferPos++];
1201 }
1202
1203 /// <summary>
1204 /// Read a fixed size of bytes from the input.
1205 /// </summary>
1206 /// <exception cref="InvalidProtocolBufferException">
1207 /// the end of the stream or the current limit was reached
1208 /// </exception>
1209 public byte[] ReadRawBytes(int size)
1210 {
1211 if (size < 0)
1212 {
1213 throw InvalidProtocolBufferException.NegativeSize();
1214 }
1215
1216 if (totalBytesRetired + bufferPos + size > currentLimit)
1217 {
1218 // Read to the end of the stream anyway.
1219 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1220 // Then fail.
1221 throw InvalidProtocolBufferException.TruncatedMessage();
1222 }
1223
1224 if (size <= bufferSize - bufferPos)
1225 {
1226 // We have all the bytes we need already.
1227 byte[] bytes = new byte[size];
1228 Array.Copy(buffer, bufferPos, bytes, 0, size);
1229 bufferPos += size;
1230 return bytes;
1231 }
1232 else if (size < BufferSize)
1233 {
1234 // Reading more bytes than are in the buffer, but not an excessive number
1235 // of bytes. We can safely allocate the resulting array ahead of time.
1236
1237 // First copy what we have.
1238 byte[] bytes = new byte[size];
1239 int pos = bufferSize - bufferPos;
1240 Array.Copy(buffer, bufferPos, bytes, 0, pos);
1241 bufferPos = bufferSize;
1242
1243 // We want to use RefillBuffer() and then copy from the buffer into our
1244 // byte array rather than reading directly into our byte array because
1245 // the input may be unbuffered.
1246 RefillBuffer(true);
1247
1248 while (size - pos > bufferSize)
1249 {
1250 Array.Copy(buffer, 0, bytes, pos, bufferSize);
1251 pos += bufferSize;
1252 bufferPos = bufferSize;
1253 RefillBuffer(true);
1254 }
1255
1256 Array.Copy(buffer, 0, bytes, pos, size - pos);
1257 bufferPos = size - pos;
1258
1259 return bytes;
1260 }
1261 else
1262 {
1263 // The size is very large. For security reasons, we can't allocate the
1264 // entire byte array yet. The size comes directly from the input, so a
1265 // maliciously-crafted message could provide a bogus very large size in
1266 // order to trick the app into allocating a lot of memory. We avoid this
1267 // by allocating and reading only a small chunk at a time, so that the
1268 // malicious message must actually *be* extremely large to cause
1269 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1270
1271 // Remember the buffer markers since we'll have to copy the bytes out of
1272 // it later.
1273 int originalBufferPos = bufferPos;
1274 int originalBufferSize = bufferSize;
1275
1276 // Mark the current buffer consumed.
1277 totalBytesRetired += bufferSize;
1278 bufferPos = 0;
1279 bufferSize = 0;
1280
1281 // Read all the rest of the bytes we need.
1282 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1283 List<byte[]> chunks = new List<byte[]>();
1284
1285 while (sizeLeft > 0)
1286 {
1287 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
1288 int pos = 0;
1289 while (pos < chunk.Length)
1290 {
1291 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1292 if (n <= 0)
1293 {
1294 throw InvalidProtocolBufferException.TruncatedMessage();
1295 }
1296 totalBytesRetired += n;
1297 pos += n;
1298 }
1299 sizeLeft -= chunk.Length;
1300 chunks.Add(chunk);
1301 }
1302
1303 // OK, got everything. Now concatenate it all into one buffer.
1304 byte[] bytes = new byte[size];
1305
1306 // Start by copying the leftover bytes from this.buffer.
1307 int newPos = originalBufferSize - originalBufferPos;
1308 Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
1309
1310 // And now all the chunks.
1311 foreach (byte[] chunk in chunks)
1312 {
1313 Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
1314 newPos += chunk.Length;
1315 }
1316
1317 // Done.
1318 return bytes;
1319 }
1320 }
1321
1322 /// <summary>
1323 /// Reads and discards a single field, given its tag value.
1324 /// </summary>
1325 /// <returns>false if the tag is an end-group tag, in which case
1326 /// nothing is skipped. Otherwise, returns true.</returns>
1327 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -05001328 public bool SkipField()
csharptest71f662c2011-05-20 15:15:34 -05001329 {
csharptest123e5342011-06-03 14:15:21 -05001330 uint tag = lastTag;
csharptest71f662c2011-05-20 15:15:34 -05001331 switch (WireFormat.GetTagWireType(tag))
1332 {
1333 case WireFormat.WireType.Varint:
csharptestd2af9e92011-06-03 21:35:02 -05001334 ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -05001335 return true;
1336 case WireFormat.WireType.Fixed64:
1337 ReadRawLittleEndian64();
1338 return true;
1339 case WireFormat.WireType.LengthDelimited:
1340 SkipRawBytes((int) ReadRawVarint32());
1341 return true;
1342 case WireFormat.WireType.StartGroup:
1343 SkipMessage();
1344 CheckLastTagWas(
1345 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
1346 WireFormat.WireType.EndGroup));
1347 return true;
1348 case WireFormat.WireType.EndGroup:
1349 return false;
1350 case WireFormat.WireType.Fixed32:
1351 ReadRawLittleEndian32();
1352 return true;
1353 default:
1354 throw InvalidProtocolBufferException.InvalidWireType();
1355 }
1356 }
1357
1358 /// <summary>
1359 /// Reads and discards an entire message. This will read either until EOF
1360 /// or until an endgroup tag, whichever comes first.
1361 /// </summary>
1362 public void SkipMessage()
1363 {
csharptest123e5342011-06-03 14:15:21 -05001364 uint tag;
1365 string name;
1366 while (ReadTag(out tag, out name))
csharptest71f662c2011-05-20 15:15:34 -05001367 {
csharptest123e5342011-06-03 14:15:21 -05001368 if (!SkipField())
csharptest71f662c2011-05-20 15:15:34 -05001369 {
1370 return;
1371 }
1372 }
1373 }
1374
1375 /// <summary>
1376 /// Reads and discards <paramref name="size"/> bytes.
1377 /// </summary>
1378 /// <exception cref="InvalidProtocolBufferException">the end of the stream
1379 /// or the current limit was reached</exception>
1380 public void SkipRawBytes(int size)
1381 {
1382 if (size < 0)
1383 {
1384 throw InvalidProtocolBufferException.NegativeSize();
1385 }
1386
1387 if (totalBytesRetired + bufferPos + size > currentLimit)
1388 {
1389 // Read to the end of the stream anyway.
1390 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1391 // Then fail.
1392 throw InvalidProtocolBufferException.TruncatedMessage();
1393 }
1394
1395 if (size <= bufferSize - bufferPos)
1396 {
1397 // We have all the bytes we need already.
1398 bufferPos += size;
1399 }
1400 else
1401 {
1402 // Skipping more bytes than are in the buffer. First skip what we have.
1403 int pos = bufferSize - bufferPos;
1404 totalBytesRetired += pos;
1405 bufferPos = 0;
1406 bufferSize = 0;
1407
1408 // Then skip directly from the InputStream for the rest.
1409 if (pos < size)
1410 {
1411 if (input == null)
1412 {
1413 throw InvalidProtocolBufferException.TruncatedMessage();
1414 }
1415 SkipImpl(size - pos);
1416 totalBytesRetired += size - pos;
1417 }
1418 }
1419 }
1420
1421 /// <summary>
1422 /// Abstraction of skipping to cope with streams which can't really skip.
1423 /// </summary>
1424 private void SkipImpl(int amountToSkip)
1425 {
1426 if (input.CanSeek)
1427 {
1428 long previousPosition = input.Position;
1429 input.Position += amountToSkip;
1430 if (input.Position != previousPosition + amountToSkip)
1431 {
1432 throw InvalidProtocolBufferException.TruncatedMessage();
1433 }
1434 }
1435 else
1436 {
1437 byte[] skipBuffer = new byte[1024];
1438 while (amountToSkip > 0)
1439 {
1440 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
1441 if (bytesRead <= 0)
1442 {
1443 throw InvalidProtocolBufferException.TruncatedMessage();
1444 }
1445 amountToSkip -= bytesRead;
1446 }
1447 }
1448 }
1449
1450 #endregion
1451 }
1452}