@ -236,17 +236,16 @@ namespace Google.Protobuf
#region Validation
#region Validation
/// <summary>
/// <summary>
/// Verifies that the last call to ReadTag() returned the given tag value.
/// Verifies that the last call to ReadTag() returned tag 0 - in other words,
/// This is used to verify that a nested group ended with the correct
/// we've reached the end of the stream when we expected to.
/// end tag.
/// </summary>
/// </summary>
/// <exception cref="InvalidProtocolBufferException">The last
/// <exception cref="InvalidProtocolBufferException">The
/// tag read was not the one specified</exception>
/// tag read was not the one specified</exception>
internal void CheckLastTagWas ( uint value )
internal void CheckReadEndOfStreamTag ( )
{
{
if ( lastTag ! = value )
if ( lastTag ! = 0 )
{
{
throw InvalidProtocolBufferException . InvalidEndTag ( ) ;
throw InvalidProtocolBufferException . MoreDataAvailable ( ) ;
}
}
}
}
# endregion
# endregion
@ -275,6 +274,11 @@ namespace Google.Protobuf
/// <summary>
/// <summary>
/// Reads a field tag, returning the tag of 0 for "end of stream".
/// Reads a field tag, returning the tag of 0 for "end of stream".
/// </summary>
/// </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>
/// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>
public uint ReadTag ( )
public uint ReadTag ( )
{
{
@ -329,22 +333,24 @@ namespace Google.Protobuf
}
}
/// <summary>
/// <summary>
/// Consume s the data for the field with the tag we've just read.
/// Skip s the data for the field with the tag we've just read.
/// This should be called directly after <see cref="ReadTag"/>, when
/// This should be called directly after <see cref="ReadTag"/>, when
/// the caller wishes to skip an unknown field.
/// the caller wishes to skip an unknown field.
/// </summary>
/// </summary>
public void Consume LastField( )
public void Skip LastField( )
{
{
if ( lastTag = = 0 )
if ( lastTag = = 0 )
{
{
throw new InvalidOperationException ( "Consume LastField cannot be called at the end of a stream" ) ;
throw new InvalidOperationException ( "Skip LastField cannot be called at the end of a stream" ) ;
}
}
switch ( WireFormat . GetTagWireType ( lastTag ) )
switch ( WireFormat . GetTagWireType ( lastTag ) )
{
{
case WireFormat . WireType . StartGroup :
case WireFormat . WireType . StartGroup :
ConsumeGroup ( ) ;
break ;
case WireFormat . WireType . EndGroup :
case WireFormat . WireType . EndGroup :
// TODO: Work out how to skip them instead? See issue 688.
// Just ignore; there's no data following the tag .
throw new InvalidProtocolBufferException ( "Group tags not supported by proto3 C# implementation" ) ;
break ;
case WireFormat . WireType . Fixed32 :
case WireFormat . WireType . Fixed32 :
ReadFixed32 ( ) ;
ReadFixed32 ( ) ;
break ;
break ;
@ -361,6 +367,29 @@ namespace Google.Protobuf
}
}
}
}
private void ConsumeGroup ( )
{
// 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>
/// <summary>
/// Reads a double field from the stream.
/// Reads a double field from the stream.
/// </summary>
/// </summary>
@ -475,7 +504,7 @@ namespace Google.Protobuf
int oldLimit = PushLimit ( length ) ;
int oldLimit = PushLimit ( length ) ;
+ + recursionDepth ;
+ + recursionDepth ;
builder . MergeFrom ( this ) ;
builder . MergeFrom ( this ) ;
CheckLastTagWas ( 0 ) ;
CheckReadEndOfStreamTag ( ) ;
// Check that we've read exactly as much data as expected.
// Check that we've read exactly as much data as expected.
if ( ! ReachedLimit )
if ( ! ReachedLimit )
{
{