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