blob: 922957f20ce149e357d8288453eced9421d1af88 [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System;
36using System.Collections.Generic;
37using System.IO;
38using System.Text;
39using Google.ProtocolBuffers.Descriptors;
40
41namespace Google.ProtocolBuffers {
42
43 /// <summary>
44 /// Readings and decodes protocol message fields.
45 /// </summary>
46 /// <remarks>
47 /// This class contains two kinds of methods: methods that read specific
48 /// protocol message constructs and field types (e.g. ReadTag and
49 /// ReadInt32) and methods that read low-level values (e.g.
50 /// ReadRawVarint32 and ReadRawBytes). If you are reading encoded protocol
51 /// messages, you should use the former methods, but if you are reading some
52 /// other format of your own design, use the latter. The names of the former
53 /// methods are taken from the protocol buffer type names, not .NET types.
54 /// (Hence ReadFloat instead of ReadSingle, and ReadBool instead of ReadBoolean.)
55 ///
56 /// TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly,
57 /// set at construction time.
58 /// </remarks>
59 public sealed class CodedInputStream {
60 private readonly byte[] buffer;
61 private int bufferSize;
62 private int bufferSizeAfterLimit = 0;
63 private int bufferPos = 0;
64 private readonly Stream input;
65 private uint lastTag = 0;
66
Jon Skeet2178b932009-06-25 07:52:07 +010067 internal const int DefaultRecursionLimit = 64;
68 internal const int DefaultSizeLimit = 64 << 20; // 64MB
69 internal const int BufferSize = 4096;
Jon Skeet68036862008-10-22 13:30:34 +010070
71 /// <summary>
72 /// The total number of bytes read before the current buffer. The
73 /// total bytes read up to the current position can be computed as
74 /// totalBytesRetired + bufferPos.
75 /// </summary>
76 private int totalBytesRetired = 0;
77
78 /// <summary>
79 /// The absolute position of the end of the current message.
80 /// </summary>
81 private int currentLimit = int.MaxValue;
82
83 /// <summary>
84 /// <see cref="SetRecursionLimit"/>
85 /// </summary>
86 private int recursionDepth = 0;
87 private int recursionLimit = DefaultRecursionLimit;
88
89 /// <summary>
90 /// <see cref="SetSizeLimit"/>
91 /// </summary>
92 private int sizeLimit = DefaultSizeLimit;
93
94 #region Construction
95 /// <summary>
96 /// Creates a new CodedInputStream reading data from the given
97 /// stream.
98 /// </summary>
99 public static CodedInputStream CreateInstance(Stream input) {
100 return new CodedInputStream(input);
101 }
102
103 /// <summary>
104 /// Creates a new CodedInputStream reading data from the given
105 /// byte array.
106 /// </summary>
107 public static CodedInputStream CreateInstance(byte[] buf) {
Charles Stanhopeef234da2010-10-19 05:37:44 -0700108 return new CodedInputStream(buf, 0, buf.Length);
Jon Skeet68036862008-10-22 13:30:34 +0100109 }
110
Charles Stanhopeef234da2010-10-19 05:37:44 -0700111 /// <summary>
112 /// Creates a new CodedInputStream that reads from the given
113 /// byte array slice.
114 /// </summary>
115 public static CodedInputStream CreateInstance(byte[] buf, int offset, int length) {
Jon Skeetf18a5b02010-10-26 19:45:03 +0100116 return new CodedInputStream(buf, offset, length);
Charles Stanhopeef234da2010-10-19 05:37:44 -0700117 }
118
119 private CodedInputStream(byte[] buffer, int offset, int length) {
Jon Skeetf18a5b02010-10-26 19:45:03 +0100120 this.buffer = buffer;
121 this.bufferPos = offset;
122 this.bufferSize = offset + length;
123 this.input = null;
Jon Skeet68036862008-10-22 13:30:34 +0100124 }
125
126 private CodedInputStream(Stream input) {
127 this.buffer = new byte[BufferSize];
128 this.bufferSize = 0;
129 this.input = input;
130 }
131 #endregion
132
133 #region Validation
134 /// <summary>
135 /// Verifies that the last call to ReadTag() returned the given tag value.
136 /// This is used to verify that a nested group ended with the correct
137 /// end tag.
138 /// </summary>
139 /// <exception cref="InvalidProtocolBufferException">The last
140 /// tag read was not the one specified</exception>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100141 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100142 public void CheckLastTagWas(uint value) {
143 if (lastTag != value) {
144 throw InvalidProtocolBufferException.InvalidEndTag();
145 }
146 }
147 #endregion
148
149 #region Reading of tags etc
150 /// <summary>
151 /// Attempt to read a field tag, returning 0 if we have reached the end
152 /// of the input data. Protocol message parsers use this to read tags,
153 /// since a protocol message may legally end wherever a tag occurs, and
154 /// zero is not a valid tag number.
155 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100156 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100157 public uint ReadTag() {
Jon Skeet2e6dc122009-05-29 06:34:52 +0100158 if (IsAtEnd) {
Jon Skeet68036862008-10-22 13:30:34 +0100159 lastTag = 0;
160 return 0;
161 }
162
163 lastTag = ReadRawVarint32();
164 if (lastTag == 0) {
165 // If we actually read zero, that's not a valid tag.
166 throw InvalidProtocolBufferException.InvalidTag();
167 }
168 return lastTag;
169 }
170
171 /// <summary>
172 /// Read a double field from the stream.
173 /// </summary>
174 public double ReadDouble() {
Jon Skeetb49d3c72009-11-03 16:51:01 +0000175#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
Jon Skeet3c808862009-09-09 13:22:36 +0100176 byte[] bytes = ReadRawBytes(8);
177 return BitConverter.ToDouble(bytes, 0);
178#else
Jon Skeet68036862008-10-22 13:30:34 +0100179 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
Jon Skeet3c808862009-09-09 13:22:36 +0100180#endif
Jon Skeet68036862008-10-22 13:30:34 +0100181 }
182
183 /// <summary>
184 /// Read a float field from the stream.
185 /// </summary>
186 public float ReadFloat() {
187 // TODO(jonskeet): Test this on different endiannesses
188 uint raw = ReadRawLittleEndian32();
189 byte[] rawBytes = BitConverter.GetBytes(raw);
190 return BitConverter.ToSingle(rawBytes, 0);
191 }
192
193 /// <summary>
194 /// Read a uint64 field from the stream.
195 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100196 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100197 public ulong ReadUInt64() {
198 return ReadRawVarint64();
199 }
200
201 /// <summary>
202 /// Read an int64 field from the stream.
203 /// </summary>
204 public long ReadInt64() {
205 return (long) ReadRawVarint64();
206 }
207
208 /// <summary>
209 /// Read an int32 field from the stream.
210 /// </summary>
211 public int ReadInt32() {
212 return (int) ReadRawVarint32();
213 }
214
215 /// <summary>
216 /// Read a fixed64 field from the stream.
217 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100218 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100219 public ulong ReadFixed64() {
220 return ReadRawLittleEndian64();
221 }
222
223 /// <summary>
224 /// Read a fixed32 field from the stream.
225 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100226 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100227 public uint ReadFixed32() {
228 return ReadRawLittleEndian32();
229 }
230
231 /// <summary>
232 /// Read a bool field from the stream.
233 /// </summary>
234 public bool ReadBool() {
235 return ReadRawVarint32() != 0;
236 }
237
238 /// <summary>
239 /// Reads a string field from the stream.
240 /// </summary>
241 public String ReadString() {
242 int size = (int) ReadRawVarint32();
Jon Skeet6a60ac32009-01-27 14:47:35 +0000243 // No need to read any data for an empty string.
244 if (size == 0) {
245 return "";
246 }
247 if (size <= bufferSize - bufferPos) {
Jon Skeet68036862008-10-22 13:30:34 +0100248 // Fast path: We already have the bytes in a contiguous buffer, so
249 // just copy directly from it.
250 String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
251 bufferPos += size;
252 return result;
Jon Skeet68036862008-10-22 13:30:34 +0100253 }
Jon Skeet60fb63e2009-06-20 20:46:28 +0100254 // Slow path: Build a byte array first then copy it.
255 return Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
Jon Skeet68036862008-10-22 13:30:34 +0100256 }
257
258 /// <summary>
259 /// Reads a group field value from the stream.
260 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500261 public void ReadGroup(int fieldNumber, IBuilderLite builder,
csharptest80e73b92010-11-05 14:49:08 -0500262 ExtensionRegistry extensionRegistry) {
Jon Skeet68036862008-10-22 13:30:34 +0100263 if (recursionDepth >= recursionLimit) {
264 throw InvalidProtocolBufferException.RecursionLimitExceeded();
265 }
266 ++recursionDepth;
267 builder.WeakMergeFrom(this, extensionRegistry);
268 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
269 --recursionDepth;
270 }
271
272 /// <summary>
273 /// Reads a group field value from the stream and merges it into the given
274 /// UnknownFieldSet.
275 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500276 [Obsolete]
277 public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
278 {
Jon Skeet68036862008-10-22 13:30:34 +0100279 if (recursionDepth >= recursionLimit) {
280 throw InvalidProtocolBufferException.RecursionLimitExceeded();
281 }
282 ++recursionDepth;
csharptestd9c59e62010-11-04 19:36:28 -0500283 builder.WeakMergeFrom(this);
Jon Skeet68036862008-10-22 13:30:34 +0100284 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
285 --recursionDepth;
286 }
287
288 /// <summary>
289 /// Reads an embedded message field value from the stream.
290 /// </summary>
csharptest80e73b92010-11-05 14:49:08 -0500291 public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry) {
Jon Skeet68036862008-10-22 13:30:34 +0100292 int length = (int) ReadRawVarint32();
293 if (recursionDepth >= recursionLimit) {
294 throw InvalidProtocolBufferException.RecursionLimitExceeded();
295 }
296 int oldLimit = PushLimit(length);
297 ++recursionDepth;
298 builder.WeakMergeFrom(this, extensionRegistry);
299 CheckLastTagWas(0);
300 --recursionDepth;
301 PopLimit(oldLimit);
302 }
303
304 /// <summary>
305 /// Reads a bytes field value from the stream.
306 /// </summary>
307 public ByteString ReadBytes() {
308 int size = (int) ReadRawVarint32();
309 if (size < bufferSize - bufferPos && size > 0) {
310 // Fast path: We already have the bytes in a contiguous buffer, so
311 // just copy directly from it.
312 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
313 bufferPos += size;
314 return result;
315 } else {
316 // Slow path: Build a byte array first then copy it.
317 return ByteString.CopyFrom(ReadRawBytes(size));
318 }
319 }
320
321 /// <summary>
322 /// Reads a uint32 field value from the stream.
323 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100324 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100325 public uint ReadUInt32() {
326 return ReadRawVarint32();
327 }
328
329 /// <summary>
330 /// Reads an enum field value from the stream. The caller is responsible
331 /// for converting the numeric value to an actual enum.
332 /// </summary>
333 public int ReadEnum() {
334 return (int) ReadRawVarint32();
335 }
336
337 /// <summary>
338 /// Reads an sfixed32 field value from the stream.
339 /// </summary>
340 public int ReadSFixed32() {
341 return (int) ReadRawLittleEndian32();
342 }
343
344 /// <summary>
345 /// Reads an sfixed64 field value from the stream.
346 /// </summary>
347 public long ReadSFixed64() {
348 return (long) ReadRawLittleEndian64();
349 }
350
351 /// <summary>
352 /// Reads an sint32 field value from the stream.
353 /// </summary>
354 public int ReadSInt32() {
355 return DecodeZigZag32(ReadRawVarint32());
356 }
357
358 /// <summary>
359 /// Reads an sint64 field value from the stream.
360 /// </summary>
361 public long ReadSInt64() {
362 return DecodeZigZag64(ReadRawVarint64());
363 }
csharptest804b6d82010-11-07 10:49:33 -0600364
Jon Skeet68036862008-10-22 13:30:34 +0100365 /// <summary>
366 /// Reads a field of any primitive type. Enums, groups and embedded
367 /// messages are not handled by this method.
368 /// </summary>
369 public object ReadPrimitiveField(FieldType fieldType) {
370 switch (fieldType) {
371 case FieldType.Double: return ReadDouble();
372 case FieldType.Float: return ReadFloat();
373 case FieldType.Int64: return ReadInt64();
374 case FieldType.UInt64: return ReadUInt64();
375 case FieldType.Int32: return ReadInt32();
376 case FieldType.Fixed64: return ReadFixed64();
377 case FieldType.Fixed32: return ReadFixed32();
378 case FieldType.Bool: return ReadBool();
379 case FieldType.String: return ReadString();
380 case FieldType.Bytes: return ReadBytes();
381 case FieldType.UInt32: return ReadUInt32();
382 case FieldType.SFixed32: return ReadSFixed32();
383 case FieldType.SFixed64: return ReadSFixed64();
384 case FieldType.SInt32: return ReadSInt32();
385 case FieldType.SInt64: return ReadSInt64();
386 case FieldType.Group:
387 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
388 case FieldType.Message:
389 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
390 // We don't handle enums because we don't know what to do if the
391 // value is not recognized.
392 case FieldType.Enum:
393 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
394 default:
395 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
396 }
397 }
Jon Skeet68036862008-10-22 13:30:34 +0100398 #endregion
399
400 #region Underlying reading primitives
401
402 /// <summary>
403 /// Same code as ReadRawVarint32, but read each byte individually, checking for
404 /// buffer overflow.
405 /// </summary>
406 private uint SlowReadRawVarint32() {
407 int tmp = ReadRawByte();
408 if (tmp < 128) {
409 return (uint)tmp;
410 }
411 int result = tmp & 0x7f;
412 if ((tmp = ReadRawByte()) < 128) {
413 result |= tmp << 7;
414 } else {
415 result |= (tmp & 0x7f) << 7;
416 if ((tmp = ReadRawByte()) < 128) {
417 result |= tmp << 14;
418 } else {
419 result |= (tmp & 0x7f) << 14;
420 if ((tmp = ReadRawByte()) < 128) {
421 result |= tmp << 21;
422 } else {
423 result |= (tmp & 0x7f) << 21;
424 result |= (tmp = ReadRawByte()) << 28;
425 if (tmp >= 128) {
426 // Discard upper 32 bits.
427 for (int i = 0; i < 5; i++) {
428 if (ReadRawByte() < 128) return (uint)result;
429 }
430 throw InvalidProtocolBufferException.MalformedVarint();
431 }
432 }
433 }
434 }
435 return (uint)result;
436 }
437
438 /// <summary>
439 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
440 /// This method is optimised for the case where we've got lots of data in the buffer.
441 /// That means we can check the size just once, then just read directly from the buffer
442 /// without constant rechecking of the buffer length.
443 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100444 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100445 public uint ReadRawVarint32() {
446 if (bufferPos + 5 > bufferSize) {
447 return SlowReadRawVarint32();
448 }
449
450 int tmp = buffer[bufferPos++];
451 if (tmp < 128) {
452 return (uint)tmp;
453 }
454 int result = tmp & 0x7f;
455 if ((tmp = buffer[bufferPos++]) < 128) {
456 result |= tmp << 7;
457 } else {
458 result |= (tmp & 0x7f) << 7;
459 if ((tmp = buffer[bufferPos++]) < 128) {
460 result |= tmp << 14;
461 } else {
462 result |= (tmp & 0x7f) << 14;
463 if ((tmp = buffer[bufferPos++]) < 128) {
464 result |= tmp << 21;
465 } else {
466 result |= (tmp & 0x7f) << 21;
467 result |= (tmp = buffer[bufferPos++]) << 28;
468 if (tmp >= 128) {
469 // Discard upper 32 bits.
470 // Note that this has to use ReadRawByte() as we only ensure we've
471 // got at least 5 bytes at the start of the method. This lets us
472 // use the fast path in more cases, and we rarely hit this section of code.
473 for (int i = 0; i < 5; i++) {
474 if (ReadRawByte() < 128) return (uint)result;
475 }
476 throw InvalidProtocolBufferException.MalformedVarint();
477 }
478 }
479 }
480 }
481 return (uint)result;
482 }
483
484 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100485 /// Reads a varint from the input one byte at a time, so that it does not
486 /// read any bytes after the end of the varint. If you simply wrapped the
487 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
488 /// then you would probably end up reading past the end of the varint since
489 /// CodedInputStream buffers its input.
490 /// </summary>
491 /// <param name="input"></param>
492 /// <returns></returns>
Jon Skeetc298c892009-05-30 10:07:09 +0100493 internal static uint ReadRawVarint32(Stream input) {
Jon Skeet2e6dc122009-05-29 06:34:52 +0100494 int result = 0;
495 int offset = 0;
496 for (; offset < 32; offset += 7) {
497 int b = input.ReadByte();
498 if (b == -1) {
499 throw InvalidProtocolBufferException.TruncatedMessage();
500 }
501 result |= (b & 0x7f) << offset;
502 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100503 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100504 }
505 }
506 // Keep reading up to 64 bits.
507 for (; offset < 64; offset += 7) {
508 int b = input.ReadByte();
509 if (b == -1) {
510 throw InvalidProtocolBufferException.TruncatedMessage();
511 }
512 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100513 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100514 }
515 }
516 throw InvalidProtocolBufferException.MalformedVarint();
517 }
518
519 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100520 /// Read a raw varint from the stream.
521 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100522 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100523 public ulong ReadRawVarint64() {
524 int shift = 0;
525 ulong result = 0;
526 while (shift < 64) {
527 byte b = ReadRawByte();
528 result |= (ulong)(b & 0x7F) << shift;
529 if ((b & 0x80) == 0) {
530 return result;
531 }
532 shift += 7;
533 }
534 throw InvalidProtocolBufferException.MalformedVarint();
535 }
536
537 /// <summary>
538 /// Read a 32-bit little-endian integer from the stream.
539 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100540 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100541 public uint ReadRawLittleEndian32() {
542 uint b1 = ReadRawByte();
543 uint b2 = ReadRawByte();
544 uint b3 = ReadRawByte();
545 uint b4 = ReadRawByte();
546 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
547 }
548
549 /// <summary>
550 /// Read a 64-bit little-endian integer from the stream.
551 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100552 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100553 public ulong ReadRawLittleEndian64() {
554 ulong b1 = ReadRawByte();
555 ulong b2 = ReadRawByte();
556 ulong b3 = ReadRawByte();
557 ulong b4 = ReadRawByte();
558 ulong b5 = ReadRawByte();
559 ulong b6 = ReadRawByte();
560 ulong b7 = ReadRawByte();
561 ulong b8 = ReadRawByte();
562 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
563 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
564 }
565 #endregion
566
567 /// <summary>
568 /// Decode a 32-bit value with ZigZag encoding.
569 /// </summary>
570 /// <remarks>
571 /// ZigZag encodes signed integers into values that can be efficiently
572 /// encoded with varint. (Otherwise, negative values must be
573 /// sign-extended to 64 bits to be varint encoded, thus always taking
574 /// 10 bytes on the wire.)
575 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100576 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100577 public static int DecodeZigZag32(uint n) {
578 return (int)(n >> 1) ^ -(int)(n & 1);
579 }
580
581 /// <summary>
582 /// Decode a 32-bit value with ZigZag encoding.
583 /// </summary>
584 /// <remarks>
585 /// ZigZag encodes signed integers into values that can be efficiently
586 /// encoded with varint. (Otherwise, negative values must be
587 /// sign-extended to 64 bits to be varint encoded, thus always taking
588 /// 10 bytes on the wire.)
589 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100590 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100591 public static long DecodeZigZag64(ulong n) {
592 return (long)(n >> 1) ^ -(long)(n & 1);
593 }
594
595 /// <summary>
596 /// Set the maximum message recursion depth.
597 /// </summary>
598 /// <remarks>
599 /// In order to prevent malicious
600 /// messages from causing stack overflows, CodedInputStream limits
601 /// how deeply messages may be nested. The default limit is 64.
602 /// </remarks>
603 public int SetRecursionLimit(int limit) {
604 if (limit < 0) {
605 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
606 }
607 int oldLimit = recursionLimit;
608 recursionLimit = limit;
609 return oldLimit;
610 }
611
612 /// <summary>
613 /// Set the maximum message size.
614 /// </summary>
615 /// <remarks>
616 /// In order to prevent malicious messages from exhausting memory or
617 /// causing integer overflows, CodedInputStream limits how large a message may be.
618 /// The default limit is 64MB. You should set this limit as small
619 /// as you can without harming your app's functionality. Note that
620 /// size limits only apply when reading from an InputStream, not
621 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
Jon Skeet2e6dc122009-05-29 06:34:52 +0100622 /// If you want to read several messages from a single CodedInputStream, you
623 /// can call ResetSizeCounter() after each message to avoid hitting the
624 /// size limit.
Jon Skeet68036862008-10-22 13:30:34 +0100625 /// </remarks>
626 public int SetSizeLimit(int limit) {
627 if (limit < 0) {
628 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
629 }
630 int oldLimit = sizeLimit;
631 sizeLimit = limit;
632 return oldLimit;
633 }
634
635 #region Internal reading and buffer management
636 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100637 /// Resets the current size counter to zero (see SetSizeLimit).
638 /// </summary>
639 public void ResetSizeCounter() {
640 totalBytesRetired = 0;
641 }
642
643 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100644 /// Sets currentLimit to (current position) + byteLimit. This is called
645 /// when descending into a length-delimited embedded message. The previous
646 /// limit is returned.
647 /// </summary>
648 /// <returns>The old limit.</returns>
649 public int PushLimit(int byteLimit) {
650 if (byteLimit < 0) {
651 throw InvalidProtocolBufferException.NegativeSize();
652 }
653 byteLimit += totalBytesRetired + bufferPos;
654 int oldLimit = currentLimit;
655 if (byteLimit > oldLimit) {
656 throw InvalidProtocolBufferException.TruncatedMessage();
657 }
658 currentLimit = byteLimit;
659
660 RecomputeBufferSizeAfterLimit();
661
662 return oldLimit;
663 }
664
665 private void RecomputeBufferSizeAfterLimit() {
666 bufferSize += bufferSizeAfterLimit;
667 int bufferEnd = totalBytesRetired + bufferSize;
668 if (bufferEnd > currentLimit) {
669 // Limit is in current buffer.
670 bufferSizeAfterLimit = bufferEnd - currentLimit;
671 bufferSize -= bufferSizeAfterLimit;
672 } else {
673 bufferSizeAfterLimit = 0;
674 }
675 }
676
677 /// <summary>
678 /// Discards the current limit, returning the previous limit.
679 /// </summary>
680 public void PopLimit(int oldLimit) {
681 currentLimit = oldLimit;
682 RecomputeBufferSizeAfterLimit();
683 }
684
685 /// <summary>
Jon Skeet25a28582009-02-18 16:06:22 +0000686 /// Returns whether or not all the data before the limit has been read.
687 /// </summary>
688 /// <returns></returns>
689 public bool ReachedLimit {
690 get {
691 if (currentLimit == int.MaxValue) {
692 return false;
693 }
694 int currentAbsolutePosition = totalBytesRetired + bufferPos;
695 return currentAbsolutePosition >= currentLimit;
696 }
697 }
Jon Skeet2e6dc122009-05-29 06:34:52 +0100698
699 /// <summary>
700 /// Returns true if the stream has reached the end of the input. This is the
701 /// case if either the end of the underlying input source has been reached or
702 /// the stream has reached a limit created using PushLimit.
703 /// </summary>
704 public bool IsAtEnd {
705 get {
706 return bufferPos == bufferSize && !RefillBuffer(false);
707 }
708 }
Jon Skeet25a28582009-02-18 16:06:22 +0000709
710 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100711 /// Called when buffer is empty to read more bytes from the
712 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
713 /// either there will be at least one byte in the buffer when it returns
714 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
715 /// RefillBuffer() returns false if no more bytes were available.
716 /// </summary>
717 /// <param name="mustSucceed"></param>
718 /// <returns></returns>
719 private bool RefillBuffer(bool mustSucceed) {
720 if (bufferPos < bufferSize) {
721 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
722 }
723
724 if (totalBytesRetired + bufferSize == currentLimit) {
725 // Oops, we hit a limit.
726 if (mustSucceed) {
727 throw InvalidProtocolBufferException.TruncatedMessage();
728 } else {
729 return false;
730 }
731 }
732
733 totalBytesRetired += bufferSize;
734
735 bufferPos = 0;
736 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
Jon Skeet2e6dc122009-05-29 06:34:52 +0100737 if (bufferSize < 0) {
738 throw new InvalidOperationException("Stream.Read returned a negative count");
739 }
Jon Skeet68036862008-10-22 13:30:34 +0100740 if (bufferSize == 0) {
741 if (mustSucceed) {
742 throw InvalidProtocolBufferException.TruncatedMessage();
743 } else {
744 return false;
745 }
746 } else {
747 RecomputeBufferSizeAfterLimit();
748 int totalBytesRead =
749 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
750 if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
751 throw InvalidProtocolBufferException.SizeLimitExceeded();
752 }
753 return true;
754 }
755 }
756
757 /// <summary>
758 /// Read one byte from the input.
759 /// </summary>
760 /// <exception cref="InvalidProtocolBufferException">
Jon Skeet2178b932009-06-25 07:52:07 +0100761 /// the end of the stream or the current limit was reached
Jon Skeet68036862008-10-22 13:30:34 +0100762 /// </exception>
763 public byte ReadRawByte() {
764 if (bufferPos == bufferSize) {
765 RefillBuffer(true);
766 }
767 return buffer[bufferPos++];
768 }
769
770 /// <summary>
771 /// Read a fixed size of bytes from the input.
772 /// </summary>
773 /// <exception cref="InvalidProtocolBufferException">
774 /// the end of the stream or the current limit was reached
775 /// </exception>
776 public byte[] ReadRawBytes(int size) {
777 if (size < 0) {
778 throw InvalidProtocolBufferException.NegativeSize();
779 }
780
781 if (totalBytesRetired + bufferPos + size > currentLimit) {
782 // Read to the end of the stream anyway.
783 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
784 // Then fail.
785 throw InvalidProtocolBufferException.TruncatedMessage();
786 }
787
788 if (size <= bufferSize - bufferPos) {
789 // We have all the bytes we need already.
790 byte[] bytes = new byte[size];
791 Array.Copy(buffer, bufferPos, bytes, 0, size);
792 bufferPos += size;
793 return bytes;
794 } else if (size < BufferSize) {
795 // Reading more bytes than are in the buffer, but not an excessive number
796 // of bytes. We can safely allocate the resulting array ahead of time.
797
798 // First copy what we have.
799 byte[] bytes = new byte[size];
800 int pos = bufferSize - bufferPos;
801 Array.Copy(buffer, bufferPos, bytes, 0, pos);
802 bufferPos = bufferSize;
803
804 // We want to use RefillBuffer() and then copy from the buffer into our
805 // byte array rather than reading directly into our byte array because
806 // the input may be unbuffered.
807 RefillBuffer(true);
808
809 while (size - pos > bufferSize) {
810 Array.Copy(buffer, 0, bytes, pos, bufferSize);
811 pos += bufferSize;
812 bufferPos = bufferSize;
813 RefillBuffer(true);
814 }
815
816 Array.Copy(buffer, 0, bytes, pos, size - pos);
817 bufferPos = size - pos;
818
819 return bytes;
820 } else {
821 // The size is very large. For security reasons, we can't allocate the
822 // entire byte array yet. The size comes directly from the input, so a
823 // maliciously-crafted message could provide a bogus very large size in
824 // order to trick the app into allocating a lot of memory. We avoid this
825 // by allocating and reading only a small chunk at a time, so that the
826 // malicious message must actually *be* extremely large to cause
827 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
828
829 // Remember the buffer markers since we'll have to copy the bytes out of
830 // it later.
831 int originalBufferPos = bufferPos;
832 int originalBufferSize = bufferSize;
833
834 // Mark the current buffer consumed.
835 totalBytesRetired += bufferSize;
836 bufferPos = 0;
837 bufferSize = 0;
838
839 // Read all the rest of the bytes we need.
840 int sizeLeft = size - (originalBufferSize - originalBufferPos);
841 List<byte[]> chunks = new List<byte[]>();
842
843 while (sizeLeft > 0) {
844 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
845 int pos = 0;
846 while (pos < chunk.Length) {
847 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
848 if (n <= 0) {
849 throw InvalidProtocolBufferException.TruncatedMessage();
850 }
851 totalBytesRetired += n;
852 pos += n;
853 }
854 sizeLeft -= chunk.Length;
855 chunks.Add(chunk);
856 }
857
858 // OK, got everything. Now concatenate it all into one buffer.
859 byte[] bytes = new byte[size];
860
861 // Start by copying the leftover bytes from this.buffer.
862 int newPos = originalBufferSize - originalBufferPos;
863 Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
864
865 // And now all the chunks.
866 foreach (byte[] chunk in chunks) {
867 Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
868 newPos += chunk.Length;
869 }
870
871 // Done.
872 return bytes;
873 }
874 }
875
876 /// <summary>
877 /// Reads and discards a single field, given its tag value.
878 /// </summary>
879 /// <returns>false if the tag is an end-group tag, in which case
880 /// nothing is skipped. Otherwise, returns true.</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100881 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100882 public bool SkipField(uint tag) {
883 switch (WireFormat.GetTagWireType(tag)) {
884 case WireFormat.WireType.Varint:
885 ReadInt32();
886 return true;
887 case WireFormat.WireType.Fixed64:
888 ReadRawLittleEndian64();
889 return true;
890 case WireFormat.WireType.LengthDelimited:
891 SkipRawBytes((int) ReadRawVarint32());
892 return true;
893 case WireFormat.WireType.StartGroup:
894 SkipMessage();
895 CheckLastTagWas(
896 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
897 WireFormat.WireType.EndGroup));
898 return true;
899 case WireFormat.WireType.EndGroup:
900 return false;
901 case WireFormat.WireType.Fixed32:
902 ReadRawLittleEndian32();
903 return true;
904 default:
905 throw InvalidProtocolBufferException.InvalidWireType();
906 }
907 }
908
909 /// <summary>
910 /// Reads and discards an entire message. This will read either until EOF
911 /// or until an endgroup tag, whichever comes first.
912 /// </summary>
913 public void SkipMessage() {
914 while (true) {
915 uint tag = ReadTag();
916 if (tag == 0 || !SkipField(tag)) {
917 return;
918 }
919 }
920 }
921
922 /// <summary>
923 /// Reads and discards <paramref name="size"/> bytes.
924 /// </summary>
925 /// <exception cref="InvalidProtocolBufferException">the end of the stream
926 /// or the current limit was reached</exception>
927 public void SkipRawBytes(int size) {
928 if (size < 0) {
929 throw InvalidProtocolBufferException.NegativeSize();
930 }
931
932 if (totalBytesRetired + bufferPos + size > currentLimit) {
933 // Read to the end of the stream anyway.
934 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
935 // Then fail.
936 throw InvalidProtocolBufferException.TruncatedMessage();
937 }
938
Jon Skeet2e6dc122009-05-29 06:34:52 +0100939 if (size <= bufferSize - bufferPos) {
Jon Skeet68036862008-10-22 13:30:34 +0100940 // We have all the bytes we need already.
941 bufferPos += size;
942 } else {
943 // Skipping more bytes than are in the buffer. First skip what we have.
944 int pos = bufferSize - bufferPos;
945 totalBytesRetired += pos;
946 bufferPos = 0;
947 bufferSize = 0;
948
949 // Then skip directly from the InputStream for the rest.
950 if (pos < size) {
Jon Skeet68036862008-10-22 13:30:34 +0100951 if (input == null) {
952 throw InvalidProtocolBufferException.TruncatedMessage();
953 }
Jon Skeetc298c892009-05-30 10:07:09 +0100954 SkipImpl(size - pos);
955 totalBytesRetired += size - pos;
956 }
957 }
958 }
959
960 /// <summary>
961 /// Abstraction of skipping to cope with streams which can't really skip.
962 /// </summary>
963 private void SkipImpl(int amountToSkip) {
964 if (input.CanSeek) {
965 long previousPosition = input.Position;
966 input.Position += amountToSkip;
967 if (input.Position != previousPosition + amountToSkip) {
968 throw InvalidProtocolBufferException.TruncatedMessage();
969 }
970 } else {
971 byte[] skipBuffer = new byte[1024];
972 while (amountToSkip > 0) {
973 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
974 if (bytesRead <= 0) {
Jon Skeet68036862008-10-22 13:30:34 +0100975 throw InvalidProtocolBufferException.TruncatedMessage();
976 }
Jon Skeetc298c892009-05-30 10:07:09 +0100977 amountToSkip -= bytesRead;
Jon Skeet68036862008-10-22 13:30:34 +0100978 }
979 }
980 }
981 #endregion
982 }
983}