blob: 1fea7346e110957e39c9cd17843e0cb4af98def1 [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
csharptest367e0222011-06-09 12:47:46 -050070 private uint nextTag = 0;
71 private bool hasNextTag = false;
72
csharptest71f662c2011-05-20 15:15:34 -050073 internal const int DefaultRecursionLimit = 64;
74 internal const int DefaultSizeLimit = 64 << 20; // 64MB
75 public const int BufferSize = 4096;
76
77 /// <summary>
78 /// The total number of bytes read before the current buffer. The
79 /// total bytes read up to the current position can be computed as
80 /// totalBytesRetired + bufferPos.
81 /// </summary>
82 private int totalBytesRetired = 0;
83
84 /// <summary>
85 /// The absolute position of the end of the current message.
86 /// </summary>
87 private int currentLimit = int.MaxValue;
88
89 /// <summary>
90 /// <see cref="SetRecursionLimit"/>
91 /// </summary>
92 private int recursionDepth = 0;
93
94 private int recursionLimit = DefaultRecursionLimit;
95
96 /// <summary>
97 /// <see cref="SetSizeLimit"/>
98 /// </summary>
99 private int sizeLimit = DefaultSizeLimit;
100
101 #region Construction
102
103 /// <summary>
104 /// Creates a new CodedInputStream reading data from the given
105 /// stream.
106 /// </summary>
107 public static CodedInputStream CreateInstance(Stream input)
108 {
109 return new CodedInputStream(input);
110 }
111
112 /// <summary>
113 /// Creates a new CodedInputStream reading data from the given
114 /// byte array.
115 /// </summary>
116 public static CodedInputStream CreateInstance(byte[] buf)
117 {
118 return new CodedInputStream(buf, 0, buf.Length);
119 }
120
121 /// <summary>
122 /// Creates a new CodedInputStream that reads from the given
123 /// byte array slice.
124 /// </summary>
125 public static CodedInputStream CreateInstance(byte[] buf, int offset, int length)
126 {
127 return new CodedInputStream(buf, offset, length);
128 }
129
130 private CodedInputStream(byte[] buffer, int offset, int length)
131 {
132 this.buffer = buffer;
133 this.bufferPos = offset;
134 this.bufferSize = offset + length;
135 this.input = null;
136 }
137
138 private CodedInputStream(Stream input)
139 {
140 this.buffer = new byte[BufferSize];
141 this.bufferSize = 0;
142 this.input = input;
143 }
144
145 #endregion
146
147 #region Validation
148
149 /// <summary>
150 /// Verifies that the last call to ReadTag() returned the given tag value.
151 /// This is used to verify that a nested group ended with the correct
152 /// end tag.
153 /// </summary>
154 /// <exception cref="InvalidProtocolBufferException">The last
155 /// tag read was not the one specified</exception>
156 [CLSCompliant(false)]
157 public void CheckLastTagWas(uint value)
158 {
159 if (lastTag != value)
160 {
161 throw InvalidProtocolBufferException.InvalidEndTag();
162 }
163 }
164
165 #endregion
166
167 #region Reading of tags etc
csharptest123e5342011-06-03 14:15:21 -0500168
csharptest71f662c2011-05-20 15:15:34 -0500169 /// <summary>
csharptest367e0222011-06-09 12:47:46 -0500170 /// Attempt to peek at the next field tag.
171 /// </summary>
172 [CLSCompliant(false)]
173 public bool PeekNextTag(out uint fieldTag, out string fieldName)
174 {
175 if (hasNextTag)
176 {
177 fieldName = null;
178 fieldTag = nextTag;
179 return true;
180 }
181
182 uint savedLast = lastTag;
183 hasNextTag = ReadTag(out nextTag, out fieldName);
184 lastTag = savedLast;
185 fieldTag = nextTag;
186 return hasNextTag;
187 }
188
189 /// <summary>
csharptest123e5342011-06-03 14:15:21 -0500190 /// Attempt to read a field tag, returning false if we have reached the end
191 /// of the input data.
csharptest71f662c2011-05-20 15:15:34 -0500192 /// </summary>
csharptest123e5342011-06-03 14:15:21 -0500193 /// <remarks>
194 /// <para>
195 /// If fieldTag is non-zero and ReadTag returns true then the value in fieldName
196 /// may or may not be populated. However, if fieldTag is zero and ReadTag returns
197 /// true, then fieldName should be populated with a non-null field name.
198 /// </para><para>
199 /// In other words if ReadTag returns true then either fieldTag will be non-zero OR
200 /// fieldName will be non-zero. In some cases both may be populated, however the
201 /// builders will always prefer the fieldTag over fieldName.
202 /// </para>
203 /// </remarks>
csharptest71f662c2011-05-20 15:15:34 -0500204 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -0500205 public bool ReadTag(out uint fieldTag, out string fieldName)
csharptest71f662c2011-05-20 15:15:34 -0500206 {
csharptest123e5342011-06-03 14:15:21 -0500207 fieldName = null;
208
csharptest367e0222011-06-09 12:47:46 -0500209 if (hasNextTag)
210 {
211 lastTag = fieldTag = nextTag;
212 hasNextTag = false;
213 return true;
214 }
215
csharptest71f662c2011-05-20 15:15:34 -0500216 if (IsAtEnd)
217 {
csharptest123e5342011-06-03 14:15:21 -0500218 lastTag = fieldTag = 0;
219 return false;
csharptest71f662c2011-05-20 15:15:34 -0500220 }
221
csharptest123e5342011-06-03 14:15:21 -0500222 lastTag = fieldTag = ReadRawVarint32();
csharptest71f662c2011-05-20 15:15:34 -0500223 if (lastTag == 0)
224 {
225 // If we actually read zero, that's not a valid tag.
226 throw InvalidProtocolBufferException.InvalidTag();
227 }
csharptest123e5342011-06-03 14:15:21 -0500228 return true;
csharptest71f662c2011-05-20 15:15:34 -0500229 }
230
231 /// <summary>
232 /// Read a double field from the stream.
233 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500234 public bool ReadDouble(ref double value)
csharptest71f662c2011-05-20 15:15:34 -0500235 {
236#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
csharptest4ba365d2011-06-08 17:40:51 -0500237 if (BitConverter.IsLittleEndian && 8 <= bufferSize - bufferPos)
238 {
239 value = BitConverter.ToDouble(buffer, bufferPos);
240 bufferPos += 8;
241 }
242 else
243 {
244 byte[] rawBytes = ReadRawBytes(8);
245 if (!BitConverter.IsLittleEndian)
csharptestaef072a2011-06-08 18:00:43 -0500246 ByteArray.Reverse(rawBytes);
csharptest4ba365d2011-06-08 17:40:51 -0500247 value = BitConverter.ToDouble(rawBytes, 0);
248 }
csharptest71f662c2011-05-20 15:15:34 -0500249#else
csharptestd2af9e92011-06-03 21:35:02 -0500250 value = BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
csharptest71f662c2011-05-20 15:15:34 -0500251#endif
csharptest4ba365d2011-06-08 17:40:51 -0500252 return true;
csharptest71f662c2011-05-20 15:15:34 -0500253 }
254
255 /// <summary>
256 /// Read a float field from the stream.
257 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500258 public bool ReadFloat(ref float value)
csharptest71f662c2011-05-20 15:15:34 -0500259 {
csharptest4ba365d2011-06-08 17:40:51 -0500260 if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
261 {
262 value = BitConverter.ToSingle(buffer, bufferPos);
263 bufferPos += 4;
264 }
265 else
266 {
267 byte[] rawBytes = ReadRawBytes(4);
268 if (!BitConverter.IsLittleEndian)
csharptestaef072a2011-06-08 18:00:43 -0500269 ByteArray.Reverse(rawBytes);
csharptest4ba365d2011-06-08 17:40:51 -0500270 value = BitConverter.ToSingle(rawBytes, 0);
271 }
csharptestd2af9e92011-06-03 21:35:02 -0500272 return true;
csharptest71f662c2011-05-20 15:15:34 -0500273 }
274
275 /// <summary>
276 /// Read a uint64 field from the stream.
277 /// </summary>
278 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500279 public bool ReadUInt64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500280 {
csharptestd2af9e92011-06-03 21:35:02 -0500281 value = ReadRawVarint64();
282 return true;
csharptest71f662c2011-05-20 15:15:34 -0500283 }
284
285 /// <summary>
286 /// Read an int64 field from the stream.
287 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500288 public bool ReadInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500289 {
csharptestd2af9e92011-06-03 21:35:02 -0500290 value = (long) ReadRawVarint64();
291 return true;
csharptest71f662c2011-05-20 15:15:34 -0500292 }
293
294 /// <summary>
295 /// Read an int32 field from the stream.
296 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500297 public bool ReadInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500298 {
csharptestd2af9e92011-06-03 21:35:02 -0500299 value = (int)ReadRawVarint32();
300 return true;
csharptest71f662c2011-05-20 15:15:34 -0500301 }
302
303 /// <summary>
304 /// Read a fixed64 field from the stream.
305 /// </summary>
306 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500307 public bool ReadFixed64(ref ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500308 {
csharptestd2af9e92011-06-03 21:35:02 -0500309 value = ReadRawLittleEndian64();
310 return true;
csharptest71f662c2011-05-20 15:15:34 -0500311 }
312
313 /// <summary>
314 /// Read a fixed32 field from the stream.
315 /// </summary>
316 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500317 public bool ReadFixed32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500318 {
csharptestd2af9e92011-06-03 21:35:02 -0500319 value = ReadRawLittleEndian32();
320 return true;
csharptest71f662c2011-05-20 15:15:34 -0500321 }
322
323 /// <summary>
324 /// Read a bool field from the stream.
325 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500326 public bool ReadBool(ref bool value)
csharptest71f662c2011-05-20 15:15:34 -0500327 {
csharptestd2af9e92011-06-03 21:35:02 -0500328 value = ReadRawVarint32() != 0;
329 return true;
csharptest71f662c2011-05-20 15:15:34 -0500330 }
331
332 /// <summary>
333 /// Reads a string field from the stream.
334 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500335 public bool ReadString(ref string value)
csharptest71f662c2011-05-20 15:15:34 -0500336 {
337 int size = (int) ReadRawVarint32();
338 // No need to read any data for an empty string.
339 if (size == 0)
340 {
csharptestd2af9e92011-06-03 21:35:02 -0500341 value = "";
342 return true;
csharptest71f662c2011-05-20 15:15:34 -0500343 }
344 if (size <= bufferSize - bufferPos)
345 {
346 // Fast path: We already have the bytes in a contiguous buffer, so
347 // just copy directly from it.
348 String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
349 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500350 value = result;
351 return true;
csharptest71f662c2011-05-20 15:15:34 -0500352 }
353 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500354 value = Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
355 return true;
csharptest71f662c2011-05-20 15:15:34 -0500356 }
357
358 /// <summary>
359 /// Reads a group field value from the stream.
360 /// </summary>
361 public void ReadGroup(int fieldNumber, IBuilderLite builder,
362 ExtensionRegistry extensionRegistry)
363 {
364 if (recursionDepth >= recursionLimit)
365 {
366 throw InvalidProtocolBufferException.RecursionLimitExceeded();
367 }
368 ++recursionDepth;
369 builder.WeakMergeFrom(this, extensionRegistry);
370 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
371 --recursionDepth;
372 }
373
374 /// <summary>
375 /// Reads a group field value from the stream and merges it into the given
376 /// UnknownFieldSet.
377 /// </summary>
378 [Obsolete]
379 public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
380 {
381 if (recursionDepth >= recursionLimit)
382 {
383 throw InvalidProtocolBufferException.RecursionLimitExceeded();
384 }
385 ++recursionDepth;
386 builder.WeakMergeFrom(this);
387 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
388 --recursionDepth;
389 }
390
391 /// <summary>
392 /// Reads an embedded message field value from the stream.
393 /// </summary>
394 public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
395 {
396 int length = (int) ReadRawVarint32();
397 if (recursionDepth >= recursionLimit)
398 {
399 throw InvalidProtocolBufferException.RecursionLimitExceeded();
400 }
401 int oldLimit = PushLimit(length);
402 ++recursionDepth;
403 builder.WeakMergeFrom(this, extensionRegistry);
404 CheckLastTagWas(0);
405 --recursionDepth;
406 PopLimit(oldLimit);
407 }
408
409 /// <summary>
410 /// Reads a bytes field value from the stream.
411 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500412 public bool ReadBytes(ref ByteString value)
csharptest71f662c2011-05-20 15:15:34 -0500413 {
414 int size = (int) ReadRawVarint32();
415 if (size < bufferSize - bufferPos && size > 0)
416 {
417 // Fast path: We already have the bytes in a contiguous buffer, so
418 // just copy directly from it.
419 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
420 bufferPos += size;
csharptestd2af9e92011-06-03 21:35:02 -0500421 value = result;
422 return true;
csharptest71f662c2011-05-20 15:15:34 -0500423 }
424 else
425 {
426 // Slow path: Build a byte array first then copy it.
csharptestd2af9e92011-06-03 21:35:02 -0500427 value = ByteString.AttachBytes(ReadRawBytes(size));
428 return true;
csharptest71f662c2011-05-20 15:15:34 -0500429 }
430 }
431
432 /// <summary>
433 /// Reads a uint32 field value from the stream.
434 /// </summary>
435 [CLSCompliant(false)]
csharptestd2af9e92011-06-03 21:35:02 -0500436 public bool ReadUInt32(ref uint value)
csharptest71f662c2011-05-20 15:15:34 -0500437 {
csharptestd2af9e92011-06-03 21:35:02 -0500438 value = ReadRawVarint32();
439 return true;
csharptest71f662c2011-05-20 15:15:34 -0500440 }
441
442 /// <summary>
443 /// Reads an enum field value from the stream. The caller is responsible
444 /// for converting the numeric value to an actual enum.
445 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500446 public bool ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
csharptest71f662c2011-05-20 15:15:34 -0500447 {
csharptestd2af9e92011-06-03 21:35:02 -0500448 int rawValue = (int)ReadRawVarint32();
449
450 value = mapping.FindValueByNumber(rawValue);
451 if (value != null)
452 {
453 unknown = null;
454 return true;
455 }
456 unknown = rawValue;
457 return false;
458 }
459
460 /// <summary>
461 /// Reads an enum field value from the stream. If the enum is valid for type T,
462 /// then the ref value is set and it returns true. Otherwise the unkown output
463 /// value is set and this method returns false.
464 /// </summary>
465 [CLSCompliant(false)]
466 public bool ReadEnum<T>(ref T value, out object unknown)
467 where T : struct, IComparable, IFormattable, IConvertible
468 {
469 int number = (int)ReadRawVarint32();
470 if (Enum.IsDefined(typeof(T), number))
471 {
472 unknown = null;
473 value = (T)(object)number;
474 return true;
475 }
476 unknown = number;
477 return false;
csharptest71f662c2011-05-20 15:15:34 -0500478 }
479
480 /// <summary>
481 /// Reads an sfixed32 field value from the stream.
482 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500483 public bool ReadSFixed32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500484 {
csharptestd2af9e92011-06-03 21:35:02 -0500485 value = (int)ReadRawLittleEndian32();
486 return true;
csharptest71f662c2011-05-20 15:15:34 -0500487 }
488
489 /// <summary>
490 /// Reads an sfixed64 field value from the stream.
491 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500492 public bool ReadSFixed64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500493 {
csharptestd2af9e92011-06-03 21:35:02 -0500494 value = (long)ReadRawLittleEndian64();
495 return true;
csharptest71f662c2011-05-20 15:15:34 -0500496 }
497
498 /// <summary>
499 /// Reads an sint32 field value from the stream.
500 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500501 public bool ReadSInt32(ref int value)
csharptest71f662c2011-05-20 15:15:34 -0500502 {
csharptestd2af9e92011-06-03 21:35:02 -0500503 value = DecodeZigZag32(ReadRawVarint32());
504 return true;
csharptest71f662c2011-05-20 15:15:34 -0500505 }
506
507 /// <summary>
508 /// Reads an sint64 field value from the stream.
509 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500510 public bool ReadSInt64(ref long value)
csharptest71f662c2011-05-20 15:15:34 -0500511 {
csharptestd2af9e92011-06-03 21:35:02 -0500512 value = DecodeZigZag64(ReadRawVarint64());
513 return true;
514 }
515
csharptest367e0222011-06-09 12:47:46 -0500516 /// <summary>
517 /// Returns true if the next tag is also part of the same unpacked array
518 /// </summary>
519 private bool ContinueArray(uint currentTag)
520 {
521 string ignore;
522 uint next;
523 if (PeekNextTag(out next, out ignore))
524 {
525 if (next == currentTag)
526 {
527 hasNextTag = false;
528 return true;
529 }
530 }
531 return false;
532 }
533
csharptestd2af9e92011-06-03 21:35:02 -0500534 [CLSCompliant(false)]
535 public void ReadPrimitiveArray<T>(FieldType fieldType, uint fieldTag, string fieldName, ICollection<T> list)
536 {
537 WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
538 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
539
540 // 2.3 allows packed form even if the field is not declared packed.
541 if(normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
542 {
543 int length = (int)(ReadRawVarint32() & int.MaxValue);
544 int limit = PushLimit(length);
545 while (!ReachedLimit)
546 {
547 Object value = null;
548 if(ReadPrimitiveField(fieldType, ref value))
549 list.Add((T)value);
550 }
551 PopLimit(limit);
552 }
553 else
554 {
555 Object value = null;
csharptest367e0222011-06-09 12:47:46 -0500556 do
557 {
558 if (ReadPrimitiveField(fieldType, ref value))
559 list.Add((T)value);
560 }
561 while (ContinueArray(fieldTag));
csharptestd2af9e92011-06-03 21:35:02 -0500562 }
563 }
564
565 [CLSCompliant(false)]
566 public void ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list, out ICollection<object> unknown, IEnumLiteMap mapping)
567 {
568 unknown = null;
569 object unkval;
570 IEnumLite value = null;
571 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
572
573 // 2.3 allows packed form even if the field is not declared packed.
574 if (wformat == WireFormat.WireType.LengthDelimited)
575 {
576 int length = (int)(ReadRawVarint32() & int.MaxValue);
577 int limit = PushLimit(length);
578 while (!ReachedLimit)
579 {
580 if (ReadEnum(ref value, out unkval, mapping))
581 list.Add(value);
582 else
583 {
584 if (unknown == null)
585 unknown = new List<object>();
586 unknown.Add(unkval);
587 }
588 }
589 PopLimit(limit);
590 }
591 else
592 {
csharptest367e0222011-06-09 12:47:46 -0500593 do
594 {
595 if (ReadEnum(ref value, out unkval, mapping))
596 list.Add(value);
597 else
598 {
599 if (unknown == null)
600 unknown = new List<object>();
601 unknown.Add(unkval);
602 }
603 }
604 while (ContinueArray(fieldTag));
csharptestd2af9e92011-06-03 21:35:02 -0500605 }
606 }
607
608 [CLSCompliant(false)]
609 public void ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list, out ICollection<object> unknown)
610 where T : struct, IComparable, IFormattable, IConvertible
611 {
612 unknown = null;
613 object unkval;
614 T value = default(T);
615 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
616
617 // 2.3 allows packed form even if the field is not declared packed.
618 if (wformat == WireFormat.WireType.LengthDelimited)
619 {
620 int length = (int)(ReadRawVarint32() & int.MaxValue);
621 int limit = PushLimit(length);
622 while (!ReachedLimit)
623 {
624 if (ReadEnum<T>(ref value, out unkval))
625 list.Add(value);
626 else
627 {
628 if (unknown == null)
629 unknown = new List<object>();
630 unknown.Add(unkval);
631 }
632 }
633 PopLimit(limit);
634 }
635 else
636 {
csharptest367e0222011-06-09 12:47:46 -0500637 do
638 {
639 if (ReadEnum(ref value, out unkval))
640 list.Add(value);
641 else
642 {
643 if (unknown == null)
644 unknown = new List<object>();
645 unknown.Add(unkval);
646 }
647 }
648 while (ContinueArray(fieldTag));
csharptestd2af9e92011-06-03 21:35:02 -0500649 }
650 }
651
652 [CLSCompliant(false)]
653 public void ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
654 {
csharptest367e0222011-06-09 12:47:46 -0500655 do
656 {
657 IBuilderLite builder = messageType.WeakCreateBuilderForType();
658 ReadMessage(builder, registry);
659 list.Add((T)builder.WeakBuildPartial());
660 }
661 while (ContinueArray(fieldTag));
csharptestd2af9e92011-06-03 21:35:02 -0500662 }
663
664 [CLSCompliant(false)]
665 public void ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
666 {
csharptest367e0222011-06-09 12:47:46 -0500667 do
668 {
669 IBuilderLite builder = messageType.WeakCreateBuilderForType();
670 ReadGroup(WireFormat.GetTagFieldNumber(fieldTag), builder, registry);
671 list.Add((T)builder.WeakBuildPartial());
672 }
673 while (ContinueArray(fieldTag));
csharptest71f662c2011-05-20 15:15:34 -0500674 }
675
676 /// <summary>
677 /// Reads a field of any primitive type. Enums, groups and embedded
678 /// messages are not handled by this method.
679 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500680 public bool ReadPrimitiveField(FieldType fieldType, ref object value)
csharptest71f662c2011-05-20 15:15:34 -0500681 {
682 switch (fieldType)
683 {
684 case FieldType.Double:
csharptestd2af9e92011-06-03 21:35:02 -0500685 {
686 double tmp = 0;
687 if (ReadDouble(ref tmp))
688 {
689 value = tmp;
690 return true;
691 }
692 return false;
693 }
csharptest71f662c2011-05-20 15:15:34 -0500694 case FieldType.Float:
csharptestd2af9e92011-06-03 21:35:02 -0500695 {
696 float tmp = 0;
697 if (ReadFloat(ref tmp))
698 {
699 value = tmp;
700 return true;
701 }
702 return false;
703 }
csharptest71f662c2011-05-20 15:15:34 -0500704 case FieldType.Int64:
csharptestd2af9e92011-06-03 21:35:02 -0500705 {
706 long tmp = 0;
707 if (ReadInt64(ref tmp))
708 {
709 value = tmp;
710 return true;
711 }
712 return false;
713 }
csharptest71f662c2011-05-20 15:15:34 -0500714 case FieldType.UInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500715 {
716 ulong tmp = 0;
717 if (ReadUInt64(ref tmp))
718 {
719 value = tmp;
720 return true;
721 }
722 return false;
723 }
csharptest71f662c2011-05-20 15:15:34 -0500724 case FieldType.Int32:
csharptestd2af9e92011-06-03 21:35:02 -0500725 {
726 int tmp = 0;
727 if (ReadInt32(ref tmp))
728 {
729 value = tmp;
730 return true;
731 }
732 return false;
733 }
csharptest71f662c2011-05-20 15:15:34 -0500734 case FieldType.Fixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500735 {
736 ulong tmp = 0;
737 if (ReadFixed64(ref tmp))
738 {
739 value = tmp;
740 return true;
741 }
742 return false;
743 }
csharptest71f662c2011-05-20 15:15:34 -0500744 case FieldType.Fixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500745 {
746 uint tmp = 0;
747 if (ReadFixed32(ref tmp))
748 {
749 value = tmp;
750 return true;
751 }
752 return false;
753 }
csharptest71f662c2011-05-20 15:15:34 -0500754 case FieldType.Bool:
csharptestd2af9e92011-06-03 21:35:02 -0500755 {
756 bool tmp = false;
757 if (ReadBool(ref tmp))
758 {
759 value = tmp;
760 return true;
761 }
762 return false;
763 }
csharptest71f662c2011-05-20 15:15:34 -0500764 case FieldType.String:
csharptestd2af9e92011-06-03 21:35:02 -0500765 {
766 string tmp = null;
767 if (ReadString(ref tmp))
768 {
769 value = tmp;
770 return true;
771 }
772 return false;
773 }
csharptest71f662c2011-05-20 15:15:34 -0500774 case FieldType.Bytes:
csharptestd2af9e92011-06-03 21:35:02 -0500775 {
776 ByteString tmp = null;
777 if (ReadBytes(ref tmp))
778 {
779 value = tmp;
780 return true;
781 }
782 return false;
783 }
csharptest71f662c2011-05-20 15:15:34 -0500784 case FieldType.UInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500785 {
786 uint tmp = 0;
787 if (ReadUInt32(ref tmp))
788 {
789 value = tmp;
790 return true;
791 }
792 return false;
793 }
csharptest71f662c2011-05-20 15:15:34 -0500794 case FieldType.SFixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500795 {
796 int tmp = 0;
797 if (ReadSFixed32(ref tmp))
798 {
799 value = tmp;
800 return true;
801 }
802 return false;
803 }
csharptest71f662c2011-05-20 15:15:34 -0500804 case FieldType.SFixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500805 {
806 long tmp = 0;
807 if (ReadSFixed64(ref tmp))
808 {
809 value = tmp;
810 return true;
811 }
812 return false;
813 }
csharptest71f662c2011-05-20 15:15:34 -0500814 case FieldType.SInt32:
csharptestd2af9e92011-06-03 21:35:02 -0500815 {
816 int tmp = 0;
817 if (ReadSInt32(ref tmp))
818 {
819 value = tmp;
820 return true;
821 }
822 return false;
823 }
csharptest71f662c2011-05-20 15:15:34 -0500824 case FieldType.SInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500825 {
826 long tmp = 0;
827 if (ReadSInt64(ref tmp))
828 {
829 value = tmp;
830 return true;
831 }
832 return false;
833 }
csharptest71f662c2011-05-20 15:15:34 -0500834 case FieldType.Group:
835 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
836 case FieldType.Message:
837 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
838 // We don't handle enums because we don't know what to do if the
839 // value is not recognized.
840 case FieldType.Enum:
841 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
842 default:
843 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
844 }
845 }
846
847 #endregion
848
849 #region Underlying reading primitives
850
851 /// <summary>
852 /// Same code as ReadRawVarint32, but read each byte individually, checking for
853 /// buffer overflow.
854 /// </summary>
855 private uint SlowReadRawVarint32()
856 {
857 int tmp = ReadRawByte();
858 if (tmp < 128)
859 {
860 return (uint) tmp;
861 }
862 int result = tmp & 0x7f;
863 if ((tmp = ReadRawByte()) < 128)
864 {
865 result |= tmp << 7;
866 }
867 else
868 {
869 result |= (tmp & 0x7f) << 7;
870 if ((tmp = ReadRawByte()) < 128)
871 {
872 result |= tmp << 14;
873 }
874 else
875 {
876 result |= (tmp & 0x7f) << 14;
877 if ((tmp = ReadRawByte()) < 128)
878 {
879 result |= tmp << 21;
880 }
881 else
882 {
883 result |= (tmp & 0x7f) << 21;
884 result |= (tmp = ReadRawByte()) << 28;
885 if (tmp >= 128)
886 {
887 // Discard upper 32 bits.
888 for (int i = 0; i < 5; i++)
889 {
890 if (ReadRawByte() < 128) return (uint) result;
891 }
892 throw InvalidProtocolBufferException.MalformedVarint();
893 }
894 }
895 }
896 }
897 return (uint) result;
898 }
899
900 /// <summary>
901 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
902 /// This method is optimised for the case where we've got lots of data in the buffer.
903 /// That means we can check the size just once, then just read directly from the buffer
904 /// without constant rechecking of the buffer length.
905 /// </summary>
906 [CLSCompliant(false)]
907 public uint ReadRawVarint32()
908 {
909 if (bufferPos + 5 > bufferSize)
910 {
911 return SlowReadRawVarint32();
912 }
913
914 int tmp = buffer[bufferPos++];
915 if (tmp < 128)
916 {
917 return (uint) tmp;
918 }
919 int result = tmp & 0x7f;
920 if ((tmp = buffer[bufferPos++]) < 128)
921 {
922 result |= tmp << 7;
923 }
924 else
925 {
926 result |= (tmp & 0x7f) << 7;
927 if ((tmp = buffer[bufferPos++]) < 128)
928 {
929 result |= tmp << 14;
930 }
931 else
932 {
933 result |= (tmp & 0x7f) << 14;
934 if ((tmp = buffer[bufferPos++]) < 128)
935 {
936 result |= tmp << 21;
937 }
938 else
939 {
940 result |= (tmp & 0x7f) << 21;
941 result |= (tmp = buffer[bufferPos++]) << 28;
942 if (tmp >= 128)
943 {
944 // Discard upper 32 bits.
945 // Note that this has to use ReadRawByte() as we only ensure we've
946 // got at least 5 bytes at the start of the method. This lets us
947 // use the fast path in more cases, and we rarely hit this section of code.
948 for (int i = 0; i < 5; i++)
949 {
950 if (ReadRawByte() < 128) return (uint) result;
951 }
952 throw InvalidProtocolBufferException.MalformedVarint();
953 }
954 }
955 }
956 }
957 return (uint) result;
958 }
959
960 /// <summary>
961 /// Reads a varint from the input one byte at a time, so that it does not
962 /// read any bytes after the end of the varint. If you simply wrapped the
963 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
964 /// then you would probably end up reading past the end of the varint since
965 /// CodedInputStream buffers its input.
966 /// </summary>
967 /// <param name="input"></param>
968 /// <returns></returns>
969 [CLSCompliant(false)]
970 public static uint ReadRawVarint32(Stream input)
971 {
972 int result = 0;
973 int offset = 0;
974 for (; offset < 32; offset += 7)
975 {
976 int b = input.ReadByte();
977 if (b == -1)
978 {
979 throw InvalidProtocolBufferException.TruncatedMessage();
980 }
981 result |= (b & 0x7f) << offset;
982 if ((b & 0x80) == 0)
983 {
984 return (uint) result;
985 }
986 }
987 // Keep reading up to 64 bits.
988 for (; offset < 64; offset += 7)
989 {
990 int b = input.ReadByte();
991 if (b == -1)
992 {
993 throw InvalidProtocolBufferException.TruncatedMessage();
994 }
995 if ((b & 0x80) == 0)
996 {
997 return (uint) result;
998 }
999 }
1000 throw InvalidProtocolBufferException.MalformedVarint();
1001 }
1002
1003 /// <summary>
1004 /// Read a raw varint from the stream.
1005 /// </summary>
1006 [CLSCompliant(false)]
1007 public ulong ReadRawVarint64()
1008 {
1009 int shift = 0;
1010 ulong result = 0;
1011 while (shift < 64)
1012 {
1013 byte b = ReadRawByte();
1014 result |= (ulong) (b & 0x7F) << shift;
1015 if ((b & 0x80) == 0)
1016 {
1017 return result;
1018 }
1019 shift += 7;
1020 }
1021 throw InvalidProtocolBufferException.MalformedVarint();
1022 }
1023
1024 /// <summary>
1025 /// Read a 32-bit little-endian integer from the stream.
1026 /// </summary>
1027 [CLSCompliant(false)]
1028 public uint ReadRawLittleEndian32()
1029 {
1030 uint b1 = ReadRawByte();
1031 uint b2 = ReadRawByte();
1032 uint b3 = ReadRawByte();
1033 uint b4 = ReadRawByte();
1034 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
1035 }
1036
1037 /// <summary>
1038 /// Read a 64-bit little-endian integer from the stream.
1039 /// </summary>
1040 [CLSCompliant(false)]
1041 public ulong ReadRawLittleEndian64()
1042 {
1043 ulong b1 = ReadRawByte();
1044 ulong b2 = ReadRawByte();
1045 ulong b3 = ReadRawByte();
1046 ulong b4 = ReadRawByte();
1047 ulong b5 = ReadRawByte();
1048 ulong b6 = ReadRawByte();
1049 ulong b7 = ReadRawByte();
1050 ulong b8 = ReadRawByte();
1051 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
1052 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
1053 }
1054
1055 #endregion
1056
1057 /// <summary>
1058 /// Decode a 32-bit value with ZigZag encoding.
1059 /// </summary>
1060 /// <remarks>
1061 /// ZigZag encodes signed integers into values that can be efficiently
1062 /// encoded with varint. (Otherwise, negative values must be
1063 /// sign-extended to 64 bits to be varint encoded, thus always taking
1064 /// 10 bytes on the wire.)
1065 /// </remarks>
1066 [CLSCompliant(false)]
1067 public static int DecodeZigZag32(uint n)
1068 {
1069 return (int) (n >> 1) ^ -(int) (n & 1);
1070 }
1071
1072 /// <summary>
1073 /// Decode a 32-bit value with ZigZag encoding.
1074 /// </summary>
1075 /// <remarks>
1076 /// ZigZag encodes signed integers into values that can be efficiently
1077 /// encoded with varint. (Otherwise, negative values must be
1078 /// sign-extended to 64 bits to be varint encoded, thus always taking
1079 /// 10 bytes on the wire.)
1080 /// </remarks>
1081 [CLSCompliant(false)]
1082 public static long DecodeZigZag64(ulong n)
1083 {
1084 return (long) (n >> 1) ^ -(long) (n & 1);
1085 }
1086
1087 /// <summary>
1088 /// Set the maximum message recursion depth.
1089 /// </summary>
1090 /// <remarks>
1091 /// In order to prevent malicious
1092 /// messages from causing stack overflows, CodedInputStream limits
1093 /// how deeply messages may be nested. The default limit is 64.
1094 /// </remarks>
1095 public int SetRecursionLimit(int limit)
1096 {
1097 if (limit < 0)
1098 {
1099 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
1100 }
1101 int oldLimit = recursionLimit;
1102 recursionLimit = limit;
1103 return oldLimit;
1104 }
1105
1106 /// <summary>
1107 /// Set the maximum message size.
1108 /// </summary>
1109 /// <remarks>
1110 /// In order to prevent malicious messages from exhausting memory or
1111 /// causing integer overflows, CodedInputStream limits how large a message may be.
1112 /// The default limit is 64MB. You should set this limit as small
1113 /// as you can without harming your app's functionality. Note that
1114 /// size limits only apply when reading from an InputStream, not
1115 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
1116 /// If you want to read several messages from a single CodedInputStream, you
1117 /// can call ResetSizeCounter() after each message to avoid hitting the
1118 /// size limit.
1119 /// </remarks>
1120 public int SetSizeLimit(int limit)
1121 {
1122 if (limit < 0)
1123 {
1124 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
1125 }
1126 int oldLimit = sizeLimit;
1127 sizeLimit = limit;
1128 return oldLimit;
1129 }
1130
1131 #region Internal reading and buffer management
1132
1133 /// <summary>
1134 /// Resets the current size counter to zero (see SetSizeLimit).
1135 /// </summary>
1136 public void ResetSizeCounter()
1137 {
1138 totalBytesRetired = 0;
1139 }
1140
1141 /// <summary>
1142 /// Sets currentLimit to (current position) + byteLimit. This is called
1143 /// when descending into a length-delimited embedded message. The previous
1144 /// limit is returned.
1145 /// </summary>
1146 /// <returns>The old limit.</returns>
1147 public int PushLimit(int byteLimit)
1148 {
1149 if (byteLimit < 0)
1150 {
1151 throw InvalidProtocolBufferException.NegativeSize();
1152 }
1153 byteLimit += totalBytesRetired + bufferPos;
1154 int oldLimit = currentLimit;
1155 if (byteLimit > oldLimit)
1156 {
1157 throw InvalidProtocolBufferException.TruncatedMessage();
1158 }
1159 currentLimit = byteLimit;
1160
1161 RecomputeBufferSizeAfterLimit();
1162
1163 return oldLimit;
1164 }
1165
1166 private void RecomputeBufferSizeAfterLimit()
1167 {
1168 bufferSize += bufferSizeAfterLimit;
1169 int bufferEnd = totalBytesRetired + bufferSize;
1170 if (bufferEnd > currentLimit)
1171 {
1172 // Limit is in current buffer.
1173 bufferSizeAfterLimit = bufferEnd - currentLimit;
1174 bufferSize -= bufferSizeAfterLimit;
1175 }
1176 else
1177 {
1178 bufferSizeAfterLimit = 0;
1179 }
1180 }
1181
1182 /// <summary>
1183 /// Discards the current limit, returning the previous limit.
1184 /// </summary>
1185 public void PopLimit(int oldLimit)
1186 {
1187 currentLimit = oldLimit;
1188 RecomputeBufferSizeAfterLimit();
1189 }
1190
1191 /// <summary>
1192 /// Returns whether or not all the data before the limit has been read.
1193 /// </summary>
1194 /// <returns></returns>
1195 public bool ReachedLimit
1196 {
1197 get
1198 {
1199 if (currentLimit == int.MaxValue)
1200 {
1201 return false;
1202 }
1203 int currentAbsolutePosition = totalBytesRetired + bufferPos;
1204 return currentAbsolutePosition >= currentLimit;
1205 }
1206 }
1207
1208 /// <summary>
1209 /// Returns true if the stream has reached the end of the input. This is the
1210 /// case if either the end of the underlying input source has been reached or
1211 /// the stream has reached a limit created using PushLimit.
1212 /// </summary>
1213 public bool IsAtEnd
1214 {
1215 get { return bufferPos == bufferSize && !RefillBuffer(false); }
1216 }
1217
1218 /// <summary>
1219 /// Called when buffer is empty to read more bytes from the
1220 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1221 /// either there will be at least one byte in the buffer when it returns
1222 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
1223 /// RefillBuffer() returns false if no more bytes were available.
1224 /// </summary>
1225 /// <param name="mustSucceed"></param>
1226 /// <returns></returns>
1227 private bool RefillBuffer(bool mustSucceed)
1228 {
1229 if (bufferPos < bufferSize)
1230 {
1231 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1232 }
1233
1234 if (totalBytesRetired + bufferSize == currentLimit)
1235 {
1236 // Oops, we hit a limit.
1237 if (mustSucceed)
1238 {
1239 throw InvalidProtocolBufferException.TruncatedMessage();
1240 }
1241 else
1242 {
1243 return false;
1244 }
1245 }
1246
1247 totalBytesRetired += bufferSize;
1248
1249 bufferPos = 0;
1250 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1251 if (bufferSize < 0)
1252 {
1253 throw new InvalidOperationException("Stream.Read returned a negative count");
1254 }
1255 if (bufferSize == 0)
1256 {
1257 if (mustSucceed)
1258 {
1259 throw InvalidProtocolBufferException.TruncatedMessage();
1260 }
1261 else
1262 {
1263 return false;
1264 }
1265 }
1266 else
1267 {
1268 RecomputeBufferSizeAfterLimit();
1269 int totalBytesRead =
1270 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1271 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1272 {
1273 throw InvalidProtocolBufferException.SizeLimitExceeded();
1274 }
1275 return true;
1276 }
1277 }
1278
1279 /// <summary>
1280 /// Read one byte from the input.
1281 /// </summary>
1282 /// <exception cref="InvalidProtocolBufferException">
1283 /// the end of the stream or the current limit was reached
1284 /// </exception>
1285 public byte ReadRawByte()
1286 {
1287 if (bufferPos == bufferSize)
1288 {
1289 RefillBuffer(true);
1290 }
1291 return buffer[bufferPos++];
1292 }
1293
1294 /// <summary>
1295 /// Read a fixed size of bytes from the input.
1296 /// </summary>
1297 /// <exception cref="InvalidProtocolBufferException">
1298 /// the end of the stream or the current limit was reached
1299 /// </exception>
1300 public byte[] ReadRawBytes(int size)
1301 {
1302 if (size < 0)
1303 {
1304 throw InvalidProtocolBufferException.NegativeSize();
1305 }
1306
1307 if (totalBytesRetired + bufferPos + size > currentLimit)
1308 {
1309 // Read to the end of the stream anyway.
1310 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1311 // Then fail.
1312 throw InvalidProtocolBufferException.TruncatedMessage();
1313 }
1314
1315 if (size <= bufferSize - bufferPos)
1316 {
1317 // We have all the bytes we need already.
1318 byte[] bytes = new byte[size];
csharptestaef072a2011-06-08 18:00:43 -05001319 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
csharptest71f662c2011-05-20 15:15:34 -05001320 bufferPos += size;
1321 return bytes;
1322 }
1323 else if (size < BufferSize)
1324 {
1325 // Reading more bytes than are in the buffer, but not an excessive number
1326 // of bytes. We can safely allocate the resulting array ahead of time.
1327
1328 // First copy what we have.
1329 byte[] bytes = new byte[size];
1330 int pos = bufferSize - bufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001331 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
csharptest71f662c2011-05-20 15:15:34 -05001332 bufferPos = bufferSize;
1333
1334 // We want to use RefillBuffer() and then copy from the buffer into our
1335 // byte array rather than reading directly into our byte array because
1336 // the input may be unbuffered.
1337 RefillBuffer(true);
1338
1339 while (size - pos > bufferSize)
1340 {
csharptest2772dfe2011-06-08 15:50:58 -05001341 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
csharptest71f662c2011-05-20 15:15:34 -05001342 pos += bufferSize;
1343 bufferPos = bufferSize;
1344 RefillBuffer(true);
1345 }
1346
csharptestaef072a2011-06-08 18:00:43 -05001347 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
csharptest71f662c2011-05-20 15:15:34 -05001348 bufferPos = size - pos;
1349
1350 return bytes;
1351 }
1352 else
1353 {
1354 // The size is very large. For security reasons, we can't allocate the
1355 // entire byte array yet. The size comes directly from the input, so a
1356 // maliciously-crafted message could provide a bogus very large size in
1357 // order to trick the app into allocating a lot of memory. We avoid this
1358 // by allocating and reading only a small chunk at a time, so that the
1359 // malicious message must actually *be* extremely large to cause
1360 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1361
1362 // Remember the buffer markers since we'll have to copy the bytes out of
1363 // it later.
1364 int originalBufferPos = bufferPos;
1365 int originalBufferSize = bufferSize;
1366
1367 // Mark the current buffer consumed.
1368 totalBytesRetired += bufferSize;
1369 bufferPos = 0;
1370 bufferSize = 0;
1371
1372 // Read all the rest of the bytes we need.
1373 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1374 List<byte[]> chunks = new List<byte[]>();
1375
1376 while (sizeLeft > 0)
1377 {
1378 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
1379 int pos = 0;
1380 while (pos < chunk.Length)
1381 {
1382 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1383 if (n <= 0)
1384 {
1385 throw InvalidProtocolBufferException.TruncatedMessage();
1386 }
1387 totalBytesRetired += n;
1388 pos += n;
1389 }
1390 sizeLeft -= chunk.Length;
1391 chunks.Add(chunk);
1392 }
1393
1394 // OK, got everything. Now concatenate it all into one buffer.
1395 byte[] bytes = new byte[size];
1396
1397 // Start by copying the leftover bytes from this.buffer.
1398 int newPos = originalBufferSize - originalBufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001399 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
csharptest71f662c2011-05-20 15:15:34 -05001400
1401 // And now all the chunks.
1402 foreach (byte[] chunk in chunks)
1403 {
csharptest2772dfe2011-06-08 15:50:58 -05001404 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
csharptest71f662c2011-05-20 15:15:34 -05001405 newPos += chunk.Length;
1406 }
1407
1408 // Done.
1409 return bytes;
1410 }
1411 }
1412
1413 /// <summary>
1414 /// Reads and discards a single field, given its tag value.
1415 /// </summary>
1416 /// <returns>false if the tag is an end-group tag, in which case
1417 /// nothing is skipped. Otherwise, returns true.</returns>
1418 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -05001419 public bool SkipField()
csharptest71f662c2011-05-20 15:15:34 -05001420 {
csharptest123e5342011-06-03 14:15:21 -05001421 uint tag = lastTag;
csharptest71f662c2011-05-20 15:15:34 -05001422 switch (WireFormat.GetTagWireType(tag))
1423 {
1424 case WireFormat.WireType.Varint:
csharptestd2af9e92011-06-03 21:35:02 -05001425 ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -05001426 return true;
1427 case WireFormat.WireType.Fixed64:
1428 ReadRawLittleEndian64();
1429 return true;
1430 case WireFormat.WireType.LengthDelimited:
1431 SkipRawBytes((int) ReadRawVarint32());
1432 return true;
1433 case WireFormat.WireType.StartGroup:
1434 SkipMessage();
1435 CheckLastTagWas(
1436 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
1437 WireFormat.WireType.EndGroup));
1438 return true;
1439 case WireFormat.WireType.EndGroup:
1440 return false;
1441 case WireFormat.WireType.Fixed32:
1442 ReadRawLittleEndian32();
1443 return true;
1444 default:
1445 throw InvalidProtocolBufferException.InvalidWireType();
1446 }
1447 }
1448
1449 /// <summary>
1450 /// Reads and discards an entire message. This will read either until EOF
1451 /// or until an endgroup tag, whichever comes first.
1452 /// </summary>
1453 public void SkipMessage()
1454 {
csharptest123e5342011-06-03 14:15:21 -05001455 uint tag;
1456 string name;
1457 while (ReadTag(out tag, out name))
csharptest71f662c2011-05-20 15:15:34 -05001458 {
csharptest123e5342011-06-03 14:15:21 -05001459 if (!SkipField())
csharptest71f662c2011-05-20 15:15:34 -05001460 {
1461 return;
1462 }
1463 }
1464 }
1465
1466 /// <summary>
1467 /// Reads and discards <paramref name="size"/> bytes.
1468 /// </summary>
1469 /// <exception cref="InvalidProtocolBufferException">the end of the stream
1470 /// or the current limit was reached</exception>
1471 public void SkipRawBytes(int size)
1472 {
1473 if (size < 0)
1474 {
1475 throw InvalidProtocolBufferException.NegativeSize();
1476 }
1477
1478 if (totalBytesRetired + bufferPos + size > currentLimit)
1479 {
1480 // Read to the end of the stream anyway.
1481 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1482 // Then fail.
1483 throw InvalidProtocolBufferException.TruncatedMessage();
1484 }
1485
1486 if (size <= bufferSize - bufferPos)
1487 {
1488 // We have all the bytes we need already.
1489 bufferPos += size;
1490 }
1491 else
1492 {
1493 // Skipping more bytes than are in the buffer. First skip what we have.
1494 int pos = bufferSize - bufferPos;
1495 totalBytesRetired += pos;
1496 bufferPos = 0;
1497 bufferSize = 0;
1498
1499 // Then skip directly from the InputStream for the rest.
1500 if (pos < size)
1501 {
1502 if (input == null)
1503 {
1504 throw InvalidProtocolBufferException.TruncatedMessage();
1505 }
1506 SkipImpl(size - pos);
1507 totalBytesRetired += size - pos;
1508 }
1509 }
1510 }
1511
1512 /// <summary>
1513 /// Abstraction of skipping to cope with streams which can't really skip.
1514 /// </summary>
1515 private void SkipImpl(int amountToSkip)
1516 {
1517 if (input.CanSeek)
1518 {
1519 long previousPosition = input.Position;
1520 input.Position += amountToSkip;
1521 if (input.Position != previousPosition + amountToSkip)
1522 {
1523 throw InvalidProtocolBufferException.TruncatedMessage();
1524 }
1525 }
1526 else
1527 {
1528 byte[] skipBuffer = new byte[1024];
1529 while (amountToSkip > 0)
1530 {
1531 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
1532 if (bytesRead <= 0)
1533 {
1534 throw InvalidProtocolBufferException.TruncatedMessage();
1535 }
1536 amountToSkip -= bytesRead;
1537 }
1538 }
1539 }
1540
1541 #endregion
1542 }
1543}