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. If the enum is valid for type T, 616 /// then the ref value is set and it returns true. Otherwise the unknown output 617 /// value is set and this method returns false. 618 /// </summary> ReadEnum()619 public int ReadEnum() 620 { 621 // Currently just a pass-through, but it's nice to separate it logically from WriteInt32. 622 return (int) ReadRawVarint32(); 623 } 624 625 /// <summary> 626 /// Reads an sfixed32 field value from the stream. 627 /// </summary> ReadSFixed32()628 public int ReadSFixed32() 629 { 630 return (int) ReadRawLittleEndian32(); 631 } 632 633 /// <summary> 634 /// Reads an sfixed64 field value from the stream. 635 /// </summary> ReadSFixed64()636 public long ReadSFixed64() 637 { 638 return (long) ReadRawLittleEndian64(); 639 } 640 641 /// <summary> 642 /// Reads an sint32 field value from the stream. 643 /// </summary> ReadSInt32()644 public int ReadSInt32() 645 { 646 return DecodeZigZag32(ReadRawVarint32()); 647 } 648 649 /// <summary> 650 /// Reads an sint64 field value from the stream. 651 /// </summary> ReadSInt64()652 public long ReadSInt64() 653 { 654 return DecodeZigZag64(ReadRawVarint64()); 655 } 656 657 /// <summary> 658 /// Reads a length for length-delimited data. 659 /// </summary> 660 /// <remarks> 661 /// This is internally just reading a varint, but this method exists 662 /// to make the calling code clearer. 663 /// </remarks> ReadLength()664 public int ReadLength() 665 { 666 return (int) ReadRawVarint32(); 667 } 668 669 /// <summary> 670 /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>, 671 /// the tag is consumed and the method returns <c>true</c>; otherwise, the 672 /// stream is left in the original position and the method returns <c>false</c>. 673 /// </summary> MaybeConsumeTag(uint tag)674 public bool MaybeConsumeTag(uint tag) 675 { 676 if (PeekTag() == tag) 677 { 678 hasNextTag = false; 679 return true; 680 } 681 return false; 682 } 683 684 #endregion 685 686 #region Underlying reading primitives 687 688 /// <summary> 689 /// Same code as ReadRawVarint32, but read each byte individually, checking for 690 /// buffer overflow. 691 /// </summary> SlowReadRawVarint32()692 private uint SlowReadRawVarint32() 693 { 694 int tmp = ReadRawByte(); 695 if (tmp < 128) 696 { 697 return (uint) tmp; 698 } 699 int result = tmp & 0x7f; 700 if ((tmp = ReadRawByte()) < 128) 701 { 702 result |= tmp << 7; 703 } 704 else 705 { 706 result |= (tmp & 0x7f) << 7; 707 if ((tmp = ReadRawByte()) < 128) 708 { 709 result |= tmp << 14; 710 } 711 else 712 { 713 result |= (tmp & 0x7f) << 14; 714 if ((tmp = ReadRawByte()) < 128) 715 { 716 result |= tmp << 21; 717 } 718 else 719 { 720 result |= (tmp & 0x7f) << 21; 721 result |= (tmp = ReadRawByte()) << 28; 722 if (tmp >= 128) 723 { 724 // Discard upper 32 bits. 725 for (int i = 0; i < 5; i++) 726 { 727 if (ReadRawByte() < 128) 728 { 729 return (uint) result; 730 } 731 } 732 throw InvalidProtocolBufferException.MalformedVarint(); 733 } 734 } 735 } 736 } 737 return (uint) result; 738 } 739 740 /// <summary> 741 /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits. 742 /// This method is optimised for the case where we've got lots of data in the buffer. 743 /// That means we can check the size just once, then just read directly from the buffer 744 /// without constant rechecking of the buffer length. 745 /// </summary> ReadRawVarint32()746 internal uint ReadRawVarint32() 747 { 748 if (bufferPos + 5 > bufferSize) 749 { 750 return SlowReadRawVarint32(); 751 } 752 753 int tmp = buffer[bufferPos++]; 754 if (tmp < 128) 755 { 756 return (uint) tmp; 757 } 758 int result = tmp & 0x7f; 759 if ((tmp = buffer[bufferPos++]) < 128) 760 { 761 result |= tmp << 7; 762 } 763 else 764 { 765 result |= (tmp & 0x7f) << 7; 766 if ((tmp = buffer[bufferPos++]) < 128) 767 { 768 result |= tmp << 14; 769 } 770 else 771 { 772 result |= (tmp & 0x7f) << 14; 773 if ((tmp = buffer[bufferPos++]) < 128) 774 { 775 result |= tmp << 21; 776 } 777 else 778 { 779 result |= (tmp & 0x7f) << 21; 780 result |= (tmp = buffer[bufferPos++]) << 28; 781 if (tmp >= 128) 782 { 783 // Discard upper 32 bits. 784 // Note that this has to use ReadRawByte() as we only ensure we've 785 // got at least 5 bytes at the start of the method. This lets us 786 // use the fast path in more cases, and we rarely hit this section of code. 787 for (int i = 0; i < 5; i++) 788 { 789 if (ReadRawByte() < 128) 790 { 791 return (uint) result; 792 } 793 } 794 throw InvalidProtocolBufferException.MalformedVarint(); 795 } 796 } 797 } 798 } 799 return (uint) result; 800 } 801 802 /// <summary> 803 /// Reads a varint from the input one byte at a time, so that it does not 804 /// read any bytes after the end of the varint. If you simply wrapped the 805 /// stream in a CodedInputStream and used ReadRawVarint32(Stream) 806 /// then you would probably end up reading past the end of the varint since 807 /// CodedInputStream buffers its input. 808 /// </summary> 809 /// <param name="input"></param> 810 /// <returns></returns> ReadRawVarint32(Stream input)811 internal static uint ReadRawVarint32(Stream input) 812 { 813 int result = 0; 814 int offset = 0; 815 for (; offset < 32; offset += 7) 816 { 817 int b = input.ReadByte(); 818 if (b == -1) 819 { 820 throw InvalidProtocolBufferException.TruncatedMessage(); 821 } 822 result |= (b & 0x7f) << offset; 823 if ((b & 0x80) == 0) 824 { 825 return (uint) result; 826 } 827 } 828 // Keep reading up to 64 bits. 829 for (; offset < 64; offset += 7) 830 { 831 int b = input.ReadByte(); 832 if (b == -1) 833 { 834 throw InvalidProtocolBufferException.TruncatedMessage(); 835 } 836 if ((b & 0x80) == 0) 837 { 838 return (uint) result; 839 } 840 } 841 throw InvalidProtocolBufferException.MalformedVarint(); 842 } 843 844 /// <summary> 845 /// Reads a raw varint from the stream. 846 /// </summary> ReadRawVarint64()847 internal ulong ReadRawVarint64() 848 { 849 int shift = 0; 850 ulong result = 0; 851 while (shift < 64) 852 { 853 byte b = ReadRawByte(); 854 result |= (ulong) (b & 0x7F) << shift; 855 if ((b & 0x80) == 0) 856 { 857 return result; 858 } 859 shift += 7; 860 } 861 throw InvalidProtocolBufferException.MalformedVarint(); 862 } 863 864 /// <summary> 865 /// Reads a 32-bit little-endian integer from the stream. 866 /// </summary> ReadRawLittleEndian32()867 internal uint ReadRawLittleEndian32() 868 { 869 uint b1 = ReadRawByte(); 870 uint b2 = ReadRawByte(); 871 uint b3 = ReadRawByte(); 872 uint b4 = ReadRawByte(); 873 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); 874 } 875 876 /// <summary> 877 /// Reads a 64-bit little-endian integer from the stream. 878 /// </summary> ReadRawLittleEndian64()879 internal ulong ReadRawLittleEndian64() 880 { 881 ulong b1 = ReadRawByte(); 882 ulong b2 = ReadRawByte(); 883 ulong b3 = ReadRawByte(); 884 ulong b4 = ReadRawByte(); 885 ulong b5 = ReadRawByte(); 886 ulong b6 = ReadRawByte(); 887 ulong b7 = ReadRawByte(); 888 ulong b8 = ReadRawByte(); 889 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) 890 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); 891 } 892 893 /// <summary> 894 /// Decode a 32-bit value with ZigZag encoding. 895 /// </summary> 896 /// <remarks> 897 /// ZigZag encodes signed integers into values that can be efficiently 898 /// encoded with varint. (Otherwise, negative values must be 899 /// sign-extended to 64 bits to be varint encoded, thus always taking 900 /// 10 bytes on the wire.) 901 /// </remarks> DecodeZigZag32(uint n)902 internal static int DecodeZigZag32(uint n) 903 { 904 return (int)(n >> 1) ^ -(int)(n & 1); 905 } 906 907 /// <summary> 908 /// Decode a 32-bit value with ZigZag encoding. 909 /// </summary> 910 /// <remarks> 911 /// ZigZag encodes signed integers into values that can be efficiently 912 /// encoded with varint. (Otherwise, negative values must be 913 /// sign-extended to 64 bits to be varint encoded, thus always taking 914 /// 10 bytes on the wire.) 915 /// </remarks> DecodeZigZag64(ulong n)916 internal static long DecodeZigZag64(ulong n) 917 { 918 return (long)(n >> 1) ^ -(long)(n & 1); 919 } 920 #endregion 921 922 #region Internal reading and buffer management 923 924 /// <summary> 925 /// Sets currentLimit to (current position) + byteLimit. This is called 926 /// when descending into a length-delimited embedded message. The previous 927 /// limit is returned. 928 /// </summary> 929 /// <returns>The old limit.</returns> PushLimit(int byteLimit)930 internal int PushLimit(int byteLimit) 931 { 932 if (byteLimit < 0) 933 { 934 throw InvalidProtocolBufferException.NegativeSize(); 935 } 936 byteLimit += totalBytesRetired + bufferPos; 937 int oldLimit = currentLimit; 938 if (byteLimit > oldLimit) 939 { 940 throw InvalidProtocolBufferException.TruncatedMessage(); 941 } 942 currentLimit = byteLimit; 943 944 RecomputeBufferSizeAfterLimit(); 945 946 return oldLimit; 947 } 948 RecomputeBufferSizeAfterLimit()949 private void RecomputeBufferSizeAfterLimit() 950 { 951 bufferSize += bufferSizeAfterLimit; 952 int bufferEnd = totalBytesRetired + bufferSize; 953 if (bufferEnd > currentLimit) 954 { 955 // Limit is in current buffer. 956 bufferSizeAfterLimit = bufferEnd - currentLimit; 957 bufferSize -= bufferSizeAfterLimit; 958 } 959 else 960 { 961 bufferSizeAfterLimit = 0; 962 } 963 } 964 965 /// <summary> 966 /// Discards the current limit, returning the previous limit. 967 /// </summary> PopLimit(int oldLimit)968 internal void PopLimit(int oldLimit) 969 { 970 currentLimit = oldLimit; 971 RecomputeBufferSizeAfterLimit(); 972 } 973 974 /// <summary> 975 /// Returns whether or not all the data before the limit has been read. 976 /// </summary> 977 /// <returns></returns> 978 internal bool ReachedLimit 979 { 980 get 981 { 982 if (currentLimit == int.MaxValue) 983 { 984 return false; 985 } 986 int currentAbsolutePosition = totalBytesRetired + bufferPos; 987 return currentAbsolutePosition >= currentLimit; 988 } 989 } 990 991 /// <summary> 992 /// Returns true if the stream has reached the end of the input. This is the 993 /// case if either the end of the underlying input source has been reached or 994 /// the stream has reached a limit created using PushLimit. 995 /// </summary> 996 public bool IsAtEnd 997 { 998 get { return bufferPos == bufferSize && !RefillBuffer(false); } 999 } 1000 1001 /// <summary> 1002 /// Called when buffer is empty to read more bytes from the 1003 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that 1004 /// either there will be at least one byte in the buffer when it returns 1005 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false, 1006 /// RefillBuffer() returns false if no more bytes were available. 1007 /// </summary> 1008 /// <param name="mustSucceed"></param> 1009 /// <returns></returns> RefillBuffer(bool mustSucceed)1010 private bool RefillBuffer(bool mustSucceed) 1011 { 1012 if (bufferPos < bufferSize) 1013 { 1014 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty."); 1015 } 1016 1017 if (totalBytesRetired + bufferSize == currentLimit) 1018 { 1019 // Oops, we hit a limit. 1020 if (mustSucceed) 1021 { 1022 throw InvalidProtocolBufferException.TruncatedMessage(); 1023 } 1024 else 1025 { 1026 return false; 1027 } 1028 } 1029 1030 totalBytesRetired += bufferSize; 1031 1032 bufferPos = 0; 1033 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length); 1034 if (bufferSize < 0) 1035 { 1036 throw new InvalidOperationException("Stream.Read returned a negative count"); 1037 } 1038 if (bufferSize == 0) 1039 { 1040 if (mustSucceed) 1041 { 1042 throw InvalidProtocolBufferException.TruncatedMessage(); 1043 } 1044 else 1045 { 1046 return false; 1047 } 1048 } 1049 else 1050 { 1051 RecomputeBufferSizeAfterLimit(); 1052 int totalBytesRead = 1053 totalBytesRetired + bufferSize + bufferSizeAfterLimit; 1054 if (totalBytesRead > sizeLimit || totalBytesRead < 0) 1055 { 1056 throw InvalidProtocolBufferException.SizeLimitExceeded(); 1057 } 1058 return true; 1059 } 1060 } 1061 1062 /// <summary> 1063 /// Read one byte from the input. 1064 /// </summary> 1065 /// <exception cref="InvalidProtocolBufferException"> 1066 /// the end of the stream or the current limit was reached 1067 /// </exception> ReadRawByte()1068 internal byte ReadRawByte() 1069 { 1070 if (bufferPos == bufferSize) 1071 { 1072 RefillBuffer(true); 1073 } 1074 return buffer[bufferPos++]; 1075 } 1076 1077 /// <summary> 1078 /// Reads a fixed size of bytes from the input. 1079 /// </summary> 1080 /// <exception cref="InvalidProtocolBufferException"> 1081 /// the end of the stream or the current limit was reached 1082 /// </exception> ReadRawBytes(int size)1083 internal byte[] ReadRawBytes(int size) 1084 { 1085 if (size < 0) 1086 { 1087 throw InvalidProtocolBufferException.NegativeSize(); 1088 } 1089 1090 if (totalBytesRetired + bufferPos + size > currentLimit) 1091 { 1092 // Read to the end of the stream (up to the current limit) anyway. 1093 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos); 1094 // Then fail. 1095 throw InvalidProtocolBufferException.TruncatedMessage(); 1096 } 1097 1098 if (size <= bufferSize - bufferPos) 1099 { 1100 // We have all the bytes we need already. 1101 byte[] bytes = new byte[size]; 1102 ByteArray.Copy(buffer, bufferPos, bytes, 0, size); 1103 bufferPos += size; 1104 return bytes; 1105 } 1106 else if (size < buffer.Length) 1107 { 1108 // Reading more bytes than are in the buffer, but not an excessive number 1109 // of bytes. We can safely allocate the resulting array ahead of time. 1110 1111 // First copy what we have. 1112 byte[] bytes = new byte[size]; 1113 int pos = bufferSize - bufferPos; 1114 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos); 1115 bufferPos = bufferSize; 1116 1117 // We want to use RefillBuffer() and then copy from the buffer into our 1118 // byte array rather than reading directly into our byte array because 1119 // the input may be unbuffered. 1120 RefillBuffer(true); 1121 1122 while (size - pos > bufferSize) 1123 { 1124 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize); 1125 pos += bufferSize; 1126 bufferPos = bufferSize; 1127 RefillBuffer(true); 1128 } 1129 1130 ByteArray.Copy(buffer, 0, bytes, pos, size - pos); 1131 bufferPos = size - pos; 1132 1133 return bytes; 1134 } 1135 else 1136 { 1137 // The size is very large. For security reasons, we can't allocate the 1138 // entire byte array yet. The size comes directly from the input, so a 1139 // maliciously-crafted message could provide a bogus very large size in 1140 // order to trick the app into allocating a lot of memory. We avoid this 1141 // by allocating and reading only a small chunk at a time, so that the 1142 // malicious message must actually *be* extremely large to cause 1143 // problems. Meanwhile, we limit the allowed size of a message elsewhere. 1144 1145 // Remember the buffer markers since we'll have to copy the bytes out of 1146 // it later. 1147 int originalBufferPos = bufferPos; 1148 int originalBufferSize = bufferSize; 1149 1150 // Mark the current buffer consumed. 1151 totalBytesRetired += bufferSize; 1152 bufferPos = 0; 1153 bufferSize = 0; 1154 1155 // Read all the rest of the bytes we need. 1156 int sizeLeft = size - (originalBufferSize - originalBufferPos); 1157 List<byte[]> chunks = new List<byte[]>(); 1158 1159 while (sizeLeft > 0) 1160 { 1161 byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)]; 1162 int pos = 0; 1163 while (pos < chunk.Length) 1164 { 1165 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos); 1166 if (n <= 0) 1167 { 1168 throw InvalidProtocolBufferException.TruncatedMessage(); 1169 } 1170 totalBytesRetired += n; 1171 pos += n; 1172 } 1173 sizeLeft -= chunk.Length; 1174 chunks.Add(chunk); 1175 } 1176 1177 // OK, got everything. Now concatenate it all into one buffer. 1178 byte[] bytes = new byte[size]; 1179 1180 // Start by copying the leftover bytes from this.buffer. 1181 int newPos = originalBufferSize - originalBufferPos; 1182 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos); 1183 1184 // And now all the chunks. 1185 foreach (byte[] chunk in chunks) 1186 { 1187 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length); 1188 newPos += chunk.Length; 1189 } 1190 1191 // Done. 1192 return bytes; 1193 } 1194 } 1195 1196 /// <summary> 1197 /// Reads and discards <paramref name="size"/> bytes. 1198 /// </summary> 1199 /// <exception cref="InvalidProtocolBufferException">the end of the stream 1200 /// or the current limit was reached</exception> SkipRawBytes(int size)1201 private void SkipRawBytes(int size) 1202 { 1203 if (size < 0) 1204 { 1205 throw InvalidProtocolBufferException.NegativeSize(); 1206 } 1207 1208 if (totalBytesRetired + bufferPos + size > currentLimit) 1209 { 1210 // Read to the end of the stream anyway. 1211 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos); 1212 // Then fail. 1213 throw InvalidProtocolBufferException.TruncatedMessage(); 1214 } 1215 1216 if (size <= bufferSize - bufferPos) 1217 { 1218 // We have all the bytes we need already. 1219 bufferPos += size; 1220 } 1221 else 1222 { 1223 // Skipping more bytes than are in the buffer. First skip what we have. 1224 int pos = bufferSize - bufferPos; 1225 1226 // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize) 1227 // totalBytesRetired += pos; 1228 totalBytesRetired += bufferSize; 1229 1230 bufferPos = 0; 1231 bufferSize = 0; 1232 1233 // Then skip directly from the InputStream for the rest. 1234 if (pos < size) 1235 { 1236 if (input == null) 1237 { 1238 throw InvalidProtocolBufferException.TruncatedMessage(); 1239 } 1240 SkipImpl(size - pos); 1241 totalBytesRetired += size - pos; 1242 } 1243 } 1244 } 1245 1246 /// <summary> 1247 /// Abstraction of skipping to cope with streams which can't really skip. 1248 /// </summary> SkipImpl(int amountToSkip)1249 private void SkipImpl(int amountToSkip) 1250 { 1251 if (input.CanSeek) 1252 { 1253 long previousPosition = input.Position; 1254 input.Position += amountToSkip; 1255 if (input.Position != previousPosition + amountToSkip) 1256 { 1257 throw InvalidProtocolBufferException.TruncatedMessage(); 1258 } 1259 } 1260 else 1261 { 1262 byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)]; 1263 while (amountToSkip > 0) 1264 { 1265 int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip)); 1266 if (bytesRead <= 0) 1267 { 1268 throw InvalidProtocolBufferException.TruncatedMessage(); 1269 } 1270 amountToSkip -= bytesRead; 1271 } 1272 } 1273 } 1274 1275 #endregion 1276 } 1277 }