diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index 3235cca2d6..4c9234dcf6 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -143,7 +143,7 @@ namespace Google.Protobuf this.state.bufferSize = bufferSize; this.state.sizeLimit = DefaultSizeLimit; this.state.recursionLimit = DefaultRecursionLimit; - this.state.segmentedBufferHelper = new SegmentedBufferHelper(this); + SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper); this.state.codedInputStream = this; this.leaveOpen = leaveOpen; diff --git a/csharp/src/Google.Protobuf/ParseContext.cs b/csharp/src/Google.Protobuf/ParseContext.cs index 4d0c140f01..6c74e73fc8 100644 --- a/csharp/src/Google.Protobuf/ParseContext.cs +++ b/csharp/src/Google.Protobuf/ParseContext.cs @@ -89,7 +89,7 @@ namespace Google.Protobuf this.state.sizeLimit = DefaultSizeLimit; this.state.recursionLimit = recursionLimit; this.state.currentLimit = int.MaxValue; - this.state.segmentedBufferHelper = new SegmentedBufferHelper(input, out this.buffer); + SegmentedBufferHelper.Initialize(input, out this.state.segmentedBufferHelper, out this.buffer); this.state.bufferPos = 0; this.state.bufferSize = this.buffer.Length; this.state.codedInputStream = null; diff --git a/csharp/src/Google.Protobuf/SegmentedBufferHelper.cs b/csharp/src/Google.Protobuf/SegmentedBufferHelper.cs index 2c9db88e95..a8a7562db1 100644 --- a/csharp/src/Google.Protobuf/SegmentedBufferHelper.cs +++ b/csharp/src/Google.Protobuf/SegmentedBufferHelper.cs @@ -43,18 +43,37 @@ namespace Google.Protobuf /// internal struct SegmentedBufferHelper { - private readonly int? totalLength; + private int? totalLength; private ReadOnlySequence.Enumerator readOnlySequenceEnumerator; - private readonly CodedInputStream codedInputStream; + private CodedInputStream codedInputStream; - public SegmentedBufferHelper(ReadOnlySequence sequence, out ReadOnlySpan firstSpan) + /// + /// Initialize an instance with a coded input stream. + /// This approach is faster than using a constructor because the instance to initialize is passed by reference + /// and we can write directly into it without copying. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Initialize(CodedInputStream codedInputStream, out SegmentedBufferHelper instance) + { + instance.totalLength = codedInputStream.InternalInputStream == null ? (int?)codedInputStream.InternalBuffer.Length : null; + instance.readOnlySequenceEnumerator = default; + instance.codedInputStream = codedInputStream; + } + + /// + /// Initialize an instance with a read only sequence. + /// This approach is faster than using a constructor because the instance to initialize is passed by reference + /// and we can write directly into it without copying. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Initialize(ReadOnlySequence sequence, out SegmentedBufferHelper instance, out ReadOnlySpan firstSpan) { - this.codedInputStream = null; + instance.codedInputStream = null; if (sequence.IsSingleSegment) { firstSpan = sequence.First.Span; - this.totalLength = firstSpan.Length; - this.readOnlySequenceEnumerator = default; + instance.totalLength = firstSpan.Length; + instance.readOnlySequenceEnumerator = default; } else { @@ -62,17 +81,10 @@ namespace Google.Protobuf // very first read will result in slowpath (because the first thing to do is to // refill to get the first buffer segment) firstSpan = default; - this.totalLength = (int) sequence.Length; - this.readOnlySequenceEnumerator = sequence.GetEnumerator(); + instance.totalLength = (int) sequence.Length; + instance.readOnlySequenceEnumerator = sequence.GetEnumerator(); } } - - public SegmentedBufferHelper(CodedInputStream codedInputStream) - { - this.totalLength = codedInputStream.InternalInputStream == null ? (int?)codedInputStream.InternalBuffer.Length : null; - this.readOnlySequenceEnumerator = default; - this.codedInputStream = codedInputStream; - } public bool RefillBuffer(ref ReadOnlySpan buffer, ref ParserInternalState state, bool mustSucceed) {