|
@@ -37,7 +37,6 @@
|
|
using System;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO;
|
|
-using Google.Protobuf.Collections;
|
|
|
|
|
|
|
|
namespace Google.Protobuf
|
|
namespace Google.Protobuf
|
|
{
|
|
{
|
|
@@ -172,8 +171,67 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- #region Validation
|
|
|
|
|
|
+ /// <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; } }
|
|
|
|
+
|
|
|
|
+ #region Limits for recursion and length
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Set the maximum message recursion depth.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// In order to prevent malicious
|
|
|
|
+ /// messages from causing stack overflows, CodedInputStream limits
|
|
|
|
+ /// how deeply messages may be nested. The default limit is 64.
|
|
|
|
+ /// </remarks>
|
|
|
|
+ public int SetRecursionLimit(int limit)
|
|
|
|
+ {
|
|
|
|
+ if (limit < 0)
|
|
|
|
+ {
|
|
|
|
+ throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
|
|
|
|
+ }
|
|
|
|
+ int oldLimit = recursionLimit;
|
|
|
|
+ recursionLimit = limit;
|
|
|
|
+ return oldLimit;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Set the maximum message size.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// In order to prevent malicious messages from exhausting memory or
|
|
|
|
+ /// causing integer overflows, CodedInputStream limits how large a message may be.
|
|
|
|
+ /// The default limit is 64MB. You should set this limit as small
|
|
|
|
+ /// as you can without harming your app's functionality. Note that
|
|
|
|
+ /// size limits only apply when reading from an InputStream, not
|
|
|
|
+ /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
|
|
|
|
+ /// If you want to read several messages from a single CodedInputStream, you
|
|
|
|
+ /// can call ResetSizeCounter() after each message to avoid hitting the
|
|
|
|
+ /// size limit.
|
|
|
|
+ /// </remarks>
|
|
|
|
+ public int SetSizeLimit(int limit)
|
|
|
|
+ {
|
|
|
|
+ if (limit < 0)
|
|
|
|
+ {
|
|
|
|
+ throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
|
|
|
|
+ }
|
|
|
|
+ int oldLimit = sizeLimit;
|
|
|
|
+ sizeLimit = limit;
|
|
|
|
+ return oldLimit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Resets the current size counter to zero (see <see cref="SetSizeLimit"/>).
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void ResetSizeCounter()
|
|
|
|
+ {
|
|
|
|
+ totalBytesRetired = 0;
|
|
|
|
+ }
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Validation
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Verifies that the last call to ReadTag() returned the given tag value.
|
|
/// Verifies that the last call to ReadTag() returned the given tag value.
|
|
/// This is used to verify that a nested group ended with the correct
|
|
/// This is used to verify that a nested group ended with the correct
|
|
@@ -188,13 +246,12 @@ namespace Google.Protobuf
|
|
throw InvalidProtocolBufferException.InvalidEndTag();
|
|
throw InvalidProtocolBufferException.InvalidEndTag();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
#region Reading of tags etc
|
|
#region Reading of tags etc
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Attempt to peek at the next field tag.
|
|
|
|
|
|
+ /// Attempts to peek at the next field tag.
|
|
/// </summary>
|
|
/// </summary>
|
|
public bool PeekNextTag(out uint fieldTag)
|
|
public bool PeekNextTag(out uint fieldTag)
|
|
{
|
|
{
|
|
@@ -212,7 +269,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Attempt to read a field tag, returning false if we have reached the end
|
|
|
|
|
|
+ /// Attempts to read a field tag, returning false if we have reached the end
|
|
/// of the input data.
|
|
/// of the input data.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
|
|
/// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
|
|
@@ -227,14 +284,42 @@ namespace Google.Protobuf
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- if (IsAtEnd)
|
|
|
|
|
|
+ // 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)
|
|
{
|
|
{
|
|
- fieldTag = 0;
|
|
|
|
- lastTag = fieldTag;
|
|
|
|
- return false;
|
|
|
|
|
|
+ int tmp = buffer[bufferPos++];
|
|
|
|
+ if (tmp < 128)
|
|
|
|
+ {
|
|
|
|
+ fieldTag = (uint)tmp;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ int result = tmp & 0x7f;
|
|
|
|
+ if ((tmp = buffer[bufferPos++]) < 128)
|
|
|
|
+ {
|
|
|
|
+ result |= tmp << 7;
|
|
|
|
+ fieldTag = (uint) result;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ // Nope, rewind and go the potentially slow route.
|
|
|
|
+ bufferPos -= 2;
|
|
|
|
+ fieldTag = ReadRawVarint32();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ if (IsAtEnd)
|
|
|
|
+ {
|
|
|
|
+ fieldTag = 0;
|
|
|
|
+ lastTag = fieldTag;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
|
|
- fieldTag = ReadRawVarint32();
|
|
|
|
|
|
+ fieldTag = ReadRawVarint32();
|
|
|
|
+ }
|
|
lastTag = fieldTag;
|
|
lastTag = fieldTag;
|
|
if (lastTag == 0)
|
|
if (lastTag == 0)
|
|
{
|
|
{
|
|
@@ -245,7 +330,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a double field from the stream.
|
|
|
|
|
|
+ /// Reads a double field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public double ReadDouble()
|
|
public double ReadDouble()
|
|
{
|
|
{
|
|
@@ -253,7 +338,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a float field from the stream.
|
|
|
|
|
|
+ /// Reads a float field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public float ReadFloat()
|
|
public float ReadFloat()
|
|
{
|
|
{
|
|
@@ -275,7 +360,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a uint64 field from the stream.
|
|
|
|
|
|
+ /// Reads a uint64 field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public ulong ReadUInt64()
|
|
public ulong ReadUInt64()
|
|
{
|
|
{
|
|
@@ -283,7 +368,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read an int64 field from the stream.
|
|
|
|
|
|
+ /// Reads an int64 field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public long ReadInt64()
|
|
public long ReadInt64()
|
|
{
|
|
{
|
|
@@ -291,7 +376,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read an int32 field from the stream.
|
|
|
|
|
|
+ /// Reads an int32 field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public int ReadInt32()
|
|
public int ReadInt32()
|
|
{
|
|
{
|
|
@@ -299,7 +384,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a fixed64 field from the stream.
|
|
|
|
|
|
+ /// Reads a fixed64 field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public ulong ReadFixed64()
|
|
public ulong ReadFixed64()
|
|
{
|
|
{
|
|
@@ -307,7 +392,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a fixed32 field from the stream.
|
|
|
|
|
|
+ /// Reads a fixed32 field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public uint ReadFixed32()
|
|
public uint ReadFixed32()
|
|
{
|
|
{
|
|
@@ -315,7 +400,7 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a bool field from the stream.
|
|
|
|
|
|
+ /// Reads a bool field from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
public bool ReadBool()
|
|
public bool ReadBool()
|
|
{
|
|
{
|
|
@@ -327,22 +412,22 @@ namespace Google.Protobuf
|
|
/// </summary>
|
|
/// </summary>
|
|
public string ReadString()
|
|
public string ReadString()
|
|
{
|
|
{
|
|
- int size = (int) ReadRawVarint32();
|
|
|
|
|
|
+ int length = ReadLength();
|
|
// No need to read any data for an empty string.
|
|
// No need to read any data for an empty string.
|
|
- if (size == 0)
|
|
|
|
|
|
+ if (length == 0)
|
|
{
|
|
{
|
|
return "";
|
|
return "";
|
|
}
|
|
}
|
|
- if (size <= bufferSize - bufferPos)
|
|
|
|
|
|
+ if (length <= bufferSize - bufferPos)
|
|
{
|
|
{
|
|
// Fast path: We already have the bytes in a contiguous buffer, so
|
|
// Fast path: We already have the bytes in a contiguous buffer, so
|
|
// just copy directly from it.
|
|
// just copy directly from it.
|
|
- String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, size);
|
|
|
|
- bufferPos += size;
|
|
|
|
|
|
+ String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
|
|
|
|
+ bufferPos += length;
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
// Slow path: Build a byte array first then copy it.
|
|
// Slow path: Build a byte array first then copy it.
|
|
- return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(size), 0, size);
|
|
|
|
|
|
+ return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -350,7 +435,7 @@ namespace Google.Protobuf
|
|
/// </summary>
|
|
/// </summary>
|
|
public void ReadMessage(IMessage builder)
|
|
public void ReadMessage(IMessage builder)
|
|
{
|
|
{
|
|
- int length = (int) ReadRawVarint32();
|
|
|
|
|
|
+ int length = ReadLength();
|
|
if (recursionDepth >= recursionLimit)
|
|
if (recursionDepth >= recursionLimit)
|
|
{
|
|
{
|
|
throw InvalidProtocolBufferException.RecursionLimitExceeded();
|
|
throw InvalidProtocolBufferException.RecursionLimitExceeded();
|
|
@@ -368,19 +453,19 @@ namespace Google.Protobuf
|
|
/// </summary>
|
|
/// </summary>
|
|
public ByteString ReadBytes()
|
|
public ByteString ReadBytes()
|
|
{
|
|
{
|
|
- int size = (int) ReadRawVarint32();
|
|
|
|
- if (size <= bufferSize - bufferPos && size > 0)
|
|
|
|
|
|
+ int length = ReadLength();
|
|
|
|
+ if (length <= bufferSize - bufferPos && length > 0)
|
|
{
|
|
{
|
|
// Fast path: We already have the bytes in a contiguous buffer, so
|
|
// Fast path: We already have the bytes in a contiguous buffer, so
|
|
// just copy directly from it.
|
|
// just copy directly from it.
|
|
- ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
|
|
|
|
- bufferPos += size;
|
|
|
|
|
|
+ ByteString result = ByteString.CopyFrom(buffer, bufferPos, length);
|
|
|
|
+ bufferPos += length;
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
// Slow path: Build a byte array and attach it to a new ByteString.
|
|
// Slow path: Build a byte array and attach it to a new ByteString.
|
|
- return ByteString.AttachBytes(ReadRawBytes(size));
|
|
|
|
|
|
+ return ByteString.AttachBytes(ReadRawBytes(length));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -435,24 +520,16 @@ namespace Google.Protobuf
|
|
return DecodeZigZag64(ReadRawVarint64());
|
|
return DecodeZigZag64(ReadRawVarint64());
|
|
}
|
|
}
|
|
|
|
|
|
- private bool BeginArray(uint fieldTag, out bool isPacked, out int oldLimit)
|
|
|
|
|
|
+ /// <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()
|
|
{
|
|
{
|
|
- isPacked = WireFormat.GetTagWireType(fieldTag) == WireFormat.WireType.LengthDelimited;
|
|
|
|
-
|
|
|
|
- if (isPacked)
|
|
|
|
- {
|
|
|
|
- int length = (int) (ReadRawVarint32() & int.MaxValue);
|
|
|
|
- if (length > 0)
|
|
|
|
- {
|
|
|
|
- oldLimit = PushLimit(length);
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- oldLimit = -1;
|
|
|
|
- return false; //packed but empty
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- oldLimit = -1;
|
|
|
|
- return true;
|
|
|
|
|
|
+ return (int) ReadRawVarint32();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -474,268 +551,6 @@ namespace Google.Protobuf
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Returns true if the next tag is also part of the same array, which may or may not be packed.
|
|
|
|
- /// </summary>
|
|
|
|
- private bool ContinueArray(uint currentTag, bool packed, int oldLimit)
|
|
|
|
- {
|
|
|
|
- if (packed)
|
|
|
|
- {
|
|
|
|
- if (ReachedLimit)
|
|
|
|
- {
|
|
|
|
- PopLimit(oldLimit);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- return MaybeConsumeTag(currentTag);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Reads a string array.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <remarks>The stream is assumed to be positioned after a tag indicating the field
|
|
|
|
- /// repeated string value. A string is read, and then if the next tag is the same,
|
|
|
|
- /// the process is repeated, until the next tag is a different one.</remarks>
|
|
|
|
- /// <param name="list"></param>
|
|
|
|
- public void ReadStringArray(ICollection<string> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadString());
|
|
|
|
- } while (MaybeConsumeTag(fieldTag));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadBytesArray(ICollection<ByteString> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadBytes());
|
|
|
|
- } while (MaybeConsumeTag(fieldTag));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadBoolArray(ICollection<bool> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadBool());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadInt32Array(ICollection<int> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadInt32());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadSInt32Array(ICollection<int> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadSInt32());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadUInt32Array(ICollection<uint> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadUInt32());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadFixed32Array(ICollection<uint> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadFixed32());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadSFixed32Array(ICollection<int> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadSFixed32());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadInt64Array(ICollection<long> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadInt64());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadSInt64Array(ICollection<long> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadSInt64());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadUInt64Array(ICollection<ulong> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadUInt64());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadFixed64Array(ICollection<ulong> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadFixed64());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadSFixed64Array(ICollection<long> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadSFixed64());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadDoubleArray(ICollection<double> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadDouble());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadFloatArray(ICollection<float> list)
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- bool isPacked;
|
|
|
|
- int holdLimit;
|
|
|
|
- if (BeginArray(fieldTag, out isPacked, out holdLimit))
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add(ReadFloat());
|
|
|
|
- } while (ContinueArray(fieldTag, isPacked, holdLimit));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadEnumArray<T>(RepeatedField<T> list)
|
|
|
|
- where T : struct, IComparable, IFormattable
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
|
|
|
|
-
|
|
|
|
- // 2.3 allows packed form even if the field is not declared packed.
|
|
|
|
- if (wformat == WireFormat.WireType.LengthDelimited)
|
|
|
|
- {
|
|
|
|
- int length = (int) (ReadRawVarint32() & int.MaxValue);
|
|
|
|
- int limit = PushLimit(length);
|
|
|
|
- while (!ReachedLimit)
|
|
|
|
- {
|
|
|
|
- // Ghastly hack, but it works...
|
|
|
|
- list.AddInt32(ReadEnum());
|
|
|
|
- }
|
|
|
|
- PopLimit(limit);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- list.Add((T)(object) ReadEnum());
|
|
|
|
- } while (MaybeConsumeTag(fieldTag));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void ReadMessageArray<T>(ICollection<T> list, MessageParser<T> messageParser)
|
|
|
|
- where T : IMessage<T>
|
|
|
|
- {
|
|
|
|
- uint fieldTag = lastTag;
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- T message = messageParser.CreateTemplate();
|
|
|
|
- ReadMessage(message);
|
|
|
|
- list.Add(message);
|
|
|
|
- } while (MaybeConsumeTag(fieldTag));
|
|
|
|
- }
|
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
#region Underlying reading primitives
|
|
#region Underlying reading primitives
|
|
@@ -793,12 +608,12 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
|
|
|
|
|
|
+ /// 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.
|
|
/// 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
|
|
/// That means we can check the size just once, then just read directly from the buffer
|
|
/// without constant rechecking of the buffer length.
|
|
/// without constant rechecking of the buffer length.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public uint ReadRawVarint32()
|
|
|
|
|
|
+ internal uint ReadRawVarint32()
|
|
{
|
|
{
|
|
if (bufferPos + 5 > bufferSize)
|
|
if (bufferPos + 5 > bufferSize)
|
|
{
|
|
{
|
|
@@ -857,13 +672,13 @@ namespace Google.Protobuf
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Reads a varint from the input one byte at a time, so that it does not
|
|
/// 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
|
|
/// read any bytes after the end of the varint. If you simply wrapped the
|
|
- /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
|
|
|
|
|
|
+ /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
|
|
/// then you would probably end up reading past the end of the varint since
|
|
/// then you would probably end up reading past the end of the varint since
|
|
/// CodedInputStream buffers its input.
|
|
/// CodedInputStream buffers its input.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="input"></param>
|
|
/// <param name="input"></param>
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
- public static uint ReadRawVarint32(Stream input)
|
|
|
|
|
|
+ internal static uint ReadRawVarint32(Stream input)
|
|
{
|
|
{
|
|
int result = 0;
|
|
int result = 0;
|
|
int offset = 0;
|
|
int offset = 0;
|
|
@@ -897,9 +712,9 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a raw varint from the stream.
|
|
|
|
|
|
+ /// Reads a raw varint from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public ulong ReadRawVarint64()
|
|
|
|
|
|
+ internal ulong ReadRawVarint64()
|
|
{
|
|
{
|
|
int shift = 0;
|
|
int shift = 0;
|
|
ulong result = 0;
|
|
ulong result = 0;
|
|
@@ -917,9 +732,9 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a 32-bit little-endian integer from the stream.
|
|
|
|
|
|
+ /// Reads a 32-bit little-endian integer from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public uint ReadRawLittleEndian32()
|
|
|
|
|
|
+ internal uint ReadRawLittleEndian32()
|
|
{
|
|
{
|
|
uint b1 = ReadRawByte();
|
|
uint b1 = ReadRawByte();
|
|
uint b2 = ReadRawByte();
|
|
uint b2 = ReadRawByte();
|
|
@@ -929,9 +744,9 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a 64-bit little-endian integer from the stream.
|
|
|
|
|
|
+ /// Reads a 64-bit little-endian integer from the stream.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public ulong ReadRawLittleEndian64()
|
|
|
|
|
|
+ internal ulong ReadRawLittleEndian64()
|
|
{
|
|
{
|
|
ulong b1 = ReadRawByte();
|
|
ulong b1 = ReadRawByte();
|
|
ulong b2 = ReadRawByte();
|
|
ulong b2 = ReadRawByte();
|
|
@@ -945,8 +760,6 @@ namespace Google.Protobuf
|
|
| (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
|
|
| (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
|
|
}
|
|
}
|
|
|
|
|
|
- #endregion
|
|
|
|
-
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Decode a 32-bit value with ZigZag encoding.
|
|
/// Decode a 32-bit value with ZigZag encoding.
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -956,9 +769,9 @@ namespace Google.Protobuf
|
|
/// sign-extended to 64 bits to be varint encoded, thus always taking
|
|
/// sign-extended to 64 bits to be varint encoded, thus always taking
|
|
/// 10 bytes on the wire.)
|
|
/// 10 bytes on the wire.)
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public static int DecodeZigZag32(uint n)
|
|
|
|
|
|
+ internal static int DecodeZigZag32(uint n)
|
|
{
|
|
{
|
|
- return (int) (n >> 1) ^ -(int) (n & 1);
|
|
|
|
|
|
+ return (int)(n >> 1) ^ -(int)(n & 1);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -970,72 +783,21 @@ namespace Google.Protobuf
|
|
/// sign-extended to 64 bits to be varint encoded, thus always taking
|
|
/// sign-extended to 64 bits to be varint encoded, thus always taking
|
|
/// 10 bytes on the wire.)
|
|
/// 10 bytes on the wire.)
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public static long DecodeZigZag64(ulong n)
|
|
|
|
|
|
+ internal static long DecodeZigZag64(ulong n)
|
|
{
|
|
{
|
|
- return (long) (n >> 1) ^ -(long) (n & 1);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Set the maximum message recursion depth.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// In order to prevent malicious
|
|
|
|
- /// messages from causing stack overflows, CodedInputStream limits
|
|
|
|
- /// how deeply messages may be nested. The default limit is 64.
|
|
|
|
- /// </remarks>
|
|
|
|
- public int SetRecursionLimit(int limit)
|
|
|
|
- {
|
|
|
|
- if (limit < 0)
|
|
|
|
- {
|
|
|
|
- throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
|
|
|
|
- }
|
|
|
|
- int oldLimit = recursionLimit;
|
|
|
|
- recursionLimit = limit;
|
|
|
|
- return oldLimit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Set the maximum message size.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// In order to prevent malicious messages from exhausting memory or
|
|
|
|
- /// causing integer overflows, CodedInputStream limits how large a message may be.
|
|
|
|
- /// The default limit is 64MB. You should set this limit as small
|
|
|
|
- /// as you can without harming your app's functionality. Note that
|
|
|
|
- /// size limits only apply when reading from an InputStream, not
|
|
|
|
- /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
|
|
|
|
- /// If you want to read several messages from a single CodedInputStream, you
|
|
|
|
- /// can call ResetSizeCounter() after each message to avoid hitting the
|
|
|
|
- /// size limit.
|
|
|
|
- /// </remarks>
|
|
|
|
- public int SetSizeLimit(int limit)
|
|
|
|
- {
|
|
|
|
- if (limit < 0)
|
|
|
|
- {
|
|
|
|
- throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
|
|
|
|
- }
|
|
|
|
- int oldLimit = sizeLimit;
|
|
|
|
- sizeLimit = limit;
|
|
|
|
- return oldLimit;
|
|
|
|
|
|
+ return (long)(n >> 1) ^ -(long)(n & 1);
|
|
}
|
|
}
|
|
|
|
+ #endregion
|
|
|
|
|
|
#region Internal reading and buffer management
|
|
#region Internal reading and buffer management
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Resets the current size counter to zero (see SetSizeLimit).
|
|
|
|
- /// </summary>
|
|
|
|
- public void ResetSizeCounter()
|
|
|
|
- {
|
|
|
|
- totalBytesRetired = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Sets currentLimit to (current position) + byteLimit. This is called
|
|
/// Sets currentLimit to (current position) + byteLimit. This is called
|
|
/// when descending into a length-delimited embedded message. The previous
|
|
/// when descending into a length-delimited embedded message. The previous
|
|
/// limit is returned.
|
|
/// limit is returned.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <returns>The old limit.</returns>
|
|
/// <returns>The old limit.</returns>
|
|
- public int PushLimit(int byteLimit)
|
|
|
|
|
|
+ internal int PushLimit(int byteLimit)
|
|
{
|
|
{
|
|
if (byteLimit < 0)
|
|
if (byteLimit < 0)
|
|
{
|
|
{
|
|
@@ -1073,7 +835,7 @@ namespace Google.Protobuf
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Discards the current limit, returning the previous limit.
|
|
/// Discards the current limit, returning the previous limit.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public void PopLimit(int oldLimit)
|
|
|
|
|
|
+ internal void PopLimit(int oldLimit)
|
|
{
|
|
{
|
|
currentLimit = oldLimit;
|
|
currentLimit = oldLimit;
|
|
RecomputeBufferSizeAfterLimit();
|
|
RecomputeBufferSizeAfterLimit();
|
|
@@ -1083,7 +845,7 @@ namespace Google.Protobuf
|
|
/// Returns whether or not all the data before the limit has been read.
|
|
/// Returns whether or not all the data before the limit has been read.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
- public bool ReachedLimit
|
|
|
|
|
|
+ internal bool ReachedLimit
|
|
{
|
|
{
|
|
get
|
|
get
|
|
{
|
|
{
|
|
@@ -1173,7 +935,7 @@ namespace Google.Protobuf
|
|
/// <exception cref="InvalidProtocolBufferException">
|
|
/// <exception cref="InvalidProtocolBufferException">
|
|
/// the end of the stream or the current limit was reached
|
|
/// the end of the stream or the current limit was reached
|
|
/// </exception>
|
|
/// </exception>
|
|
- public byte ReadRawByte()
|
|
|
|
|
|
+ internal byte ReadRawByte()
|
|
{
|
|
{
|
|
if (bufferPos == bufferSize)
|
|
if (bufferPos == bufferSize)
|
|
{
|
|
{
|
|
@@ -1183,12 +945,12 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Read a fixed size of bytes from the input.
|
|
|
|
|
|
+ /// Reads a fixed size of bytes from the input.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <exception cref="InvalidProtocolBufferException">
|
|
/// <exception cref="InvalidProtocolBufferException">
|
|
/// the end of the stream or the current limit was reached
|
|
/// the end of the stream or the current limit was reached
|
|
/// </exception>
|
|
/// </exception>
|
|
- public byte[] ReadRawBytes(int size)
|
|
|
|
|
|
+ internal byte[] ReadRawBytes(int size)
|
|
{
|
|
{
|
|
if (size < 0)
|
|
if (size < 0)
|
|
{
|
|
{
|
|
@@ -1197,7 +959,8 @@ namespace Google.Protobuf
|
|
|
|
|
|
if (totalBytesRetired + bufferPos + size > currentLimit)
|
|
if (totalBytesRetired + bufferPos + size > currentLimit)
|
|
{
|
|
{
|
|
- // Read to the end of the stream anyway.
|
|
|
|
|
|
+ // Read to the end of the stream (up to the current limit) anyway.
|
|
|
|
+ // TODO(jonskeet): This is the only usage of SkipRawBytes. Do we really need to do it?
|
|
SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
|
SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
|
// Then fail.
|
|
// Then fail.
|
|
throw InvalidProtocolBufferException.TruncatedMessage();
|
|
throw InvalidProtocolBufferException.TruncatedMessage();
|
|
@@ -1301,63 +1064,12 @@ namespace Google.Protobuf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Reads and discards a single field, given its tag value.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <returns>false if the tag is an end-group tag, in which case
|
|
|
|
- /// nothing is skipped. Otherwise, returns true.</returns>
|
|
|
|
- public bool SkipField()
|
|
|
|
- {
|
|
|
|
- uint tag = lastTag;
|
|
|
|
- switch (WireFormat.GetTagWireType(tag))
|
|
|
|
- {
|
|
|
|
- case WireFormat.WireType.Varint:
|
|
|
|
- ReadRawVarint64();
|
|
|
|
- return true;
|
|
|
|
- case WireFormat.WireType.Fixed64:
|
|
|
|
- ReadRawLittleEndian64();
|
|
|
|
- return true;
|
|
|
|
- case WireFormat.WireType.LengthDelimited:
|
|
|
|
- SkipRawBytes((int) ReadRawVarint32());
|
|
|
|
- return true;
|
|
|
|
- case WireFormat.WireType.StartGroup:
|
|
|
|
- SkipMessage();
|
|
|
|
- CheckLastTagWas(
|
|
|
|
- WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
|
|
|
|
- WireFormat.WireType.EndGroup));
|
|
|
|
- return true;
|
|
|
|
- case WireFormat.WireType.EndGroup:
|
|
|
|
- return false;
|
|
|
|
- case WireFormat.WireType.Fixed32:
|
|
|
|
- ReadRawLittleEndian32();
|
|
|
|
- return true;
|
|
|
|
- default:
|
|
|
|
- throw InvalidProtocolBufferException.InvalidWireType();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Reads and discards an entire message. This will read either until EOF
|
|
|
|
- /// or until an endgroup tag, whichever comes first.
|
|
|
|
- /// </summary>
|
|
|
|
- public void SkipMessage()
|
|
|
|
- {
|
|
|
|
- uint tag;
|
|
|
|
- while (ReadTag(out tag))
|
|
|
|
- {
|
|
|
|
- if (!SkipField())
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Reads and discards <paramref name="size"/> bytes.
|
|
/// Reads and discards <paramref name="size"/> bytes.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <exception cref="InvalidProtocolBufferException">the end of the stream
|
|
/// <exception cref="InvalidProtocolBufferException">the end of the stream
|
|
/// or the current limit was reached</exception>
|
|
/// or the current limit was reached</exception>
|
|
- public void SkipRawBytes(int size)
|
|
|
|
|
|
+ private void SkipRawBytes(int size)
|
|
{
|
|
{
|
|
if (size < 0)
|
|
if (size < 0)
|
|
{
|
|
{
|