Index: third_party/protobuf/csharp/src/Google.Protobuf/CodedInputStream.cs |
diff --git a/third_party/protobuf/csharp/src/Google.Protobuf/CodedInputStream.cs b/third_party/protobuf/csharp/src/Google.Protobuf/CodedInputStream.cs |
new file mode 100644 |
index 0000000000000000000000000000000000000000..65d0e710d35af4cd2d64adddc8dd083e6346afce |
--- /dev/null |
+++ b/third_party/protobuf/csharp/src/Google.Protobuf/CodedInputStream.cs |
@@ -0,0 +1,1221 @@ |
+#region Copyright notice and license |
+// Protocol Buffers - Google's data interchange format |
+// Copyright 2008 Google Inc. All rights reserved. |
+// https://developers.google.com/protocol-buffers/ |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+#endregion |
+ |
+using Google.Protobuf.Collections; |
+using System; |
+using System.Collections.Generic; |
+using System.IO; |
+ |
+namespace Google.Protobuf |
+{ |
+ /// <summary> |
+ /// Reads and decodes protocol message fields. |
+ /// </summary> |
+ /// <remarks> |
+ /// <para> |
+ /// This class is generally used by generated code to read appropriate |
+ /// primitives from the stream. It effectively encapsulates the lowest |
+ /// levels of protocol buffer format. |
+ /// </para> |
+ /// <para> |
+ /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/> |
+ /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields. |
+ /// </para> |
+ /// </remarks> |
+ public sealed class CodedInputStream |
+ { |
+ /// <summary> |
+ /// Buffer of data read from the stream or provided at construction time. |
+ /// </summary> |
+ private readonly byte[] buffer; |
+ |
+ /// <summary> |
+ /// The index of the buffer at which we need to refill from the stream (if there is one). |
+ /// </summary> |
+ private int bufferSize; |
+ |
+ private int bufferSizeAfterLimit = 0; |
+ /// <summary> |
+ /// The position within the current buffer (i.e. the next byte to read) |
+ /// </summary> |
+ private int bufferPos = 0; |
+ |
+ /// <summary> |
+ /// The stream to read further input from, or null if the byte array buffer was provided |
+ /// directly on construction, with no further data available. |
+ /// </summary> |
+ private readonly Stream input; |
+ |
+ /// <summary> |
+ /// The last tag we read. 0 indicates we've read to the end of the stream |
+ /// (or haven't read anything yet). |
+ /// </summary> |
+ private uint lastTag = 0; |
+ |
+ /// <summary> |
+ /// The next tag, used to store the value read by PeekTag. |
+ /// </summary> |
+ private uint nextTag = 0; |
+ private bool hasNextTag = false; |
+ |
+ internal const int DefaultRecursionLimit = 64; |
+ internal const int DefaultSizeLimit = 64 << 20; // 64MB |
+ internal const int BufferSize = 4096; |
+ |
+ /// <summary> |
+ /// The total number of bytes read before the current buffer. The |
+ /// total bytes read up to the current position can be computed as |
+ /// totalBytesRetired + bufferPos. |
+ /// </summary> |
+ private int totalBytesRetired = 0; |
+ |
+ /// <summary> |
+ /// The absolute position of the end of the current message. |
+ /// </summary> |
+ private int currentLimit = int.MaxValue; |
+ |
+ private int recursionDepth = 0; |
+ |
+ private readonly int recursionLimit; |
+ private readonly int sizeLimit; |
+ |
+ #region Construction |
+ // Note that the checks are performed such that we don't end up checking obviously-valid things |
+ // like non-null references for arrays we've just created. |
+ |
+ /// <summary> |
+ /// Creates a new CodedInputStream reading data from the given byte array. |
+ /// </summary> |
+ public CodedInputStream(byte[] buffer) : this(null, Preconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length) |
+ { |
+ } |
+ |
+ /// <summary> |
+ /// Creates a new CodedInputStream that reads from the given byte array slice. |
+ /// </summary> |
+ public CodedInputStream(byte[] buffer, int offset, int length) |
+ : this(null, Preconditions.CheckNotNull(buffer, "buffer"), offset, offset + length) |
+ { |
+ if (offset < 0 || offset > buffer.Length) |
+ { |
+ throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer"); |
+ } |
+ if (length < 0 || offset + length > buffer.Length) |
+ { |
+ throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer"); |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Creates a new CodedInputStream reading data from the given stream. |
+ /// </summary> |
+ public CodedInputStream(Stream input) : this(input, new byte[BufferSize], 0, 0) |
+ { |
+ Preconditions.CheckNotNull(input, "input"); |
+ } |
+ |
+ /// <summary> |
+ /// Creates a new CodedInputStream reading data from the given |
+ /// stream and buffer, using the default limits. |
+ /// </summary> |
+ internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize) |
+ { |
+ this.input = input; |
+ this.buffer = buffer; |
+ this.bufferPos = bufferPos; |
+ this.bufferSize = bufferSize; |
+ this.sizeLimit = DefaultSizeLimit; |
+ this.recursionLimit = DefaultRecursionLimit; |
+ } |
+ |
+ /// <summary> |
+ /// Creates a new CodedInputStream reading data from the given |
+ /// stream and buffer, using the specified limits. |
+ /// </summary> |
+ /// <remarks> |
+ /// This chains to the version with the default limits instead of vice versa to avoid |
+ /// having to check that the default values are valid every time. |
+ /// </remarks> |
+ internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit) |
+ : this(input, buffer, bufferPos, bufferSize) |
+ { |
+ if (sizeLimit <= 0) |
+ { |
+ throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive"); |
+ } |
+ if (recursionLimit <= 0) |
+ { |
+ throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive"); |
+ } |
+ this.sizeLimit = sizeLimit; |
+ this.recursionLimit = recursionLimit; |
+ } |
+ #endregion |
+ |
+ /// <summary> |
+ /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading |
+ /// from an input stream. |
+ /// </summary> |
+ /// <remarks> |
+ /// This method exists separately from the constructor to reduce the number of constructor overloads. |
+ /// It is likely to be used considerably less frequently than the constructors, as the default limits |
+ /// are suitable for most use cases. |
+ /// </remarks> |
+ /// <param name="input">The input stream to read from</param> |
+ /// <param name="sizeLimit">The total limit of data to read from the stream.</param> |
+ /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param> |
+ /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size |
+ /// and recursion limits.</returns> |
+ public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit) |
+ { |
+ return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit); |
+ } |
+ |
+ /// <summary> |
+ /// Returns the current position in the input stream, or the position in the input buffer |
+ /// </summary> |
+ public long Position |
+ { |
+ get |
+ { |
+ if (input != null) |
+ { |
+ return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos); |
+ } |
+ return bufferPos; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Returns the last tag read, or 0 if no tags have been read or we've read beyond |
+ /// the end of the stream. |
+ /// </summary> |
+ internal uint LastTag { get { return lastTag; } } |
+ |
+ /// <summary> |
+ /// Returns the size limit for this stream. |
+ /// </summary> |
+ /// <remarks> |
+ /// This limit is applied when reading from the underlying stream, as a sanity check. It is |
+ /// not applied when reading from a byte array data source without an underlying stream. |
+ /// The default value is 64MB. |
+ /// </remarks> |
+ /// <value> |
+ /// The size limit. |
+ /// </value> |
+ public int SizeLimit { get { return sizeLimit; } } |
+ |
+ /// <summary> |
+ /// Returns the recursion limit for this stream. This limit is applied whilst reading messages, |
+ /// to avoid maliciously-recursive data. |
+ /// </summary> |
+ /// <remarks> |
+ /// The default limit is 64. |
+ /// </remarks> |
+ /// <value> |
+ /// The recursion limit for this stream. |
+ /// </value> |
+ public int RecursionLimit { get { return recursionLimit; } } |
+ |
+ #region Validation |
+ /// <summary> |
+ /// Verifies that the last call to ReadTag() returned tag 0 - in other words, |
+ /// we've reached the end of the stream when we expected to. |
+ /// </summary> |
+ /// <exception cref="InvalidProtocolBufferException">The |
+ /// tag read was not the one specified</exception> |
+ internal void CheckReadEndOfStreamTag() |
+ { |
+ if (lastTag != 0) |
+ { |
+ throw InvalidProtocolBufferException.MoreDataAvailable(); |
+ } |
+ } |
+ #endregion |
+ |
+ #region Reading of tags etc |
+ |
+ /// <summary> |
+ /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the |
+ /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the |
+ /// same value.) |
+ /// </summary> |
+ public uint PeekTag() |
+ { |
+ if (hasNextTag) |
+ { |
+ return nextTag; |
+ } |
+ |
+ uint savedLast = lastTag; |
+ nextTag = ReadTag(); |
+ hasNextTag = true; |
+ lastTag = savedLast; // Undo the side effect of ReadTag |
+ return nextTag; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a field tag, returning the tag of 0 for "end of stream". |
+ /// </summary> |
+ /// <remarks> |
+ /// If this method returns 0, it doesn't necessarily mean the end of all |
+ /// the data in this CodedInputStream; it may be the end of the logical stream |
+ /// for an embedded message, for example. |
+ /// </remarks> |
+ /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns> |
+ public uint ReadTag() |
+ { |
+ if (hasNextTag) |
+ { |
+ lastTag = nextTag; |
+ hasNextTag = false; |
+ return lastTag; |
+ } |
+ |
+ // Optimize for the incredibly common case of having at least two bytes left in the buffer, |
+ // and those two bytes being enough to get the tag. This will be true for fields up to 4095. |
+ if (bufferPos + 2 <= bufferSize) |
+ { |
+ int tmp = buffer[bufferPos++]; |
+ if (tmp < 128) |
+ { |
+ lastTag = (uint)tmp; |
+ } |
+ else |
+ { |
+ int result = tmp & 0x7f; |
+ if ((tmp = buffer[bufferPos++]) < 128) |
+ { |
+ result |= tmp << 7; |
+ lastTag = (uint) result; |
+ } |
+ else |
+ { |
+ // Nope, rewind and go the potentially slow route. |
+ bufferPos -= 2; |
+ lastTag = ReadRawVarint32(); |
+ } |
+ } |
+ } |
+ else |
+ { |
+ if (IsAtEnd) |
+ { |
+ lastTag = 0; |
+ return 0; // This is the only case in which we return 0. |
+ } |
+ |
+ lastTag = ReadRawVarint32(); |
+ } |
+ if (lastTag == 0) |
+ { |
+ // If we actually read zero, that's not a valid tag. |
+ throw InvalidProtocolBufferException.InvalidTag(); |
+ } |
+ return lastTag; |
+ } |
+ |
+ /// <summary> |
+ /// Skips the data for the field with the tag we've just read. |
+ /// This should be called directly after <see cref="ReadTag"/>, when |
+ /// the caller wishes to skip an unknown field. |
+ /// </summary> |
+ public void SkipLastField() |
+ { |
+ if (lastTag == 0) |
+ { |
+ throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream"); |
+ } |
+ switch (WireFormat.GetTagWireType(lastTag)) |
+ { |
+ case WireFormat.WireType.StartGroup: |
+ SkipGroup(); |
+ break; |
+ case WireFormat.WireType.EndGroup: |
+ // Just ignore; there's no data following the tag. |
+ break; |
+ case WireFormat.WireType.Fixed32: |
+ ReadFixed32(); |
+ break; |
+ case WireFormat.WireType.Fixed64: |
+ ReadFixed64(); |
+ break; |
+ case WireFormat.WireType.LengthDelimited: |
+ var length = ReadLength(); |
+ SkipRawBytes(length); |
+ break; |
+ case WireFormat.WireType.Varint: |
+ ReadRawVarint32(); |
+ break; |
+ } |
+ } |
+ |
+ private void SkipGroup() |
+ { |
+ // Note: Currently we expect this to be the way that groups are read. We could put the recursion |
+ // depth changes into the ReadTag method instead, potentially... |
+ recursionDepth++; |
+ if (recursionDepth >= recursionLimit) |
+ { |
+ throw InvalidProtocolBufferException.RecursionLimitExceeded(); |
+ } |
+ uint tag; |
+ do |
+ { |
+ tag = ReadTag(); |
+ if (tag == 0) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ // This recursion will allow us to handle nested groups. |
+ SkipLastField(); |
+ } while (WireFormat.GetTagWireType(tag) != WireFormat.WireType.EndGroup); |
+ recursionDepth--; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a double field from the stream. |
+ /// </summary> |
+ public double ReadDouble() |
+ { |
+ return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64()); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a float field from the stream. |
+ /// </summary> |
+ public float ReadFloat() |
+ { |
+ if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos) |
+ { |
+ float ret = BitConverter.ToSingle(buffer, bufferPos); |
+ bufferPos += 4; |
+ return ret; |
+ } |
+ else |
+ { |
+ byte[] rawBytes = ReadRawBytes(4); |
+ if (!BitConverter.IsLittleEndian) |
+ { |
+ ByteArray.Reverse(rawBytes); |
+ } |
+ return BitConverter.ToSingle(rawBytes, 0); |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Reads a uint64 field from the stream. |
+ /// </summary> |
+ public ulong ReadUInt64() |
+ { |
+ return ReadRawVarint64(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an int64 field from the stream. |
+ /// </summary> |
+ public long ReadInt64() |
+ { |
+ return (long) ReadRawVarint64(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an int32 field from the stream. |
+ /// </summary> |
+ public int ReadInt32() |
+ { |
+ return (int) ReadRawVarint32(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a fixed64 field from the stream. |
+ /// </summary> |
+ public ulong ReadFixed64() |
+ { |
+ return ReadRawLittleEndian64(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a fixed32 field from the stream. |
+ /// </summary> |
+ public uint ReadFixed32() |
+ { |
+ return ReadRawLittleEndian32(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a bool field from the stream. |
+ /// </summary> |
+ public bool ReadBool() |
+ { |
+ return ReadRawVarint32() != 0; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a string field from the stream. |
+ /// </summary> |
+ public string ReadString() |
+ { |
+ int length = ReadLength(); |
+ // No need to read any data for an empty string. |
+ if (length == 0) |
+ { |
+ return ""; |
+ } |
+ if (length <= bufferSize - bufferPos) |
+ { |
+ // Fast path: We already have the bytes in a contiguous buffer, so |
+ // just copy directly from it. |
+ String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length); |
+ bufferPos += length; |
+ return result; |
+ } |
+ // Slow path: Build a byte array first then copy it. |
+ return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an embedded message field value from the stream. |
+ /// </summary> |
+ public void ReadMessage(IMessage builder) |
+ { |
+ int length = ReadLength(); |
+ if (recursionDepth >= recursionLimit) |
+ { |
+ throw InvalidProtocolBufferException.RecursionLimitExceeded(); |
+ } |
+ int oldLimit = PushLimit(length); |
+ ++recursionDepth; |
+ builder.MergeFrom(this); |
+ CheckReadEndOfStreamTag(); |
+ // Check that we've read exactly as much data as expected. |
+ if (!ReachedLimit) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ --recursionDepth; |
+ PopLimit(oldLimit); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a bytes field value from the stream. |
+ /// </summary> |
+ public ByteString ReadBytes() |
+ { |
+ int length = ReadLength(); |
+ if (length <= bufferSize - bufferPos && length > 0) |
+ { |
+ // Fast path: We already have the bytes in a contiguous buffer, so |
+ // just copy directly from it. |
+ ByteString result = ByteString.CopyFrom(buffer, bufferPos, length); |
+ bufferPos += length; |
+ return result; |
+ } |
+ else |
+ { |
+ // Slow path: Build a byte array and attach it to a new ByteString. |
+ return ByteString.AttachBytes(ReadRawBytes(length)); |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Reads a uint32 field value from the stream. |
+ /// </summary> |
+ public uint ReadUInt32() |
+ { |
+ return ReadRawVarint32(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an enum field value from the stream. If the enum is valid for type T, |
+ /// then the ref value is set and it returns true. Otherwise the unknown output |
+ /// value is set and this method returns false. |
+ /// </summary> |
+ public int ReadEnum() |
+ { |
+ // Currently just a pass-through, but it's nice to separate it logically from WriteInt32. |
+ return (int) ReadRawVarint32(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an sfixed32 field value from the stream. |
+ /// </summary> |
+ public int ReadSFixed32() |
+ { |
+ return (int) ReadRawLittleEndian32(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an sfixed64 field value from the stream. |
+ /// </summary> |
+ public long ReadSFixed64() |
+ { |
+ return (long) ReadRawLittleEndian64(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an sint32 field value from the stream. |
+ /// </summary> |
+ public int ReadSInt32() |
+ { |
+ return DecodeZigZag32(ReadRawVarint32()); |
+ } |
+ |
+ /// <summary> |
+ /// Reads an sint64 field value from the stream. |
+ /// </summary> |
+ public long ReadSInt64() |
+ { |
+ return DecodeZigZag64(ReadRawVarint64()); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a length for length-delimited data. |
+ /// </summary> |
+ /// <remarks> |
+ /// This is internally just reading a varint, but this method exists |
+ /// to make the calling code clearer. |
+ /// </remarks> |
+ public int ReadLength() |
+ { |
+ return (int) ReadRawVarint32(); |
+ } |
+ |
+ /// <summary> |
+ /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>, |
+ /// the tag is consumed and the method returns <c>true</c>; otherwise, the |
+ /// stream is left in the original position and the method returns <c>false</c>. |
+ /// </summary> |
+ public bool MaybeConsumeTag(uint tag) |
+ { |
+ if (PeekTag() == tag) |
+ { |
+ hasNextTag = false; |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ #endregion |
+ |
+ #region Underlying reading primitives |
+ |
+ /// <summary> |
+ /// Same code as ReadRawVarint32, but read each byte individually, checking for |
+ /// buffer overflow. |
+ /// </summary> |
+ private uint SlowReadRawVarint32() |
+ { |
+ int tmp = ReadRawByte(); |
+ if (tmp < 128) |
+ { |
+ return (uint) tmp; |
+ } |
+ int result = tmp & 0x7f; |
+ if ((tmp = ReadRawByte()) < 128) |
+ { |
+ result |= tmp << 7; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 7; |
+ if ((tmp = ReadRawByte()) < 128) |
+ { |
+ result |= tmp << 14; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 14; |
+ if ((tmp = ReadRawByte()) < 128) |
+ { |
+ result |= tmp << 21; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 21; |
+ result |= (tmp = ReadRawByte()) << 28; |
+ if (tmp >= 128) |
+ { |
+ // Discard upper 32 bits. |
+ for (int i = 0; i < 5; i++) |
+ { |
+ if (ReadRawByte() < 128) |
+ { |
+ return (uint) result; |
+ } |
+ } |
+ throw InvalidProtocolBufferException.MalformedVarint(); |
+ } |
+ } |
+ } |
+ } |
+ return (uint) result; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits. |
+ /// This method is optimised for the case where we've got lots of data in the buffer. |
+ /// That means we can check the size just once, then just read directly from the buffer |
+ /// without constant rechecking of the buffer length. |
+ /// </summary> |
+ internal uint ReadRawVarint32() |
+ { |
+ if (bufferPos + 5 > bufferSize) |
+ { |
+ return SlowReadRawVarint32(); |
+ } |
+ |
+ int tmp = buffer[bufferPos++]; |
+ if (tmp < 128) |
+ { |
+ return (uint) tmp; |
+ } |
+ int result = tmp & 0x7f; |
+ if ((tmp = buffer[bufferPos++]) < 128) |
+ { |
+ result |= tmp << 7; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 7; |
+ if ((tmp = buffer[bufferPos++]) < 128) |
+ { |
+ result |= tmp << 14; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 14; |
+ if ((tmp = buffer[bufferPos++]) < 128) |
+ { |
+ result |= tmp << 21; |
+ } |
+ else |
+ { |
+ result |= (tmp & 0x7f) << 21; |
+ result |= (tmp = buffer[bufferPos++]) << 28; |
+ if (tmp >= 128) |
+ { |
+ // Discard upper 32 bits. |
+ // Note that this has to use ReadRawByte() as we only ensure we've |
+ // got at least 5 bytes at the start of the method. This lets us |
+ // use the fast path in more cases, and we rarely hit this section of code. |
+ for (int i = 0; i < 5; i++) |
+ { |
+ if (ReadRawByte() < 128) |
+ { |
+ return (uint) result; |
+ } |
+ } |
+ throw InvalidProtocolBufferException.MalformedVarint(); |
+ } |
+ } |
+ } |
+ } |
+ return (uint) result; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a varint from the input one byte at a time, so that it does not |
+ /// read any bytes after the end of the varint. If you simply wrapped the |
+ /// stream in a CodedInputStream and used ReadRawVarint32(Stream) |
+ /// then you would probably end up reading past the end of the varint since |
+ /// CodedInputStream buffers its input. |
+ /// </summary> |
+ /// <param name="input"></param> |
+ /// <returns></returns> |
+ internal static uint ReadRawVarint32(Stream input) |
+ { |
+ int result = 0; |
+ int offset = 0; |
+ for (; offset < 32; offset += 7) |
+ { |
+ int b = input.ReadByte(); |
+ if (b == -1) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ result |= (b & 0x7f) << offset; |
+ if ((b & 0x80) == 0) |
+ { |
+ return (uint) result; |
+ } |
+ } |
+ // Keep reading up to 64 bits. |
+ for (; offset < 64; offset += 7) |
+ { |
+ int b = input.ReadByte(); |
+ if (b == -1) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ if ((b & 0x80) == 0) |
+ { |
+ return (uint) result; |
+ } |
+ } |
+ throw InvalidProtocolBufferException.MalformedVarint(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a raw varint from the stream. |
+ /// </summary> |
+ internal ulong ReadRawVarint64() |
+ { |
+ int shift = 0; |
+ ulong result = 0; |
+ while (shift < 64) |
+ { |
+ byte b = ReadRawByte(); |
+ result |= (ulong) (b & 0x7F) << shift; |
+ if ((b & 0x80) == 0) |
+ { |
+ return result; |
+ } |
+ shift += 7; |
+ } |
+ throw InvalidProtocolBufferException.MalformedVarint(); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a 32-bit little-endian integer from the stream. |
+ /// </summary> |
+ internal uint ReadRawLittleEndian32() |
+ { |
+ uint b1 = ReadRawByte(); |
+ uint b2 = ReadRawByte(); |
+ uint b3 = ReadRawByte(); |
+ uint b4 = ReadRawByte(); |
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); |
+ } |
+ |
+ /// <summary> |
+ /// Reads a 64-bit little-endian integer from the stream. |
+ /// </summary> |
+ internal ulong ReadRawLittleEndian64() |
+ { |
+ ulong b1 = ReadRawByte(); |
+ ulong b2 = ReadRawByte(); |
+ ulong b3 = ReadRawByte(); |
+ ulong b4 = ReadRawByte(); |
+ ulong b5 = ReadRawByte(); |
+ ulong b6 = ReadRawByte(); |
+ ulong b7 = ReadRawByte(); |
+ ulong b8 = ReadRawByte(); |
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) |
+ | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); |
+ } |
+ |
+ /// <summary> |
+ /// Decode a 32-bit value with ZigZag encoding. |
+ /// </summary> |
+ /// <remarks> |
+ /// ZigZag encodes signed integers into values that can be efficiently |
+ /// encoded with varint. (Otherwise, negative values must be |
+ /// sign-extended to 64 bits to be varint encoded, thus always taking |
+ /// 10 bytes on the wire.) |
+ /// </remarks> |
+ internal static int DecodeZigZag32(uint n) |
+ { |
+ return (int)(n >> 1) ^ -(int)(n & 1); |
+ } |
+ |
+ /// <summary> |
+ /// Decode a 32-bit value with ZigZag encoding. |
+ /// </summary> |
+ /// <remarks> |
+ /// ZigZag encodes signed integers into values that can be efficiently |
+ /// encoded with varint. (Otherwise, negative values must be |
+ /// sign-extended to 64 bits to be varint encoded, thus always taking |
+ /// 10 bytes on the wire.) |
+ /// </remarks> |
+ internal static long DecodeZigZag64(ulong n) |
+ { |
+ return (long)(n >> 1) ^ -(long)(n & 1); |
+ } |
+ #endregion |
+ |
+ #region Internal reading and buffer management |
+ |
+ /// <summary> |
+ /// Sets currentLimit to (current position) + byteLimit. This is called |
+ /// when descending into a length-delimited embedded message. The previous |
+ /// limit is returned. |
+ /// </summary> |
+ /// <returns>The old limit.</returns> |
+ internal int PushLimit(int byteLimit) |
+ { |
+ if (byteLimit < 0) |
+ { |
+ throw InvalidProtocolBufferException.NegativeSize(); |
+ } |
+ byteLimit += totalBytesRetired + bufferPos; |
+ int oldLimit = currentLimit; |
+ if (byteLimit > oldLimit) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ currentLimit = byteLimit; |
+ |
+ RecomputeBufferSizeAfterLimit(); |
+ |
+ return oldLimit; |
+ } |
+ |
+ private void RecomputeBufferSizeAfterLimit() |
+ { |
+ bufferSize += bufferSizeAfterLimit; |
+ int bufferEnd = totalBytesRetired + bufferSize; |
+ if (bufferEnd > currentLimit) |
+ { |
+ // Limit is in current buffer. |
+ bufferSizeAfterLimit = bufferEnd - currentLimit; |
+ bufferSize -= bufferSizeAfterLimit; |
+ } |
+ else |
+ { |
+ bufferSizeAfterLimit = 0; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Discards the current limit, returning the previous limit. |
+ /// </summary> |
+ internal void PopLimit(int oldLimit) |
+ { |
+ currentLimit = oldLimit; |
+ RecomputeBufferSizeAfterLimit(); |
+ } |
+ |
+ /// <summary> |
+ /// Returns whether or not all the data before the limit has been read. |
+ /// </summary> |
+ /// <returns></returns> |
+ internal bool ReachedLimit |
+ { |
+ get |
+ { |
+ if (currentLimit == int.MaxValue) |
+ { |
+ return false; |
+ } |
+ int currentAbsolutePosition = totalBytesRetired + bufferPos; |
+ return currentAbsolutePosition >= currentLimit; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Returns true if the stream has reached the end of the input. This is the |
+ /// case if either the end of the underlying input source has been reached or |
+ /// the stream has reached a limit created using PushLimit. |
+ /// </summary> |
+ public bool IsAtEnd |
+ { |
+ get { return bufferPos == bufferSize && !RefillBuffer(false); } |
+ } |
+ |
+ /// <summary> |
+ /// Called when buffer is empty to read more bytes from the |
+ /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that |
+ /// either there will be at least one byte in the buffer when it returns |
+ /// or it will throw an exception. If <paramref name="mustSucceed"/> is false, |
+ /// RefillBuffer() returns false if no more bytes were available. |
+ /// </summary> |
+ /// <param name="mustSucceed"></param> |
+ /// <returns></returns> |
+ private bool RefillBuffer(bool mustSucceed) |
+ { |
+ if (bufferPos < bufferSize) |
+ { |
+ throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty."); |
+ } |
+ |
+ if (totalBytesRetired + bufferSize == currentLimit) |
+ { |
+ // Oops, we hit a limit. |
+ if (mustSucceed) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ else |
+ { |
+ return false; |
+ } |
+ } |
+ |
+ totalBytesRetired += bufferSize; |
+ |
+ bufferPos = 0; |
+ bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length); |
+ if (bufferSize < 0) |
+ { |
+ throw new InvalidOperationException("Stream.Read returned a negative count"); |
+ } |
+ if (bufferSize == 0) |
+ { |
+ if (mustSucceed) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ else |
+ { |
+ return false; |
+ } |
+ } |
+ else |
+ { |
+ RecomputeBufferSizeAfterLimit(); |
+ int totalBytesRead = |
+ totalBytesRetired + bufferSize + bufferSizeAfterLimit; |
+ if (totalBytesRead > sizeLimit || totalBytesRead < 0) |
+ { |
+ throw InvalidProtocolBufferException.SizeLimitExceeded(); |
+ } |
+ return true; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Read one byte from the input. |
+ /// </summary> |
+ /// <exception cref="InvalidProtocolBufferException"> |
+ /// the end of the stream or the current limit was reached |
+ /// </exception> |
+ internal byte ReadRawByte() |
+ { |
+ if (bufferPos == bufferSize) |
+ { |
+ RefillBuffer(true); |
+ } |
+ return buffer[bufferPos++]; |
+ } |
+ |
+ /// <summary> |
+ /// Reads a fixed size of bytes from the input. |
+ /// </summary> |
+ /// <exception cref="InvalidProtocolBufferException"> |
+ /// the end of the stream or the current limit was reached |
+ /// </exception> |
+ internal byte[] ReadRawBytes(int size) |
+ { |
+ if (size < 0) |
+ { |
+ throw InvalidProtocolBufferException.NegativeSize(); |
+ } |
+ |
+ if (totalBytesRetired + bufferPos + size > currentLimit) |
+ { |
+ // Read to the end of the stream (up to the current limit) anyway. |
+ SkipRawBytes(currentLimit - totalBytesRetired - bufferPos); |
+ // Then fail. |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ |
+ if (size <= bufferSize - bufferPos) |
+ { |
+ // We have all the bytes we need already. |
+ byte[] bytes = new byte[size]; |
+ ByteArray.Copy(buffer, bufferPos, bytes, 0, size); |
+ bufferPos += size; |
+ return bytes; |
+ } |
+ else if (size < buffer.Length) |
+ { |
+ // Reading more bytes than are in the buffer, but not an excessive number |
+ // of bytes. We can safely allocate the resulting array ahead of time. |
+ |
+ // First copy what we have. |
+ byte[] bytes = new byte[size]; |
+ int pos = bufferSize - bufferPos; |
+ ByteArray.Copy(buffer, bufferPos, bytes, 0, pos); |
+ bufferPos = bufferSize; |
+ |
+ // We want to use RefillBuffer() and then copy from the buffer into our |
+ // byte array rather than reading directly into our byte array because |
+ // the input may be unbuffered. |
+ RefillBuffer(true); |
+ |
+ while (size - pos > bufferSize) |
+ { |
+ Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize); |
+ pos += bufferSize; |
+ bufferPos = bufferSize; |
+ RefillBuffer(true); |
+ } |
+ |
+ ByteArray.Copy(buffer, 0, bytes, pos, size - pos); |
+ bufferPos = size - pos; |
+ |
+ return bytes; |
+ } |
+ else |
+ { |
+ // The size is very large. For security reasons, we can't allocate the |
+ // entire byte array yet. The size comes directly from the input, so a |
+ // maliciously-crafted message could provide a bogus very large size in |
+ // order to trick the app into allocating a lot of memory. We avoid this |
+ // by allocating and reading only a small chunk at a time, so that the |
+ // malicious message must actually *be* extremely large to cause |
+ // problems. Meanwhile, we limit the allowed size of a message elsewhere. |
+ |
+ // Remember the buffer markers since we'll have to copy the bytes out of |
+ // it later. |
+ int originalBufferPos = bufferPos; |
+ int originalBufferSize = bufferSize; |
+ |
+ // Mark the current buffer consumed. |
+ totalBytesRetired += bufferSize; |
+ bufferPos = 0; |
+ bufferSize = 0; |
+ |
+ // Read all the rest of the bytes we need. |
+ int sizeLeft = size - (originalBufferSize - originalBufferPos); |
+ List<byte[]> chunks = new List<byte[]>(); |
+ |
+ while (sizeLeft > 0) |
+ { |
+ byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)]; |
+ int pos = 0; |
+ while (pos < chunk.Length) |
+ { |
+ int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos); |
+ if (n <= 0) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ totalBytesRetired += n; |
+ pos += n; |
+ } |
+ sizeLeft -= chunk.Length; |
+ chunks.Add(chunk); |
+ } |
+ |
+ // OK, got everything. Now concatenate it all into one buffer. |
+ byte[] bytes = new byte[size]; |
+ |
+ // Start by copying the leftover bytes from this.buffer. |
+ int newPos = originalBufferSize - originalBufferPos; |
+ ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos); |
+ |
+ // And now all the chunks. |
+ foreach (byte[] chunk in chunks) |
+ { |
+ Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length); |
+ newPos += chunk.Length; |
+ } |
+ |
+ // Done. |
+ return bytes; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Reads and discards <paramref name="size"/> bytes. |
+ /// </summary> |
+ /// <exception cref="InvalidProtocolBufferException">the end of the stream |
+ /// or the current limit was reached</exception> |
+ private void SkipRawBytes(int size) |
+ { |
+ if (size < 0) |
+ { |
+ throw InvalidProtocolBufferException.NegativeSize(); |
+ } |
+ |
+ if (totalBytesRetired + bufferPos + size > currentLimit) |
+ { |
+ // Read to the end of the stream anyway. |
+ SkipRawBytes(currentLimit - totalBytesRetired - bufferPos); |
+ // Then fail. |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ |
+ if (size <= bufferSize - bufferPos) |
+ { |
+ // We have all the bytes we need already. |
+ bufferPos += size; |
+ } |
+ else |
+ { |
+ // Skipping more bytes than are in the buffer. First skip what we have. |
+ int pos = bufferSize - bufferPos; |
+ |
+ // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize) |
+ // totalBytesRetired += pos; |
+ totalBytesRetired += bufferSize; |
+ |
+ bufferPos = 0; |
+ bufferSize = 0; |
+ |
+ // Then skip directly from the InputStream for the rest. |
+ if (pos < size) |
+ { |
+ if (input == null) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ SkipImpl(size - pos); |
+ totalBytesRetired += size - pos; |
+ } |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Abstraction of skipping to cope with streams which can't really skip. |
+ /// </summary> |
+ private void SkipImpl(int amountToSkip) |
+ { |
+ if (input.CanSeek) |
+ { |
+ long previousPosition = input.Position; |
+ input.Position += amountToSkip; |
+ if (input.Position != previousPosition + amountToSkip) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ } |
+ else |
+ { |
+ byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)]; |
+ while (amountToSkip > 0) |
+ { |
+ int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip)); |
+ if (bytesRead <= 0) |
+ { |
+ throw InvalidProtocolBufferException.TruncatedMessage(); |
+ } |
+ amountToSkip -= bytesRead; |
+ } |
+ } |
+ } |
+ |
+ #endregion |
+ } |
+} |