blob: 000065bd4f6a3c263d654355f53c21b4c6a59ed8 [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
csharptest4ba365d2011-06-08 17:40:51 -0500207 if (BitConverter.IsLittleEndian && 8 <= bufferSize - bufferPos)
208 {
209 value = BitConverter.ToDouble(buffer, bufferPos);
210 bufferPos += 8;
211 }
212 else
213 {
214 byte[] rawBytes = ReadRawBytes(8);
215 if (!BitConverter.IsLittleEndian)
csharptestaef072a2011-06-08 18:00:43 -0500216 ByteArray.Reverse(rawBytes);
csharptest4ba365d2011-06-08 17:40:51 -0500217 value = BitConverter.ToDouble(rawBytes, 0);
218 }
csharptest71f662c2011-05-20 15:15:34 -0500219#else
csharptestd2af9e92011-06-03 21:35:02 -0500220 value = BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
csharptest71f662c2011-05-20 15:15:34 -0500221#endif
csharptest4ba365d2011-06-08 17:40:51 -0500222 return true;
csharptest71f662c2011-05-20 15:15:34 -0500223 }
224
225 /// <summary>
226 /// Read a float field from the stream.
227 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500228 public bool ReadFloat(ref float value)
csharptest71f662c2011-05-20 15:15:34 -0500229 {
csharptest4ba365d2011-06-08 17:40:51 -0500230 if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
231 {
232 value = BitConverter.ToSingle(buffer, bufferPos);
233 bufferPos += 4;
234 }
235 else
236 {
237 byte[] rawBytes = ReadRawBytes(4);
238 if (!BitConverter.IsLittleEndian)
csharptestaef072a2011-06-08 18:00:43 -0500239 ByteArray.Reverse(rawBytes);
csharptest4ba365d2011-06-08 17:40:51 -0500240 value = BitConverter.ToSingle(rawBytes, 0);
241 }
csharptestd2af9e92011-06-03 21:35:02 -0500242 return true;
csharptest71f662c2011-05-20 15:15:34 -0500243 }
244
245 /// <summary>
246 /// Read a uint64 field from the stream.
247 /// </summary>
248 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500249 public bool ReadUInt64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500250 {
csharptestd2af9e92011-06-03 21:35:02 -0500251 value = ReadRawVarint64();
252 return true;
csharptest71f662c2011-05-20 15:15:34 -0500253 }
254
255 /// <summary>
256 /// Read an int64 field from the stream.
257 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500258 public bool ReadInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500259 {
csharptestd2af9e92011-06-03 21:35:02 -0500260 value = (long) ReadRawVarint64();
261 return true;
csharptest71f662c2011-05-20 15:15:34 -0500262 }
263
264 /// <summary>
265 /// Read an int32 field from the stream.
266 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500267 public bool ReadInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500268 {
csharptestd2af9e92011-06-03 21:35:02 -0500269 value = (int)ReadRawVarint32();
270 return true;
csharptest71f662c2011-05-20 15:15:34 -0500271 }
272
273 /// <summary>
274 /// Read a fixed64 field from the stream.
275 /// </summary>
276 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500277 public bool ReadFixed64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500278 {
csharptestd2af9e92011-06-03 21:35:02 -0500279 value = ReadRawLittleEndian64();
280 return true;
csharptest71f662c2011-05-20 15:15:34 -0500281 }
282
283 /// <summary>
284 /// Read a fixed32 field from the stream.
285 /// </summary>
286 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500287 public bool ReadFixed32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500288 {
csharptestd2af9e92011-06-03 21:35:02 -0500289 value = ReadRawLittleEndian32();
290 return true;
csharptest71f662c2011-05-20 15:15:34 -0500291 }
292
293 /// <summary>
294 /// Read a bool field from the stream.
295 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500296 public bool ReadBool(ref bool value)
csharptest71f662c2011-05-20 15:15:34 -0500297 {
csharptestd2af9e92011-06-03 21:35:02 -0500298 value = ReadRawVarint32() != 0;
299 return true;
csharptest71f662c2011-05-20 15:15:34 -0500300 }
301
302 /// <summary>
303 /// Reads a string field from the stream.
304 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500305 public bool ReadString(ref string value)
csharptest71f662c2011-05-20 15:15:34 -0500306 {
307 int size = (int) ReadRawVarint32();
308 // No need to read any data for an empty string.
309 if (size == 0)
310 {
csharptestd2af9e92011-06-03 21:35:02 -0500311 value = "";
312 return true;
csharptest71f662c2011-05-20 15:15:34 -0500313 }
314 if (size <= bufferSize - bufferPos)
315 {
316 // Fast path: We already have the bytes in a contiguous buffer, so
317 // just copy directly from it.
318 String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
319 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500320 value = result;
321 return true;
csharptest71f662c2011-05-20 15:15:34 -0500322 }
323 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500324 value = Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
325 return true;
csharptest71f662c2011-05-20 15:15:34 -0500326 }
327
328 /// <summary>
329 /// Reads a group field value from the stream.
330 /// </summary>
331 public void ReadGroup(int fieldNumber, IBuilderLite builder,
332 ExtensionRegistry extensionRegistry)
333 {
334 if (recursionDepth >= recursionLimit)
335 {
336 throw InvalidProtocolBufferException.RecursionLimitExceeded();
337 }
338 ++recursionDepth;
339 builder.WeakMergeFrom(this, extensionRegistry);
340 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
341 --recursionDepth;
342 }
343
344 /// <summary>
345 /// Reads a group field value from the stream and merges it into the given
346 /// UnknownFieldSet.
347 /// </summary>
348 [Obsolete]
349 public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
350 {
351 if (recursionDepth >= recursionLimit)
352 {
353 throw InvalidProtocolBufferException.RecursionLimitExceeded();
354 }
355 ++recursionDepth;
356 builder.WeakMergeFrom(this);
357 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
358 --recursionDepth;
359 }
360
361 /// <summary>
362 /// Reads an embedded message field value from the stream.
363 /// </summary>
364 public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
365 {
366 int length = (int) ReadRawVarint32();
367 if (recursionDepth >= recursionLimit)
368 {
369 throw InvalidProtocolBufferException.RecursionLimitExceeded();
370 }
371 int oldLimit = PushLimit(length);
372 ++recursionDepth;
373 builder.WeakMergeFrom(this, extensionRegistry);
374 CheckLastTagWas(0);
375 --recursionDepth;
376 PopLimit(oldLimit);
377 }
378
379 /// <summary>
380 /// Reads a bytes field value from the stream.
381 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500382 public bool ReadBytes(ref ByteString value)
csharptest71f662c2011-05-20 15:15:34 -0500383 {
384 int size = (int) ReadRawVarint32();
385 if (size < bufferSize - bufferPos && size > 0)
386 {
387 // Fast path: We already have the bytes in a contiguous buffer, so
388 // just copy directly from it.
389 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
390 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500391 value = result;
392 return true;
csharptest71f662c2011-05-20 15:15:34 -0500393 }
394 else
395 {
396 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500397 value = ByteString.AttachBytes(ReadRawBytes(size));
398 return true;
csharptest71f662c2011-05-20 15:15:34 -0500399 }
400 }
401
402 /// <summary>
403 /// Reads a uint32 field value from the stream.
404 /// </summary>
405 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500406 public bool ReadUInt32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500407 {
csharptestd2af9e92011-06-03 21:35:02 -0500408 value = ReadRawVarint32();
409 return true;
csharptest71f662c2011-05-20 15:15:34 -0500410 }
411
412 /// <summary>
413 /// Reads an enum field value from the stream. The caller is responsible
414 /// for converting the numeric value to an actual enum.
415 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500416 public bool ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
csharptest71f662c2011-05-20 15:15:34 -0500417 {
csharptestd2af9e92011-06-03 21:35:02 -0500418 int rawValue = (int)ReadRawVarint32();
419
420 value = mapping.FindValueByNumber(rawValue);
421 if (value != null)
422 {
423 unknown = null;
424 return true;
425 }
426 unknown = rawValue;
427 return false;
428 }
429
430 /// <summary>
431 /// Reads an enum field value from the stream. If the enum is valid for type T,
432 /// then the ref value is set and it returns true. Otherwise the unkown output
433 /// value is set and this method returns false.
434 /// </summary>
435 [CLSCompliant(false)]
436 public bool ReadEnum<T>(ref T value, out object unknown)
437 where T : struct, IComparable, IFormattable, IConvertible
438 {
439 int number = (int)ReadRawVarint32();
440 if (Enum.IsDefined(typeof(T), number))
441 {
442 unknown = null;
443 value = (T)(object)number;
444 return true;
445 }
446 unknown = number;
447 return false;
csharptest71f662c2011-05-20 15:15:34 -0500448 }
449
450 /// <summary>
451 /// Reads an sfixed32 field value from the stream.
452 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500453 public bool ReadSFixed32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500454 {
csharptestd2af9e92011-06-03 21:35:02 -0500455 value = (int)ReadRawLittleEndian32();
456 return true;
csharptest71f662c2011-05-20 15:15:34 -0500457 }
458
459 /// <summary>
460 /// Reads an sfixed64 field value from the stream.
461 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500462 public bool ReadSFixed64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500463 {
csharptestd2af9e92011-06-03 21:35:02 -0500464 value = (long)ReadRawLittleEndian64();
465 return true;
csharptest71f662c2011-05-20 15:15:34 -0500466 }
467
468 /// <summary>
469 /// Reads an sint32 field value from the stream.
470 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500471 public bool ReadSInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500472 {
csharptestd2af9e92011-06-03 21:35:02 -0500473 value = DecodeZigZag32(ReadRawVarint32());
474 return true;
csharptest71f662c2011-05-20 15:15:34 -0500475 }
476
477 /// <summary>
478 /// Reads an sint64 field value from the stream.
479 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500480 public bool ReadSInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500481 {
csharptestd2af9e92011-06-03 21:35:02 -0500482 value = DecodeZigZag64(ReadRawVarint64());
483 return true;
484 }
485
486 [CLSCompliant(false)]
487 public void ReadPrimitiveArray<T>(FieldType fieldType, uint fieldTag, string fieldName, ICollection<T> list)
488 {
489 WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
490 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
491
492 // 2.3 allows packed form even if the field is not declared packed.
493 if(normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
494 {
495 int length = (int)(ReadRawVarint32() & int.MaxValue);
496 int limit = PushLimit(length);
497 while (!ReachedLimit)
498 {
499 Object value = null;
500 if(ReadPrimitiveField(fieldType, ref value))
501 list.Add((T)value);
502 }
503 PopLimit(limit);
504 }
505 else
506 {
507 Object value = null;
508 if (ReadPrimitiveField(fieldType, ref value))
509 list.Add((T)value);
510 }
511 }
512
513 [CLSCompliant(false)]
514 public void ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list, out ICollection<object> unknown, IEnumLiteMap mapping)
515 {
516 unknown = null;
517 object unkval;
518 IEnumLite value = null;
519 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
520
521 // 2.3 allows packed form even if the field is not declared packed.
522 if (wformat == WireFormat.WireType.LengthDelimited)
523 {
524 int length = (int)(ReadRawVarint32() & int.MaxValue);
525 int limit = PushLimit(length);
526 while (!ReachedLimit)
527 {
528 if (ReadEnum(ref value, out unkval, mapping))
529 list.Add(value);
530 else
531 {
532 if (unknown == null)
533 unknown = new List<object>();
534 unknown.Add(unkval);
535 }
536 }
537 PopLimit(limit);
538 }
539 else
540 {
541 if (ReadEnum(ref value, out unkval, mapping))
542 list.Add(value);
543 else
544 unknown = new object[] { unkval };
545 }
546 }
547
548 [CLSCompliant(false)]
549 public void ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list, out ICollection<object> unknown)
550 where T : struct, IComparable, IFormattable, IConvertible
551 {
552 unknown = null;
553 object unkval;
554 T value = default(T);
555 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
556
557 // 2.3 allows packed form even if the field is not declared packed.
558 if (wformat == WireFormat.WireType.LengthDelimited)
559 {
560 int length = (int)(ReadRawVarint32() & int.MaxValue);
561 int limit = PushLimit(length);
562 while (!ReachedLimit)
563 {
564 if (ReadEnum<T>(ref value, out unkval))
565 list.Add(value);
566 else
567 {
568 if (unknown == null)
569 unknown = new List<object>();
570 unknown.Add(unkval);
571 }
572 }
573 PopLimit(limit);
574 }
575 else
576 {
577 if (ReadEnum(ref value, out unkval))
578 list.Add(value);
579 else
580 unknown = new object[] { unkval };
581 }
582 }
583
584 [CLSCompliant(false)]
585 public void ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
586 {
587 IBuilderLite builder = messageType.WeakCreateBuilderForType();
588 ReadMessage(builder, registry);
589 list.Add((T)builder.WeakBuildPartial());
590 }
591
592 [CLSCompliant(false)]
593 public void ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
594 {
595 IBuilderLite builder = messageType.WeakCreateBuilderForType();
596 ReadGroup(WireFormat.GetTagFieldNumber(fieldTag), builder, registry);
597 list.Add((T)builder.WeakBuildPartial());
csharptest71f662c2011-05-20 15:15:34 -0500598 }
599
600 /// <summary>
601 /// Reads a field of any primitive type. Enums, groups and embedded
602 /// messages are not handled by this method.
603 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500604 public bool ReadPrimitiveField(FieldType fieldType, ref object value)
csharptest71f662c2011-05-20 15:15:34 -0500605 {
606 switch (fieldType)
607 {
608 case FieldType.Double:
csharptestd2af9e92011-06-03 21:35:02 -0500609 {
610 double tmp = 0;
611 if (ReadDouble(ref tmp))
612 {
613 value = tmp;
614 return true;
615 }
616 return false;
617 }
csharptest71f662c2011-05-20 15:15:34 -0500618 case FieldType.Float:
csharptestd2af9e92011-06-03 21:35:02 -0500619 {
620 float tmp = 0;
621 if (ReadFloat(ref tmp))
622 {
623 value = tmp;
624 return true;
625 }
626 return false;
627 }
csharptest71f662c2011-05-20 15:15:34 -0500628 case FieldType.Int64:
csharptestd2af9e92011-06-03 21:35:02 -0500629 {
630 long tmp = 0;
631 if (ReadInt64(ref tmp))
632 {
633 value = tmp;
634 return true;
635 }
636 return false;
637 }
csharptest71f662c2011-05-20 15:15:34 -0500638 case FieldType.UInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500639 {
640 ulong tmp = 0;
641 if (ReadUInt64(ref tmp))
642 {
643 value = tmp;
644 return true;
645 }
646 return false;
647 }
csharptest71f662c2011-05-20 15:15:34 -0500648 case FieldType.Int32:
csharptestd2af9e92011-06-03 21:35:02 -0500649 {
650 int tmp = 0;
651 if (ReadInt32(ref tmp))
652 {
653 value = tmp;
654 return true;
655 }
656 return false;
657 }
csharptest71f662c2011-05-20 15:15:34 -0500658 case FieldType.Fixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500659 {
660 ulong tmp = 0;
661 if (ReadFixed64(ref tmp))
662 {
663 value = tmp;
664 return true;
665 }
666 return false;
667 }
csharptest71f662c2011-05-20 15:15:34 -0500668 case FieldType.Fixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500669 {
670 uint tmp = 0;
671 if (ReadFixed32(ref tmp))
672 {
673 value = tmp;
674 return true;
675 }
676 return false;
677 }
csharptest71f662c2011-05-20 15:15:34 -0500678 case FieldType.Bool:
csharptestd2af9e92011-06-03 21:35:02 -0500679 {
680 bool tmp = false;
681 if (ReadBool(ref tmp))
682 {
683 value = tmp;
684 return true;
685 }
686 return false;
687 }
csharptest71f662c2011-05-20 15:15:34 -0500688 case FieldType.String:
csharptestd2af9e92011-06-03 21:35:02 -0500689 {
690 string tmp = null;
691 if (ReadString(ref tmp))
692 {
693 value = tmp;
694 return true;
695 }
696 return false;
697 }
csharptest71f662c2011-05-20 15:15:34 -0500698 case FieldType.Bytes:
csharptestd2af9e92011-06-03 21:35:02 -0500699 {
700 ByteString tmp = null;
701 if (ReadBytes(ref tmp))
702 {
703 value = tmp;
704 return true;
705 }
706 return false;
707 }
csharptest71f662c2011-05-20 15:15:34 -0500708 case FieldType.UInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500709 {
710 uint tmp = 0;
711 if (ReadUInt32(ref tmp))
712 {
713 value = tmp;
714 return true;
715 }
716 return false;
717 }
csharptest71f662c2011-05-20 15:15:34 -0500718 case FieldType.SFixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500719 {
720 int tmp = 0;
721 if (ReadSFixed32(ref tmp))
722 {
723 value = tmp;
724 return true;
725 }
726 return false;
727 }
csharptest71f662c2011-05-20 15:15:34 -0500728 case FieldType.SFixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500729 {
730 long tmp = 0;
731 if (ReadSFixed64(ref tmp))
732 {
733 value = tmp;
734 return true;
735 }
736 return false;
737 }
csharptest71f662c2011-05-20 15:15:34 -0500738 case FieldType.SInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500739 {
740 int tmp = 0;
741 if (ReadSInt32(ref tmp))
742 {
743 value = tmp;
744 return true;
745 }
746 return false;
747 }
csharptest71f662c2011-05-20 15:15:34 -0500748 case FieldType.SInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500749 {
750 long tmp = 0;
751 if (ReadSInt64(ref tmp))
752 {
753 value = tmp;
754 return true;
755 }
756 return false;
757 }
csharptest71f662c2011-05-20 15:15:34 -0500758 case FieldType.Group:
759 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
760 case FieldType.Message:
761 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
762 // We don't handle enums because we don't know what to do if the
763 // value is not recognized.
764 case FieldType.Enum:
765 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
766 default:
767 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
768 }
769 }
770
771 #endregion
772
773 #region Underlying reading primitives
774
775 /// <summary>
776 /// Same code as ReadRawVarint32, but read each byte individually, checking for
777 /// buffer overflow.
778 /// </summary>
779 private uint SlowReadRawVarint32()
780 {
781 int tmp = ReadRawByte();
782 if (tmp < 128)
783 {
784 return (uint) tmp;
785 }
786 int result = tmp & 0x7f;
787 if ((tmp = ReadRawByte()) < 128)
788 {
789 result |= tmp << 7;
790 }
791 else
792 {
793 result |= (tmp & 0x7f) << 7;
794 if ((tmp = ReadRawByte()) < 128)
795 {
796 result |= tmp << 14;
797 }
798 else
799 {
800 result |= (tmp & 0x7f) << 14;
801 if ((tmp = ReadRawByte()) < 128)
802 {
803 result |= tmp << 21;
804 }
805 else
806 {
807 result |= (tmp & 0x7f) << 21;
808 result |= (tmp = ReadRawByte()) << 28;
809 if (tmp >= 128)
810 {
811 // Discard upper 32 bits.
812 for (int i = 0; i < 5; i++)
813 {
814 if (ReadRawByte() < 128) return (uint) result;
815 }
816 throw InvalidProtocolBufferException.MalformedVarint();
817 }
818 }
819 }
820 }
821 return (uint) result;
822 }
823
824 /// <summary>
825 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
826 /// This method is optimised for the case where we've got lots of data in the buffer.
827 /// That means we can check the size just once, then just read directly from the buffer
828 /// without constant rechecking of the buffer length.
829 /// </summary>
830 [CLSCompliant(false)]
831 public uint ReadRawVarint32()
832 {
833 if (bufferPos + 5 > bufferSize)
834 {
835 return SlowReadRawVarint32();
836 }
837
838 int tmp = buffer[bufferPos++];
839 if (tmp < 128)
840 {
841 return (uint) tmp;
842 }
843 int result = tmp & 0x7f;
844 if ((tmp = buffer[bufferPos++]) < 128)
845 {
846 result |= tmp << 7;
847 }
848 else
849 {
850 result |= (tmp & 0x7f) << 7;
851 if ((tmp = buffer[bufferPos++]) < 128)
852 {
853 result |= tmp << 14;
854 }
855 else
856 {
857 result |= (tmp & 0x7f) << 14;
858 if ((tmp = buffer[bufferPos++]) < 128)
859 {
860 result |= tmp << 21;
861 }
862 else
863 {
864 result |= (tmp & 0x7f) << 21;
865 result |= (tmp = buffer[bufferPos++]) << 28;
866 if (tmp >= 128)
867 {
868 // Discard upper 32 bits.
869 // Note that this has to use ReadRawByte() as we only ensure we've
870 // got at least 5 bytes at the start of the method. This lets us
871 // use the fast path in more cases, and we rarely hit this section of code.
872 for (int i = 0; i < 5; i++)
873 {
874 if (ReadRawByte() < 128) return (uint) result;
875 }
876 throw InvalidProtocolBufferException.MalformedVarint();
877 }
878 }
879 }
880 }
881 return (uint) result;
882 }
883
884 /// <summary>
885 /// Reads a varint from the input one byte at a time, so that it does not
886 /// read any bytes after the end of the varint. If you simply wrapped the
887 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
888 /// then you would probably end up reading past the end of the varint since
889 /// CodedInputStream buffers its input.
890 /// </summary>
891 /// <param name="input"></param>
892 /// <returns></returns>
893 [CLSCompliant(false)]
894 public static uint ReadRawVarint32(Stream input)
895 {
896 int result = 0;
897 int offset = 0;
898 for (; offset < 32; offset += 7)
899 {
900 int b = input.ReadByte();
901 if (b == -1)
902 {
903 throw InvalidProtocolBufferException.TruncatedMessage();
904 }
905 result |= (b & 0x7f) << offset;
906 if ((b & 0x80) == 0)
907 {
908 return (uint) result;
909 }
910 }
911 // Keep reading up to 64 bits.
912 for (; offset < 64; offset += 7)
913 {
914 int b = input.ReadByte();
915 if (b == -1)
916 {
917 throw InvalidProtocolBufferException.TruncatedMessage();
918 }
919 if ((b & 0x80) == 0)
920 {
921 return (uint) result;
922 }
923 }
924 throw InvalidProtocolBufferException.MalformedVarint();
925 }
926
927 /// <summary>
928 /// Read a raw varint from the stream.
929 /// </summary>
930 [CLSCompliant(false)]
931 public ulong ReadRawVarint64()
932 {
933 int shift = 0;
934 ulong result = 0;
935 while (shift < 64)
936 {
937 byte b = ReadRawByte();
938 result |= (ulong) (b & 0x7F) << shift;
939 if ((b & 0x80) == 0)
940 {
941 return result;
942 }
943 shift += 7;
944 }
945 throw InvalidProtocolBufferException.MalformedVarint();
946 }
947
948 /// <summary>
949 /// Read a 32-bit little-endian integer from the stream.
950 /// </summary>
951 [CLSCompliant(false)]
952 public uint ReadRawLittleEndian32()
953 {
954 uint b1 = ReadRawByte();
955 uint b2 = ReadRawByte();
956 uint b3 = ReadRawByte();
957 uint b4 = ReadRawByte();
958 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
959 }
960
961 /// <summary>
962 /// Read a 64-bit little-endian integer from the stream.
963 /// </summary>
964 [CLSCompliant(false)]
965 public ulong ReadRawLittleEndian64()
966 {
967 ulong b1 = ReadRawByte();
968 ulong b2 = ReadRawByte();
969 ulong b3 = ReadRawByte();
970 ulong b4 = ReadRawByte();
971 ulong b5 = ReadRawByte();
972 ulong b6 = ReadRawByte();
973 ulong b7 = ReadRawByte();
974 ulong b8 = ReadRawByte();
975 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
976 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
977 }
978
979 #endregion
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 int DecodeZigZag32(uint n)
992 {
993 return (int) (n >> 1) ^ -(int) (n & 1);
994 }
995
996 /// <summary>
997 /// Decode a 32-bit value with ZigZag encoding.
998 /// </summary>
999 /// <remarks>
1000 /// ZigZag encodes signed integers into values that can be efficiently
1001 /// encoded with varint. (Otherwise, negative values must be
1002 /// sign-extended to 64 bits to be varint encoded, thus always taking
1003 /// 10 bytes on the wire.)
1004 /// </remarks>
1005 [CLSCompliant(false)]
1006 public static long DecodeZigZag64(ulong n)
1007 {
1008 return (long) (n >> 1) ^ -(long) (n & 1);
1009 }
1010
1011 /// <summary>
1012 /// Set the maximum message recursion depth.
1013 /// </summary>
1014 /// <remarks>
1015 /// In order to prevent malicious
1016 /// messages from causing stack overflows, CodedInputStream limits
1017 /// how deeply messages may be nested. The default limit is 64.
1018 /// </remarks>
1019 public int SetRecursionLimit(int limit)
1020 {
1021 if (limit < 0)
1022 {
1023 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
1024 }
1025 int oldLimit = recursionLimit;
1026 recursionLimit = limit;
1027 return oldLimit;
1028 }
1029
1030 /// <summary>
1031 /// Set the maximum message size.
1032 /// </summary>
1033 /// <remarks>
1034 /// In order to prevent malicious messages from exhausting memory or
1035 /// causing integer overflows, CodedInputStream limits how large a message may be.
1036 /// The default limit is 64MB. You should set this limit as small
1037 /// as you can without harming your app's functionality. Note that
1038 /// size limits only apply when reading from an InputStream, not
1039 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
1040 /// If you want to read several messages from a single CodedInputStream, you
1041 /// can call ResetSizeCounter() after each message to avoid hitting the
1042 /// size limit.
1043 /// </remarks>
1044 public int SetSizeLimit(int limit)
1045 {
1046 if (limit < 0)
1047 {
1048 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
1049 }
1050 int oldLimit = sizeLimit;
1051 sizeLimit = limit;
1052 return oldLimit;
1053 }
1054
1055 #region Internal reading and buffer management
1056
1057 /// <summary>
1058 /// Resets the current size counter to zero (see SetSizeLimit).
1059 /// </summary>
1060 public void ResetSizeCounter()
1061 {
1062 totalBytesRetired = 0;
1063 }
1064
1065 /// <summary>
1066 /// Sets currentLimit to (current position) + byteLimit. This is called
1067 /// when descending into a length-delimited embedded message. The previous
1068 /// limit is returned.
1069 /// </summary>
1070 /// <returns>The old limit.</returns>
1071 public int PushLimit(int byteLimit)
1072 {
1073 if (byteLimit < 0)
1074 {
1075 throw InvalidProtocolBufferException.NegativeSize();
1076 }
1077 byteLimit += totalBytesRetired + bufferPos;
1078 int oldLimit = currentLimit;
1079 if (byteLimit > oldLimit)
1080 {
1081 throw InvalidProtocolBufferException.TruncatedMessage();
1082 }
1083 currentLimit = byteLimit;
1084
1085 RecomputeBufferSizeAfterLimit();
1086
1087 return oldLimit;
1088 }
1089
1090 private void RecomputeBufferSizeAfterLimit()
1091 {
1092 bufferSize += bufferSizeAfterLimit;
1093 int bufferEnd = totalBytesRetired + bufferSize;
1094 if (bufferEnd > currentLimit)
1095 {
1096 // Limit is in current buffer.
1097 bufferSizeAfterLimit = bufferEnd - currentLimit;
1098 bufferSize -= bufferSizeAfterLimit;
1099 }
1100 else
1101 {
1102 bufferSizeAfterLimit = 0;
1103 }
1104 }
1105
1106 /// <summary>
1107 /// Discards the current limit, returning the previous limit.
1108 /// </summary>
1109 public void PopLimit(int oldLimit)
1110 {
1111 currentLimit = oldLimit;
1112 RecomputeBufferSizeAfterLimit();
1113 }
1114
1115 /// <summary>
1116 /// Returns whether or not all the data before the limit has been read.
1117 /// </summary>
1118 /// <returns></returns>
1119 public bool ReachedLimit
1120 {
1121 get
1122 {
1123 if (currentLimit == int.MaxValue)
1124 {
1125 return false;
1126 }
1127 int currentAbsolutePosition = totalBytesRetired + bufferPos;
1128 return currentAbsolutePosition >= currentLimit;
1129 }
1130 }
1131
1132 /// <summary>
1133 /// Returns true if the stream has reached the end of the input. This is the
1134 /// case if either the end of the underlying input source has been reached or
1135 /// the stream has reached a limit created using PushLimit.
1136 /// </summary>
1137 public bool IsAtEnd
1138 {
1139 get { return bufferPos == bufferSize && !RefillBuffer(false); }
1140 }
1141
1142 /// <summary>
1143 /// Called when buffer is empty to read more bytes from the
1144 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1145 /// either there will be at least one byte in the buffer when it returns
1146 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
1147 /// RefillBuffer() returns false if no more bytes were available.
1148 /// </summary>
1149 /// <param name="mustSucceed"></param>
1150 /// <returns></returns>
1151 private bool RefillBuffer(bool mustSucceed)
1152 {
1153 if (bufferPos < bufferSize)
1154 {
1155 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1156 }
1157
1158 if (totalBytesRetired + bufferSize == currentLimit)
1159 {
1160 // Oops, we hit a limit.
1161 if (mustSucceed)
1162 {
1163 throw InvalidProtocolBufferException.TruncatedMessage();
1164 }
1165 else
1166 {
1167 return false;
1168 }
1169 }
1170
1171 totalBytesRetired += bufferSize;
1172
1173 bufferPos = 0;
1174 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1175 if (bufferSize < 0)
1176 {
1177 throw new InvalidOperationException("Stream.Read returned a negative count");
1178 }
1179 if (bufferSize == 0)
1180 {
1181 if (mustSucceed)
1182 {
1183 throw InvalidProtocolBufferException.TruncatedMessage();
1184 }
1185 else
1186 {
1187 return false;
1188 }
1189 }
1190 else
1191 {
1192 RecomputeBufferSizeAfterLimit();
1193 int totalBytesRead =
1194 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1195 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1196 {
1197 throw InvalidProtocolBufferException.SizeLimitExceeded();
1198 }
1199 return true;
1200 }
1201 }
1202
1203 /// <summary>
1204 /// Read one byte 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 ReadRawByte()
1210 {
1211 if (bufferPos == bufferSize)
1212 {
1213 RefillBuffer(true);
1214 }
1215 return buffer[bufferPos++];
1216 }
1217
1218 /// <summary>
1219 /// Read a fixed size of bytes from the input.
1220 /// </summary>
1221 /// <exception cref="InvalidProtocolBufferException">
1222 /// the end of the stream or the current limit was reached
1223 /// </exception>
1224 public byte[] ReadRawBytes(int size)
1225 {
1226 if (size < 0)
1227 {
1228 throw InvalidProtocolBufferException.NegativeSize();
1229 }
1230
1231 if (totalBytesRetired + bufferPos + size > currentLimit)
1232 {
1233 // Read to the end of the stream anyway.
1234 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1235 // Then fail.
1236 throw InvalidProtocolBufferException.TruncatedMessage();
1237 }
1238
1239 if (size <= bufferSize - bufferPos)
1240 {
1241 // We have all the bytes we need already.
1242 byte[] bytes = new byte[size];
csharptestaef072a2011-06-08 18:00:43 -05001243 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
csharptest71f662c2011-05-20 15:15:34 -05001244 bufferPos += size;
1245 return bytes;
1246 }
1247 else if (size < BufferSize)
1248 {
1249 // Reading more bytes than are in the buffer, but not an excessive number
1250 // of bytes. We can safely allocate the resulting array ahead of time.
1251
1252 // First copy what we have.
1253 byte[] bytes = new byte[size];
1254 int pos = bufferSize - bufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001255 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
csharptest71f662c2011-05-20 15:15:34 -05001256 bufferPos = bufferSize;
1257
1258 // We want to use RefillBuffer() and then copy from the buffer into our
1259 // byte array rather than reading directly into our byte array because
1260 // the input may be unbuffered.
1261 RefillBuffer(true);
1262
1263 while (size - pos > bufferSize)
1264 {
csharptest2772dfe2011-06-08 15:50:58 -05001265 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
csharptest71f662c2011-05-20 15:15:34 -05001266 pos += bufferSize;
1267 bufferPos = bufferSize;
1268 RefillBuffer(true);
1269 }
1270
csharptestaef072a2011-06-08 18:00:43 -05001271 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
csharptest71f662c2011-05-20 15:15:34 -05001272 bufferPos = size - pos;
1273
1274 return bytes;
1275 }
1276 else
1277 {
1278 // The size is very large. For security reasons, we can't allocate the
1279 // entire byte array yet. The size comes directly from the input, so a
1280 // maliciously-crafted message could provide a bogus very large size in
1281 // order to trick the app into allocating a lot of memory. We avoid this
1282 // by allocating and reading only a small chunk at a time, so that the
1283 // malicious message must actually *be* extremely large to cause
1284 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1285
1286 // Remember the buffer markers since we'll have to copy the bytes out of
1287 // it later.
1288 int originalBufferPos = bufferPos;
1289 int originalBufferSize = bufferSize;
1290
1291 // Mark the current buffer consumed.
1292 totalBytesRetired += bufferSize;
1293 bufferPos = 0;
1294 bufferSize = 0;
1295
1296 // Read all the rest of the bytes we need.
1297 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1298 List<byte[]> chunks = new List<byte[]>();
1299
1300 while (sizeLeft > 0)
1301 {
1302 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
1303 int pos = 0;
1304 while (pos < chunk.Length)
1305 {
1306 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1307 if (n <= 0)
1308 {
1309 throw InvalidProtocolBufferException.TruncatedMessage();
1310 }
1311 totalBytesRetired += n;
1312 pos += n;
1313 }
1314 sizeLeft -= chunk.Length;
1315 chunks.Add(chunk);
1316 }
1317
1318 // OK, got everything. Now concatenate it all into one buffer.
1319 byte[] bytes = new byte[size];
1320
1321 // Start by copying the leftover bytes from this.buffer.
1322 int newPos = originalBufferSize - originalBufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001323 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
csharptest71f662c2011-05-20 15:15:34 -05001324
1325 // And now all the chunks.
1326 foreach (byte[] chunk in chunks)
1327 {
csharptest2772dfe2011-06-08 15:50:58 -05001328 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
csharptest71f662c2011-05-20 15:15:34 -05001329 newPos += chunk.Length;
1330 }
1331
1332 // Done.
1333 return bytes;
1334 }
1335 }
1336
1337 /// <summary>
1338 /// Reads and discards a single field, given its tag value.
1339 /// </summary>
1340 /// <returns>false if the tag is an end-group tag, in which case
1341 /// nothing is skipped. Otherwise, returns true.</returns>
1342 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -05001343 public bool SkipField()
csharptest71f662c2011-05-20 15:15:34 -05001344 {
csharptest123e5342011-06-03 14:15:21 -05001345 uint tag = lastTag;
csharptest71f662c2011-05-20 15:15:34 -05001346 switch (WireFormat.GetTagWireType(tag))
1347 {
1348 case WireFormat.WireType.Varint:
csharptestd2af9e92011-06-03 21:35:02 -05001349 ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -05001350 return true;
1351 case WireFormat.WireType.Fixed64:
1352 ReadRawLittleEndian64();
1353 return true;
1354 case WireFormat.WireType.LengthDelimited:
1355 SkipRawBytes((int) ReadRawVarint32());
1356 return true;
1357 case WireFormat.WireType.StartGroup:
1358 SkipMessage();
1359 CheckLastTagWas(
1360 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
1361 WireFormat.WireType.EndGroup));
1362 return true;
1363 case WireFormat.WireType.EndGroup:
1364 return false;
1365 case WireFormat.WireType.Fixed32:
1366 ReadRawLittleEndian32();
1367 return true;
1368 default:
1369 throw InvalidProtocolBufferException.InvalidWireType();
1370 }
1371 }
1372
1373 /// <summary>
1374 /// Reads and discards an entire message. This will read either until EOF
1375 /// or until an endgroup tag, whichever comes first.
1376 /// </summary>
1377 public void SkipMessage()
1378 {
csharptest123e5342011-06-03 14:15:21 -05001379 uint tag;
1380 string name;
1381 while (ReadTag(out tag, out name))
csharptest71f662c2011-05-20 15:15:34 -05001382 {
csharptest123e5342011-06-03 14:15:21 -05001383 if (!SkipField())
csharptest71f662c2011-05-20 15:15:34 -05001384 {
1385 return;
1386 }
1387 }
1388 }
1389
1390 /// <summary>
1391 /// Reads and discards <paramref name="size"/> bytes.
1392 /// </summary>
1393 /// <exception cref="InvalidProtocolBufferException">the end of the stream
1394 /// or the current limit was reached</exception>
1395 public void SkipRawBytes(int size)
1396 {
1397 if (size < 0)
1398 {
1399 throw InvalidProtocolBufferException.NegativeSize();
1400 }
1401
1402 if (totalBytesRetired + bufferPos + size > currentLimit)
1403 {
1404 // Read to the end of the stream anyway.
1405 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1406 // Then fail.
1407 throw InvalidProtocolBufferException.TruncatedMessage();
1408 }
1409
1410 if (size <= bufferSize - bufferPos)
1411 {
1412 // We have all the bytes we need already.
1413 bufferPos += size;
1414 }
1415 else
1416 {
1417 // Skipping more bytes than are in the buffer. First skip what we have.
1418 int pos = bufferSize - bufferPos;
1419 totalBytesRetired += pos;
1420 bufferPos = 0;
1421 bufferSize = 0;
1422
1423 // Then skip directly from the InputStream for the rest.
1424 if (pos < size)
1425 {
1426 if (input == null)
1427 {
1428 throw InvalidProtocolBufferException.TruncatedMessage();
1429 }
1430 SkipImpl(size - pos);
1431 totalBytesRetired += size - pos;
1432 }
1433 }
1434 }
1435
1436 /// <summary>
1437 /// Abstraction of skipping to cope with streams which can't really skip.
1438 /// </summary>
1439 private void SkipImpl(int amountToSkip)
1440 {
1441 if (input.CanSeek)
1442 {
1443 long previousPosition = input.Position;
1444 input.Position += amountToSkip;
1445 if (input.Position != previousPosition + amountToSkip)
1446 {
1447 throw InvalidProtocolBufferException.TruncatedMessage();
1448 }
1449 }
1450 else
1451 {
1452 byte[] skipBuffer = new byte[1024];
1453 while (amountToSkip > 0)
1454 {
1455 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
1456 if (bytesRead <= 0)
1457 {
1458 throw InvalidProtocolBufferException.TruncatedMessage();
1459 }
1460 amountToSkip -= bytesRead;
1461 }
1462 }
1463 }
1464
1465 #endregion
1466 }
1467}