@ -53,16 +53,13 @@ namespace Google.Protobuf
/// </remarks>
public sealed class CodedInputStream
{
// TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly,
// set at construction time.
/// <summary>
/// Buffer of data read from the stream or provided at construction time.
/// </summary>
private readonly byte [ ] buffer ;
/// <summary>
/// The number of valid bytes in the buffer .
/// The index of the buffer at which we need to refill from the stream (if there is one) .
/// </summary>
private int bufferSize ;
@ -106,61 +103,103 @@ namespace Google.Protobuf
/// </summary>
private int currentLimit = int . MaxValue ;
/// <summary>
/// <see cref="SetRecursionLimit"/>
/// </summary>
private int recursionDepth = 0 ;
private int recursionLimit = DefaultRecursionLimit ;
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>
/// <see cref="SetSizeLimit"/>
/// Creates a new CodedInputStream reading data from the given byte array.
/// </summary>
private int sizeLimit = DefaultSizeLimit ;
public CodedInputStream ( byte [ ] buffer ) : this ( null , Preconditions . CheckNotNull ( buffer , "buffer" ) , 0 , buffer . Length )
{
}
#region Construction
/// <summary>
/// Creates a new CodedInputStream reading data from the given
/// byte array.
/// Creates a new CodedInputStream that reads from the given byte array slice.
/// </summary>
public CodedInputStream ( byte [ ] buf ) : this ( buf , 0 , buf . Length )
{
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 that reads from the given
/// byte array slice.
/// Creates a new CodedInputStream reading data from the given stream.
/// </summary>
public CodedInputStream ( byte [ ] buffer , int offset , int length )
public CodedInputStream ( Stream input ) : this ( input , new byte [ BufferSize ] , 0 , 0 )
{
this . buffer = buffer ;
this . bufferPos = offset ;
this . bufferSize = offset + length ;
this . input = null ;
Preconditions . CheckNotNull ( input , "input" ) ;
}
/// <summary>
/// Creates a new CodedInputStream reading data from the given stream.
/// Creates a new CodedInputStream reading data from the given
/// stream and buffer, using the default limits.
/// </summary>
public CodedInputStream ( Stream input )
internal CodedInputStream ( Stream input , byte [ ] buffer , int bufferPos , int bufferSize )
{
this . buffer = new byte [ BufferSize ] ;
this . bufferSize = 0 ;
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, with a pre-allocated buffer.
/// stream and buffer, using the specified limits .
/// </summary>
internal CodedInputStream ( Stream input , byte [ ] buffer )
/// <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 )
{
this . buffer = buffer ;
this . bufferSize = 0 ;
this . input = input ;
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>
@ -182,59 +221,30 @@ namespace Google.Protobuf
/// </summary>
internal uint LastTag { get { return lastTag ; } }
#region Limits for recursion and length
/// <summary>
/// Set the maximum message recursion depth .
/// Returns the size limit for this stream .
/// </summary>
/// <remarks>
/// In order to prevent maliciou s
/// messages from causing stack overflows, CodedInputStream limits
/// how deeply messages may be nested. The default limit is 64 .
/// This limit is applied when reading from the underlying stream, as a sanity check. It i s
/// not applied when reading from a byte array data source without an underlying stream.
/// The default value is 64MB .
/// </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 ;
}
/// <value>
/// The size limit.
/// </value>
public int SizeLimit { get { return sizeLimit ; } }
/// <summary>
/// Set the maximum message size.
/// Returns the recursion limit for this stream. This limit is applied whilst reading messages,
/// to avoid maliciously-recursive data.
/// </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.
/// The default limit is 64.
/// </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
/// <value>
/// The recursion limit for this stream.
/// </value>
public int RecursionLimit { get { return recursionLimit ; } }
#region Validation
/// <summary>