blob: e20ad0bbda980970824da706da65fdb46dcc683f [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) {
116 return new CodedInputStream(buf, offset, length);
117 }
118
119 private CodedInputStream(byte[] buffer, int offset, int length) {
120 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>
261 public void ReadGroup(int fieldNumber, IBuilder builder,
262 ExtensionRegistry extensionRegistry) {
263 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>
276 public void ReadUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) {
277 if (recursionDepth >= recursionLimit) {
278 throw InvalidProtocolBufferException.RecursionLimitExceeded();
279 }
280 ++recursionDepth;
281 builder.MergeFrom(this);
282 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
283 --recursionDepth;
284 }
285
286 /// <summary>
287 /// Reads an embedded message field value from the stream.
288 /// </summary>
289 public void ReadMessage(IBuilder builder, ExtensionRegistry extensionRegistry) {
290 int length = (int) ReadRawVarint32();
291 if (recursionDepth >= recursionLimit) {
292 throw InvalidProtocolBufferException.RecursionLimitExceeded();
293 }
294 int oldLimit = PushLimit(length);
295 ++recursionDepth;
296 builder.WeakMergeFrom(this, extensionRegistry);
297 CheckLastTagWas(0);
298 --recursionDepth;
299 PopLimit(oldLimit);
300 }
301
302 /// <summary>
303 /// Reads a bytes field value from the stream.
304 /// </summary>
305 public ByteString ReadBytes() {
306 int size = (int) ReadRawVarint32();
307 if (size < bufferSize - bufferPos && size > 0) {
308 // Fast path: We already have the bytes in a contiguous buffer, so
309 // just copy directly from it.
310 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
311 bufferPos += size;
312 return result;
313 } else {
314 // Slow path: Build a byte array first then copy it.
315 return ByteString.CopyFrom(ReadRawBytes(size));
316 }
317 }
318
319 /// <summary>
320 /// Reads a uint32 field value from the stream.
321 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100322 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100323 public uint ReadUInt32() {
324 return ReadRawVarint32();
325 }
326
327 /// <summary>
328 /// Reads an enum field value from the stream. The caller is responsible
329 /// for converting the numeric value to an actual enum.
330 /// </summary>
331 public int ReadEnum() {
332 return (int) ReadRawVarint32();
333 }
334
335 /// <summary>
336 /// Reads an sfixed32 field value from the stream.
337 /// </summary>
338 public int ReadSFixed32() {
339 return (int) ReadRawLittleEndian32();
340 }
341
342 /// <summary>
343 /// Reads an sfixed64 field value from the stream.
344 /// </summary>
345 public long ReadSFixed64() {
346 return (long) ReadRawLittleEndian64();
347 }
348
349 /// <summary>
350 /// Reads an sint32 field value from the stream.
351 /// </summary>
352 public int ReadSInt32() {
353 return DecodeZigZag32(ReadRawVarint32());
354 }
355
356 /// <summary>
357 /// Reads an sint64 field value from the stream.
358 /// </summary>
359 public long ReadSInt64() {
360 return DecodeZigZag64(ReadRawVarint64());
361 }
362
363 /// <summary>
364 /// Reads a field of any primitive type. Enums, groups and embedded
365 /// messages are not handled by this method.
366 /// </summary>
367 public object ReadPrimitiveField(FieldType fieldType) {
368 switch (fieldType) {
369 case FieldType.Double: return ReadDouble();
370 case FieldType.Float: return ReadFloat();
371 case FieldType.Int64: return ReadInt64();
372 case FieldType.UInt64: return ReadUInt64();
373 case FieldType.Int32: return ReadInt32();
374 case FieldType.Fixed64: return ReadFixed64();
375 case FieldType.Fixed32: return ReadFixed32();
376 case FieldType.Bool: return ReadBool();
377 case FieldType.String: return ReadString();
378 case FieldType.Bytes: return ReadBytes();
379 case FieldType.UInt32: return ReadUInt32();
380 case FieldType.SFixed32: return ReadSFixed32();
381 case FieldType.SFixed64: return ReadSFixed64();
382 case FieldType.SInt32: return ReadSInt32();
383 case FieldType.SInt64: return ReadSInt64();
384 case FieldType.Group:
385 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
386 case FieldType.Message:
387 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
388 // We don't handle enums because we don't know what to do if the
389 // value is not recognized.
390 case FieldType.Enum:
391 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
392 default:
393 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
394 }
395 }
396
397 #endregion
398
399 #region Underlying reading primitives
400
401 /// <summary>
402 /// Same code as ReadRawVarint32, but read each byte individually, checking for
403 /// buffer overflow.
404 /// </summary>
405 private uint SlowReadRawVarint32() {
406 int tmp = ReadRawByte();
407 if (tmp < 128) {
408 return (uint)tmp;
409 }
410 int result = tmp & 0x7f;
411 if ((tmp = ReadRawByte()) < 128) {
412 result |= tmp << 7;
413 } else {
414 result |= (tmp & 0x7f) << 7;
415 if ((tmp = ReadRawByte()) < 128) {
416 result |= tmp << 14;
417 } else {
418 result |= (tmp & 0x7f) << 14;
419 if ((tmp = ReadRawByte()) < 128) {
420 result |= tmp << 21;
421 } else {
422 result |= (tmp & 0x7f) << 21;
423 result |= (tmp = ReadRawByte()) << 28;
424 if (tmp >= 128) {
425 // Discard upper 32 bits.
426 for (int i = 0; i < 5; i++) {
427 if (ReadRawByte() < 128) return (uint)result;
428 }
429 throw InvalidProtocolBufferException.MalformedVarint();
430 }
431 }
432 }
433 }
434 return (uint)result;
435 }
436
437 /// <summary>
438 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
439 /// This method is optimised for the case where we've got lots of data in the buffer.
440 /// That means we can check the size just once, then just read directly from the buffer
441 /// without constant rechecking of the buffer length.
442 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100443 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100444 public uint ReadRawVarint32() {
445 if (bufferPos + 5 > bufferSize) {
446 return SlowReadRawVarint32();
447 }
448
449 int tmp = buffer[bufferPos++];
450 if (tmp < 128) {
451 return (uint)tmp;
452 }
453 int result = tmp & 0x7f;
454 if ((tmp = buffer[bufferPos++]) < 128) {
455 result |= tmp << 7;
456 } else {
457 result |= (tmp & 0x7f) << 7;
458 if ((tmp = buffer[bufferPos++]) < 128) {
459 result |= tmp << 14;
460 } else {
461 result |= (tmp & 0x7f) << 14;
462 if ((tmp = buffer[bufferPos++]) < 128) {
463 result |= tmp << 21;
464 } else {
465 result |= (tmp & 0x7f) << 21;
466 result |= (tmp = buffer[bufferPos++]) << 28;
467 if (tmp >= 128) {
468 // Discard upper 32 bits.
469 // Note that this has to use ReadRawByte() as we only ensure we've
470 // got at least 5 bytes at the start of the method. This lets us
471 // use the fast path in more cases, and we rarely hit this section of code.
472 for (int i = 0; i < 5; i++) {
473 if (ReadRawByte() < 128) return (uint)result;
474 }
475 throw InvalidProtocolBufferException.MalformedVarint();
476 }
477 }
478 }
479 }
480 return (uint)result;
481 }
482
483 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100484 /// Reads a varint from the input one byte at a time, so that it does not
485 /// read any bytes after the end of the varint. If you simply wrapped the
486 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
487 /// then you would probably end up reading past the end of the varint since
488 /// CodedInputStream buffers its input.
489 /// </summary>
490 /// <param name="input"></param>
491 /// <returns></returns>
Jon Skeetc298c892009-05-30 10:07:09 +0100492 internal static uint ReadRawVarint32(Stream input) {
Jon Skeet2e6dc122009-05-29 06:34:52 +0100493 int result = 0;
494 int offset = 0;
495 for (; offset < 32; offset += 7) {
496 int b = input.ReadByte();
497 if (b == -1) {
498 throw InvalidProtocolBufferException.TruncatedMessage();
499 }
500 result |= (b & 0x7f) << offset;
501 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100502 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100503 }
504 }
505 // Keep reading up to 64 bits.
506 for (; offset < 64; offset += 7) {
507 int b = input.ReadByte();
508 if (b == -1) {
509 throw InvalidProtocolBufferException.TruncatedMessage();
510 }
511 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100512 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100513 }
514 }
515 throw InvalidProtocolBufferException.MalformedVarint();
516 }
517
518 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100519 /// Read a raw varint from the stream.
520 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100521 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100522 public ulong ReadRawVarint64() {
523 int shift = 0;
524 ulong result = 0;
525 while (shift < 64) {
526 byte b = ReadRawByte();
527 result |= (ulong)(b & 0x7F) << shift;
528 if ((b & 0x80) == 0) {
529 return result;
530 }
531 shift += 7;
532 }
533 throw InvalidProtocolBufferException.MalformedVarint();
534 }
535
536 /// <summary>
537 /// Read a 32-bit little-endian integer from the stream.
538 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100539 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100540 public uint ReadRawLittleEndian32() {
541 uint b1 = ReadRawByte();
542 uint b2 = ReadRawByte();
543 uint b3 = ReadRawByte();
544 uint b4 = ReadRawByte();
545 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
546 }
547
548 /// <summary>
549 /// Read a 64-bit little-endian integer from the stream.
550 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100551 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100552 public ulong ReadRawLittleEndian64() {
553 ulong b1 = ReadRawByte();
554 ulong b2 = ReadRawByte();
555 ulong b3 = ReadRawByte();
556 ulong b4 = ReadRawByte();
557 ulong b5 = ReadRawByte();
558 ulong b6 = ReadRawByte();
559 ulong b7 = ReadRawByte();
560 ulong b8 = ReadRawByte();
561 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
562 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
563 }
564 #endregion
565
566 /// <summary>
567 /// Decode a 32-bit value with ZigZag encoding.
568 /// </summary>
569 /// <remarks>
570 /// ZigZag encodes signed integers into values that can be efficiently
571 /// encoded with varint. (Otherwise, negative values must be
572 /// sign-extended to 64 bits to be varint encoded, thus always taking
573 /// 10 bytes on the wire.)
574 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100575 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100576 public static int DecodeZigZag32(uint n) {
577 return (int)(n >> 1) ^ -(int)(n & 1);
578 }
579
580 /// <summary>
581 /// Decode a 32-bit value with ZigZag encoding.
582 /// </summary>
583 /// <remarks>
584 /// ZigZag encodes signed integers into values that can be efficiently
585 /// encoded with varint. (Otherwise, negative values must be
586 /// sign-extended to 64 bits to be varint encoded, thus always taking
587 /// 10 bytes on the wire.)
588 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100589 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100590 public static long DecodeZigZag64(ulong n) {
591 return (long)(n >> 1) ^ -(long)(n & 1);
592 }
593
594 /// <summary>
595 /// Set the maximum message recursion depth.
596 /// </summary>
597 /// <remarks>
598 /// In order to prevent malicious
599 /// messages from causing stack overflows, CodedInputStream limits
600 /// how deeply messages may be nested. The default limit is 64.
601 /// </remarks>
602 public int SetRecursionLimit(int limit) {
603 if (limit < 0) {
604 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
605 }
606 int oldLimit = recursionLimit;
607 recursionLimit = limit;
608 return oldLimit;
609 }
610
611 /// <summary>
612 /// Set the maximum message size.
613 /// </summary>
614 /// <remarks>
615 /// In order to prevent malicious messages from exhausting memory or
616 /// causing integer overflows, CodedInputStream limits how large a message may be.
617 /// The default limit is 64MB. You should set this limit as small
618 /// as you can without harming your app's functionality. Note that
619 /// size limits only apply when reading from an InputStream, not
620 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
Jon Skeet2e6dc122009-05-29 06:34:52 +0100621 /// If you want to read several messages from a single CodedInputStream, you
622 /// can call ResetSizeCounter() after each message to avoid hitting the
623 /// size limit.
Jon Skeet68036862008-10-22 13:30:34 +0100624 /// </remarks>
625 public int SetSizeLimit(int limit) {
626 if (limit < 0) {
627 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
628 }
629 int oldLimit = sizeLimit;
630 sizeLimit = limit;
631 return oldLimit;
632 }
633
634 #region Internal reading and buffer management
635 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100636 /// Resets the current size counter to zero (see SetSizeLimit).
637 /// </summary>
638 public void ResetSizeCounter() {
639 totalBytesRetired = 0;
640 }
641
642 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100643 /// Sets currentLimit to (current position) + byteLimit. This is called
644 /// when descending into a length-delimited embedded message. The previous
645 /// limit is returned.
646 /// </summary>
647 /// <returns>The old limit.</returns>
648 public int PushLimit(int byteLimit) {
649 if (byteLimit < 0) {
650 throw InvalidProtocolBufferException.NegativeSize();
651 }
652 byteLimit += totalBytesRetired + bufferPos;
653 int oldLimit = currentLimit;
654 if (byteLimit > oldLimit) {
655 throw InvalidProtocolBufferException.TruncatedMessage();
656 }
657 currentLimit = byteLimit;
658
659 RecomputeBufferSizeAfterLimit();
660
661 return oldLimit;
662 }
663
664 private void RecomputeBufferSizeAfterLimit() {
665 bufferSize += bufferSizeAfterLimit;
666 int bufferEnd = totalBytesRetired + bufferSize;
667 if (bufferEnd > currentLimit) {
668 // Limit is in current buffer.
669 bufferSizeAfterLimit = bufferEnd - currentLimit;
670 bufferSize -= bufferSizeAfterLimit;
671 } else {
672 bufferSizeAfterLimit = 0;
673 }
674 }
675
676 /// <summary>
677 /// Discards the current limit, returning the previous limit.
678 /// </summary>
679 public void PopLimit(int oldLimit) {
680 currentLimit = oldLimit;
681 RecomputeBufferSizeAfterLimit();
682 }
683
684 /// <summary>
Jon Skeet25a28582009-02-18 16:06:22 +0000685 /// Returns whether or not all the data before the limit has been read.
686 /// </summary>
687 /// <returns></returns>
688 public bool ReachedLimit {
689 get {
690 if (currentLimit == int.MaxValue) {
691 return false;
692 }
693 int currentAbsolutePosition = totalBytesRetired + bufferPos;
694 return currentAbsolutePosition >= currentLimit;
695 }
696 }
Jon Skeet2e6dc122009-05-29 06:34:52 +0100697
698 /// <summary>
699 /// Returns true if the stream has reached the end of the input. This is the
700 /// case if either the end of the underlying input source has been reached or
701 /// the stream has reached a limit created using PushLimit.
702 /// </summary>
703 public bool IsAtEnd {
704 get {
705 return bufferPos == bufferSize && !RefillBuffer(false);
706 }
707 }
Jon Skeet25a28582009-02-18 16:06:22 +0000708
709 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100710 /// Called when buffer is empty to read more bytes from the
711 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
712 /// either there will be at least one byte in the buffer when it returns
713 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
714 /// RefillBuffer() returns false if no more bytes were available.
715 /// </summary>
716 /// <param name="mustSucceed"></param>
717 /// <returns></returns>
718 private bool RefillBuffer(bool mustSucceed) {
719 if (bufferPos < bufferSize) {
720 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
721 }
722
723 if (totalBytesRetired + bufferSize == currentLimit) {
724 // Oops, we hit a limit.
725 if (mustSucceed) {
726 throw InvalidProtocolBufferException.TruncatedMessage();
727 } else {
728 return false;
729 }
730 }
731
732 totalBytesRetired += bufferSize;
733
734 bufferPos = 0;
735 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
Jon Skeet2e6dc122009-05-29 06:34:52 +0100736 if (bufferSize < 0) {
737 throw new InvalidOperationException("Stream.Read returned a negative count");
738 }
Jon Skeet68036862008-10-22 13:30:34 +0100739 if (bufferSize == 0) {
740 if (mustSucceed) {
741 throw InvalidProtocolBufferException.TruncatedMessage();
742 } else {
743 return false;
744 }
745 } else {
746 RecomputeBufferSizeAfterLimit();
747 int totalBytesRead =
748 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
749 if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
750 throw InvalidProtocolBufferException.SizeLimitExceeded();
751 }
752 return true;
753 }
754 }
755
756 /// <summary>
757 /// Read one byte from the input.
758 /// </summary>
759 /// <exception cref="InvalidProtocolBufferException">
Jon Skeet2178b932009-06-25 07:52:07 +0100760 /// the end of the stream or the current limit was reached
Jon Skeet68036862008-10-22 13:30:34 +0100761 /// </exception>
762 public byte ReadRawByte() {
763 if (bufferPos == bufferSize) {
764 RefillBuffer(true);
765 }
766 return buffer[bufferPos++];
767 }
768
769 /// <summary>
770 /// Read a fixed size of bytes from the input.
771 /// </summary>
772 /// <exception cref="InvalidProtocolBufferException">
773 /// the end of the stream or the current limit was reached
774 /// </exception>
775 public byte[] ReadRawBytes(int size) {
776 if (size < 0) {
777 throw InvalidProtocolBufferException.NegativeSize();
778 }
779
780 if (totalBytesRetired + bufferPos + size > currentLimit) {
781 // Read to the end of the stream anyway.
782 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
783 // Then fail.
784 throw InvalidProtocolBufferException.TruncatedMessage();
785 }
786
787 if (size <= bufferSize - bufferPos) {
788 // We have all the bytes we need already.
789 byte[] bytes = new byte[size];
790 Array.Copy(buffer, bufferPos, bytes, 0, size);
791 bufferPos += size;
792 return bytes;
793 } else if (size < BufferSize) {
794 // Reading more bytes than are in the buffer, but not an excessive number
795 // of bytes. We can safely allocate the resulting array ahead of time.
796
797 // First copy what we have.
798 byte[] bytes = new byte[size];
799 int pos = bufferSize - bufferPos;
800 Array.Copy(buffer, bufferPos, bytes, 0, pos);
801 bufferPos = bufferSize;
802
803 // We want to use RefillBuffer() and then copy from the buffer into our
804 // byte array rather than reading directly into our byte array because
805 // the input may be unbuffered.
806 RefillBuffer(true);
807
808 while (size - pos > bufferSize) {
809 Array.Copy(buffer, 0, bytes, pos, bufferSize);
810 pos += bufferSize;
811 bufferPos = bufferSize;
812 RefillBuffer(true);
813 }
814
815 Array.Copy(buffer, 0, bytes, pos, size - pos);
816 bufferPos = size - pos;
817
818 return bytes;
819 } else {
820 // The size is very large. For security reasons, we can't allocate the
821 // entire byte array yet. The size comes directly from the input, so a
822 // maliciously-crafted message could provide a bogus very large size in
823 // order to trick the app into allocating a lot of memory. We avoid this
824 // by allocating and reading only a small chunk at a time, so that the
825 // malicious message must actually *be* extremely large to cause
826 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
827
828 // Remember the buffer markers since we'll have to copy the bytes out of
829 // it later.
830 int originalBufferPos = bufferPos;
831 int originalBufferSize = bufferSize;
832
833 // Mark the current buffer consumed.
834 totalBytesRetired += bufferSize;
835 bufferPos = 0;
836 bufferSize = 0;
837
838 // Read all the rest of the bytes we need.
839 int sizeLeft = size - (originalBufferSize - originalBufferPos);
840 List<byte[]> chunks = new List<byte[]>();
841
842 while (sizeLeft > 0) {
843 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
844 int pos = 0;
845 while (pos < chunk.Length) {
846 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
847 if (n <= 0) {
848 throw InvalidProtocolBufferException.TruncatedMessage();
849 }
850 totalBytesRetired += n;
851 pos += n;
852 }
853 sizeLeft -= chunk.Length;
854 chunks.Add(chunk);
855 }
856
857 // OK, got everything. Now concatenate it all into one buffer.
858 byte[] bytes = new byte[size];
859
860 // Start by copying the leftover bytes from this.buffer.
861 int newPos = originalBufferSize - originalBufferPos;
862 Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
863
864 // And now all the chunks.
865 foreach (byte[] chunk in chunks) {
866 Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
867 newPos += chunk.Length;
868 }
869
870 // Done.
871 return bytes;
872 }
873 }
874
875 /// <summary>
876 /// Reads and discards a single field, given its tag value.
877 /// </summary>
878 /// <returns>false if the tag is an end-group tag, in which case
879 /// nothing is skipped. Otherwise, returns true.</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100880 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100881 public bool SkipField(uint tag) {
882 switch (WireFormat.GetTagWireType(tag)) {
883 case WireFormat.WireType.Varint:
884 ReadInt32();
885 return true;
886 case WireFormat.WireType.Fixed64:
887 ReadRawLittleEndian64();
888 return true;
889 case WireFormat.WireType.LengthDelimited:
890 SkipRawBytes((int) ReadRawVarint32());
891 return true;
892 case WireFormat.WireType.StartGroup:
893 SkipMessage();
894 CheckLastTagWas(
895 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
896 WireFormat.WireType.EndGroup));
897 return true;
898 case WireFormat.WireType.EndGroup:
899 return false;
900 case WireFormat.WireType.Fixed32:
901 ReadRawLittleEndian32();
902 return true;
903 default:
904 throw InvalidProtocolBufferException.InvalidWireType();
905 }
906 }
907
908 /// <summary>
909 /// Reads and discards an entire message. This will read either until EOF
910 /// or until an endgroup tag, whichever comes first.
911 /// </summary>
912 public void SkipMessage() {
913 while (true) {
914 uint tag = ReadTag();
915 if (tag == 0 || !SkipField(tag)) {
916 return;
917 }
918 }
919 }
920
921 /// <summary>
922 /// Reads and discards <paramref name="size"/> bytes.
923 /// </summary>
924 /// <exception cref="InvalidProtocolBufferException">the end of the stream
925 /// or the current limit was reached</exception>
926 public void SkipRawBytes(int size) {
927 if (size < 0) {
928 throw InvalidProtocolBufferException.NegativeSize();
929 }
930
931 if (totalBytesRetired + bufferPos + size > currentLimit) {
932 // Read to the end of the stream anyway.
933 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
934 // Then fail.
935 throw InvalidProtocolBufferException.TruncatedMessage();
936 }
937
Jon Skeet2e6dc122009-05-29 06:34:52 +0100938 if (size <= bufferSize - bufferPos) {
Jon Skeet68036862008-10-22 13:30:34 +0100939 // We have all the bytes we need already.
940 bufferPos += size;
941 } else {
942 // Skipping more bytes than are in the buffer. First skip what we have.
943 int pos = bufferSize - bufferPos;
944 totalBytesRetired += pos;
945 bufferPos = 0;
946 bufferSize = 0;
947
948 // Then skip directly from the InputStream for the rest.
949 if (pos < size) {
Jon Skeet68036862008-10-22 13:30:34 +0100950 if (input == null) {
951 throw InvalidProtocolBufferException.TruncatedMessage();
952 }
Jon Skeetc298c892009-05-30 10:07:09 +0100953 SkipImpl(size - pos);
954 totalBytesRetired += size - pos;
955 }
956 }
957 }
958
959 /// <summary>
960 /// Abstraction of skipping to cope with streams which can't really skip.
961 /// </summary>
962 private void SkipImpl(int amountToSkip) {
963 if (input.CanSeek) {
964 long previousPosition = input.Position;
965 input.Position += amountToSkip;
966 if (input.Position != previousPosition + amountToSkip) {
967 throw InvalidProtocolBufferException.TruncatedMessage();
968 }
969 } else {
970 byte[] skipBuffer = new byte[1024];
971 while (amountToSkip > 0) {
972 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
973 if (bytesRead <= 0) {
Jon Skeet68036862008-10-22 13:30:34 +0100974 throw InvalidProtocolBufferException.TruncatedMessage();
975 }
Jon Skeetc298c892009-05-30 10:07:09 +0100976 amountToSkip -= bytesRead;
Jon Skeet68036862008-10-22 13:30:34 +0100977 }
978 }
979 }
980 #endregion
981 }
982}