diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 8795fa65f4d5..7c3df59d16ff 100644
--- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
@@ -417,7 +417,7 @@ public void TestSlowPathAvoidance()
output.Flush();
ms.Position = 0;
- CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
+ CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], null, null, 0, 0, false);
uint tag = input.ReadTag();
Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
diff --git a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
index 98cabd55adc4..f664250ee9df 100644
--- a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
@@ -334,7 +334,7 @@ public void TestCodedInputOutputPosition()
}
// Now test Input stream:
{
- CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
+ CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], null, null, 0, 0, false);
Assert.AreEqual(0, cin.Position);
// Field 1:
uint tag = cin.ReadTag();
diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs
index ade751c63661..706c812a4fe3 100644
--- a/csharp/src/Google.Protobuf/ByteString.cs
+++ b/csharp/src/Google.Protobuf/ByteString.cs
@@ -34,6 +34,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Security;
using System.Text;
#if !NET35
using System.Threading;
@@ -114,7 +115,11 @@ public bool IsEmpty
/// Provides read-only access to the data of this .
/// No data is copied so this is the most efficient way of accessing.
///
- public ReadOnlySpan Span => new ReadOnlySpan(bytes);
+ public ReadOnlySpan Span
+ {
+ [SecurityCritical]
+ get => bytes;
+ }
///
/// Converts this into a byte array.
@@ -221,6 +226,7 @@ public static ByteString CopyFrom(byte[] bytes, int offset, int count)
/// are copied, so further modifications to the span will not
/// be reflected in the returned .
///
+ [SecurityCritical]
public static ByteString CopyFrom(ReadOnlySpan bytes)
{
return new ByteString(bytes.ToArray());
diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs
index 0a829545e257..7d75b01b3466 100644
--- a/csharp/src/Google.Protobuf/CodedInputStream.cs
+++ b/csharp/src/Google.Protobuf/CodedInputStream.cs
@@ -32,8 +32,13 @@
using Google.Protobuf.Collections;
using System;
+using System.Buffers;
+using System.Buffers.Binary;
using System.Collections.Generic;
+using System.ComponentModel;
using System.IO;
+using System.Runtime.CompilerServices;
+using System.Security;
namespace Google.Protobuf
{
@@ -81,6 +86,11 @@ public sealed class CodedInputStream : IDisposable
///
private readonly Stream input;
+ private ReadOnlySequence.Enumerator nativeInput;
+ private bool hasNativeInput;
+ private long nativeInputPastBuffersLength;
+ private ReadOnlyMemory currentNativeBuffer;
+
///
/// The last tag we read. 0 indicates we've read to the end of the stream
/// (or haven't read anything yet).
@@ -121,16 +131,35 @@ public sealed class CodedInputStream : IDisposable
///
/// Creates a new CodedInputStream reading data from the given byte array.
///
- public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true)
- {
+ [SecuritySafeCritical]
+ public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), null, null, 0, buffer.Length, true)
+ {
+ }
+
+ ///
+ /// Creates a new CodedInputStream reading data from the given contiguous memory segment.
+ ///
+ [SecurityCritical]
+ public CodedInputStream(ReadOnlyMemory buffer) : this(null, null, null, buffer, 0, buffer.Length, true)
+ {
+ }
+
+ ///
+ /// Creates a new CodedInputStream reading data from the given non-contiguous memory sequence.
+ ///
+ [SecurityCritical]
+ public CodedInputStream(ReadOnlySequence buffer)
+ : this(null, null, buffer.IsSingleSegment ? default(ReadOnlySequence?) : buffer, buffer.First, 0, buffer.First.Length, true)
+ {
}
///
/// Creates a new that reads from the given byte array slice.
///
+ [SecuritySafeCritical]
public CodedInputStream(byte[] buffer, int offset, int length)
- : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true)
- {
+ : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), null, null, offset, offset + length, true)
+ {
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
@@ -146,6 +175,7 @@ public CodedInputStream(byte[] buffer, int offset, int length)
/// when the returned object is disposed.
///
/// The stream to read from.
+ [SecuritySafeCritical]
public CodedInputStream(Stream input) : this(input, false)
{
}
@@ -157,19 +187,32 @@ public CodedInputStream(Stream input) : this(input, false)
/// true to leave open when the returned
/// is disposed; false to dispose of the given stream when the
/// returned object is disposed.
+ [SecuritySafeCritical]
public CodedInputStream(Stream input, bool leaveOpen)
- : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen)
+ : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], null, null, 0, 0, leaveOpen)
{
}
-
+
///
/// Creates a new CodedInputStream reading data from the given
/// stream and buffer, using the default limits.
///
- internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)
+ [SecurityCritical]
+ internal CodedInputStream(Stream input, byte[] buffer, ReadOnlySequence? nativeInput, ReadOnlyMemory? nativeBuffer, int bufferPos, int bufferSize, bool leaveOpen)
{
this.input = input;
this.buffer = buffer;
+ if (nativeInput.HasValue)
+ {
+ var en = nativeInput.Value.GetEnumerator();
+ en.MoveNext();
+ this.nativeInput = en;
+ hasNativeInput = true;
+ }
+ if (nativeBuffer.HasValue)
+ {
+ this.currentNativeBuffer = nativeBuffer.Value;
+ }
this.bufferPos = bufferPos;
this.bufferSize = bufferSize;
this.sizeLimit = DefaultSizeLimit;
@@ -185,8 +228,9 @@ internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int buffer
/// 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.
///
+ [SecuritySafeCritical]
internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)
- : this(input, buffer, bufferPos, bufferSize, leaveOpen)
+ : this(input, buffer, null, null, bufferPos, bufferSize, leaveOpen)
{
if (sizeLimit <= 0)
{
@@ -224,7 +268,7 @@ public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int
///
/// Returns the current position in the input stream, or the position in the input buffer
///
- public long Position
+ public long Position
{
get
{
@@ -232,7 +276,14 @@ public long Position
{
return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos);
}
- return bufferPos;
+ else if (hasNativeInput)
+ {
+ return nativeInputPastBuffersLength + bufferPos;
+ }
+ else
+ {
+ return bufferPos;
+ }
}
}
@@ -242,6 +293,12 @@ public long Position
///
internal uint LastTag { get { return lastTag; } }
+ internal ReadOnlySpan ImmediateBuffer
+ {
+ [SecurityCritical]
+ get => buffer != null ? buffer : currentNativeBuffer.Span;
+ }
+
///
/// Returns the size limit for this stream.
///
@@ -307,8 +364,8 @@ internal void CheckReadEndOfStreamTag()
#region Reading of tags etc
///
- /// Peeks at the next field tag. This is like calling , but the
- /// tag is not consumed. (So a subsequent call to will return the
+ /// Peeks at the next field tag. This is like calling , but the
+ /// tag is not consumed. (So a subsequent call to will return the
/// same value.)
///
public uint PeekTag()
@@ -334,7 +391,19 @@ public uint PeekTag()
/// for an embedded message, for example.
///
/// The next field tag, or 0 for end of stream. (0 is never a valid tag.)
+ [SecuritySafeCritical]
public uint ReadTag()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadTag(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public uint ReadTag(ref ReadOnlySpan immediateBuffer)
{
if (hasNextTag)
{
@@ -347,7 +416,7 @@ public uint ReadTag()
// 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++];
+ int tmp = immediateBuffer[bufferPos++];
if (tmp < 128)
{
lastTag = (uint)tmp;
@@ -355,28 +424,28 @@ public uint ReadTag()
else
{
int result = tmp & 0x7f;
- if ((tmp = buffer[bufferPos++]) < 128)
+ if ((tmp = immediateBuffer[bufferPos++]) < 128)
{
result |= tmp << 7;
- lastTag = (uint) result;
+ lastTag = (uint)result;
}
else
{
// Nope, rewind and go the potentially slow route.
bufferPos -= 2;
- lastTag = ReadRawVarint32();
+ lastTag = ReadRawVarint32(ref immediateBuffer);
}
}
}
else
{
- if (IsAtEnd)
+ if (IsAtEndCore(ref immediateBuffer))
{
lastTag = 0;
return 0; // This is the only case in which we return 0.
}
- lastTag = ReadRawVarint32();
+ lastTag = ReadRawVarint32(ref immediateBuffer);
}
if (WireFormat.GetTagFieldNumber(lastTag) == 0)
{
@@ -388,7 +457,7 @@ public uint ReadTag()
///
/// Skips the data for the field with the tag we've just read.
- /// This should be called directly after , when
+ /// This should be called directly after , when
/// the caller wishes to skip an unknown field.
///
///
@@ -399,7 +468,19 @@ public uint ReadTag()
///
/// The last tag was an end-group tag
/// The last read operation read to the end of the logical stream
+ [SecuritySafeCritical]
public void SkipLastField()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ SkipLastField(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public void SkipLastField(ref ReadOnlySpan immediateBuffer)
{
if (lastTag == 0)
{
@@ -408,23 +489,23 @@ public void SkipLastField()
switch (WireFormat.GetTagWireType(lastTag))
{
case WireFormat.WireType.StartGroup:
- SkipGroup(lastTag);
+ SkipGroup(lastTag, ref immediateBuffer);
break;
case WireFormat.WireType.EndGroup:
throw new InvalidProtocolBufferException(
"SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing");
case WireFormat.WireType.Fixed32:
- ReadFixed32();
+ ReadFixed32(ref immediateBuffer);
break;
case WireFormat.WireType.Fixed64:
- ReadFixed64();
+ ReadFixed64(ref immediateBuffer);
break;
case WireFormat.WireType.LengthDelimited:
- var length = ReadLength();
- SkipRawBytes(length);
+ var length = ReadLength(ref immediateBuffer);
+ SkipRawBytes(length, ref immediateBuffer);
break;
case WireFormat.WireType.Varint:
- ReadRawVarint32();
+ ReadRawVarint32(ref immediateBuffer);
break;
}
}
@@ -433,6 +514,14 @@ public void SkipLastField()
/// Skip a group.
///
internal void SkipGroup(uint startGroupTag)
+ {
+ var immediateBuffer = ImmediateBuffer;
+ SkipGroup(startGroupTag, ref immediateBuffer);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ internal void SkipGroup(uint startGroupTag, ref ReadOnlySpan immediateBuffer)
{
// 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...
@@ -444,7 +533,7 @@ internal void SkipGroup(uint startGroupTag)
uint tag;
while (true)
{
- tag = ReadTag();
+ tag = ReadTag(ref immediateBuffer);
if (tag == 0)
{
throw InvalidProtocolBufferException.TruncatedMessage();
@@ -455,7 +544,7 @@ internal void SkipGroup(uint startGroupTag)
break;
}
// This recursion will allow us to handle nested groups.
- SkipLastField();
+ SkipLastField(ref immediateBuffer);
}
int startField = WireFormat.GetTagFieldNumber(startGroupTag);
int endField = WireFormat.GetTagFieldNumber(tag);
@@ -470,87 +559,240 @@ internal void SkipGroup(uint startGroupTag)
///
/// Reads a double field from the stream.
///
+ [SecuritySafeCritical]
public double ReadDouble()
{
- return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
+ var immediateBuffer = ImmediateBuffer;
+ return ReadDouble(ref immediateBuffer);
}
///
- /// Reads a float field from the stream.
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
///
- public float ReadFloat()
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public double ReadDouble(ref ReadOnlySpan immediateBuffer) => BitConverter.Int64BitsToDouble((long)ReadRawLittleEndian64(ref immediateBuffer));
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public double ReadWrappedDouble(ref ReadOnlySpan immediateBuffer)
{
- if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ double value = default(double);
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
{
- float ret = BitConverter.ToSingle(buffer, bufferPos);
- bufferPos += 4;
- return ret;
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueFixed64Tag)
+ {
+ value = ReadDouble(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
+ }
}
- else
+ EndReadNested(oldLimit);
+
+ return value;
+ }
+
+ ///
+ /// Reads a float field from the stream.
+ ///
+ [SecuritySafeCritical]
+ public float ReadFloat()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadFloat(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ //NOTE: We cannot use BitConverter.Int32BitsToSingle because it requires .NET Core 2.1
+ public float ReadFloat(ref ReadOnlySpan immediateBuffer) => Int32BitsToSingleSlow((int)ReadRawLittleEndian32(ref immediateBuffer));
+
+ private static float Int32BitsToSingleSlow(int value) => BitConverter.ToSingle(BitConverter.GetBytes(value), 0);
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public float ReadWrappedFloat(ref ReadOnlySpan immediateBuffer)
+ {
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ float value = default(float);
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
{
- byte[] rawBytes = ReadRawBytes(4);
- if (!BitConverter.IsLittleEndian)
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueFixed32Tag)
{
- ByteArray.Reverse(rawBytes);
+ value = ReadFloat(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
}
- return BitConverter.ToSingle(rawBytes, 0);
}
+ EndReadNested(oldLimit);
+
+ return value;
}
///
/// Reads a uint64 field from the stream.
///
+ [SecuritySafeCritical]
public ulong ReadUInt64()
{
- return ReadRawVarint64();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadUInt64(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public ulong ReadUInt64(ref ReadOnlySpan immediateBuffer) => ReadRawVarint64(ref immediateBuffer);
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public ulong ReadWrappedUInt64(ref ReadOnlySpan immediateBuffer) => ReadRawWrappedVarint64(ref immediateBuffer);
+
///
/// Reads an int64 field from the stream.
///
+ [SecuritySafeCritical]
public long ReadInt64()
{
- return (long) ReadRawVarint64();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadInt64(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public long ReadInt64(ref ReadOnlySpan immediateBuffer) => (long)ReadRawVarint64(ref immediateBuffer);
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public long ReadWrappedInt64(ref ReadOnlySpan immediateBuffer) => (long)ReadRawWrappedVarint64(ref immediateBuffer);
+
///
/// Reads an int32 field from the stream.
///
+ [SecuritySafeCritical]
public int ReadInt32()
{
- return (int) ReadRawVarint32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadInt32(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadInt32(ref ReadOnlySpan immediateBuffer) => (int)ReadRawVarint32(ref immediateBuffer);
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadWrappedInt32(ref ReadOnlySpan immediateBuffer) => (int)ReadRawWrappedVarint32(ref immediateBuffer);
+
///
/// Reads a fixed64 field from the stream.
///
+ [SecuritySafeCritical]
public ulong ReadFixed64()
{
- return ReadRawLittleEndian64();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadFixed64(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public ulong ReadFixed64(ref ReadOnlySpan immediateBuffer) => ReadRawLittleEndian64(ref immediateBuffer);
+
///
/// Reads a fixed32 field from the stream.
///
+ [SecuritySafeCritical]
public uint ReadFixed32()
{
- return ReadRawLittleEndian32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadFixed32(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public uint ReadFixed32(ref ReadOnlySpan immediateBuffer) => ReadRawLittleEndian32(ref immediateBuffer);
+
///
/// Reads a bool field from the stream.
///
+ [SecuritySafeCritical]
public bool ReadBool()
{
- return ReadRawVarint32() != 0;
+ var immediateBuffer = ImmediateBuffer;
+ return ReadBool(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public bool ReadBool(ref ReadOnlySpan immediateBuffer) => ReadRawVarint32(ref immediateBuffer) != 0;
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public bool ReadWrappedBool(ref ReadOnlySpan immediateBuffer) => ReadRawWrappedVarint32(ref immediateBuffer) != 0;
+
///
/// Reads a string field from the stream.
///
+ [SecuritySafeCritical]
public string ReadString()
{
- int length = ReadLength();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadString(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public string ReadString(ref ReadOnlySpan immediateBuffer)
+ {
+ int length = ReadLength(ref immediateBuffer);
// No need to read any data for an empty string.
if (length == 0)
{
@@ -560,27 +802,77 @@ public string ReadString()
{
// 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);
+ //NOTE: We cannot use span-based overload because it is available only in .NET Core 2.1
+ String result = buffer != null ? CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length) : CodedOutputStream.Utf8Encoding.GetString(immediateBuffer.Slice(bufferPos, length).ToArray(), 0, length);
bufferPos += length;
return result;
}
// Slow path: Build a byte array first then copy it.
- return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
+ return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length, ref immediateBuffer), 0, length);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public string ReadWrappedString(ref ReadOnlySpan immediateBuffer)
+ {
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ string value = string.Empty;
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
+ {
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueLengthDelimitedTag)
+ {
+ value = ReadString(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
+ }
+ }
+ EndReadNested(oldLimit);
+
+ return value;
}
///
/// Reads an embedded message field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public void ReadMessage(IMessage builder)
{
- int length = ReadLength();
+ var immediateBuffer = ImmediateBuffer;
+ var oldLimit = BeginReadNested(ref immediateBuffer);
+ //NOTE: IMessage does not support MergeFrom with immediateBuffer yet - so we are using legacy path for reverse compat
+ builder.MergeFrom(this);
+ EndReadNested(oldLimit);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int BeginReadNested(ref ReadOnlySpan immediateBuffer)
+ {
+ int length = ReadLength(ref immediateBuffer);
if (recursionDepth >= recursionLimit)
{
throw InvalidProtocolBufferException.RecursionLimitExceeded();
}
int oldLimit = PushLimit(length);
++recursionDepth;
- builder.MergeFrom(this);
+ return oldLimit;
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void EndReadNested(int oldLimit)
+ {
CheckReadEndOfStreamTag();
// Check that we've read exactly as much data as expected.
if (!ReachedLimit)
@@ -594,85 +886,188 @@ public void ReadMessage(IMessage builder)
///
/// Reads a bytes field value from the stream.
///
+ [SecuritySafeCritical]
public ByteString ReadBytes()
{
- int length = ReadLength();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadBytes(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public ByteString ReadBytes(ref ReadOnlySpan immediateBuffer)
+ {
+ int length = ReadLength(ref immediateBuffer);
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);
+ ByteString result = ByteString.CopyFrom(immediateBuffer.Slice(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));
+ return ByteString.AttachBytes(ReadRawBytes(length, ref immediateBuffer));
+ }
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public ByteString ReadWrappedBytes(ref ReadOnlySpan immediateBuffer)
+ {
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ ByteString value = ByteString.Empty;
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
+ {
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueLengthDelimitedTag)
+ {
+ value = ReadBytes(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
+ }
}
+ EndReadNested(oldLimit);
+
+ return value;
}
///
/// Reads a uint32 field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public uint ReadUInt32()
{
- return ReadRawVarint32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadUInt32(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public uint ReadUInt32(ref ReadOnlySpan immediateBuffer) => ReadRawVarint32(ref immediateBuffer);
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public uint ReadWrappedUInt32(ref ReadOnlySpan immediateBuffer) => ReadRawWrappedVarint32(ref immediateBuffer);
+
///
/// Reads an enum field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public int ReadEnum()
{
- // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
- return (int) ReadRawVarint32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadEnum(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadEnum(ref ReadOnlySpan immediateBuffer) => (int)ReadRawVarint32(ref immediateBuffer);
+
///
/// Reads an sfixed32 field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public int ReadSFixed32()
{
- return (int) ReadRawLittleEndian32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadSFixed32(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadSFixed32(ref ReadOnlySpan immediateBuffer) => (int)ReadRawLittleEndian32(ref immediateBuffer);
+
///
/// Reads an sfixed64 field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public long ReadSFixed64()
{
- return (long) ReadRawLittleEndian64();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadSFixed64(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public long ReadSFixed64(ref ReadOnlySpan immediateBuffer) => (long)ReadRawLittleEndian64(ref immediateBuffer);
+
///
/// Reads an sint32 field value from the stream.
- ///
+ ///
+ [SecuritySafeCritical]
public int ReadSInt32()
{
- return DecodeZigZag32(ReadRawVarint32());
+ var immediateBuffer = ImmediateBuffer;
+ return ReadSInt32(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadSInt32(ref ReadOnlySpan immediateBuffer) => DecodeZigZag32(ReadRawVarint32(ref immediateBuffer));
+
///
/// Reads an sint64 field value from the stream.
///
+ [SecuritySafeCritical]
public long ReadSInt64()
{
- return DecodeZigZag64(ReadRawVarint64());
+ var immediateBuffer = ImmediateBuffer;
+ return ReadSInt64(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public long ReadSInt64(ref ReadOnlySpan immediateBuffer) => DecodeZigZag64(ReadRawVarint64(ref immediateBuffer));
+
///
/// Reads a length for length-delimited data.
///
- ///
- /// This is internally just reading a varint, but this method exists
- /// to make the calling code clearer.
- ///
+ [SecuritySafeCritical]
public int ReadLength()
{
- return (int) ReadRawVarint32();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadLength(ref immediateBuffer);
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ public int ReadLength(ref ReadOnlySpan immediateBuffer) => (int)ReadRawVarint32(ref immediateBuffer);
+
///
/// Peeks at the next tag in the stream. If it matches ,
/// the tag is consumed and the method returns true; otherwise, the
@@ -696,44 +1091,45 @@ public bool MaybeConsumeTag(uint tag)
/// Same code as ReadRawVarint32, but read each byte individually, checking for
/// buffer overflow.
///
- private uint SlowReadRawVarint32()
+ [SecurityCritical]
+ private uint SlowReadRawVarint32(ref ReadOnlySpan immediateBuffer)
{
- int tmp = ReadRawByte();
+ int tmp = ReadRawByte(ref immediateBuffer);
if (tmp < 128)
{
- return (uint) tmp;
+ return (uint)tmp;
}
int result = tmp & 0x7f;
- if ((tmp = ReadRawByte()) < 128)
+ if ((tmp = ReadRawByte(ref immediateBuffer)) < 128)
{
result |= tmp << 7;
}
else
{
result |= (tmp & 0x7f) << 7;
- if ((tmp = ReadRawByte()) < 128)
+ if ((tmp = ReadRawByte(ref immediateBuffer)) < 128)
{
result |= tmp << 14;
}
else
{
result |= (tmp & 0x7f) << 14;
- if ((tmp = ReadRawByte()) < 128)
+ if ((tmp = ReadRawByte(ref immediateBuffer)) < 128)
{
result |= tmp << 21;
}
else
{
result |= (tmp & 0x7f) << 21;
- result |= (tmp = ReadRawByte()) << 28;
+ result |= (tmp = ReadRawByte(ref immediateBuffer)) << 28;
if (tmp >= 128)
{
// Discard upper 32 bits.
for (int i = 0; i < 5; i++)
{
- if (ReadRawByte() < 128)
+ if (ReadRawByte(ref immediateBuffer) < 128)
{
- return (uint) result;
+ return (uint)result;
}
}
throw InvalidProtocolBufferException.MalformedVarint();
@@ -741,7 +1137,29 @@ private uint SlowReadRawVarint32()
}
}
}
- return (uint) result;
+ return (uint)result;
+ }
+
+ [SecurityCritical]
+ private uint ReadRawWrappedVarint32(ref ReadOnlySpan immediateBuffer)
+ {
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ uint value = default(uint);
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
+ {
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueVarintTag)
+ {
+ value = ReadRawVarint32(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
+ }
+ }
+ EndReadNested(oldLimit);
+
+ return value;
}
///
@@ -750,41 +1168,53 @@ private uint SlowReadRawVarint32()
/// That means we can check the size just once, then just read directly from the buffer
/// without constant rechecking of the buffer length.
///
+ [SecuritySafeCritical]
internal uint ReadRawVarint32()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawVarint32(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ private uint ReadRawVarint32(ref ReadOnlySpan immediateBuffer)
{
if (bufferPos + 5 > bufferSize)
{
- return SlowReadRawVarint32();
+ return SlowReadRawVarint32(ref immediateBuffer);
}
- int tmp = buffer[bufferPos++];
+ int tmp = immediateBuffer[bufferPos++];
if (tmp < 128)
{
- return (uint) tmp;
+ return (uint)tmp;
}
int result = tmp & 0x7f;
- if ((tmp = buffer[bufferPos++]) < 128)
+ if ((tmp = immediateBuffer[bufferPos++]) < 128)
{
result |= tmp << 7;
}
else
{
result |= (tmp & 0x7f) << 7;
- if ((tmp = buffer[bufferPos++]) < 128)
+ if ((tmp = immediateBuffer[bufferPos++]) < 128)
{
result |= tmp << 14;
}
else
{
result |= (tmp & 0x7f) << 14;
- if ((tmp = buffer[bufferPos++]) < 128)
+ if ((tmp = immediateBuffer[bufferPos++]) < 128)
{
result |= tmp << 21;
}
else
{
result |= (tmp & 0x7f) << 21;
- result |= (tmp = buffer[bufferPos++]) << 28;
+ result |= (tmp = immediateBuffer[bufferPos++]) << 28;
if (tmp >= 128)
{
// Discard upper 32 bits.
@@ -793,9 +1223,9 @@ internal uint ReadRawVarint32()
// 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)
+ if (ReadRawByte(ref immediateBuffer) < 128)
{
- return (uint) result;
+ return (uint)result;
}
}
throw InvalidProtocolBufferException.MalformedVarint();
@@ -803,7 +1233,7 @@ internal uint ReadRawVarint32()
}
}
}
- return (uint) result;
+ return (uint)result;
}
///
@@ -829,7 +1259,7 @@ internal static uint ReadRawVarint32(Stream input)
result |= (b & 0x7f) << offset;
if ((b & 0x80) == 0)
{
- return (uint) result;
+ return (uint)result;
}
}
// Keep reading up to 64 bits.
@@ -842,7 +1272,7 @@ internal static uint ReadRawVarint32(Stream input)
}
if ((b & 0x80) == 0)
{
- return (uint) result;
+ return (uint)result;
}
}
throw InvalidProtocolBufferException.MalformedVarint();
@@ -851,14 +1281,27 @@ internal static uint ReadRawVarint32(Stream input)
///
/// Reads a raw varint from the stream.
///
+ [SecuritySafeCritical]
internal ulong ReadRawVarint64()
{
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawVarint64(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ internal ulong ReadRawVarint64(ref ReadOnlySpan immediateBuffer)
+ {
+ //TODO: Implement fast path
int shift = 0;
ulong result = 0;
while (shift < 64)
{
- byte b = ReadRawByte();
- result |= (ulong) (b & 0x7F) << shift;
+ byte b = ReadRawByte(ref immediateBuffer);
+ result |= (ulong)(b & 0x7F) << shift;
if ((b & 0x80) == 0)
{
return result;
@@ -868,31 +1311,107 @@ internal ulong ReadRawVarint64()
throw InvalidProtocolBufferException.MalformedVarint();
}
+ [SecurityCritical]
+ private ulong ReadRawWrappedVarint64(ref ReadOnlySpan immediateBuffer)
+ {
+ int oldLimit = BeginReadNested(ref immediateBuffer);
+ uint tag;
+ ulong value = default(ulong);
+ while ((tag = ReadTag(ref immediateBuffer)) != 0)
+ {
+ if (tag == WellKnownTypes.WrappersReflection.WrapperValueVarintTag)
+ {
+ value = ReadRawVarint64(ref immediateBuffer);
+ }
+ else
+ {
+ SkipLastField(ref immediateBuffer);
+ }
+ }
+ EndReadNested(oldLimit);
+
+ return value;
+ }
+
///
/// Reads a 32-bit little-endian integer from the stream.
///
+ [SecuritySafeCritical]
internal uint ReadRawLittleEndian32()
{
- uint b1 = ReadRawByte();
- uint b2 = ReadRawByte();
- uint b3 = ReadRawByte();
- uint b4 = ReadRawByte();
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawLittleEndian32(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ private uint ReadRawLittleEndian32(ref ReadOnlySpan immediateBuffer)
+ {
+ if (bufferPos + 4 > bufferSize)
+ {
+ return SlowReadRawLittleEndian32(ref immediateBuffer);
+ }
+ else
+ {
+ var ret = BinaryPrimitives.ReadUInt32LittleEndian(immediateBuffer.Slice(bufferPos, 4));
+ bufferPos += 4;
+ return ret;
+ }
+ }
+
+ [SecurityCritical]
+ private uint SlowReadRawLittleEndian32(ref ReadOnlySpan immediateBuffer)
+ {
+ uint b1 = ReadRawByte(ref immediateBuffer);
+ uint b2 = ReadRawByte(ref immediateBuffer);
+ uint b3 = ReadRawByte(ref immediateBuffer);
+ uint b4 = ReadRawByte(ref immediateBuffer);
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
}
///
/// Reads a 64-bit little-endian integer from the stream.
///
- internal ulong ReadRawLittleEndian64()
+ [SecuritySafeCritical]
+ public ulong ReadRawLittleEndian64()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawLittleEndian64(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ internal ulong ReadRawLittleEndian64(ref ReadOnlySpan immediateBuffer)
{
- ulong b1 = ReadRawByte();
- ulong b2 = ReadRawByte();
- ulong b3 = ReadRawByte();
- ulong b4 = ReadRawByte();
- ulong b5 = ReadRawByte();
- ulong b6 = ReadRawByte();
- ulong b7 = ReadRawByte();
- ulong b8 = ReadRawByte();
+ if (bufferPos + 8 > bufferSize)
+ {
+ return SlowReadRawLittleEndian64(ref immediateBuffer);
+ }
+ else
+ {
+ var ret = BinaryPrimitives.ReadUInt64LittleEndian(immediateBuffer.Slice(bufferPos, 8));
+ bufferPos += 8;
+ return ret;
+ }
+ }
+
+ [SecurityCritical]
+ private ulong SlowReadRawLittleEndian64(ref ReadOnlySpan immediateBuffer)
+ {
+ ulong b1 = ReadRawByte(ref immediateBuffer);
+ ulong b2 = ReadRawByte(ref immediateBuffer);
+ ulong b3 = ReadRawByte(ref immediateBuffer);
+ ulong b4 = ReadRawByte(ref immediateBuffer);
+ ulong b5 = ReadRawByte(ref immediateBuffer);
+ ulong b6 = ReadRawByte(ref immediateBuffer);
+ ulong b7 = ReadRawByte(ref immediateBuffer);
+ ulong b8 = ReadRawByte(ref immediateBuffer);
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
| (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
}
@@ -934,7 +1453,8 @@ internal static long DecodeZigZag64(ulong n)
/// limit is returned.
///
/// The old limit.
- internal int PushLimit(int byteLimit)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int PushLimit(int byteLimit)
{
if (byteLimit < 0)
{
@@ -972,7 +1492,8 @@ private void RecomputeBufferSizeAfterLimit()
///
/// Discards the current limit, returning the previous limit.
///
- internal void PopLimit(int oldLimit)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PopLimit(int oldLimit)
{
currentLimit = oldLimit;
RecomputeBufferSizeAfterLimit();
@@ -982,7 +1503,8 @@ internal void PopLimit(int oldLimit)
/// Returns whether or not all the data before the limit has been read.
///
///
- internal bool ReachedLimit
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool ReachedLimit
{
get
{
@@ -1002,19 +1524,29 @@ internal bool ReachedLimit
///
public bool IsAtEnd
{
- get { return bufferPos == bufferSize && !RefillBuffer(false); }
+ [SecuritySafeCritical]
+ get
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return IsAtEndCore(ref immediateBuffer);
+ }
}
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [SecurityCritical]
+ internal bool IsAtEndCore(ref ReadOnlySpan immediateBuffer) => bufferPos == bufferSize && (input == null && !hasNativeInput || !RefillBuffer(false, ref immediateBuffer));
+
///
/// Called when buffer is empty to read more bytes from the
- /// input. If is true, RefillBuffer() gurantees that
+ /// input. If 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 is false,
+ /// or it will throw an exception. If mustSucceed is false,
/// RefillBuffer() returns false if no more bytes were available.
///
- ///
- ///
- private bool RefillBuffer(bool mustSucceed)
+ [SecurityCritical]
+ private bool RefillBuffer(bool mustSucceed, ref ReadOnlySpan immediateBuffer)
{
if (bufferPos < bufferSize)
{
@@ -1037,7 +1569,29 @@ private bool RefillBuffer(bool mustSucceed)
totalBytesRetired += bufferSize;
bufferPos = 0;
- bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
+ if (input != null)
+ {
+ bufferSize = input.Read(buffer, 0, buffer.Length);
+ }
+ else if (hasNativeInput)
+ {
+ nativeInputPastBuffersLength += bufferSize;
+ if (nativeInput.MoveNext())
+ {
+ currentNativeBuffer = nativeInput.Current;
+ immediateBuffer = nativeInput.Current.Span;
+ bufferSize = immediateBuffer.Length;
+ }
+ else
+ {
+ bufferSize = 0;
+ }
+ }
+ else
+ {
+ bufferSize = 0;
+ }
+
if (bufferSize < 0)
{
throw new InvalidOperationException("Stream.Read returned a negative count");
@@ -1072,13 +1626,25 @@ private bool RefillBuffer(bool mustSucceed)
///
/// the end of the stream or the current limit was reached
///
+ [SecuritySafeCritical]
internal byte ReadRawByte()
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawByte(ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ private byte ReadRawByte(ref ReadOnlySpan immediateBuffer)
{
if (bufferPos == bufferSize)
{
- RefillBuffer(true);
+ RefillBuffer(true, ref immediateBuffer);
}
- return buffer[bufferPos++];
+ return immediateBuffer[bufferPos++];
}
///
@@ -1087,7 +1653,19 @@ internal byte ReadRawByte()
///
/// the end of the stream or the current limit was reached
///
+ [SecuritySafeCritical]
internal byte[] ReadRawBytes(int size)
+ {
+ var immediateBuffer = ImmediateBuffer;
+ return ReadRawBytes(size, ref immediateBuffer);
+ }
+
+ ///
+ /// This supports the Protocol Buffers infrastructure and is not meant to be used directly from your code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [SecurityCritical]
+ private byte[] ReadRawBytes(int size, ref ReadOnlySpan immediateBuffer)
{
if (size < 0)
{
@@ -1097,7 +1675,7 @@ internal byte[] ReadRawBytes(int size)
if (totalBytesRetired + bufferPos + size > currentLimit)
{
// Read to the end of the stream (up to the current limit) anyway.
- SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
+ SkipRawBytes(currentLimit - totalBytesRetired - bufferPos, ref immediateBuffer);
// Then fail.
throw InvalidProtocolBufferException.TruncatedMessage();
}
@@ -1106,35 +1684,36 @@ internal byte[] ReadRawBytes(int size)
{
// We have all the bytes we need already.
byte[] bytes = new byte[size];
- ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
+ immediateBuffer.Slice(bufferPos, size).CopyTo(bytes);
bufferPos += size;
return bytes;
}
- else if (size < buffer.Length)
+ else if (size < immediateBuffer.Length || hasNativeInput)
{
// 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.
+ // of bytes or all the bytes are already in a contiguous buffer.
+ // 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);
+ immediateBuffer.Slice(bufferPos, pos).CopyTo(bytes);
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);
+ RefillBuffer(true, ref immediateBuffer);
while (size - pos > bufferSize)
{
- Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
+ immediateBuffer.Slice(0, bufferSize).CopyTo(bytes.AsSpan(pos, bufferSize));
pos += bufferSize;
bufferPos = bufferSize;
- RefillBuffer(true);
+ RefillBuffer(true, ref immediateBuffer);
}
- ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
+ immediateBuffer.Slice(0, size - pos).CopyTo(bytes.AsSpan(pos));
bufferPos = size - pos;
return bytes;
@@ -1165,7 +1744,7 @@ internal byte[] ReadRawBytes(int size)
while (sizeLeft > 0)
{
- byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
+ byte[] chunk = new byte[Math.Min(sizeLeft, immediateBuffer.Length)];
int pos = 0;
while (pos < chunk.Length)
{
@@ -1186,7 +1765,7 @@ internal byte[] ReadRawBytes(int size)
// Start by copying the leftover bytes from this.buffer.
int newPos = originalBufferSize - originalBufferPos;
- ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
+ immediateBuffer.Slice(originalBufferPos, newPos).CopyTo(bytes);
// And now all the chunks.
foreach (byte[] chunk in chunks)
@@ -1205,7 +1784,8 @@ internal byte[] ReadRawBytes(int size)
///
/// the end of the stream
/// or the current limit was reached
- private void SkipRawBytes(int size)
+ [SecurityCritical]
+ private void SkipRawBytes(int size, ref ReadOnlySpan immediateBuffer)
{
if (size < 0)
{
@@ -1215,7 +1795,7 @@ private void SkipRawBytes(int size)
if (totalBytesRetired + bufferPos + size > currentLimit)
{
// Read to the end of the stream anyway.
- SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
+ SkipRawBytes(currentLimit - totalBytesRetired - bufferPos, ref immediateBuffer);
// Then fail.
throw InvalidProtocolBufferException.TruncatedMessage();
}
@@ -1233,18 +1813,18 @@ private void SkipRawBytes(int size)
// 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)
+ if (input == null && !hasNativeInput)
{
throw InvalidProtocolBufferException.TruncatedMessage();
}
- SkipImpl(size - pos);
+ SkipImpl(size - pos, ref immediateBuffer);
totalBytesRetired += size - pos;
}
}
@@ -1253,9 +1833,34 @@ private void SkipRawBytes(int size)
///
/// Abstraction of skipping to cope with streams which can't really skip.
///
- private void SkipImpl(int amountToSkip)
+ [SecuritySafeCritical]
+ private void SkipImpl(int amountToSkip, ref ReadOnlySpan immediateBuffer)
{
- if (input.CanSeek)
+ if (hasNativeInput)
+ {
+ while (amountToSkip > 0)
+ {
+ if (!nativeInput.MoveNext())
+ {
+ throw InvalidProtocolBufferException.TruncatedMessage();
+ }
+ currentNativeBuffer = nativeInput.Current;
+ immediateBuffer = nativeInput.Current.Span;
+ bufferSize = immediateBuffer.Length;
+ bufferPos = 0;
+ if (bufferSize < amountToSkip)
+ {
+ bufferPos = bufferSize;
+ amountToSkip -= bufferSize;
+ }
+ else
+ {
+ bufferPos += amountToSkip;
+ amountToSkip = 0;
+ }
+ }
+ }
+ else if (input.CanSeek)
{
long previousPosition = input.Position;
input.Position += amountToSkip;
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs
index 9f620eb4012e..fbef8424a78c 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs
@@ -38,5 +38,24 @@ public static partial class WrappersReflection
/// Field number for the single "value" field in all wrapper types.
///
internal const int WrapperValueFieldNumber = Int32Value.ValueFieldNumber;
+
+
+ internal static readonly uint WrapperValueVarintTag = WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint);
+
+ internal static readonly uint WrapperValueFixed32Tag = WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed32);
+
+ internal static readonly uint WrapperValueFixed64Tag = WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed64);
+
+ internal static readonly uint WrapperValueLengthDelimitedTag = WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited);
+
+
+
+ internal static readonly byte WrapperValueVarintTagByte = (byte)WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint);
+
+ internal static readonly byte WrapperValueFixed32TagByte = (byte)WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed32);
+
+ internal static readonly byte WrapperValueFixed64TagByte = (byte)WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed64);
+
+ internal static readonly byte WrapperValueLengthDelimitedTagByte = (byte)WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited);
}
}