blob: aad47f96299b6e979ab41d9f204e247cfe4cc4cb [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>
csharptestced18e12011-06-09 19:47:56 -0500519 private bool ContinueArray(uint currentTag, bool packed)
csharptest367e0222011-06-09 12:47:46 -0500520 {
csharptestced18e12011-06-09 19:47:56 -0500521 if (packed)
522 {
523 return !ReachedLimit;
524 }
525
csharptest367e0222011-06-09 12:47:46 -0500526 string ignore;
527 uint next;
528 if (PeekNextTag(out next, out ignore))
529 {
530 if (next == currentTag)
531 {
532 hasNextTag = false;
533 return true;
534 }
535 }
536 return false;
537 }
538
csharptestd2af9e92011-06-03 21:35:02 -0500539 [CLSCompliant(false)]
csharptestced18e12011-06-09 19:47:56 -0500540 public void ReadPrimitiveArray(FieldType fieldType, uint fieldTag, string fieldName, ICollection<object> list)
csharptestd2af9e92011-06-03 21:35:02 -0500541 {
542 WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
543 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
544
545 // 2.3 allows packed form even if the field is not declared packed.
546 if(normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
547 {
548 int length = (int)(ReadRawVarint32() & int.MaxValue);
549 int limit = PushLimit(length);
550 while (!ReachedLimit)
551 {
552 Object value = null;
553 if(ReadPrimitiveField(fieldType, ref value))
csharptestced18e12011-06-09 19:47:56 -0500554 list.Add(value);
csharptestd2af9e92011-06-03 21:35:02 -0500555 }
556 PopLimit(limit);
557 }
558 else
559 {
560 Object value = null;
csharptest367e0222011-06-09 12:47:46 -0500561 do
562 {
563 if (ReadPrimitiveField(fieldType, ref value))
csharptestced18e12011-06-09 19:47:56 -0500564 list.Add(value);
csharptest367e0222011-06-09 12:47:46 -0500565 }
csharptestced18e12011-06-09 19:47:56 -0500566 while (ContinueArray(fieldTag, false));
567 }
568 }
569
570 [CLSCompliant(false)]
571 public void ReadPrimitiveArray<T>(FieldType fieldType, uint fieldTag, string fieldName, ICollection<T> list)
572 {
573 WireFormat.WireType normal = WireFormat.GetWireType(fieldType);
574 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
575
576 // 2.3 allows packed form even if the field is not declared packed.
577 if (normal != wformat && wformat == WireFormat.WireType.LengthDelimited)
578 {
579 int length = (int)(ReadRawVarint32() & int.MaxValue);
580 int limit = PushLimit(length);
581 //while (!ReachedLimit)
582 //{
583 // Object value = null;
584 // if (ReadPrimitiveField(fieldType, ref value))
585 // list.Add((T)value);
586 //}
587 if (!ReachedLimit)
588 ReadPrimitiveArrayItems(fieldType, fieldTag, list, true);
589
590 PopLimit(limit);
591 }
592 else
593 {
594 ReadPrimitiveArrayItems(fieldType, fieldTag, list, false);
595 //Object value = null;
596 //do
597 //{
598 // if (ReadPrimitiveField(fieldType, ref value))
599 // list.Add((T)value);
600 //}
601 //while (ContinueArray(fieldTag, false));
602 }
603 }
604
605 void ReadPrimitiveArrayItems<T>(FieldType fieldType, uint fieldTag, ICollection<T> list, bool packed)
606 {
607 switch (fieldType)
608 {
609 case FieldType.Double:
610 {
611 ICollection<double> output = (ICollection<double>)list;
612 double tmp = 0;
613 do
614 {
615 ReadDouble(ref tmp);
616 output.Add(tmp);
617 }
618 while (ContinueArray(fieldTag, packed));
619 }
620 break;
621 case FieldType.Float:
622 {
623 ICollection<float> output = (ICollection<float>)list;
624 float tmp = 0;
625 do
626 {
627 ReadFloat(ref tmp);
628 output.Add(tmp);
629 }
630 while (ContinueArray(fieldTag, packed));
631 }
632 break;
633 case FieldType.Int64:
634 {
635 ICollection<long> output = (ICollection<long>)list;
636 long tmp = 0;
637 do
638 {
639 ReadInt64(ref tmp);
640 output.Add(tmp);
641 }
642 while (ContinueArray(fieldTag, packed));
643 }
644 break;
645 case FieldType.UInt64:
646 {
647 ICollection<ulong> output = (ICollection<ulong>)list;
648 ulong tmp = 0;
649 do
650 {
651 ReadUInt64(ref tmp);
652 output.Add(tmp);
653 }
654 while (ContinueArray(fieldTag, packed));
655 }
656 break;
657 case FieldType.Int32:
658 {
659 ICollection<int> output = (ICollection<int>)list;
660 int tmp = 0;
661 do
662 {
663 ReadInt32(ref tmp);
664 output.Add(tmp);
665 }
666 while (ContinueArray(fieldTag, packed));
667 }
668 break;
669 case FieldType.Fixed64:
670 {
671 ICollection<ulong> output = (ICollection<ulong>)list;
672 ulong tmp = 0;
673 do
674 {
675 ReadFixed64(ref tmp);
676 output.Add(tmp);
677 }
678 while (ContinueArray(fieldTag, packed));
679 }
680 break;
681 case FieldType.Fixed32:
682 {
683 ICollection<uint> output = (ICollection<uint>)list;
684 uint tmp = 0;
685 do
686 {
687 ReadFixed32(ref tmp);
688 output.Add(tmp);
689 }
690 while (ContinueArray(fieldTag, packed));
691 }
692 break;
693 case FieldType.Bool:
694 {
695 ICollection<bool> output = (ICollection<bool>)list;
696 bool tmp = false;
697 do
698 {
699 ReadBool(ref tmp);
700 output.Add(tmp);
701 }
702 while (ContinueArray(fieldTag, packed));
703 }
704 break;
705 case FieldType.String:
706 {
707 ICollection<string> output = (ICollection<string>)list;
708 string tmp = null;
709 do
710 {
711 ReadString(ref tmp);
712 output.Add(tmp);
713 }
714 while (ContinueArray(fieldTag, packed));
715 }
716 break;
717 case FieldType.Bytes:
718 {
719 ICollection<ByteString> output = (ICollection<ByteString>)list;
720 ByteString tmp = null;
721 do
722 {
723 ReadBytes(ref tmp);
724 output.Add(tmp);
725 }
726 while (ContinueArray(fieldTag, packed));
727 }
728 break;
729 case FieldType.UInt32:
730 {
731 ICollection<uint> output = (ICollection<uint>)list;
732 uint tmp = 0;
733 do
734 {
735 ReadUInt32(ref tmp);
736 output.Add(tmp);
737 }
738 while (ContinueArray(fieldTag, packed));
739 }
740 break;
741 case FieldType.SFixed32:
742 {
743 ICollection<int> output = (ICollection<int>)list;
744 int tmp = 0;
745 do
746 {
747 ReadSFixed32(ref tmp);
748 output.Add(tmp);
749 }
750 while (ContinueArray(fieldTag, packed));
751 }
752 break;
753 case FieldType.SFixed64:
754 {
755 ICollection<long> output = (ICollection<long>)list;
756 long tmp = 0;
757 do
758 {
759 ReadSFixed64(ref tmp);
760 output.Add(tmp);
761 }
762 while (ContinueArray(fieldTag, packed));
763 }
764 break;
765 case FieldType.SInt32:
766 {
767 ICollection<int> output = (ICollection<int>)list;
768 int tmp = 0;
769 do
770 {
771 ReadSInt32(ref tmp);
772 output.Add(tmp);
773 }
774 while (ContinueArray(fieldTag, packed));
775 }
776 break;
777 case FieldType.SInt64:
778 {
779 ICollection<long> output = (ICollection<long>)list;
780 long tmp = 0;
781 do
782 {
783 ReadSInt64(ref tmp);
784 output.Add(tmp);
785 }
786 while (ContinueArray(fieldTag, packed));
787 }
788 break;
789 case FieldType.Group:
790 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
791 case FieldType.Message:
792 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
793 // We don't handle enums because we don't know what to do if the
794 // value is not recognized.
795 case FieldType.Enum:
796 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
797 default:
798 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
csharptestd2af9e92011-06-03 21:35:02 -0500799 }
800 }
801
802 [CLSCompliant(false)]
803 public void ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list, out ICollection<object> unknown, IEnumLiteMap mapping)
804 {
805 unknown = null;
806 object unkval;
807 IEnumLite value = null;
808 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
809
810 // 2.3 allows packed form even if the field is not declared packed.
811 if (wformat == WireFormat.WireType.LengthDelimited)
812 {
813 int length = (int)(ReadRawVarint32() & int.MaxValue);
814 int limit = PushLimit(length);
815 while (!ReachedLimit)
816 {
817 if (ReadEnum(ref value, out unkval, mapping))
818 list.Add(value);
819 else
820 {
821 if (unknown == null)
822 unknown = new List<object>();
823 unknown.Add(unkval);
824 }
825 }
826 PopLimit(limit);
827 }
828 else
829 {
csharptest367e0222011-06-09 12:47:46 -0500830 do
831 {
832 if (ReadEnum(ref value, out unkval, mapping))
833 list.Add(value);
834 else
835 {
836 if (unknown == null)
837 unknown = new List<object>();
838 unknown.Add(unkval);
839 }
840 }
csharptestced18e12011-06-09 19:47:56 -0500841 while (ContinueArray(fieldTag, false));
csharptestd2af9e92011-06-03 21:35:02 -0500842 }
843 }
844
845 [CLSCompliant(false)]
846 public void ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list, out ICollection<object> unknown)
847 where T : struct, IComparable, IFormattable, IConvertible
848 {
849 unknown = null;
850 object unkval;
851 T value = default(T);
852 WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
853
854 // 2.3 allows packed form even if the field is not declared packed.
855 if (wformat == WireFormat.WireType.LengthDelimited)
856 {
857 int length = (int)(ReadRawVarint32() & int.MaxValue);
858 int limit = PushLimit(length);
859 while (!ReachedLimit)
860 {
861 if (ReadEnum<T>(ref value, out unkval))
862 list.Add(value);
863 else
864 {
865 if (unknown == null)
866 unknown = new List<object>();
867 unknown.Add(unkval);
868 }
869 }
870 PopLimit(limit);
871 }
872 else
873 {
csharptest367e0222011-06-09 12:47:46 -0500874 do
875 {
876 if (ReadEnum(ref value, out unkval))
877 list.Add(value);
878 else
879 {
880 if (unknown == null)
881 unknown = new List<object>();
882 unknown.Add(unkval);
883 }
884 }
csharptestced18e12011-06-09 19:47:56 -0500885 while (ContinueArray(fieldTag, false));
csharptestd2af9e92011-06-03 21:35:02 -0500886 }
887 }
888
889 [CLSCompliant(false)]
890 public void ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
891 {
csharptest367e0222011-06-09 12:47:46 -0500892 do
893 {
894 IBuilderLite builder = messageType.WeakCreateBuilderForType();
895 ReadMessage(builder, registry);
896 list.Add((T)builder.WeakBuildPartial());
897 }
csharptestced18e12011-06-09 19:47:56 -0500898 while (ContinueArray(fieldTag, false));
csharptestd2af9e92011-06-03 21:35:02 -0500899 }
900
901 [CLSCompliant(false)]
902 public void ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry) where T : IMessageLite
903 {
csharptest367e0222011-06-09 12:47:46 -0500904 do
905 {
906 IBuilderLite builder = messageType.WeakCreateBuilderForType();
907 ReadGroup(WireFormat.GetTagFieldNumber(fieldTag), builder, registry);
908 list.Add((T)builder.WeakBuildPartial());
909 }
csharptestced18e12011-06-09 19:47:56 -0500910 while (ContinueArray(fieldTag, false));
csharptest71f662c2011-05-20 15:15:34 -0500911 }
912
913 /// <summary>
914 /// Reads a field of any primitive type. Enums, groups and embedded
915 /// messages are not handled by this method.
916 /// </summary>
csharptestd2af9e92011-06-03 21:35:02 -0500917 public bool ReadPrimitiveField(FieldType fieldType, ref object value)
csharptest71f662c2011-05-20 15:15:34 -0500918 {
919 switch (fieldType)
920 {
921 case FieldType.Double:
csharptestd2af9e92011-06-03 21:35:02 -0500922 {
923 double tmp = 0;
924 if (ReadDouble(ref tmp))
925 {
926 value = tmp;
927 return true;
928 }
929 return false;
930 }
csharptest71f662c2011-05-20 15:15:34 -0500931 case FieldType.Float:
csharptestd2af9e92011-06-03 21:35:02 -0500932 {
933 float tmp = 0;
934 if (ReadFloat(ref tmp))
935 {
936 value = tmp;
937 return true;
938 }
939 return false;
940 }
csharptest71f662c2011-05-20 15:15:34 -0500941 case FieldType.Int64:
csharptestd2af9e92011-06-03 21:35:02 -0500942 {
943 long tmp = 0;
944 if (ReadInt64(ref tmp))
945 {
946 value = tmp;
947 return true;
948 }
949 return false;
950 }
csharptest71f662c2011-05-20 15:15:34 -0500951 case FieldType.UInt64:
csharptestd2af9e92011-06-03 21:35:02 -0500952 {
953 ulong tmp = 0;
954 if (ReadUInt64(ref tmp))
955 {
956 value = tmp;
957 return true;
958 }
959 return false;
960 }
csharptest71f662c2011-05-20 15:15:34 -0500961 case FieldType.Int32:
csharptestd2af9e92011-06-03 21:35:02 -0500962 {
963 int tmp = 0;
964 if (ReadInt32(ref tmp))
965 {
966 value = tmp;
967 return true;
968 }
969 return false;
970 }
csharptest71f662c2011-05-20 15:15:34 -0500971 case FieldType.Fixed64:
csharptestd2af9e92011-06-03 21:35:02 -0500972 {
973 ulong tmp = 0;
974 if (ReadFixed64(ref tmp))
975 {
976 value = tmp;
977 return true;
978 }
979 return false;
980 }
csharptest71f662c2011-05-20 15:15:34 -0500981 case FieldType.Fixed32:
csharptestd2af9e92011-06-03 21:35:02 -0500982 {
983 uint tmp = 0;
984 if (ReadFixed32(ref tmp))
985 {
986 value = tmp;
987 return true;
988 }
989 return false;
990 }
csharptest71f662c2011-05-20 15:15:34 -0500991 case FieldType.Bool:
csharptestd2af9e92011-06-03 21:35:02 -0500992 {
993 bool tmp = false;
994 if (ReadBool(ref tmp))
995 {
996 value = tmp;
997 return true;
998 }
999 return false;
1000 }
csharptest71f662c2011-05-20 15:15:34 -05001001 case FieldType.String:
csharptestd2af9e92011-06-03 21:35:02 -05001002 {
1003 string tmp = null;
1004 if (ReadString(ref tmp))
1005 {
1006 value = tmp;
1007 return true;
1008 }
1009 return false;
1010 }
csharptest71f662c2011-05-20 15:15:34 -05001011 case FieldType.Bytes:
csharptestd2af9e92011-06-03 21:35:02 -05001012 {
1013 ByteString tmp = null;
1014 if (ReadBytes(ref tmp))
1015 {
1016 value = tmp;
1017 return true;
1018 }
1019 return false;
1020 }
csharptest71f662c2011-05-20 15:15:34 -05001021 case FieldType.UInt32:
csharptestd2af9e92011-06-03 21:35:02 -05001022 {
1023 uint tmp = 0;
1024 if (ReadUInt32(ref tmp))
1025 {
1026 value = tmp;
1027 return true;
1028 }
1029 return false;
1030 }
csharptest71f662c2011-05-20 15:15:34 -05001031 case FieldType.SFixed32:
csharptestd2af9e92011-06-03 21:35:02 -05001032 {
1033 int tmp = 0;
1034 if (ReadSFixed32(ref tmp))
1035 {
1036 value = tmp;
1037 return true;
1038 }
1039 return false;
1040 }
csharptest71f662c2011-05-20 15:15:34 -05001041 case FieldType.SFixed64:
csharptestd2af9e92011-06-03 21:35:02 -05001042 {
1043 long tmp = 0;
1044 if (ReadSFixed64(ref tmp))
1045 {
1046 value = tmp;
1047 return true;
1048 }
1049 return false;
1050 }
csharptest71f662c2011-05-20 15:15:34 -05001051 case FieldType.SInt32:
csharptestd2af9e92011-06-03 21:35:02 -05001052 {
1053 int tmp = 0;
1054 if (ReadSInt32(ref tmp))
1055 {
1056 value = tmp;
1057 return true;
1058 }
1059 return false;
1060 }
csharptest71f662c2011-05-20 15:15:34 -05001061 case FieldType.SInt64:
csharptestd2af9e92011-06-03 21:35:02 -05001062 {
1063 long tmp = 0;
1064 if (ReadSInt64(ref tmp))
1065 {
1066 value = tmp;
1067 return true;
1068 }
1069 return false;
1070 }
csharptest71f662c2011-05-20 15:15:34 -05001071 case FieldType.Group:
1072 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
1073 case FieldType.Message:
1074 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
1075 // We don't handle enums because we don't know what to do if the
1076 // value is not recognized.
1077 case FieldType.Enum:
1078 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
1079 default:
1080 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1081 }
1082 }
1083
1084 #endregion
1085
1086 #region Underlying reading primitives
1087
1088 /// <summary>
1089 /// Same code as ReadRawVarint32, but read each byte individually, checking for
1090 /// buffer overflow.
1091 /// </summary>
1092 private uint SlowReadRawVarint32()
1093 {
1094 int tmp = ReadRawByte();
1095 if (tmp < 128)
1096 {
1097 return (uint) tmp;
1098 }
1099 int result = tmp & 0x7f;
1100 if ((tmp = ReadRawByte()) < 128)
1101 {
1102 result |= tmp << 7;
1103 }
1104 else
1105 {
1106 result |= (tmp & 0x7f) << 7;
1107 if ((tmp = ReadRawByte()) < 128)
1108 {
1109 result |= tmp << 14;
1110 }
1111 else
1112 {
1113 result |= (tmp & 0x7f) << 14;
1114 if ((tmp = ReadRawByte()) < 128)
1115 {
1116 result |= tmp << 21;
1117 }
1118 else
1119 {
1120 result |= (tmp & 0x7f) << 21;
1121 result |= (tmp = ReadRawByte()) << 28;
1122 if (tmp >= 128)
1123 {
1124 // Discard upper 32 bits.
1125 for (int i = 0; i < 5; i++)
1126 {
1127 if (ReadRawByte() < 128) return (uint) result;
1128 }
1129 throw InvalidProtocolBufferException.MalformedVarint();
1130 }
1131 }
1132 }
1133 }
1134 return (uint) result;
1135 }
1136
1137 /// <summary>
1138 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
1139 /// This method is optimised for the case where we've got lots of data in the buffer.
1140 /// That means we can check the size just once, then just read directly from the buffer
1141 /// without constant rechecking of the buffer length.
1142 /// </summary>
1143 [CLSCompliant(false)]
1144 public uint ReadRawVarint32()
1145 {
1146 if (bufferPos + 5 > bufferSize)
1147 {
1148 return SlowReadRawVarint32();
1149 }
1150
1151 int tmp = buffer[bufferPos++];
1152 if (tmp < 128)
1153 {
1154 return (uint) tmp;
1155 }
1156 int result = tmp & 0x7f;
1157 if ((tmp = buffer[bufferPos++]) < 128)
1158 {
1159 result |= tmp << 7;
1160 }
1161 else
1162 {
1163 result |= (tmp & 0x7f) << 7;
1164 if ((tmp = buffer[bufferPos++]) < 128)
1165 {
1166 result |= tmp << 14;
1167 }
1168 else
1169 {
1170 result |= (tmp & 0x7f) << 14;
1171 if ((tmp = buffer[bufferPos++]) < 128)
1172 {
1173 result |= tmp << 21;
1174 }
1175 else
1176 {
1177 result |= (tmp & 0x7f) << 21;
1178 result |= (tmp = buffer[bufferPos++]) << 28;
1179 if (tmp >= 128)
1180 {
1181 // Discard upper 32 bits.
1182 // Note that this has to use ReadRawByte() as we only ensure we've
1183 // got at least 5 bytes at the start of the method. This lets us
1184 // use the fast path in more cases, and we rarely hit this section of code.
1185 for (int i = 0; i < 5; i++)
1186 {
1187 if (ReadRawByte() < 128) return (uint) result;
1188 }
1189 throw InvalidProtocolBufferException.MalformedVarint();
1190 }
1191 }
1192 }
1193 }
1194 return (uint) result;
1195 }
1196
1197 /// <summary>
1198 /// Reads a varint from the input one byte at a time, so that it does not
1199 /// read any bytes after the end of the varint. If you simply wrapped the
1200 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
1201 /// then you would probably end up reading past the end of the varint since
1202 /// CodedInputStream buffers its input.
1203 /// </summary>
1204 /// <param name="input"></param>
1205 /// <returns></returns>
1206 [CLSCompliant(false)]
1207 public static uint ReadRawVarint32(Stream input)
1208 {
1209 int result = 0;
1210 int offset = 0;
1211 for (; offset < 32; offset += 7)
1212 {
1213 int b = input.ReadByte();
1214 if (b == -1)
1215 {
1216 throw InvalidProtocolBufferException.TruncatedMessage();
1217 }
1218 result |= (b & 0x7f) << offset;
1219 if ((b & 0x80) == 0)
1220 {
1221 return (uint) result;
1222 }
1223 }
1224 // Keep reading up to 64 bits.
1225 for (; offset < 64; offset += 7)
1226 {
1227 int b = input.ReadByte();
1228 if (b == -1)
1229 {
1230 throw InvalidProtocolBufferException.TruncatedMessage();
1231 }
1232 if ((b & 0x80) == 0)
1233 {
1234 return (uint) result;
1235 }
1236 }
1237 throw InvalidProtocolBufferException.MalformedVarint();
1238 }
1239
1240 /// <summary>
1241 /// Read a raw varint from the stream.
1242 /// </summary>
1243 [CLSCompliant(false)]
1244 public ulong ReadRawVarint64()
1245 {
1246 int shift = 0;
1247 ulong result = 0;
1248 while (shift < 64)
1249 {
1250 byte b = ReadRawByte();
1251 result |= (ulong) (b & 0x7F) << shift;
1252 if ((b & 0x80) == 0)
1253 {
1254 return result;
1255 }
1256 shift += 7;
1257 }
1258 throw InvalidProtocolBufferException.MalformedVarint();
1259 }
1260
1261 /// <summary>
1262 /// Read a 32-bit little-endian integer from the stream.
1263 /// </summary>
1264 [CLSCompliant(false)]
1265 public uint ReadRawLittleEndian32()
1266 {
1267 uint b1 = ReadRawByte();
1268 uint b2 = ReadRawByte();
1269 uint b3 = ReadRawByte();
1270 uint b4 = ReadRawByte();
1271 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
1272 }
1273
1274 /// <summary>
1275 /// Read a 64-bit little-endian integer from the stream.
1276 /// </summary>
1277 [CLSCompliant(false)]
1278 public ulong ReadRawLittleEndian64()
1279 {
1280 ulong b1 = ReadRawByte();
1281 ulong b2 = ReadRawByte();
1282 ulong b3 = ReadRawByte();
1283 ulong b4 = ReadRawByte();
1284 ulong b5 = ReadRawByte();
1285 ulong b6 = ReadRawByte();
1286 ulong b7 = ReadRawByte();
1287 ulong b8 = ReadRawByte();
1288 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
1289 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
1290 }
1291
1292 #endregion
1293
1294 /// <summary>
1295 /// Decode a 32-bit value with ZigZag encoding.
1296 /// </summary>
1297 /// <remarks>
1298 /// ZigZag encodes signed integers into values that can be efficiently
1299 /// encoded with varint. (Otherwise, negative values must be
1300 /// sign-extended to 64 bits to be varint encoded, thus always taking
1301 /// 10 bytes on the wire.)
1302 /// </remarks>
1303 [CLSCompliant(false)]
1304 public static int DecodeZigZag32(uint n)
1305 {
1306 return (int) (n >> 1) ^ -(int) (n & 1);
1307 }
1308
1309 /// <summary>
1310 /// Decode a 32-bit value with ZigZag encoding.
1311 /// </summary>
1312 /// <remarks>
1313 /// ZigZag encodes signed integers into values that can be efficiently
1314 /// encoded with varint. (Otherwise, negative values must be
1315 /// sign-extended to 64 bits to be varint encoded, thus always taking
1316 /// 10 bytes on the wire.)
1317 /// </remarks>
1318 [CLSCompliant(false)]
1319 public static long DecodeZigZag64(ulong n)
1320 {
1321 return (long) (n >> 1) ^ -(long) (n & 1);
1322 }
1323
1324 /// <summary>
1325 /// Set the maximum message recursion depth.
1326 /// </summary>
1327 /// <remarks>
1328 /// In order to prevent malicious
1329 /// messages from causing stack overflows, CodedInputStream limits
1330 /// how deeply messages may be nested. The default limit is 64.
1331 /// </remarks>
1332 public int SetRecursionLimit(int limit)
1333 {
1334 if (limit < 0)
1335 {
1336 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
1337 }
1338 int oldLimit = recursionLimit;
1339 recursionLimit = limit;
1340 return oldLimit;
1341 }
1342
1343 /// <summary>
1344 /// Set the maximum message size.
1345 /// </summary>
1346 /// <remarks>
1347 /// In order to prevent malicious messages from exhausting memory or
1348 /// causing integer overflows, CodedInputStream limits how large a message may be.
1349 /// The default limit is 64MB. You should set this limit as small
1350 /// as you can without harming your app's functionality. Note that
1351 /// size limits only apply when reading from an InputStream, not
1352 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
1353 /// If you want to read several messages from a single CodedInputStream, you
1354 /// can call ResetSizeCounter() after each message to avoid hitting the
1355 /// size limit.
1356 /// </remarks>
1357 public int SetSizeLimit(int limit)
1358 {
1359 if (limit < 0)
1360 {
1361 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
1362 }
1363 int oldLimit = sizeLimit;
1364 sizeLimit = limit;
1365 return oldLimit;
1366 }
1367
1368 #region Internal reading and buffer management
1369
1370 /// <summary>
1371 /// Resets the current size counter to zero (see SetSizeLimit).
1372 /// </summary>
1373 public void ResetSizeCounter()
1374 {
1375 totalBytesRetired = 0;
1376 }
1377
1378 /// <summary>
1379 /// Sets currentLimit to (current position) + byteLimit. This is called
1380 /// when descending into a length-delimited embedded message. The previous
1381 /// limit is returned.
1382 /// </summary>
1383 /// <returns>The old limit.</returns>
1384 public int PushLimit(int byteLimit)
1385 {
1386 if (byteLimit < 0)
1387 {
1388 throw InvalidProtocolBufferException.NegativeSize();
1389 }
1390 byteLimit += totalBytesRetired + bufferPos;
1391 int oldLimit = currentLimit;
1392 if (byteLimit > oldLimit)
1393 {
1394 throw InvalidProtocolBufferException.TruncatedMessage();
1395 }
1396 currentLimit = byteLimit;
1397
1398 RecomputeBufferSizeAfterLimit();
1399
1400 return oldLimit;
1401 }
1402
1403 private void RecomputeBufferSizeAfterLimit()
1404 {
1405 bufferSize += bufferSizeAfterLimit;
1406 int bufferEnd = totalBytesRetired + bufferSize;
1407 if (bufferEnd > currentLimit)
1408 {
1409 // Limit is in current buffer.
1410 bufferSizeAfterLimit = bufferEnd - currentLimit;
1411 bufferSize -= bufferSizeAfterLimit;
1412 }
1413 else
1414 {
1415 bufferSizeAfterLimit = 0;
1416 }
1417 }
1418
1419 /// <summary>
1420 /// Discards the current limit, returning the previous limit.
1421 /// </summary>
1422 public void PopLimit(int oldLimit)
1423 {
1424 currentLimit = oldLimit;
1425 RecomputeBufferSizeAfterLimit();
1426 }
1427
1428 /// <summary>
1429 /// Returns whether or not all the data before the limit has been read.
1430 /// </summary>
1431 /// <returns></returns>
1432 public bool ReachedLimit
1433 {
1434 get
1435 {
1436 if (currentLimit == int.MaxValue)
1437 {
1438 return false;
1439 }
1440 int currentAbsolutePosition = totalBytesRetired + bufferPos;
1441 return currentAbsolutePosition >= currentLimit;
1442 }
1443 }
1444
1445 /// <summary>
1446 /// Returns true if the stream has reached the end of the input. This is the
1447 /// case if either the end of the underlying input source has been reached or
1448 /// the stream has reached a limit created using PushLimit.
1449 /// </summary>
1450 public bool IsAtEnd
1451 {
1452 get { return bufferPos == bufferSize && !RefillBuffer(false); }
1453 }
1454
1455 /// <summary>
1456 /// Called when buffer is empty to read more bytes from the
1457 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
1458 /// either there will be at least one byte in the buffer when it returns
1459 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
1460 /// RefillBuffer() returns false if no more bytes were available.
1461 /// </summary>
1462 /// <param name="mustSucceed"></param>
1463 /// <returns></returns>
1464 private bool RefillBuffer(bool mustSucceed)
1465 {
1466 if (bufferPos < bufferSize)
1467 {
1468 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
1469 }
1470
1471 if (totalBytesRetired + bufferSize == currentLimit)
1472 {
1473 // Oops, we hit a limit.
1474 if (mustSucceed)
1475 {
1476 throw InvalidProtocolBufferException.TruncatedMessage();
1477 }
1478 else
1479 {
1480 return false;
1481 }
1482 }
1483
1484 totalBytesRetired += bufferSize;
1485
1486 bufferPos = 0;
1487 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
1488 if (bufferSize < 0)
1489 {
1490 throw new InvalidOperationException("Stream.Read returned a negative count");
1491 }
1492 if (bufferSize == 0)
1493 {
1494 if (mustSucceed)
1495 {
1496 throw InvalidProtocolBufferException.TruncatedMessage();
1497 }
1498 else
1499 {
1500 return false;
1501 }
1502 }
1503 else
1504 {
1505 RecomputeBufferSizeAfterLimit();
1506 int totalBytesRead =
1507 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
1508 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
1509 {
1510 throw InvalidProtocolBufferException.SizeLimitExceeded();
1511 }
1512 return true;
1513 }
1514 }
1515
1516 /// <summary>
1517 /// Read one byte from the input.
1518 /// </summary>
1519 /// <exception cref="InvalidProtocolBufferException">
1520 /// the end of the stream or the current limit was reached
1521 /// </exception>
1522 public byte ReadRawByte()
1523 {
1524 if (bufferPos == bufferSize)
1525 {
1526 RefillBuffer(true);
1527 }
1528 return buffer[bufferPos++];
1529 }
1530
1531 /// <summary>
1532 /// Read a fixed size of bytes from the input.
1533 /// </summary>
1534 /// <exception cref="InvalidProtocolBufferException">
1535 /// the end of the stream or the current limit was reached
1536 /// </exception>
1537 public byte[] ReadRawBytes(int size)
1538 {
1539 if (size < 0)
1540 {
1541 throw InvalidProtocolBufferException.NegativeSize();
1542 }
1543
1544 if (totalBytesRetired + bufferPos + size > currentLimit)
1545 {
1546 // Read to the end of the stream anyway.
1547 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1548 // Then fail.
1549 throw InvalidProtocolBufferException.TruncatedMessage();
1550 }
1551
1552 if (size <= bufferSize - bufferPos)
1553 {
1554 // We have all the bytes we need already.
1555 byte[] bytes = new byte[size];
csharptestaef072a2011-06-08 18:00:43 -05001556 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
csharptest71f662c2011-05-20 15:15:34 -05001557 bufferPos += size;
1558 return bytes;
1559 }
1560 else if (size < BufferSize)
1561 {
1562 // Reading more bytes than are in the buffer, but not an excessive number
1563 // of bytes. We can safely allocate the resulting array ahead of time.
1564
1565 // First copy what we have.
1566 byte[] bytes = new byte[size];
1567 int pos = bufferSize - bufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001568 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
csharptest71f662c2011-05-20 15:15:34 -05001569 bufferPos = bufferSize;
1570
1571 // We want to use RefillBuffer() and then copy from the buffer into our
1572 // byte array rather than reading directly into our byte array because
1573 // the input may be unbuffered.
1574 RefillBuffer(true);
1575
1576 while (size - pos > bufferSize)
1577 {
csharptest2772dfe2011-06-08 15:50:58 -05001578 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
csharptest71f662c2011-05-20 15:15:34 -05001579 pos += bufferSize;
1580 bufferPos = bufferSize;
1581 RefillBuffer(true);
1582 }
1583
csharptestaef072a2011-06-08 18:00:43 -05001584 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
csharptest71f662c2011-05-20 15:15:34 -05001585 bufferPos = size - pos;
1586
1587 return bytes;
1588 }
1589 else
1590 {
1591 // The size is very large. For security reasons, we can't allocate the
1592 // entire byte array yet. The size comes directly from the input, so a
1593 // maliciously-crafted message could provide a bogus very large size in
1594 // order to trick the app into allocating a lot of memory. We avoid this
1595 // by allocating and reading only a small chunk at a time, so that the
1596 // malicious message must actually *be* extremely large to cause
1597 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1598
1599 // Remember the buffer markers since we'll have to copy the bytes out of
1600 // it later.
1601 int originalBufferPos = bufferPos;
1602 int originalBufferSize = bufferSize;
1603
1604 // Mark the current buffer consumed.
1605 totalBytesRetired += bufferSize;
1606 bufferPos = 0;
1607 bufferSize = 0;
1608
1609 // Read all the rest of the bytes we need.
1610 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1611 List<byte[]> chunks = new List<byte[]>();
1612
1613 while (sizeLeft > 0)
1614 {
1615 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
1616 int pos = 0;
1617 while (pos < chunk.Length)
1618 {
1619 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1620 if (n <= 0)
1621 {
1622 throw InvalidProtocolBufferException.TruncatedMessage();
1623 }
1624 totalBytesRetired += n;
1625 pos += n;
1626 }
1627 sizeLeft -= chunk.Length;
1628 chunks.Add(chunk);
1629 }
1630
1631 // OK, got everything. Now concatenate it all into one buffer.
1632 byte[] bytes = new byte[size];
1633
1634 // Start by copying the leftover bytes from this.buffer.
1635 int newPos = originalBufferSize - originalBufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001636 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
csharptest71f662c2011-05-20 15:15:34 -05001637
1638 // And now all the chunks.
1639 foreach (byte[] chunk in chunks)
1640 {
csharptest2772dfe2011-06-08 15:50:58 -05001641 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
csharptest71f662c2011-05-20 15:15:34 -05001642 newPos += chunk.Length;
1643 }
1644
1645 // Done.
1646 return bytes;
1647 }
1648 }
1649
1650 /// <summary>
1651 /// Reads and discards a single field, given its tag value.
1652 /// </summary>
1653 /// <returns>false if the tag is an end-group tag, in which case
1654 /// nothing is skipped. Otherwise, returns true.</returns>
1655 [CLSCompliant(false)]
csharptest123e5342011-06-03 14:15:21 -05001656 public bool SkipField()
csharptest71f662c2011-05-20 15:15:34 -05001657 {
csharptest123e5342011-06-03 14:15:21 -05001658 uint tag = lastTag;
csharptest71f662c2011-05-20 15:15:34 -05001659 switch (WireFormat.GetTagWireType(tag))
1660 {
1661 case WireFormat.WireType.Varint:
csharptestd2af9e92011-06-03 21:35:02 -05001662 ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -05001663 return true;
1664 case WireFormat.WireType.Fixed64:
1665 ReadRawLittleEndian64();
1666 return true;
1667 case WireFormat.WireType.LengthDelimited:
1668 SkipRawBytes((int) ReadRawVarint32());
1669 return true;
1670 case WireFormat.WireType.StartGroup:
1671 SkipMessage();
1672 CheckLastTagWas(
1673 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
1674 WireFormat.WireType.EndGroup));
1675 return true;
1676 case WireFormat.WireType.EndGroup:
1677 return false;
1678 case WireFormat.WireType.Fixed32:
1679 ReadRawLittleEndian32();
1680 return true;
1681 default:
1682 throw InvalidProtocolBufferException.InvalidWireType();
1683 }
1684 }
1685
1686 /// <summary>
1687 /// Reads and discards an entire message. This will read either until EOF
1688 /// or until an endgroup tag, whichever comes first.
1689 /// </summary>
1690 public void SkipMessage()
1691 {
csharptest123e5342011-06-03 14:15:21 -05001692 uint tag;
1693 string name;
1694 while (ReadTag(out tag, out name))
csharptest71f662c2011-05-20 15:15:34 -05001695 {
csharptest123e5342011-06-03 14:15:21 -05001696 if (!SkipField())
csharptest71f662c2011-05-20 15:15:34 -05001697 {
1698 return;
1699 }
1700 }
1701 }
1702
1703 /// <summary>
1704 /// Reads and discards <paramref name="size"/> bytes.
1705 /// </summary>
1706 /// <exception cref="InvalidProtocolBufferException">the end of the stream
1707 /// or the current limit was reached</exception>
1708 public void SkipRawBytes(int size)
1709 {
1710 if (size < 0)
1711 {
1712 throw InvalidProtocolBufferException.NegativeSize();
1713 }
1714
1715 if (totalBytesRetired + bufferPos + size > currentLimit)
1716 {
1717 // Read to the end of the stream anyway.
1718 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1719 // Then fail.
1720 throw InvalidProtocolBufferException.TruncatedMessage();
1721 }
1722
1723 if (size <= bufferSize - bufferPos)
1724 {
1725 // We have all the bytes we need already.
1726 bufferPos += size;
1727 }
1728 else
1729 {
1730 // Skipping more bytes than are in the buffer. First skip what we have.
1731 int pos = bufferSize - bufferPos;
1732 totalBytesRetired += pos;
1733 bufferPos = 0;
1734 bufferSize = 0;
1735
1736 // Then skip directly from the InputStream for the rest.
1737 if (pos < size)
1738 {
1739 if (input == null)
1740 {
1741 throw InvalidProtocolBufferException.TruncatedMessage();
1742 }
1743 SkipImpl(size - pos);
1744 totalBytesRetired += size - pos;
1745 }
1746 }
1747 }
1748
1749 /// <summary>
1750 /// Abstraction of skipping to cope with streams which can't really skip.
1751 /// </summary>
1752 private void SkipImpl(int amountToSkip)
1753 {
1754 if (input.CanSeek)
1755 {
1756 long previousPosition = input.Position;
1757 input.Position += amountToSkip;
1758 if (input.Position != previousPosition + amountToSkip)
1759 {
1760 throw InvalidProtocolBufferException.TruncatedMessage();
1761 }
1762 }
1763 else
1764 {
1765 byte[] skipBuffer = new byte[1024];
1766 while (amountToSkip > 0)
1767 {
1768 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
1769 if (bytesRead <= 0)
1770 {
1771 throw InvalidProtocolBufferException.TruncatedMessage();
1772 }
1773 amountToSkip -= bytesRead;
1774 }
1775 }
1776 }
1777
1778 #endregion
1779 }
1780}