aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtocolBuffers
diff options
context:
space:
mode:
authorJon Skeet <jonskeet@google.com>2015-06-30 13:16:20 +0100
committerJon Skeet <jonskeet@google.com>2015-06-30 13:20:31 +0100
commitf34d37a3d4d64621bc87aa0a65a05cab64062399 (patch)
tree416cceb9b343b21004b030deea069553644928d3 /csharp/src/ProtocolBuffers
parentb9d1d3891f4e68886398bbf0caf40229275a448a (diff)
downloadprotobuf-f34d37a3d4d64621bc87aa0a65a05cab64062399.tar.gz
protobuf-f34d37a3d4d64621bc87aa0a65a05cab64062399.tar.bz2
protobuf-f34d37a3d4d64621bc87aa0a65a05cab64062399.zip
Tidying up and extra tests.
This is mostly just making things internal instead of public, removing and reordering a bunch of code in CodedInputStream/CodedOutputStream, and generally tidying up.
Diffstat (limited to 'csharp/src/ProtocolBuffers')
-rw-r--r--csharp/src/ProtocolBuffers/CodedInputStream.cs302
-rw-r--r--csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs51
-rw-r--r--csharp/src/ProtocolBuffers/CodedOutputStream.cs97
-rw-r--r--csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs2
-rw-r--r--csharp/src/ProtocolBuffers/Collections/RepeatedField.cs7
-rw-r--r--csharp/src/ProtocolBuffers/FieldCodec.cs3
-rw-r--r--csharp/src/ProtocolBuffers/MessageExtensions.cs2
-rw-r--r--csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs7
-rw-r--r--csharp/src/ProtocolBuffers/WireFormat.cs36
9 files changed, 282 insertions, 225 deletions
diff --git a/csharp/src/ProtocolBuffers/CodedInputStream.cs b/csharp/src/ProtocolBuffers/CodedInputStream.cs
index 75178d14..5c64fd97 100644
--- a/csharp/src/ProtocolBuffers/CodedInputStream.cs
+++ b/csharp/src/ProtocolBuffers/CodedInputStream.cs
@@ -37,7 +37,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using Google.Protobuf.Collections;
namespace Google.Protobuf
{
@@ -178,8 +177,61 @@ namespace Google.Protobuf
/// </summary>
internal uint LastTag { get { return lastTag; } }
- #region Validation
+ #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>
/// 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
@@ -194,13 +246,12 @@ namespace Google.Protobuf
throw InvalidProtocolBufferException.InvalidEndTag();
}
}
-
#endregion
#region Reading of tags etc
/// <summary>
- /// Attempt to peek at the next field tag.
+ /// Attempts to peek at the next field tag.
/// </summary>
public bool PeekNextTag(out uint fieldTag)
{
@@ -218,7 +269,7 @@ namespace Google.Protobuf
}
/// <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.
/// </summary>
/// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
@@ -233,14 +284,42 @@ namespace Google.Protobuf
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;
if (lastTag == 0)
{
@@ -251,7 +330,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a double field from the stream.
+ /// Reads a double field from the stream.
/// </summary>
public double ReadDouble()
{
@@ -259,7 +338,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a float field from the stream.
+ /// Reads a float field from the stream.
/// </summary>
public float ReadFloat()
{
@@ -281,7 +360,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a uint64 field from the stream.
+ /// Reads a uint64 field from the stream.
/// </summary>
public ulong ReadUInt64()
{
@@ -289,7 +368,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read an int64 field from the stream.
+ /// Reads an int64 field from the stream.
/// </summary>
public long ReadInt64()
{
@@ -297,7 +376,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read an int32 field from the stream.
+ /// Reads an int32 field from the stream.
/// </summary>
public int ReadInt32()
{
@@ -305,7 +384,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a fixed64 field from the stream.
+ /// Reads a fixed64 field from the stream.
/// </summary>
public ulong ReadFixed64()
{
@@ -313,7 +392,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a fixed32 field from the stream.
+ /// Reads a fixed32 field from the stream.
/// </summary>
public uint ReadFixed32()
{
@@ -321,7 +400,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a bool field from the stream.
+ /// Reads a bool field from the stream.
/// </summary>
public bool ReadBool()
{
@@ -333,22 +412,22 @@ namespace Google.Protobuf
/// </summary>
public string ReadString()
{
- int size = (int) ReadRawVarint32();
+ int length = ReadLength();
// No need to read any data for an empty string.
- if (size == 0)
+ if (length == 0)
{
return "";
}
- if (size <= bufferSize - bufferPos)
+ 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, size);
- bufferPos += size;
+ 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(size), 0, size);
+ return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
}
/// <summary>
@@ -356,7 +435,7 @@ namespace Google.Protobuf
/// </summary>
public void ReadMessage(IMessage builder)
{
- int length = (int) ReadRawVarint32();
+ int length = ReadLength();
if (recursionDepth >= recursionLimit)
{
throw InvalidProtocolBufferException.RecursionLimitExceeded();
@@ -374,19 +453,19 @@ namespace Google.Protobuf
/// </summary>
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
// 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;
}
else
{
// Slow path: Build a byte array and attach it to a new ByteString.
- return ByteString.AttachBytes(ReadRawBytes(size));
+ return ByteString.AttachBytes(ReadRawBytes(length));
}
}
@@ -442,6 +521,18 @@ namespace Google.Protobuf
}
/// <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>.
@@ -517,12 +608,12 @@ namespace Google.Protobuf
}
/// <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.
/// That means we can check the size just once, then just read directly from the buffer
/// without constant rechecking of the buffer length.
/// </summary>
- public uint ReadRawVarint32()
+ internal uint ReadRawVarint32()
{
if (bufferPos + 5 > bufferSize)
{
@@ -581,13 +672,13 @@ namespace Google.Protobuf
/// <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)}
+ /// 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>
- public static uint ReadRawVarint32(Stream input)
+ internal static uint ReadRawVarint32(Stream input)
{
int result = 0;
int offset = 0;
@@ -621,9 +712,9 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a raw varint from the stream.
+ /// Reads a raw varint from the stream.
/// </summary>
- public ulong ReadRawVarint64()
+ internal ulong ReadRawVarint64()
{
int shift = 0;
ulong result = 0;
@@ -641,9 +732,9 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a 32-bit little-endian integer from the stream.
+ /// Reads a 32-bit little-endian integer from the stream.
/// </summary>
- public uint ReadRawLittleEndian32()
+ internal uint ReadRawLittleEndian32()
{
uint b1 = ReadRawByte();
uint b2 = ReadRawByte();
@@ -653,9 +744,9 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a 64-bit little-endian integer from the stream.
+ /// Reads a 64-bit little-endian integer from the stream.
/// </summary>
- public ulong ReadRawLittleEndian64()
+ internal ulong ReadRawLittleEndian64()
{
ulong b1 = ReadRawByte();
ulong b2 = ReadRawByte();
@@ -669,8 +760,6 @@ namespace Google.Protobuf
| (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
}
- #endregion
-
/// <summary>
/// Decode a 32-bit value with ZigZag encoding.
/// </summary>
@@ -680,9 +769,9 @@ namespace Google.Protobuf
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </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>
@@ -694,72 +783,21 @@ namespace Google.Protobuf
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </remarks>
- public 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)
+ internal static long DecodeZigZag64(ulong n)
{
- 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
/// <summary>
- /// Resets the current size counter to zero (see SetSizeLimit).
- /// </summary>
- public void ResetSizeCounter()
- {
- totalBytesRetired = 0;
- }
-
- /// <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>
- public int PushLimit(int byteLimit)
+ internal int PushLimit(int byteLimit)
{
if (byteLimit < 0)
{
@@ -797,7 +835,7 @@ namespace Google.Protobuf
/// <summary>
/// Discards the current limit, returning the previous limit.
/// </summary>
- public void PopLimit(int oldLimit)
+ internal void PopLimit(int oldLimit)
{
currentLimit = oldLimit;
RecomputeBufferSizeAfterLimit();
@@ -807,7 +845,7 @@ namespace Google.Protobuf
/// Returns whether or not all the data before the limit has been read.
/// </summary>
/// <returns></returns>
- public bool ReachedLimit
+ internal bool ReachedLimit
{
get
{
@@ -897,7 +935,7 @@ namespace Google.Protobuf
/// <exception cref="InvalidProtocolBufferException">
/// the end of the stream or the current limit was reached
/// </exception>
- public byte ReadRawByte()
+ internal byte ReadRawByte()
{
if (bufferPos == bufferSize)
{
@@ -907,12 +945,12 @@ namespace Google.Protobuf
}
/// <summary>
- /// Read a fixed size of bytes from the input.
+ /// 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>
- public byte[] ReadRawBytes(int size)
+ internal byte[] ReadRawBytes(int size)
{
if (size < 0)
{
@@ -921,7 +959,8 @@ namespace Google.Protobuf
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);
// Then fail.
throw InvalidProtocolBufferException.TruncatedMessage();
@@ -1026,62 +1065,11 @@ 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>
/// Reads and discards <paramref name="size"/> bytes.
/// </summary>
/// <exception cref="InvalidProtocolBufferException">the end of the stream
/// or the current limit was reached</exception>
- public void SkipRawBytes(int size)
+ private void SkipRawBytes(int size)
{
if (size < 0)
{
diff --git a/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs b/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
index ef1f4c0c..82aba51b 100644
--- a/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
+++ b/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
@@ -50,7 +50,7 @@ namespace Google.Protobuf
private const int LittleEndian32Size = 4;
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// double field, including the tag.
/// </summary>
public static int ComputeDoubleSize(double value)
@@ -59,7 +59,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// float field, including the tag.
/// </summary>
public static int ComputeFloatSize(float value)
@@ -68,7 +68,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// uint64 field, including the tag.
/// </summary>
public static int ComputeUInt64Size(ulong value)
@@ -77,7 +77,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// int64 field, including the tag.
/// </summary>
public static int ComputeInt64Size(long value)
@@ -86,7 +86,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// int32 field, including the tag.
/// </summary>
public static int ComputeInt32Size(int value)
@@ -103,7 +103,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// fixed64 field, including the tag.
/// </summary>
public static int ComputeFixed64Size(ulong value)
@@ -112,7 +112,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// fixed32 field, including the tag.
/// </summary>
public static int ComputeFixed32Size(uint value)
@@ -121,7 +121,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// bool field, including the tag.
/// </summary>
public static int ComputeBoolSize(bool value)
@@ -130,7 +130,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// string field, including the tag.
/// </summary>
public static int ComputeStringSize(String value)
@@ -141,7 +141,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// group field, including the tag.
/// </summary>
public static int ComputeGroupSize(IMessage value)
@@ -150,7 +150,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// embedded message field, including the tag.
/// </summary>
public static int ComputeMessageSize(IMessage value)
@@ -160,7 +160,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// bytes field, including the tag.
/// </summary>
public static int ComputeBytesSize(ByteString value)
@@ -170,7 +170,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// uint32 field, including the tag.
/// </summary>
public static int ComputeUInt32Size(uint value)
@@ -179,7 +179,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a
+ /// Computes the number of bytes that would be needed to encode a
/// enum field, including the tag. The caller is responsible for
/// converting the enum value to its numeric value.
/// </summary>
@@ -190,7 +190,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// sfixed32 field, including the tag.
/// </summary>
public static int ComputeSFixed32Size(int value)
@@ -199,7 +199,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// sfixed64 field, including the tag.
/// </summary>
public static int ComputeSFixed64Size(long value)
@@ -208,7 +208,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// sint32 field, including the tag.
/// </summary>
public static int ComputeSInt32Size(int value)
@@ -217,7 +217,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode an
+ /// Computes the number of bytes that would be needed to encode an
/// sint64 field, including the tag.
/// </summary>
public static int ComputeSInt64Size(long value)
@@ -226,7 +226,16 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a varint.
+ /// Computes the number of bytes that would be needed to encode a length,
+ /// as written by <see cref="WriteLength"/>.
+ /// </summary>
+ public static int ComputeLengthSize(int length)
+ {
+ return ComputeRawVarint32Size((uint) length);
+ }
+
+ /// <summary>
+ /// Computes the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint32Size(uint value)
{
@@ -250,7 +259,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a varint.
+ /// Computes the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint64Size(ulong value)
{
@@ -294,7 +303,7 @@ namespace Google.Protobuf
}
/// <summary>
- /// Compute the number of bytes that would be needed to encode a tag.
+ /// Computes the number of bytes that would be needed to encode a tag.
/// </summary>
public static int ComputeTagSize(int fieldNumber)
{
diff --git a/csharp/src/ProtocolBuffers/CodedOutputStream.cs b/csharp/src/ProtocolBuffers/CodedOutputStream.cs
index 99a99ae2..161f48f4 100644
--- a/csharp/src/ProtocolBuffers/CodedOutputStream.cs
+++ b/csharp/src/ProtocolBuffers/CodedOutputStream.cs
@@ -37,7 +37,6 @@
using System;
using System.IO;
using System.Text;
-using Google.Protobuf.Collections;
namespace Google.Protobuf
{
@@ -141,11 +140,12 @@ namespace Google.Protobuf
}
}
- #region Writing of values without tags
+ #region Writing of values (not including tags)
/// <summary>
- /// Writes a double field value, including tag, to the stream.
+ /// Writes a double field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteDouble(double value)
{
WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
@@ -154,6 +154,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes a float field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteFloat(float value)
{
byte[] rawBytes = BitConverter.GetBytes(value);
@@ -178,6 +179,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes a uint64 field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteUInt64(ulong value)
{
WriteRawVarint64(value);
@@ -186,6 +188,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes an int64 field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteInt64(long value)
{
WriteRawVarint64((ulong) value);
@@ -194,6 +197,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes an int32 field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteInt32(int value)
{
if (value >= 0)
@@ -210,6 +214,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes a fixed64 field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteFixed64(ulong value)
{
WriteRawLittleEndian64(value);
@@ -218,6 +223,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes a fixed32 field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteFixed32(uint value)
{
WriteRawLittleEndian32(value);
@@ -226,6 +232,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes a bool field value, without a tag, to the stream.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteBool(bool value)
{
WriteRawByte(value ? (byte) 1 : (byte) 0);
@@ -233,13 +240,15 @@ namespace Google.Protobuf
/// <summary>
/// Writes a string field value, without a tag, to the stream.
+ /// The data is length-prefixed.
/// </summary>
+ /// <param name="value">The value to write</param>
public void WriteString(string value)
{
// Optimise the case where we have enough space to write
// the string directly to the buffer, which should be common.
int length = Utf8Encoding.GetByteCount(value);
- WriteRawVarint32((uint)length);
+ WriteLength(length);
if (limit - position >= length)
{
if (length == value.Length) // Must be all ASCII...
@@ -262,23 +271,41 @@ namespace Google.Protobuf
}
}
+ /// <summary>
+ /// Writes a message, without a tag, to the stream.
+ /// The data is length-prefixed.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteMessage(IMessage value)
{
WriteRawVarint32((uint) value.CalculateSize());
value.WriteTo(this);
}
+ /// <summary>
+ /// Write a byte string, without a tag, to the stream.
+ /// The data is length-prefixed.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteBytes(ByteString value)
{
WriteRawVarint32((uint) value.Length);
value.WriteRawBytesTo(this);
}
+ /// <summary>
+ /// Writes a uint32 value, without a tag, to the stream.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteUInt32(uint value)
{
WriteRawVarint32(value);
}
+ /// <summary>
+ /// Writes an enum value, without a tag, to the stream.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteEnum(int value)
{
WriteInt32(value);
@@ -289,27 +316,53 @@ namespace Google.Protobuf
WriteRawLittleEndian32((uint) value);
}
+ /// <summary>
+ /// Writes an sfixed64 value, without a tag, to the stream.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteSFixed64(long value)
{
WriteRawLittleEndian64((ulong) value);
}
+ /// <summary>
+ /// Writes an sint32 value, without a tag, to the stream.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteSInt32(int value)
{
WriteRawVarint32(EncodeZigZag32(value));
}
+ /// <summary>
+ /// Writes an sint64 value, without a tag, to the stream.
+ /// </summary>
+ /// <param name="value">The value to write</param>
public void WriteSInt64(long value)
{
WriteRawVarint64(EncodeZigZag64(value));
}
+ /// <summary>
+ /// Writes a length (in bytes) for length-delimited data.
+ /// </summary>
+ /// <remarks>
+ /// This method simply writes a rawint, but exists for clarity in calling code.
+ /// </remarks>
+ /// <param name="length">Length value, in bytes.</param>
+ public void WriteLength(int length)
+ {
+ WriteRawVarint32((uint) length);
+ }
+
#endregion
#region Raw tag writing
/// <summary>
/// Encodes and writes a tag.
/// </summary>
+ /// <param name="fieldNumber">The number of the field to write the tag for</param>
+ /// <param name="type">The wire format type of the tag to write</param>
public void WriteTag(int fieldNumber, WireFormat.WireType type)
{
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
@@ -318,6 +371,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes an already-encoded tag.
/// </summary>
+ /// <param name="tag">The encoded tag</param>
public void WriteTag(uint tag)
{
WriteRawVarint32(tag);
@@ -326,6 +380,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes the given single-byte tag directly to the stream.
/// </summary>
+ /// <param name="b1">The encoded tag</param>
public void WriteRawTag(byte b1)
{
WriteRawByte(b1);
@@ -334,6 +389,8 @@ namespace Google.Protobuf
/// <summary>
/// Writes the given two-byte tag directly to the stream.
/// </summary>
+ /// <param name="b1">The first byte of the encoded tag</param>
+ /// <param name="b2">The second byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2)
{
WriteRawByte(b1);
@@ -343,6 +400,9 @@ namespace Google.Protobuf
/// <summary>
/// Writes the given three-byte tag directly to the stream.
/// </summary>
+ /// <param name="b1">The first byte of the encoded tag</param>
+ /// <param name="b2">The second byte of the encoded tag</param>
+ /// <param name="b3">The third byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3)
{
WriteRawByte(b1);
@@ -353,6 +413,10 @@ namespace Google.Protobuf
/// <summary>
/// Writes the given four-byte tag directly to the stream.
/// </summary>
+ /// <param name="b1">The first byte of the encoded tag</param>
+ /// <param name="b2">The second byte of the encoded tag</param>
+ /// <param name="b3">The third byte of the encoded tag</param>
+ /// <param name="b4">The fourth byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
{
WriteRawByte(b1);
@@ -364,6 +428,11 @@ namespace Google.Protobuf
/// <summary>
/// Writes the given five-byte tag directly to the stream.
/// </summary>
+ /// <param name="b1">The first byte of the encoded tag</param>
+ /// <param name="b2">The second byte of the encoded tag</param>
+ /// <param name="b3">The third byte of the encoded tag</param>
+ /// <param name="b4">The fourth byte of the encoded tag</param>
+ /// <param name="b5">The fifth byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
{
WriteRawByte(b1);
@@ -380,7 +449,7 @@ namespace Google.Protobuf
/// there's enough buffer space left to whizz through without checking
/// for each byte; otherwise, we resort to calling WriteRawByte each time.
/// </summary>
- public void WriteRawVarint32(uint value)
+ internal void WriteRawVarint32(uint value)
{
// Optimize for the common case of a single byte value
if (value < 128 && position < limit)
@@ -409,7 +478,7 @@ namespace Google.Protobuf
}
}
- public void WriteRawVarint64(ulong value)
+ internal void WriteRawVarint64(ulong value)
{
while (value > 127 && position < limit)
{
@@ -431,7 +500,7 @@ namespace Google.Protobuf
}
}
- public void WriteRawLittleEndian32(uint value)
+ internal void WriteRawLittleEndian32(uint value)
{
if (position + 4 > limit)
{
@@ -449,7 +518,7 @@ namespace Google.Protobuf
}
}
- public void WriteRawLittleEndian64(ulong value)
+ internal void WriteRawLittleEndian64(ulong value)
{
if (position + 8 > limit)
{
@@ -475,7 +544,7 @@ namespace Google.Protobuf
}
}
- public void WriteRawByte(byte value)
+ internal void WriteRawByte(byte value)
{
if (position == limit)
{
@@ -485,7 +554,7 @@ namespace Google.Protobuf
buffer[position++] = value;
}
- public void WriteRawByte(uint value)
+ internal void WriteRawByte(uint value)
{
WriteRawByte((byte) value);
}
@@ -493,7 +562,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes out an array of bytes.
/// </summary>
- public void WriteRawBytes(byte[] value)
+ internal void WriteRawBytes(byte[] value)
{
WriteRawBytes(value, 0, value.Length);
}
@@ -501,7 +570,7 @@ namespace Google.Protobuf
/// <summary>
/// Writes out part of an array of bytes.
/// </summary>
- public void WriteRawBytes(byte[] value, int offset, int length)
+ internal void WriteRawBytes(byte[] value, int offset, int length)
{
if (limit - position >= length)
{
@@ -548,7 +617,7 @@ namespace Google.Protobuf
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </remarks>
- public static uint EncodeZigZag32(int n)
+ internal static uint EncodeZigZag32(int n)
{
// Note: the right-shift must be arithmetic
return (uint) ((n << 1) ^ (n >> 31));
@@ -563,7 +632,7 @@ namespace Google.Protobuf
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </remarks>
- public static ulong EncodeZigZag64(long n)
+ internal static ulong EncodeZigZag64(long n)
{
return (ulong) ((n << 1) ^ (n >> 63));
}
diff --git a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs b/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
index 031ebd02..cf3ff83c 100644
--- a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
+++ b/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
@@ -38,7 +38,7 @@ namespace Google.Protobuf.Collections
/// <summary>
/// Read-only wrapper around another dictionary.
/// </summary>
- public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
+ internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> wrapped;
diff --git a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
index 588f66a4..0d82e3bc 100644
--- a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
+++ b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
@@ -51,12 +51,14 @@ namespace Google.Protobuf.Collections
public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
{
+ // TODO: Inline some of the Add code, so we can avoid checking the size on every
+ // iteration and the mutability.
uint tag = input.LastTag;
var reader = codec.ValueReader;
// Value types can be packed or not.
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
- int length = (int)(input.ReadRawVarint32() & int.MaxValue);
+ int length = input.ReadLength();
if (length > 0)
{
int oldLimit = input.PushLimit(length);
@@ -125,7 +127,6 @@ namespace Google.Protobuf.Collections
public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
{
- // TODO: Assert that T is a value type, and that codec.Tag is packed?
if (count == 0)
{
return;
@@ -172,9 +173,9 @@ namespace Google.Protobuf.Collections
private void EnsureSize(int size)
{
- size = Math.Max(size, MinArraySize);
if (array.Length < size)
{
+ size = Math.Max(size, MinArraySize);
int newSize = Math.Max(array.Length * 2, size);
var tmp = new T[newSize];
Array.Copy(array, 0, tmp, 0, array.Length);
diff --git a/csharp/src/ProtocolBuffers/FieldCodec.cs b/csharp/src/ProtocolBuffers/FieldCodec.cs
index d3fc2f71..f075dbbf 100644
--- a/csharp/src/ProtocolBuffers/FieldCodec.cs
+++ b/csharp/src/ProtocolBuffers/FieldCodec.cs
@@ -8,6 +8,7 @@ namespace Google.Protobuf
/// </summary>
public static class FieldCodec
{
+ // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
public static FieldCodec<string> ForString(uint tag)
{
return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
@@ -84,7 +85,7 @@ namespace Google.Protobuf
}
// Enums are tricky. We can probably use expression trees to build these delegates automatically,
- // but it's easy to generate the code fdor it.
+ // but it's easy to generate the code for it.
public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
{
return new FieldCodec<T>(input => fromInt32(
diff --git a/csharp/src/ProtocolBuffers/MessageExtensions.cs b/csharp/src/ProtocolBuffers/MessageExtensions.cs
index 57cecfd4..253c18ae 100644
--- a/csharp/src/ProtocolBuffers/MessageExtensions.cs
+++ b/csharp/src/ProtocolBuffers/MessageExtensions.cs
@@ -38,7 +38,7 @@ namespace Google.Protobuf
{
ThrowHelper.ThrowIfNull(message, "message");
ThrowHelper.ThrowIfNull(input, "input");
- int size = (int)CodedInputStream.ReadRawVarint32(input);
+ int size = (int) CodedInputStream.ReadRawVarint32(input);
Stream limitedStream = new LimitedInputStream(input, size);
message.MergeFrom(limitedStream);
}
diff --git a/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs b/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
index 806bd5d5..27ccddbc 100644
--- a/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
+++ b/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
@@ -61,6 +61,13 @@ using System.Security;
[assembly: AssemblyVersion("2.4.1.555")]
+[assembly: InternalsVisibleTo("Google.Protobuf.Test, PublicKey=" +
+ "00240000048000009400000006020000002400005253413100040000110000003b4611704c5379" +
+ "39c3e0fbe9447dd6fa5462507f9dd4fd9fbf0712457e415b037da6d2c4eb5d2c7d29c86380af68" +
+ "7cf400401bb183f2a70bd3b631c1fcb7db8aa66c766694a9fb53fa765df6303104da8c978f3b6d" +
+ "53909cd30685b8bc9922c726cd82b5995e9e2cfca6df7a2d189d851492e49f4b76f269ce6dfd08" +
+ "c34a7d98")]
+
#if !NOFILEVERSION
[assembly: AssemblyFileVersion("2.4.1.555")]
#endif
diff --git a/csharp/src/ProtocolBuffers/WireFormat.cs b/csharp/src/ProtocolBuffers/WireFormat.cs
index 221ffef6..974665f1 100644
--- a/csharp/src/ProtocolBuffers/WireFormat.cs
+++ b/csharp/src/ProtocolBuffers/WireFormat.cs
@@ -53,13 +53,13 @@ namespace Google.Protobuf
#region Fixed sizes.
// TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
- public const int Fixed32Size = 4;
- public const int Fixed64Size = 8;
- public const int SFixed32Size = 4;
- public const int SFixed64Size = 8;
- public const int FloatSize = 4;
- public const int DoubleSize = 8;
- public const int BoolSize = 1;
+ internal const int Fixed32Size = 4;
+ internal const int Fixed64Size = 8;
+ internal const int SFixed32Size = 4;
+ internal const int SFixed64Size = 8;
+ internal const int FloatSize = 4;
+ internal const int DoubleSize = 8;
+ internal const int BoolSize = 1;
#endregion
@@ -72,22 +72,7 @@ namespace Google.Protobuf
EndGroup = 4,
Fixed32 = 5
}
-
- internal static class MessageSetField
- {
- internal const int Item = 1;
- internal const int TypeID = 2;
- internal const int Message = 3;
- }
-
- internal static class MessageSetTag
- {
- internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup);
- internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup);
- internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint);
- internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited);
- }
-
+
private const int TagTypeBits = 3;
private const uint TagTypeMask = (1 << TagTypeBits) - 1;
@@ -120,7 +105,6 @@ namespace Google.Protobuf
return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
}
-#if !LITE
public static uint MakeTag(FieldDescriptor field)
{
return MakeTag(field.FieldNumber, GetWireType(field));
@@ -135,8 +119,6 @@ namespace Google.Protobuf
return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
}
-#endif
-
/// <summary>
/// Converts a field type to its wire type. Done with a switch for the sake
/// of speed - this is significantly faster than a dictionary lookup.
@@ -177,7 +159,7 @@ namespace Google.Protobuf
case FieldType.Enum:
return WireType.Varint;
default:
- throw new ArgumentOutOfRangeException("No such field type");
+ throw new ArgumentOutOfRangeException("fieldType", "No such field type");
}
}
}