OLD | NEW |
(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 } |
OLD | NEW |