Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Side by Side Diff: third_party/protobuf/csharp/src/Google.Protobuf/CodedInputStream.cs

Issue 1842653006: Update //third_party/protobuf to version 3. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2008 Google Inc. All rights reserved.
4 // https://developers.google.com/protocol-buffers/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 // * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 // * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32
33 using Google.Protobuf.Collections;
34 using System;
35 using System.Collections.Generic;
36 using System.IO;
37
38 namespace Google.Protobuf
39 {
40 /// <summary>
41 /// Reads and decodes protocol message fields.
42 /// </summary>
43 /// <remarks>
44 /// <para>
45 /// This class is generally used by generated code to read appropriate
46 /// primitives from the stream. It effectively encapsulates the lowest
47 /// levels of protocol buffer format.
48 /// </para>
49 /// <para>
50 /// Repeated fields and map fields are not handled by this class; use <see c ref="RepeatedField{T}"/>
51 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
52 /// </para>
53 /// </remarks>
54 public sealed class CodedInputStream
55 {
56 /// <summary>
57 /// Buffer of data read from the stream or provided at construction time .
58 /// </summary>
59 private readonly byte[] buffer;
60
61 /// <summary>
62 /// The index of the buffer at which we need to refill from the stream ( if there is one).
63 /// </summary>
64 private int bufferSize;
65
66 private int bufferSizeAfterLimit = 0;
67 /// <summary>
68 /// The position within the current buffer (i.e. the next byte to read)
69 /// </summary>
70 private int bufferPos = 0;
71
72 /// <summary>
73 /// The stream to read further input from, or null if the byte array buf fer was provided
74 /// directly on construction, with no further data available.
75 /// </summary>
76 private readonly Stream input;
77
78 /// <summary>
79 /// The last tag we read. 0 indicates we've read to the end of the strea m
80 /// (or haven't read anything yet).
81 /// </summary>
82 private uint lastTag = 0;
83
84 /// <summary>
85 /// The next tag, used to store the value read by PeekTag.
86 /// </summary>
87 private uint nextTag = 0;
88 private bool hasNextTag = false;
89
90 internal const int DefaultRecursionLimit = 64;
91 internal const int DefaultSizeLimit = 64 << 20; // 64MB
92 internal const int BufferSize = 4096;
93
94 /// <summary>
95 /// The total number of bytes read before the current buffer. The
96 /// total bytes read up to the current position can be computed as
97 /// totalBytesRetired + bufferPos.
98 /// </summary>
99 private int totalBytesRetired = 0;
100
101 /// <summary>
102 /// The absolute position of the end of the current message.
103 /// </summary>
104 private int currentLimit = int.MaxValue;
105
106 private int recursionDepth = 0;
107
108 private readonly int recursionLimit;
109 private readonly int sizeLimit;
110
111 #region Construction
112 // Note that the checks are performed such that we don't end up checking obviously-valid things
113 // like non-null references for arrays we've just created.
114
115 /// <summary>
116 /// Creates a new CodedInputStream reading data from the given byte arra y.
117 /// </summary>
118 public CodedInputStream(byte[] buffer) : this(null, Preconditions.CheckN otNull(buffer, "buffer"), 0, buffer.Length)
119 {
120 }
121
122 /// <summary>
123 /// Creates a new CodedInputStream that reads from the given byte array slice.
124 /// </summary>
125 public CodedInputStream(byte[] buffer, int offset, int length)
126 : this(null, Preconditions.CheckNotNull(buffer, "buffer"), offset, o ffset + length)
127 {
128 if (offset < 0 || offset > buffer.Length)
129 {
130 throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
131 }
132 if (length < 0 || offset + length > buffer.Length)
133 {
134 throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
135 }
136 }
137
138 /// <summary>
139 /// Creates a new CodedInputStream reading data from the given stream.
140 /// </summary>
141 public CodedInputStream(Stream input) : this(input, new byte[BufferSize] , 0, 0)
142 {
143 Preconditions.CheckNotNull(input, "input");
144 }
145
146 /// <summary>
147 /// Creates a new CodedInputStream reading data from the given
148 /// stream and buffer, using the default limits.
149 /// </summary>
150 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, in t bufferSize)
151 {
152 this.input = input;
153 this.buffer = buffer;
154 this.bufferPos = bufferPos;
155 this.bufferSize = bufferSize;
156 this.sizeLimit = DefaultSizeLimit;
157 this.recursionLimit = DefaultRecursionLimit;
158 }
159
160 /// <summary>
161 /// Creates a new CodedInputStream reading data from the given
162 /// stream and buffer, using the specified limits.
163 /// </summary>
164 /// <remarks>
165 /// This chains to the version with the default limits instead of vice v ersa to avoid
166 /// having to check that the default values are valid every time.
167 /// </remarks>
168 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, in t bufferSize, int sizeLimit, int recursionLimit)
169 : this(input, buffer, bufferPos, bufferSize)
170 {
171 if (sizeLimit <= 0)
172 {
173 throw new ArgumentOutOfRangeException("sizeLimit", "Size limit m ust be positive");
174 }
175 if (recursionLimit <= 0)
176 {
177 throw new ArgumentOutOfRangeException("recursionLimit!", "Recurs ion limit must be positive");
178 }
179 this.sizeLimit = sizeLimit;
180 this.recursionLimit = recursionLimit;
181 }
182 #endregion
183
184 /// <summary>
185 /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading
186 /// from an input stream.
187 /// </summary>
188 /// <remarks>
189 /// This method exists separately from the constructor to reduce the num ber of constructor overloads.
190 /// It is likely to be used considerably less frequently than the constr uctors, as the default limits
191 /// are suitable for most use cases.
192 /// </remarks>
193 /// <param name="input">The input stream to read from</param>
194 /// <param name="sizeLimit">The total limit of data to read from the str eam.</param>
195 /// <param name="recursionLimit">The maximum recursion depth to allow wh ile reading.</param>
196 /// <returns>A <c>CodedInputStream</c> reading from <paramref name="inpu t"/> with the specified size
197 /// and recursion limits.</returns>
198 public static CodedInputStream CreateWithLimits(Stream input, int sizeLi mit, int recursionLimit)
199 {
200 return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeL imit, recursionLimit);
201 }
202
203 /// <summary>
204 /// Returns the current position in the input stream, or the position in the input buffer
205 /// </summary>
206 public long Position
207 {
208 get
209 {
210 if (input != null)
211 {
212 return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos);
213 }
214 return bufferPos;
215 }
216 }
217
218 /// <summary>
219 /// Returns the last tag read, or 0 if no tags have been read or we've r ead beyond
220 /// the end of the stream.
221 /// </summary>
222 internal uint LastTag { get { return lastTag; } }
223
224 /// <summary>
225 /// Returns the size limit for this stream.
226 /// </summary>
227 /// <remarks>
228 /// This limit is applied when reading from the underlying stream, as a sanity check. It is
229 /// not applied when reading from a byte array data source without an un derlying stream.
230 /// The default value is 64MB.
231 /// </remarks>
232 /// <value>
233 /// The size limit.
234 /// </value>
235 public int SizeLimit { get { return sizeLimit; } }
236
237 /// <summary>
238 /// Returns the recursion limit for this stream. This limit is applied w hilst reading messages,
239 /// to avoid maliciously-recursive data.
240 /// </summary>
241 /// <remarks>
242 /// The default limit is 64.
243 /// </remarks>
244 /// <value>
245 /// The recursion limit for this stream.
246 /// </value>
247 public int RecursionLimit { get { return recursionLimit; } }
248
249 #region Validation
250 /// <summary>
251 /// Verifies that the last call to ReadTag() returned tag 0 - in other w ords,
252 /// we've reached the end of the stream when we expected to.
253 /// </summary>
254 /// <exception cref="InvalidProtocolBufferException">The
255 /// tag read was not the one specified</exception>
256 internal void CheckReadEndOfStreamTag()
257 {
258 if (lastTag != 0)
259 {
260 throw InvalidProtocolBufferException.MoreDataAvailable();
261 }
262 }
263 #endregion
264
265 #region Reading of tags etc
266
267 /// <summary>
268 /// Peeks at the next field tag. This is like calling <see cref="ReadTag "/>, but the
269 /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
270 /// same value.)
271 /// </summary>
272 public uint PeekTag()
273 {
274 if (hasNextTag)
275 {
276 return nextTag;
277 }
278
279 uint savedLast = lastTag;
280 nextTag = ReadTag();
281 hasNextTag = true;
282 lastTag = savedLast; // Undo the side effect of ReadTag
283 return nextTag;
284 }
285
286 /// <summary>
287 /// Reads a field tag, returning the tag of 0 for "end of stream".
288 /// </summary>
289 /// <remarks>
290 /// If this method returns 0, it doesn't necessarily mean the end of all
291 /// the data in this CodedInputStream; it may be the end of the logical stream
292 /// for an embedded message, for example.
293 /// </remarks>
294 /// <returns>The next field tag, or 0 for end of stream. (0 is never a v alid tag.)</returns>
295 public uint ReadTag()
296 {
297 if (hasNextTag)
298 {
299 lastTag = nextTag;
300 hasNextTag = false;
301 return lastTag;
302 }
303
304 // Optimize for the incredibly common case of having at least two by tes left in the buffer,
305 // and those two bytes being enough to get the tag. This will be tru e for fields up to 4095.
306 if (bufferPos + 2 <= bufferSize)
307 {
308 int tmp = buffer[bufferPos++];
309 if (tmp < 128)
310 {
311 lastTag = (uint)tmp;
312 }
313 else
314 {
315 int result = tmp & 0x7f;
316 if ((tmp = buffer[bufferPos++]) < 128)
317 {
318 result |= tmp << 7;
319 lastTag = (uint) result;
320 }
321 else
322 {
323 // Nope, rewind and go the potentially slow route.
324 bufferPos -= 2;
325 lastTag = ReadRawVarint32();
326 }
327 }
328 }
329 else
330 {
331 if (IsAtEnd)
332 {
333 lastTag = 0;
334 return 0; // This is the only case in which we return 0.
335 }
336
337 lastTag = ReadRawVarint32();
338 }
339 if (lastTag == 0)
340 {
341 // If we actually read zero, that's not a valid tag.
342 throw InvalidProtocolBufferException.InvalidTag();
343 }
344 return lastTag;
345 }
346
347 /// <summary>
348 /// Skips the data for the field with the tag we've just read.
349 /// This should be called directly after <see cref="ReadTag"/>, when
350 /// the caller wishes to skip an unknown field.
351 /// </summary>
352 public void SkipLastField()
353 {
354 if (lastTag == 0)
355 {
356 throw new InvalidOperationException("SkipLastField cannot be cal led at the end of a stream");
357 }
358 switch (WireFormat.GetTagWireType(lastTag))
359 {
360 case WireFormat.WireType.StartGroup:
361 SkipGroup();
362 break;
363 case WireFormat.WireType.EndGroup:
364 // Just ignore; there's no data following the tag.
365 break;
366 case WireFormat.WireType.Fixed32:
367 ReadFixed32();
368 break;
369 case WireFormat.WireType.Fixed64:
370 ReadFixed64();
371 break;
372 case WireFormat.WireType.LengthDelimited:
373 var length = ReadLength();
374 SkipRawBytes(length);
375 break;
376 case WireFormat.WireType.Varint:
377 ReadRawVarint32();
378 break;
379 }
380 }
381
382 private void SkipGroup()
383 {
384 // Note: Currently we expect this to be the way that groups are read . We could put the recursion
385 // depth changes into the ReadTag method instead, potentially...
386 recursionDepth++;
387 if (recursionDepth >= recursionLimit)
388 {
389 throw InvalidProtocolBufferException.RecursionLimitExceeded();
390 }
391 uint tag;
392 do
393 {
394 tag = ReadTag();
395 if (tag == 0)
396 {
397 throw InvalidProtocolBufferException.TruncatedMessage();
398 }
399 // This recursion will allow us to handle nested groups.
400 SkipLastField();
401 } while (WireFormat.GetTagWireType(tag) != WireFormat.WireType.EndGr oup);
402 recursionDepth--;
403 }
404
405 /// <summary>
406 /// Reads a double field from the stream.
407 /// </summary>
408 public double ReadDouble()
409 {
410 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64() );
411 }
412
413 /// <summary>
414 /// Reads a float field from the stream.
415 /// </summary>
416 public float ReadFloat()
417 {
418 if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
419 {
420 float ret = BitConverter.ToSingle(buffer, bufferPos);
421 bufferPos += 4;
422 return ret;
423 }
424 else
425 {
426 byte[] rawBytes = ReadRawBytes(4);
427 if (!BitConverter.IsLittleEndian)
428 {
429 ByteArray.Reverse(rawBytes);
430 }
431 return BitConverter.ToSingle(rawBytes, 0);
432 }
433 }
434
435 /// <summary>
436 /// Reads a uint64 field from the stream.
437 /// </summary>
438 public ulong ReadUInt64()
439 {
440 return ReadRawVarint64();
441 }
442
443 /// <summary>
444 /// Reads an int64 field from the stream.
445 /// </summary>
446 public long ReadInt64()
447 {
448 return (long) ReadRawVarint64();
449 }
450
451 /// <summary>
452 /// Reads an int32 field from the stream.
453 /// </summary>
454 public int ReadInt32()
455 {
456 return (int) ReadRawVarint32();
457 }
458
459 /// <summary>
460 /// Reads a fixed64 field from the stream.
461 /// </summary>
462 public ulong ReadFixed64()
463 {
464 return ReadRawLittleEndian64();
465 }
466
467 /// <summary>
468 /// Reads a fixed32 field from the stream.
469 /// </summary>
470 public uint ReadFixed32()
471 {
472 return ReadRawLittleEndian32();
473 }
474
475 /// <summary>
476 /// Reads a bool field from the stream.
477 /// </summary>
478 public bool ReadBool()
479 {
480 return ReadRawVarint32() != 0;
481 }
482
483 /// <summary>
484 /// Reads a string field from the stream.
485 /// </summary>
486 public string ReadString()
487 {
488 int length = ReadLength();
489 // No need to read any data for an empty string.
490 if (length == 0)
491 {
492 return "";
493 }
494 if (length <= bufferSize - bufferPos)
495 {
496 // Fast path: We already have the bytes in a contiguous buffer, so
497 // just copy directly from it.
498 String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
499 bufferPos += length;
500 return result;
501 }
502 // Slow path: Build a byte array first then copy it.
503 return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length) , 0, length);
504 }
505
506 /// <summary>
507 /// Reads an embedded message field value from the stream.
508 /// </summary>
509 public void ReadMessage(IMessage builder)
510 {
511 int length = ReadLength();
512 if (recursionDepth >= recursionLimit)
513 {
514 throw InvalidProtocolBufferException.RecursionLimitExceeded();
515 }
516 int oldLimit = PushLimit(length);
517 ++recursionDepth;
518 builder.MergeFrom(this);
519 CheckReadEndOfStreamTag();
520 // Check that we've read exactly as much data as expected.
521 if (!ReachedLimit)
522 {
523 throw InvalidProtocolBufferException.TruncatedMessage();
524 }
525 --recursionDepth;
526 PopLimit(oldLimit);
527 }
528
529 /// <summary>
530 /// Reads a bytes field value from the stream.
531 /// </summary>
532 public ByteString ReadBytes()
533 {
534 int length = ReadLength();
535 if (length <= bufferSize - bufferPos && length > 0)
536 {
537 // Fast path: We already have the bytes in a contiguous buffer, so
538 // just copy directly from it.
539 ByteString result = ByteString.CopyFrom(buffer, bufferPos, lengt h);
540 bufferPos += length;
541 return result;
542 }
543 else
544 {
545 // Slow path: Build a byte array and attach it to a new ByteStr ing.
546 return ByteString.AttachBytes(ReadRawBytes(length));
547 }
548 }
549
550 /// <summary>
551 /// Reads a uint32 field value from the stream.
552 /// </summary>
553 public uint ReadUInt32()
554 {
555 return ReadRawVarint32();
556 }
557
558 /// <summary>
559 /// Reads an enum field value from the stream. If the enum is valid for type T,
560 /// then the ref value is set and it returns true. Otherwise the unknow n output
561 /// value is set and this method returns false.
562 /// </summary>
563 public int ReadEnum()
564 {
565 // Currently just a pass-through, but it's nice to separate it logic ally from WriteInt32.
566 return (int) ReadRawVarint32();
567 }
568
569 /// <summary>
570 /// Reads an sfixed32 field value from the stream.
571 /// </summary>
572 public int ReadSFixed32()
573 {
574 return (int) ReadRawLittleEndian32();
575 }
576
577 /// <summary>
578 /// Reads an sfixed64 field value from the stream.
579 /// </summary>
580 public long ReadSFixed64()
581 {
582 return (long) ReadRawLittleEndian64();
583 }
584
585 /// <summary>
586 /// Reads an sint32 field value from the stream.
587 /// </summary>
588 public int ReadSInt32()
589 {
590 return DecodeZigZag32(ReadRawVarint32());
591 }
592
593 /// <summary>
594 /// Reads an sint64 field value from the stream.
595 /// </summary>
596 public long ReadSInt64()
597 {
598 return DecodeZigZag64(ReadRawVarint64());
599 }
600
601 /// <summary>
602 /// Reads a length for length-delimited data.
603 /// </summary>
604 /// <remarks>
605 /// This is internally just reading a varint, but this method exists
606 /// to make the calling code clearer.
607 /// </remarks>
608 public int ReadLength()
609 {
610 return (int) ReadRawVarint32();
611 }
612
613 /// <summary>
614 /// Peeks at the next tag in the stream. If it matches <paramref name="t ag"/>,
615 /// the tag is consumed and the method returns <c>true</c>; otherwise, t he
616 /// stream is left in the original position and the method returns <c>fa lse</c>.
617 /// </summary>
618 public bool MaybeConsumeTag(uint tag)
619 {
620 if (PeekTag() == tag)
621 {
622 hasNextTag = false;
623 return true;
624 }
625 return false;
626 }
627
628 #endregion
629
630 #region Underlying reading primitives
631
632 /// <summary>
633 /// Same code as ReadRawVarint32, but read each byte individually, check ing for
634 /// buffer overflow.
635 /// </summary>
636 private uint SlowReadRawVarint32()
637 {
638 int tmp = ReadRawByte();
639 if (tmp < 128)
640 {
641 return (uint) tmp;
642 }
643 int result = tmp & 0x7f;
644 if ((tmp = ReadRawByte()) < 128)
645 {
646 result |= tmp << 7;
647 }
648 else
649 {
650 result |= (tmp & 0x7f) << 7;
651 if ((tmp = ReadRawByte()) < 128)
652 {
653 result |= tmp << 14;
654 }
655 else
656 {
657 result |= (tmp & 0x7f) << 14;
658 if ((tmp = ReadRawByte()) < 128)
659 {
660 result |= tmp << 21;
661 }
662 else
663 {
664 result |= (tmp & 0x7f) << 21;
665 result |= (tmp = ReadRawByte()) << 28;
666 if (tmp >= 128)
667 {
668 // Discard upper 32 bits.
669 for (int i = 0; i < 5; i++)
670 {
671 if (ReadRawByte() < 128)
672 {
673 return (uint) result;
674 }
675 }
676 throw InvalidProtocolBufferException.MalformedVarint ();
677 }
678 }
679 }
680 }
681 return (uint) result;
682 }
683
684 /// <summary>
685 /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
686 /// This method is optimised for the case where we've got lots of data i n the buffer.
687 /// That means we can check the size just once, then just read directly from the buffer
688 /// without constant rechecking of the buffer length.
689 /// </summary>
690 internal uint ReadRawVarint32()
691 {
692 if (bufferPos + 5 > bufferSize)
693 {
694 return SlowReadRawVarint32();
695 }
696
697 int tmp = buffer[bufferPos++];
698 if (tmp < 128)
699 {
700 return (uint) tmp;
701 }
702 int result = tmp & 0x7f;
703 if ((tmp = buffer[bufferPos++]) < 128)
704 {
705 result |= tmp << 7;
706 }
707 else
708 {
709 result |= (tmp & 0x7f) << 7;
710 if ((tmp = buffer[bufferPos++]) < 128)
711 {
712 result |= tmp << 14;
713 }
714 else
715 {
716 result |= (tmp & 0x7f) << 14;
717 if ((tmp = buffer[bufferPos++]) < 128)
718 {
719 result |= tmp << 21;
720 }
721 else
722 {
723 result |= (tmp & 0x7f) << 21;
724 result |= (tmp = buffer[bufferPos++]) << 28;
725 if (tmp >= 128)
726 {
727 // Discard upper 32 bits.
728 // Note that this has to use ReadRawByte() as we onl y ensure we've
729 // got at least 5 bytes at the start of the method. This lets us
730 // use the fast path in more cases, and we rarely hi t this section of code.
731 for (int i = 0; i < 5; i++)
732 {
733 if (ReadRawByte() < 128)
734 {
735 return (uint) result;
736 }
737 }
738 throw InvalidProtocolBufferException.MalformedVarint ();
739 }
740 }
741 }
742 }
743 return (uint) result;
744 }
745
746 /// <summary>
747 /// Reads a varint from the input one byte at a time, so that it does no t
748 /// read any bytes after the end of the varint. If you simply wrapped th e
749 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
750 /// then you would probably end up reading past the end of the varint si nce
751 /// CodedInputStream buffers its input.
752 /// </summary>
753 /// <param name="input"></param>
754 /// <returns></returns>
755 internal static uint ReadRawVarint32(Stream input)
756 {
757 int result = 0;
758 int offset = 0;
759 for (; offset < 32; offset += 7)
760 {
761 int b = input.ReadByte();
762 if (b == -1)
763 {
764 throw InvalidProtocolBufferException.TruncatedMessage();
765 }
766 result |= (b & 0x7f) << offset;
767 if ((b & 0x80) == 0)
768 {
769 return (uint) result;
770 }
771 }
772 // Keep reading up to 64 bits.
773 for (; offset < 64; offset += 7)
774 {
775 int b = input.ReadByte();
776 if (b == -1)
777 {
778 throw InvalidProtocolBufferException.TruncatedMessage();
779 }
780 if ((b & 0x80) == 0)
781 {
782 return (uint) result;
783 }
784 }
785 throw InvalidProtocolBufferException.MalformedVarint();
786 }
787
788 /// <summary>
789 /// Reads a raw varint from the stream.
790 /// </summary>
791 internal ulong ReadRawVarint64()
792 {
793 int shift = 0;
794 ulong result = 0;
795 while (shift < 64)
796 {
797 byte b = ReadRawByte();
798 result |= (ulong) (b & 0x7F) << shift;
799 if ((b & 0x80) == 0)
800 {
801 return result;
802 }
803 shift += 7;
804 }
805 throw InvalidProtocolBufferException.MalformedVarint();
806 }
807
808 /// <summary>
809 /// Reads a 32-bit little-endian integer from the stream.
810 /// </summary>
811 internal uint ReadRawLittleEndian32()
812 {
813 uint b1 = ReadRawByte();
814 uint b2 = ReadRawByte();
815 uint b3 = ReadRawByte();
816 uint b4 = ReadRawByte();
817 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
818 }
819
820 /// <summary>
821 /// Reads a 64-bit little-endian integer from the stream.
822 /// </summary>
823 internal ulong ReadRawLittleEndian64()
824 {
825 ulong b1 = ReadRawByte();
826 ulong b2 = ReadRawByte();
827 ulong b3 = ReadRawByte();
828 ulong b4 = ReadRawByte();
829 ulong b5 = ReadRawByte();
830 ulong b6 = ReadRawByte();
831 ulong b7 = ReadRawByte();
832 ulong b8 = ReadRawByte();
833 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
834 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
835 }
836
837 /// <summary>
838 /// Decode a 32-bit value with ZigZag encoding.
839 /// </summary>
840 /// <remarks>
841 /// ZigZag encodes signed integers into values that can be efficiently
842 /// encoded with varint. (Otherwise, negative values must be
843 /// sign-extended to 64 bits to be varint encoded, thus always taking
844 /// 10 bytes on the wire.)
845 /// </remarks>
846 internal static int DecodeZigZag32(uint n)
847 {
848 return (int)(n >> 1) ^ -(int)(n & 1);
849 }
850
851 /// <summary>
852 /// Decode a 32-bit value with ZigZag encoding.
853 /// </summary>
854 /// <remarks>
855 /// ZigZag encodes signed integers into values that can be efficiently
856 /// encoded with varint. (Otherwise, negative values must be
857 /// sign-extended to 64 bits to be varint encoded, thus always taking
858 /// 10 bytes on the wire.)
859 /// </remarks>
860 internal static long DecodeZigZag64(ulong n)
861 {
862 return (long)(n >> 1) ^ -(long)(n & 1);
863 }
864 #endregion
865
866 #region Internal reading and buffer management
867
868 /// <summary>
869 /// Sets currentLimit to (current position) + byteLimit. This is called
870 /// when descending into a length-delimited embedded message. The previo us
871 /// limit is returned.
872 /// </summary>
873 /// <returns>The old limit.</returns>
874 internal int PushLimit(int byteLimit)
875 {
876 if (byteLimit < 0)
877 {
878 throw InvalidProtocolBufferException.NegativeSize();
879 }
880 byteLimit += totalBytesRetired + bufferPos;
881 int oldLimit = currentLimit;
882 if (byteLimit > oldLimit)
883 {
884 throw InvalidProtocolBufferException.TruncatedMessage();
885 }
886 currentLimit = byteLimit;
887
888 RecomputeBufferSizeAfterLimit();
889
890 return oldLimit;
891 }
892
893 private void RecomputeBufferSizeAfterLimit()
894 {
895 bufferSize += bufferSizeAfterLimit;
896 int bufferEnd = totalBytesRetired + bufferSize;
897 if (bufferEnd > currentLimit)
898 {
899 // Limit is in current buffer.
900 bufferSizeAfterLimit = bufferEnd - currentLimit;
901 bufferSize -= bufferSizeAfterLimit;
902 }
903 else
904 {
905 bufferSizeAfterLimit = 0;
906 }
907 }
908
909 /// <summary>
910 /// Discards the current limit, returning the previous limit.
911 /// </summary>
912 internal void PopLimit(int oldLimit)
913 {
914 currentLimit = oldLimit;
915 RecomputeBufferSizeAfterLimit();
916 }
917
918 /// <summary>
919 /// Returns whether or not all the data before the limit has been read.
920 /// </summary>
921 /// <returns></returns>
922 internal bool ReachedLimit
923 {
924 get
925 {
926 if (currentLimit == int.MaxValue)
927 {
928 return false;
929 }
930 int currentAbsolutePosition = totalBytesRetired + bufferPos;
931 return currentAbsolutePosition >= currentLimit;
932 }
933 }
934
935 /// <summary>
936 /// Returns true if the stream has reached the end of the input. This is the
937 /// case if either the end of the underlying input source has been reach ed or
938 /// the stream has reached a limit created using PushLimit.
939 /// </summary>
940 public bool IsAtEnd
941 {
942 get { return bufferPos == bufferSize && !RefillBuffer(false); }
943 }
944
945 /// <summary>
946 /// Called when buffer is empty to read more bytes from the
947 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gu rantees that
948 /// either there will be at least one byte in the buffer when it returns
949 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
950 /// RefillBuffer() returns false if no more bytes were available.
951 /// </summary>
952 /// <param name="mustSucceed"></param>
953 /// <returns></returns>
954 private bool RefillBuffer(bool mustSucceed)
955 {
956 if (bufferPos < bufferSize)
957 {
958 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
959 }
960
961 if (totalBytesRetired + bufferSize == currentLimit)
962 {
963 // Oops, we hit a limit.
964 if (mustSucceed)
965 {
966 throw InvalidProtocolBufferException.TruncatedMessage();
967 }
968 else
969 {
970 return false;
971 }
972 }
973
974 totalBytesRetired += bufferSize;
975
976 bufferPos = 0;
977 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Leng th);
978 if (bufferSize < 0)
979 {
980 throw new InvalidOperationException("Stream.Read returned a nega tive count");
981 }
982 if (bufferSize == 0)
983 {
984 if (mustSucceed)
985 {
986 throw InvalidProtocolBufferException.TruncatedMessage();
987 }
988 else
989 {
990 return false;
991 }
992 }
993 else
994 {
995 RecomputeBufferSizeAfterLimit();
996 int totalBytesRead =
997 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
998 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
999 {
1000 throw InvalidProtocolBufferException.SizeLimitExceeded();
1001 }
1002 return true;
1003 }
1004 }
1005
1006 /// <summary>
1007 /// Read one byte from the input.
1008 /// </summary>
1009 /// <exception cref="InvalidProtocolBufferException">
1010 /// the end of the stream or the current limit was reached
1011 /// </exception>
1012 internal byte ReadRawByte()
1013 {
1014 if (bufferPos == bufferSize)
1015 {
1016 RefillBuffer(true);
1017 }
1018 return buffer[bufferPos++];
1019 }
1020
1021 /// <summary>
1022 /// Reads a fixed size of bytes from the input.
1023 /// </summary>
1024 /// <exception cref="InvalidProtocolBufferException">
1025 /// the end of the stream or the current limit was reached
1026 /// </exception>
1027 internal byte[] ReadRawBytes(int size)
1028 {
1029 if (size < 0)
1030 {
1031 throw InvalidProtocolBufferException.NegativeSize();
1032 }
1033
1034 if (totalBytesRetired + bufferPos + size > currentLimit)
1035 {
1036 // Read to the end of the stream (up to the current limit) anywa y.
1037 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1038 // Then fail.
1039 throw InvalidProtocolBufferException.TruncatedMessage();
1040 }
1041
1042 if (size <= bufferSize - bufferPos)
1043 {
1044 // We have all the bytes we need already.
1045 byte[] bytes = new byte[size];
1046 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
1047 bufferPos += size;
1048 return bytes;
1049 }
1050 else if (size < buffer.Length)
1051 {
1052 // Reading more bytes than are in the buffer, but not an excessi ve number
1053 // of bytes. We can safely allocate the resulting array ahead o f time.
1054
1055 // First copy what we have.
1056 byte[] bytes = new byte[size];
1057 int pos = bufferSize - bufferPos;
1058 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
1059 bufferPos = bufferSize;
1060
1061 // We want to use RefillBuffer() and then copy from the buffer i nto our
1062 // byte array rather than reading directly into our byte array b ecause
1063 // the input may be unbuffered.
1064 RefillBuffer(true);
1065
1066 while (size - pos > bufferSize)
1067 {
1068 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
1069 pos += bufferSize;
1070 bufferPos = bufferSize;
1071 RefillBuffer(true);
1072 }
1073
1074 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
1075 bufferPos = size - pos;
1076
1077 return bytes;
1078 }
1079 else
1080 {
1081 // The size is very large. For security reasons, we can't alloc ate the
1082 // entire byte array yet. The size comes directly from the inpu t, so a
1083 // maliciously-crafted message could provide a bogus very large size in
1084 // order to trick the app into allocating a lot of memory. We a void this
1085 // by allocating and reading only a small chunk at a time, so th at the
1086 // malicious message must actually *be* extremely large to cause
1087 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1088
1089 // Remember the buffer markers since we'll have to copy the byte s out of
1090 // it later.
1091 int originalBufferPos = bufferPos;
1092 int originalBufferSize = bufferSize;
1093
1094 // Mark the current buffer consumed.
1095 totalBytesRetired += bufferSize;
1096 bufferPos = 0;
1097 bufferSize = 0;
1098
1099 // Read all the rest of the bytes we need.
1100 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1101 List<byte[]> chunks = new List<byte[]>();
1102
1103 while (sizeLeft > 0)
1104 {
1105 byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
1106 int pos = 0;
1107 while (pos < chunk.Length)
1108 {
1109 int n = (input == null) ? -1 : input.Read(chunk, pos, ch unk.Length - pos);
1110 if (n <= 0)
1111 {
1112 throw InvalidProtocolBufferException.TruncatedMessag e();
1113 }
1114 totalBytesRetired += n;
1115 pos += n;
1116 }
1117 sizeLeft -= chunk.Length;
1118 chunks.Add(chunk);
1119 }
1120
1121 // OK, got everything. Now concatenate it all into one buffer.
1122 byte[] bytes = new byte[size];
1123
1124 // Start by copying the leftover bytes from this.buffer.
1125 int newPos = originalBufferSize - originalBufferPos;
1126 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
1127
1128 // And now all the chunks.
1129 foreach (byte[] chunk in chunks)
1130 {
1131 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
1132 newPos += chunk.Length;
1133 }
1134
1135 // Done.
1136 return bytes;
1137 }
1138 }
1139
1140 /// <summary>
1141 /// Reads and discards <paramref name="size"/> bytes.
1142 /// </summary>
1143 /// <exception cref="InvalidProtocolBufferException">the end of the stre am
1144 /// or the current limit was reached</exception>
1145 private void SkipRawBytes(int size)
1146 {
1147 if (size < 0)
1148 {
1149 throw InvalidProtocolBufferException.NegativeSize();
1150 }
1151
1152 if (totalBytesRetired + bufferPos + size > currentLimit)
1153 {
1154 // Read to the end of the stream anyway.
1155 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1156 // Then fail.
1157 throw InvalidProtocolBufferException.TruncatedMessage();
1158 }
1159
1160 if (size <= bufferSize - bufferPos)
1161 {
1162 // We have all the bytes we need already.
1163 bufferPos += size;
1164 }
1165 else
1166 {
1167 // Skipping more bytes than are in the buffer. First skip what we have.
1168 int pos = bufferSize - bufferPos;
1169
1170 // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bu fferSize)
1171 // totalBytesRetired += pos;
1172 totalBytesRetired += bufferSize;
1173
1174 bufferPos = 0;
1175 bufferSize = 0;
1176
1177 // Then skip directly from the InputStream for the rest.
1178 if (pos < size)
1179 {
1180 if (input == null)
1181 {
1182 throw InvalidProtocolBufferException.TruncatedMessage();
1183 }
1184 SkipImpl(size - pos);
1185 totalBytesRetired += size - pos;
1186 }
1187 }
1188 }
1189
1190 /// <summary>
1191 /// Abstraction of skipping to cope with streams which can't really skip .
1192 /// </summary>
1193 private void SkipImpl(int amountToSkip)
1194 {
1195 if (input.CanSeek)
1196 {
1197 long previousPosition = input.Position;
1198 input.Position += amountToSkip;
1199 if (input.Position != previousPosition + amountToSkip)
1200 {
1201 throw InvalidProtocolBufferException.TruncatedMessage();
1202 }
1203 }
1204 else
1205 {
1206 byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)];
1207 while (amountToSkip > 0)
1208 {
1209 int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffe r.Length, amountToSkip));
1210 if (bytesRead <= 0)
1211 {
1212 throw InvalidProtocolBufferException.TruncatedMessage();
1213 }
1214 amountToSkip -= bytesRead;
1215 }
1216 }
1217 }
1218
1219 #endregion
1220 }
1221 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698