|
|
|
@ -24,64 +24,42 @@ using System.Buffers; |
|
|
|
|
|
|
|
|
|
namespace Grpc.Core.Internal |
|
|
|
|
{ |
|
|
|
|
internal class ReusableSliceBuffer |
|
|
|
|
// Allow creating instances of Memory<byte> from Slice. |
|
|
|
|
// Represents a chunk of native memory, but doesn't manage its lifetime. |
|
|
|
|
// Instances of this class are reuseable - they can be reset to point to a different memory chunk. |
|
|
|
|
// That is important to make the instances cacheable (rather then creating new instances |
|
|
|
|
// the old ones will be reused to reduce GC pressure). |
|
|
|
|
internal class SliceMemoryManager : MemoryManager<byte> |
|
|
|
|
{ |
|
|
|
|
public const int MaxCachedSegments = 1024; // ~4MB payload for 4K slices |
|
|
|
|
private Slice slice; |
|
|
|
|
|
|
|
|
|
readonly SliceSegment[] cachedSegments = new SliceSegment[MaxCachedSegments]; |
|
|
|
|
int populatedSegmentCount; |
|
|
|
|
|
|
|
|
|
public ReadOnlySequence<byte> PopulateFrom(IBufferReader bufferReader) |
|
|
|
|
public void Reset(Slice slice) |
|
|
|
|
{ |
|
|
|
|
populatedSegmentCount = 0; |
|
|
|
|
long offset = 0; |
|
|
|
|
SliceSegment prevSegment = null; |
|
|
|
|
while (bufferReader.TryGetNextSlice(out Slice slice)) |
|
|
|
|
{ |
|
|
|
|
// Initialize cached segment if still null or just allocate a new segment if we already reached MaxCachedSegments |
|
|
|
|
var current = populatedSegmentCount < cachedSegments.Length ? cachedSegments[populatedSegmentCount] : new SliceSegment(); |
|
|
|
|
if (current == null) |
|
|
|
|
{ |
|
|
|
|
current = cachedSegments[populatedSegmentCount] = new SliceSegment(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
current.Reset(slice, offset); |
|
|
|
|
prevSegment?.SetNext(current); |
|
|
|
|
this.slice = slice; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
populatedSegmentCount ++; |
|
|
|
|
offset += slice.Length; |
|
|
|
|
prevSegment = current; |
|
|
|
|
} |
|
|
|
|
public void Reset() |
|
|
|
|
{ |
|
|
|
|
Reset(new Slice(IntPtr.Zero, 0)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Not necessary for ending the ReadOnlySequence, but for making sure we |
|
|
|
|
// don't keep more than MaxCachedSegments alive. |
|
|
|
|
prevSegment?.SetNext(null); |
|
|
|
|
public override Span<byte> GetSpan() |
|
|
|
|
{ |
|
|
|
|
return slice.ToSpanUnsafe(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (populatedSegmentCount == 0) |
|
|
|
|
{ |
|
|
|
|
return ReadOnlySequence<byte>.Empty; |
|
|
|
|
} |
|
|
|
|
public override MemoryHandle Pin(int elementIndex = 0) |
|
|
|
|
{ |
|
|
|
|
throw new NotSupportedException(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var firstSegment = cachedSegments[0]; |
|
|
|
|
var lastSegment = prevSegment; |
|
|
|
|
return new ReadOnlySequence<byte>(firstSegment, 0, lastSegment, lastSegment.Memory.Length); |
|
|
|
|
public override void Unpin() |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void Invalidate() |
|
|
|
|
protected override void Dispose(bool disposing) |
|
|
|
|
{ |
|
|
|
|
if (populatedSegmentCount == 0) |
|
|
|
|
{ |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
var segment = cachedSegments[0]; |
|
|
|
|
while (segment != null) |
|
|
|
|
{ |
|
|
|
|
segment.Reset(new Slice(IntPtr.Zero, 0), 0); |
|
|
|
|
var nextSegment = (SliceSegment) segment.Next; |
|
|
|
|
segment.SetNext(null); |
|
|
|
|
segment = nextSegment; |
|
|
|
|
} |
|
|
|
|
populatedSegmentCount = 0; |
|
|
|
|
// NOP |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|