blob: 9173423cb09c73614ec0d6932127a43135b29a11 [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;
csharptestd9c59e62010-11-04 19:36:28 -050039#if !LITE
Jon Skeet68036862008-10-22 13:30:34 +010040using Google.ProtocolBuffers.Descriptors;
csharptestd9c59e62010-11-04 19:36:28 -050041#endif
Jon Skeet68036862008-10-22 13:30:34 +010042
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>
61 public sealed class CodedInputStream {
62 private readonly byte[] buffer;
63 private int bufferSize;
64 private int bufferSizeAfterLimit = 0;
65 private int bufferPos = 0;
66 private readonly Stream input;
67 private uint lastTag = 0;
68
Jon Skeet2178b932009-06-25 07:52:07 +010069 internal const int DefaultRecursionLimit = 64;
70 internal const int DefaultSizeLimit = 64 << 20; // 64MB
71 internal const int BufferSize = 4096;
Jon Skeet68036862008-10-22 13:30:34 +010072
73 /// <summary>
74 /// The total number of bytes read before the current buffer. The
75 /// total bytes read up to the current position can be computed as
76 /// totalBytesRetired + bufferPos.
77 /// </summary>
78 private int totalBytesRetired = 0;
79
80 /// <summary>
81 /// The absolute position of the end of the current message.
82 /// </summary>
83 private int currentLimit = int.MaxValue;
84
85 /// <summary>
86 /// <see cref="SetRecursionLimit"/>
87 /// </summary>
88 private int recursionDepth = 0;
89 private int recursionLimit = DefaultRecursionLimit;
90
91 /// <summary>
92 /// <see cref="SetSizeLimit"/>
93 /// </summary>
94 private int sizeLimit = DefaultSizeLimit;
95
96 #region Construction
97 /// <summary>
98 /// Creates a new CodedInputStream reading data from the given
99 /// stream.
100 /// </summary>
101 public static CodedInputStream CreateInstance(Stream input) {
102 return new CodedInputStream(input);
103 }
104
105 /// <summary>
106 /// Creates a new CodedInputStream reading data from the given
107 /// byte array.
108 /// </summary>
109 public static CodedInputStream CreateInstance(byte[] buf) {
Charles Stanhopeef234da2010-10-19 05:37:44 -0700110 return new CodedInputStream(buf, 0, buf.Length);
Jon Skeet68036862008-10-22 13:30:34 +0100111 }
112
Charles Stanhopeef234da2010-10-19 05:37:44 -0700113 /// <summary>
114 /// Creates a new CodedInputStream that reads from the given
115 /// byte array slice.
116 /// </summary>
117 public static CodedInputStream CreateInstance(byte[] buf, int offset, int length) {
Jon Skeetf18a5b02010-10-26 19:45:03 +0100118 return new CodedInputStream(buf, offset, length);
Charles Stanhopeef234da2010-10-19 05:37:44 -0700119 }
120
121 private CodedInputStream(byte[] buffer, int offset, int length) {
Jon Skeetf18a5b02010-10-26 19:45:03 +0100122 this.buffer = buffer;
123 this.bufferPos = offset;
124 this.bufferSize = offset + length;
125 this.input = null;
Jon Skeet68036862008-10-22 13:30:34 +0100126 }
127
128 private CodedInputStream(Stream input) {
129 this.buffer = new byte[BufferSize];
130 this.bufferSize = 0;
131 this.input = input;
132 }
133 #endregion
134
135 #region Validation
136 /// <summary>
137 /// Verifies that the last call to ReadTag() returned the given tag value.
138 /// This is used to verify that a nested group ended with the correct
139 /// end tag.
140 /// </summary>
141 /// <exception cref="InvalidProtocolBufferException">The last
142 /// tag read was not the one specified</exception>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100143 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100144 public void CheckLastTagWas(uint value) {
145 if (lastTag != value) {
146 throw InvalidProtocolBufferException.InvalidEndTag();
147 }
148 }
149 #endregion
150
151 #region Reading of tags etc
152 /// <summary>
153 /// Attempt to read a field tag, returning 0 if we have reached the end
154 /// of the input data. Protocol message parsers use this to read tags,
155 /// since a protocol message may legally end wherever a tag occurs, and
156 /// zero is not a valid tag number.
157 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100158 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100159 public uint ReadTag() {
Jon Skeet2e6dc122009-05-29 06:34:52 +0100160 if (IsAtEnd) {
Jon Skeet68036862008-10-22 13:30:34 +0100161 lastTag = 0;
162 return 0;
163 }
164
165 lastTag = ReadRawVarint32();
166 if (lastTag == 0) {
167 // If we actually read zero, that's not a valid tag.
168 throw InvalidProtocolBufferException.InvalidTag();
169 }
170 return lastTag;
171 }
172
173 /// <summary>
174 /// Read a double field from the stream.
175 /// </summary>
176 public double ReadDouble() {
Jon Skeetb49d3c72009-11-03 16:51:01 +0000177#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
Jon Skeet3c808862009-09-09 13:22:36 +0100178 byte[] bytes = ReadRawBytes(8);
179 return BitConverter.ToDouble(bytes, 0);
180#else
Jon Skeet68036862008-10-22 13:30:34 +0100181 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
Jon Skeet3c808862009-09-09 13:22:36 +0100182#endif
Jon Skeet68036862008-10-22 13:30:34 +0100183 }
184
185 /// <summary>
186 /// Read a float field from the stream.
187 /// </summary>
188 public float ReadFloat() {
189 // TODO(jonskeet): Test this on different endiannesses
190 uint raw = ReadRawLittleEndian32();
191 byte[] rawBytes = BitConverter.GetBytes(raw);
192 return BitConverter.ToSingle(rawBytes, 0);
193 }
194
195 /// <summary>
196 /// Read a uint64 field from the stream.
197 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100198 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100199 public ulong ReadUInt64() {
200 return ReadRawVarint64();
201 }
202
203 /// <summary>
204 /// Read an int64 field from the stream.
205 /// </summary>
206 public long ReadInt64() {
207 return (long) ReadRawVarint64();
208 }
209
210 /// <summary>
211 /// Read an int32 field from the stream.
212 /// </summary>
213 public int ReadInt32() {
214 return (int) ReadRawVarint32();
215 }
216
217 /// <summary>
218 /// Read a fixed64 field from the stream.
219 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100220 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100221 public ulong ReadFixed64() {
222 return ReadRawLittleEndian64();
223 }
224
225 /// <summary>
226 /// Read a fixed32 field from the stream.
227 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100228 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100229 public uint ReadFixed32() {
230 return ReadRawLittleEndian32();
231 }
232
233 /// <summary>
234 /// Read a bool field from the stream.
235 /// </summary>
236 public bool ReadBool() {
237 return ReadRawVarint32() != 0;
238 }
239
240 /// <summary>
241 /// Reads a string field from the stream.
242 /// </summary>
243 public String ReadString() {
244 int size = (int) ReadRawVarint32();
Jon Skeet6a60ac32009-01-27 14:47:35 +0000245 // No need to read any data for an empty string.
246 if (size == 0) {
247 return "";
248 }
249 if (size <= bufferSize - bufferPos) {
Jon Skeet68036862008-10-22 13:30:34 +0100250 // Fast path: We already have the bytes in a contiguous buffer, so
251 // just copy directly from it.
252 String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
253 bufferPos += size;
254 return result;
Jon Skeet68036862008-10-22 13:30:34 +0100255 }
Jon Skeet60fb63e2009-06-20 20:46:28 +0100256 // Slow path: Build a byte array first then copy it.
257 return Encoding.UTF8.GetString(ReadRawBytes(size), 0, size);
Jon Skeet68036862008-10-22 13:30:34 +0100258 }
259
260 /// <summary>
261 /// Reads a group field value from the stream.
262 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500263 public void ReadGroup(int fieldNumber, IBuilderLite builder,
264 ExtensionRegistryLite extensionRegistry) {
Jon Skeet68036862008-10-22 13:30:34 +0100265 if (recursionDepth >= recursionLimit) {
266 throw InvalidProtocolBufferException.RecursionLimitExceeded();
267 }
268 ++recursionDepth;
269 builder.WeakMergeFrom(this, extensionRegistry);
270 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
271 --recursionDepth;
272 }
273
274 /// <summary>
275 /// Reads a group field value from the stream and merges it into the given
276 /// UnknownFieldSet.
277 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500278 [Obsolete]
279 public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
280 {
Jon Skeet68036862008-10-22 13:30:34 +0100281 if (recursionDepth >= recursionLimit) {
282 throw InvalidProtocolBufferException.RecursionLimitExceeded();
283 }
284 ++recursionDepth;
csharptestd9c59e62010-11-04 19:36:28 -0500285 builder.WeakMergeFrom(this);
Jon Skeet68036862008-10-22 13:30:34 +0100286 CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
287 --recursionDepth;
288 }
289
290 /// <summary>
291 /// Reads an embedded message field value from the stream.
292 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500293 public void ReadMessage(IBuilderLite builder, ExtensionRegistryLite extensionRegistry) {
Jon Skeet68036862008-10-22 13:30:34 +0100294 int length = (int) ReadRawVarint32();
295 if (recursionDepth >= recursionLimit) {
296 throw InvalidProtocolBufferException.RecursionLimitExceeded();
297 }
298 int oldLimit = PushLimit(length);
299 ++recursionDepth;
300 builder.WeakMergeFrom(this, extensionRegistry);
301 CheckLastTagWas(0);
302 --recursionDepth;
303 PopLimit(oldLimit);
304 }
305
306 /// <summary>
307 /// Reads a bytes field value from the stream.
308 /// </summary>
309 public ByteString ReadBytes() {
310 int size = (int) ReadRawVarint32();
311 if (size < bufferSize - bufferPos && size > 0) {
312 // Fast path: We already have the bytes in a contiguous buffer, so
313 // just copy directly from it.
314 ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
315 bufferPos += size;
316 return result;
317 } else {
318 // Slow path: Build a byte array first then copy it.
319 return ByteString.CopyFrom(ReadRawBytes(size));
320 }
321 }
322
323 /// <summary>
324 /// Reads a uint32 field value from the stream.
325 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100326 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100327 public uint ReadUInt32() {
328 return ReadRawVarint32();
329 }
330
331 /// <summary>
332 /// Reads an enum field value from the stream. The caller is responsible
333 /// for converting the numeric value to an actual enum.
334 /// </summary>
335 public int ReadEnum() {
336 return (int) ReadRawVarint32();
337 }
338
339 /// <summary>
340 /// Reads an sfixed32 field value from the stream.
341 /// </summary>
342 public int ReadSFixed32() {
343 return (int) ReadRawLittleEndian32();
344 }
345
346 /// <summary>
347 /// Reads an sfixed64 field value from the stream.
348 /// </summary>
349 public long ReadSFixed64() {
350 return (long) ReadRawLittleEndian64();
351 }
352
353 /// <summary>
354 /// Reads an sint32 field value from the stream.
355 /// </summary>
356 public int ReadSInt32() {
357 return DecodeZigZag32(ReadRawVarint32());
358 }
359
360 /// <summary>
361 /// Reads an sint64 field value from the stream.
362 /// </summary>
363 public long ReadSInt64() {
364 return DecodeZigZag64(ReadRawVarint64());
365 }
csharptestd9c59e62010-11-04 19:36:28 -0500366#if !LITE
Jon Skeet68036862008-10-22 13:30:34 +0100367 /// <summary>
368 /// Reads a field of any primitive type. Enums, groups and embedded
369 /// messages are not handled by this method.
370 /// </summary>
371 public object ReadPrimitiveField(FieldType fieldType) {
372 switch (fieldType) {
373 case FieldType.Double: return ReadDouble();
374 case FieldType.Float: return ReadFloat();
375 case FieldType.Int64: return ReadInt64();
376 case FieldType.UInt64: return ReadUInt64();
377 case FieldType.Int32: return ReadInt32();
378 case FieldType.Fixed64: return ReadFixed64();
379 case FieldType.Fixed32: return ReadFixed32();
380 case FieldType.Bool: return ReadBool();
381 case FieldType.String: return ReadString();
382 case FieldType.Bytes: return ReadBytes();
383 case FieldType.UInt32: return ReadUInt32();
384 case FieldType.SFixed32: return ReadSFixed32();
385 case FieldType.SFixed64: return ReadSFixed64();
386 case FieldType.SInt32: return ReadSInt32();
387 case FieldType.SInt64: return ReadSInt64();
388 case FieldType.Group:
389 throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
390 case FieldType.Message:
391 throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
392 // We don't handle enums because we don't know what to do if the
393 // value is not recognized.
394 case FieldType.Enum:
395 throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
396 default:
397 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
398 }
399 }
csharptestd9c59e62010-11-04 19:36:28 -0500400#endif
Jon Skeet68036862008-10-22 13:30:34 +0100401 #endregion
402
403 #region Underlying reading primitives
404
405 /// <summary>
406 /// Same code as ReadRawVarint32, but read each byte individually, checking for
407 /// buffer overflow.
408 /// </summary>
409 private uint SlowReadRawVarint32() {
410 int tmp = ReadRawByte();
411 if (tmp < 128) {
412 return (uint)tmp;
413 }
414 int result = tmp & 0x7f;
415 if ((tmp = ReadRawByte()) < 128) {
416 result |= tmp << 7;
417 } else {
418 result |= (tmp & 0x7f) << 7;
419 if ((tmp = ReadRawByte()) < 128) {
420 result |= tmp << 14;
421 } else {
422 result |= (tmp & 0x7f) << 14;
423 if ((tmp = ReadRawByte()) < 128) {
424 result |= tmp << 21;
425 } else {
426 result |= (tmp & 0x7f) << 21;
427 result |= (tmp = ReadRawByte()) << 28;
428 if (tmp >= 128) {
429 // Discard upper 32 bits.
430 for (int i = 0; i < 5; i++) {
431 if (ReadRawByte() < 128) return (uint)result;
432 }
433 throw InvalidProtocolBufferException.MalformedVarint();
434 }
435 }
436 }
437 }
438 return (uint)result;
439 }
440
441 /// <summary>
442 /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
443 /// This method is optimised for the case where we've got lots of data in the buffer.
444 /// That means we can check the size just once, then just read directly from the buffer
445 /// without constant rechecking of the buffer length.
446 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100447 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100448 public uint ReadRawVarint32() {
449 if (bufferPos + 5 > bufferSize) {
450 return SlowReadRawVarint32();
451 }
452
453 int tmp = buffer[bufferPos++];
454 if (tmp < 128) {
455 return (uint)tmp;
456 }
457 int result = tmp & 0x7f;
458 if ((tmp = buffer[bufferPos++]) < 128) {
459 result |= tmp << 7;
460 } else {
461 result |= (tmp & 0x7f) << 7;
462 if ((tmp = buffer[bufferPos++]) < 128) {
463 result |= tmp << 14;
464 } else {
465 result |= (tmp & 0x7f) << 14;
466 if ((tmp = buffer[bufferPos++]) < 128) {
467 result |= tmp << 21;
468 } else {
469 result |= (tmp & 0x7f) << 21;
470 result |= (tmp = buffer[bufferPos++]) << 28;
471 if (tmp >= 128) {
472 // Discard upper 32 bits.
473 // Note that this has to use ReadRawByte() as we only ensure we've
474 // got at least 5 bytes at the start of the method. This lets us
475 // use the fast path in more cases, and we rarely hit this section of code.
476 for (int i = 0; i < 5; i++) {
477 if (ReadRawByte() < 128) return (uint)result;
478 }
479 throw InvalidProtocolBufferException.MalformedVarint();
480 }
481 }
482 }
483 }
484 return (uint)result;
485 }
486
487 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100488 /// Reads a varint from the input one byte at a time, so that it does not
489 /// read any bytes after the end of the varint. If you simply wrapped the
490 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
491 /// then you would probably end up reading past the end of the varint since
492 /// CodedInputStream buffers its input.
493 /// </summary>
494 /// <param name="input"></param>
495 /// <returns></returns>
Jon Skeetc298c892009-05-30 10:07:09 +0100496 internal static uint ReadRawVarint32(Stream input) {
Jon Skeet2e6dc122009-05-29 06:34:52 +0100497 int result = 0;
498 int offset = 0;
499 for (; offset < 32; offset += 7) {
500 int b = input.ReadByte();
501 if (b == -1) {
502 throw InvalidProtocolBufferException.TruncatedMessage();
503 }
504 result |= (b & 0x7f) << offset;
505 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100506 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100507 }
508 }
509 // Keep reading up to 64 bits.
510 for (; offset < 64; offset += 7) {
511 int b = input.ReadByte();
512 if (b == -1) {
513 throw InvalidProtocolBufferException.TruncatedMessage();
514 }
515 if ((b & 0x80) == 0) {
Jon Skeetc298c892009-05-30 10:07:09 +0100516 return (uint) result;
Jon Skeet2e6dc122009-05-29 06:34:52 +0100517 }
518 }
519 throw InvalidProtocolBufferException.MalformedVarint();
520 }
521
522 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100523 /// Read a raw varint from the stream.
524 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100525 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100526 public ulong ReadRawVarint64() {
527 int shift = 0;
528 ulong result = 0;
529 while (shift < 64) {
530 byte b = ReadRawByte();
531 result |= (ulong)(b & 0x7F) << shift;
532 if ((b & 0x80) == 0) {
533 return result;
534 }
535 shift += 7;
536 }
537 throw InvalidProtocolBufferException.MalformedVarint();
538 }
539
540 /// <summary>
541 /// Read a 32-bit little-endian integer from the stream.
542 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100543 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100544 public uint ReadRawLittleEndian32() {
545 uint b1 = ReadRawByte();
546 uint b2 = ReadRawByte();
547 uint b3 = ReadRawByte();
548 uint b4 = ReadRawByte();
549 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
550 }
551
552 /// <summary>
553 /// Read a 64-bit little-endian integer from the stream.
554 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100555 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100556 public ulong ReadRawLittleEndian64() {
557 ulong b1 = ReadRawByte();
558 ulong b2 = ReadRawByte();
559 ulong b3 = ReadRawByte();
560 ulong b4 = ReadRawByte();
561 ulong b5 = ReadRawByte();
562 ulong b6 = ReadRawByte();
563 ulong b7 = ReadRawByte();
564 ulong b8 = ReadRawByte();
565 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
566 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
567 }
568 #endregion
569
570 /// <summary>
571 /// Decode a 32-bit value with ZigZag encoding.
572 /// </summary>
573 /// <remarks>
574 /// ZigZag encodes signed integers into values that can be efficiently
575 /// encoded with varint. (Otherwise, negative values must be
576 /// sign-extended to 64 bits to be varint encoded, thus always taking
577 /// 10 bytes on the wire.)
578 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100579 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100580 public static int DecodeZigZag32(uint n) {
581 return (int)(n >> 1) ^ -(int)(n & 1);
582 }
583
584 /// <summary>
585 /// Decode a 32-bit value with ZigZag encoding.
586 /// </summary>
587 /// <remarks>
588 /// ZigZag encodes signed integers into values that can be efficiently
589 /// encoded with varint. (Otherwise, negative values must be
590 /// sign-extended to 64 bits to be varint encoded, thus always taking
591 /// 10 bytes on the wire.)
592 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100593 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100594 public static long DecodeZigZag64(ulong n) {
595 return (long)(n >> 1) ^ -(long)(n & 1);
596 }
597
598 /// <summary>
599 /// Set the maximum message recursion depth.
600 /// </summary>
601 /// <remarks>
602 /// In order to prevent malicious
603 /// messages from causing stack overflows, CodedInputStream limits
604 /// how deeply messages may be nested. The default limit is 64.
605 /// </remarks>
606 public int SetRecursionLimit(int limit) {
607 if (limit < 0) {
608 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
609 }
610 int oldLimit = recursionLimit;
611 recursionLimit = limit;
612 return oldLimit;
613 }
614
615 /// <summary>
616 /// Set the maximum message size.
617 /// </summary>
618 /// <remarks>
619 /// In order to prevent malicious messages from exhausting memory or
620 /// causing integer overflows, CodedInputStream limits how large a message may be.
621 /// The default limit is 64MB. You should set this limit as small
622 /// as you can without harming your app's functionality. Note that
623 /// size limits only apply when reading from an InputStream, not
624 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
Jon Skeet2e6dc122009-05-29 06:34:52 +0100625 /// If you want to read several messages from a single CodedInputStream, you
626 /// can call ResetSizeCounter() after each message to avoid hitting the
627 /// size limit.
Jon Skeet68036862008-10-22 13:30:34 +0100628 /// </remarks>
629 public int SetSizeLimit(int limit) {
630 if (limit < 0) {
631 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
632 }
633 int oldLimit = sizeLimit;
634 sizeLimit = limit;
635 return oldLimit;
636 }
637
638 #region Internal reading and buffer management
639 /// <summary>
Jon Skeet2e6dc122009-05-29 06:34:52 +0100640 /// Resets the current size counter to zero (see SetSizeLimit).
641 /// </summary>
642 public void ResetSizeCounter() {
643 totalBytesRetired = 0;
644 }
645
646 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100647 /// Sets currentLimit to (current position) + byteLimit. This is called
648 /// when descending into a length-delimited embedded message. The previous
649 /// limit is returned.
650 /// </summary>
651 /// <returns>The old limit.</returns>
652 public int PushLimit(int byteLimit) {
653 if (byteLimit < 0) {
654 throw InvalidProtocolBufferException.NegativeSize();
655 }
656 byteLimit += totalBytesRetired + bufferPos;
657 int oldLimit = currentLimit;
658 if (byteLimit > oldLimit) {
659 throw InvalidProtocolBufferException.TruncatedMessage();
660 }
661 currentLimit = byteLimit;
662
663 RecomputeBufferSizeAfterLimit();
664
665 return oldLimit;
666 }
667
668 private void RecomputeBufferSizeAfterLimit() {
669 bufferSize += bufferSizeAfterLimit;
670 int bufferEnd = totalBytesRetired + bufferSize;
671 if (bufferEnd > currentLimit) {
672 // Limit is in current buffer.
673 bufferSizeAfterLimit = bufferEnd - currentLimit;
674 bufferSize -= bufferSizeAfterLimit;
675 } else {
676 bufferSizeAfterLimit = 0;
677 }
678 }
679
680 /// <summary>
681 /// Discards the current limit, returning the previous limit.
682 /// </summary>
683 public void PopLimit(int oldLimit) {
684 currentLimit = oldLimit;
685 RecomputeBufferSizeAfterLimit();
686 }
687
688 /// <summary>
Jon Skeet25a28582009-02-18 16:06:22 +0000689 /// Returns whether or not all the data before the limit has been read.
690 /// </summary>
691 /// <returns></returns>
692 public bool ReachedLimit {
693 get {
694 if (currentLimit == int.MaxValue) {
695 return false;
696 }
697 int currentAbsolutePosition = totalBytesRetired + bufferPos;
698 return currentAbsolutePosition >= currentLimit;
699 }
700 }
Jon Skeet2e6dc122009-05-29 06:34:52 +0100701
702 /// <summary>
703 /// Returns true if the stream has reached the end of the input. This is the
704 /// case if either the end of the underlying input source has been reached or
705 /// the stream has reached a limit created using PushLimit.
706 /// </summary>
707 public bool IsAtEnd {
708 get {
709 return bufferPos == bufferSize && !RefillBuffer(false);
710 }
711 }
Jon Skeet25a28582009-02-18 16:06:22 +0000712
713 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +0100714 /// Called when buffer is empty to read more bytes from the
715 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
716 /// either there will be at least one byte in the buffer when it returns
717 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
718 /// RefillBuffer() returns false if no more bytes were available.
719 /// </summary>
720 /// <param name="mustSucceed"></param>
721 /// <returns></returns>
722 private bool RefillBuffer(bool mustSucceed) {
723 if (bufferPos < bufferSize) {
724 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
725 }
726
727 if (totalBytesRetired + bufferSize == currentLimit) {
728 // Oops, we hit a limit.
729 if (mustSucceed) {
730 throw InvalidProtocolBufferException.TruncatedMessage();
731 } else {
732 return false;
733 }
734 }
735
736 totalBytesRetired += bufferSize;
737
738 bufferPos = 0;
739 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
Jon Skeet2e6dc122009-05-29 06:34:52 +0100740 if (bufferSize < 0) {
741 throw new InvalidOperationException("Stream.Read returned a negative count");
742 }
Jon Skeet68036862008-10-22 13:30:34 +0100743 if (bufferSize == 0) {
744 if (mustSucceed) {
745 throw InvalidProtocolBufferException.TruncatedMessage();
746 } else {
747 return false;
748 }
749 } else {
750 RecomputeBufferSizeAfterLimit();
751 int totalBytesRead =
752 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
753 if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
754 throw InvalidProtocolBufferException.SizeLimitExceeded();
755 }
756 return true;
757 }
758 }
759
760 /// <summary>
761 /// Read one byte from the input.
762 /// </summary>
763 /// <exception cref="InvalidProtocolBufferException">
Jon Skeet2178b932009-06-25 07:52:07 +0100764 /// the end of the stream or the current limit was reached
Jon Skeet68036862008-10-22 13:30:34 +0100765 /// </exception>
766 public byte ReadRawByte() {
767 if (bufferPos == bufferSize) {
768 RefillBuffer(true);
769 }
770 return buffer[bufferPos++];
771 }
772
773 /// <summary>
774 /// Read a fixed size of bytes from the input.
775 /// </summary>
776 /// <exception cref="InvalidProtocolBufferException">
777 /// the end of the stream or the current limit was reached
778 /// </exception>
779 public byte[] ReadRawBytes(int size) {
780 if (size < 0) {
781 throw InvalidProtocolBufferException.NegativeSize();
782 }
783
784 if (totalBytesRetired + bufferPos + size > currentLimit) {
785 // Read to the end of the stream anyway.
786 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
787 // Then fail.
788 throw InvalidProtocolBufferException.TruncatedMessage();
789 }
790
791 if (size <= bufferSize - bufferPos) {
792 // We have all the bytes we need already.
793 byte[] bytes = new byte[size];
794 Array.Copy(buffer, bufferPos, bytes, 0, size);
795 bufferPos += size;
796 return bytes;
797 } else if (size < BufferSize) {
798 // Reading more bytes than are in the buffer, but not an excessive number
799 // of bytes. We can safely allocate the resulting array ahead of time.
800
801 // First copy what we have.
802 byte[] bytes = new byte[size];
803 int pos = bufferSize - bufferPos;
804 Array.Copy(buffer, bufferPos, bytes, 0, pos);
805 bufferPos = bufferSize;
806
807 // We want to use RefillBuffer() and then copy from the buffer into our
808 // byte array rather than reading directly into our byte array because
809 // the input may be unbuffered.
810 RefillBuffer(true);
811
812 while (size - pos > bufferSize) {
813 Array.Copy(buffer, 0, bytes, pos, bufferSize);
814 pos += bufferSize;
815 bufferPos = bufferSize;
816 RefillBuffer(true);
817 }
818
819 Array.Copy(buffer, 0, bytes, pos, size - pos);
820 bufferPos = size - pos;
821
822 return bytes;
823 } else {
824 // The size is very large. For security reasons, we can't allocate the
825 // entire byte array yet. The size comes directly from the input, so a
826 // maliciously-crafted message could provide a bogus very large size in
827 // order to trick the app into allocating a lot of memory. We avoid this
828 // by allocating and reading only a small chunk at a time, so that the
829 // malicious message must actually *be* extremely large to cause
830 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
831
832 // Remember the buffer markers since we'll have to copy the bytes out of
833 // it later.
834 int originalBufferPos = bufferPos;
835 int originalBufferSize = bufferSize;
836
837 // Mark the current buffer consumed.
838 totalBytesRetired += bufferSize;
839 bufferPos = 0;
840 bufferSize = 0;
841
842 // Read all the rest of the bytes we need.
843 int sizeLeft = size - (originalBufferSize - originalBufferPos);
844 List<byte[]> chunks = new List<byte[]>();
845
846 while (sizeLeft > 0) {
847 byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
848 int pos = 0;
849 while (pos < chunk.Length) {
850 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
851 if (n <= 0) {
852 throw InvalidProtocolBufferException.TruncatedMessage();
853 }
854 totalBytesRetired += n;
855 pos += n;
856 }
857 sizeLeft -= chunk.Length;
858 chunks.Add(chunk);
859 }
860
861 // OK, got everything. Now concatenate it all into one buffer.
862 byte[] bytes = new byte[size];
863
864 // Start by copying the leftover bytes from this.buffer.
865 int newPos = originalBufferSize - originalBufferPos;
866 Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
867
868 // And now all the chunks.
869 foreach (byte[] chunk in chunks) {
870 Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
871 newPos += chunk.Length;
872 }
873
874 // Done.
875 return bytes;
876 }
877 }
878
879 /// <summary>
880 /// Reads and discards a single field, given its tag value.
881 /// </summary>
882 /// <returns>false if the tag is an end-group tag, in which case
883 /// nothing is skipped. Otherwise, returns true.</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100884 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100885 public bool SkipField(uint tag) {
886 switch (WireFormat.GetTagWireType(tag)) {
887 case WireFormat.WireType.Varint:
888 ReadInt32();
889 return true;
890 case WireFormat.WireType.Fixed64:
891 ReadRawLittleEndian64();
892 return true;
893 case WireFormat.WireType.LengthDelimited:
894 SkipRawBytes((int) ReadRawVarint32());
895 return true;
896 case WireFormat.WireType.StartGroup:
897 SkipMessage();
898 CheckLastTagWas(
899 WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
900 WireFormat.WireType.EndGroup));
901 return true;
902 case WireFormat.WireType.EndGroup:
903 return false;
904 case WireFormat.WireType.Fixed32:
905 ReadRawLittleEndian32();
906 return true;
907 default:
908 throw InvalidProtocolBufferException.InvalidWireType();
909 }
910 }
911
912 /// <summary>
913 /// Reads and discards an entire message. This will read either until EOF
914 /// or until an endgroup tag, whichever comes first.
915 /// </summary>
916 public void SkipMessage() {
917 while (true) {
918 uint tag = ReadTag();
919 if (tag == 0 || !SkipField(tag)) {
920 return;
921 }
922 }
923 }
924
925 /// <summary>
926 /// Reads and discards <paramref name="size"/> bytes.
927 /// </summary>
928 /// <exception cref="InvalidProtocolBufferException">the end of the stream
929 /// or the current limit was reached</exception>
930 public void SkipRawBytes(int size) {
931 if (size < 0) {
932 throw InvalidProtocolBufferException.NegativeSize();
933 }
934
935 if (totalBytesRetired + bufferPos + size > currentLimit) {
936 // Read to the end of the stream anyway.
937 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
938 // Then fail.
939 throw InvalidProtocolBufferException.TruncatedMessage();
940 }
941
Jon Skeet2e6dc122009-05-29 06:34:52 +0100942 if (size <= bufferSize - bufferPos) {
Jon Skeet68036862008-10-22 13:30:34 +0100943 // We have all the bytes we need already.
944 bufferPos += size;
945 } else {
946 // Skipping more bytes than are in the buffer. First skip what we have.
947 int pos = bufferSize - bufferPos;
948 totalBytesRetired += pos;
949 bufferPos = 0;
950 bufferSize = 0;
951
952 // Then skip directly from the InputStream for the rest.
953 if (pos < size) {
Jon Skeet68036862008-10-22 13:30:34 +0100954 if (input == null) {
955 throw InvalidProtocolBufferException.TruncatedMessage();
956 }
Jon Skeetc298c892009-05-30 10:07:09 +0100957 SkipImpl(size - pos);
958 totalBytesRetired += size - pos;
959 }
960 }
961 }
962
963 /// <summary>
964 /// Abstraction of skipping to cope with streams which can't really skip.
965 /// </summary>
966 private void SkipImpl(int amountToSkip) {
967 if (input.CanSeek) {
968 long previousPosition = input.Position;
969 input.Position += amountToSkip;
970 if (input.Position != previousPosition + amountToSkip) {
971 throw InvalidProtocolBufferException.TruncatedMessage();
972 }
973 } else {
974 byte[] skipBuffer = new byte[1024];
975 while (amountToSkip > 0) {
976 int bytesRead = input.Read(skipBuffer, 0, skipBuffer.Length);
977 if (bytesRead <= 0) {
Jon Skeet68036862008-10-22 13:30:34 +0100978 throw InvalidProtocolBufferException.TruncatedMessage();
979 }
Jon Skeetc298c892009-05-30 10:07:09 +0100980 amountToSkip -= bytesRead;
Jon Skeet68036862008-10-22 13:30:34 +0100981 }
982 }
983 }
984 #endregion
985 }
986}