|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.IO.Pipelines;
// Copied from https://github.com/dotnet/corefx/blob/de3902bb56f1254ec1af4bf7d092fc2c048734cc/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs
internal sealed class BufferSegment : ReadOnlySequenceSegment<byte>
{
private object? _memoryOwner;
private BufferSegment? _next;
private int _end;
/// <summary>
/// The End represents the offset into AvailableMemory where the range of "active" bytes ends. At the point when the block is leased
/// the End is guaranteed to be equal to Start. The value of Start may be assigned anywhere between 0 and
/// Buffer.Length, and must be equal to or less than End.
/// </summary>
public int End
{
get => _end;
set
{
Debug.Assert(value <= AvailableMemory.Length);
_end = value;
Memory = AvailableMemory.Slice(0, value);
}
}
/// <summary>
/// Reference to the next block of data when the overall "active" bytes spans multiple blocks. At the point when the block is
/// leased Next is guaranteed to be null. Start, End, and Next are used together in order to create a linked-list of discontiguous
/// working memory. The "active" memory is grown when bytes are copied in, End is increased, and Next is assigned. The "active"
/// memory is shrunk when bytes are consumed, Start is increased, and blocks are returned to the pool.
/// </summary>
public BufferSegment? NextSegment
{
get => _next;
set
{
Next = value;
_next = value;
}
}
public void SetOwnedMemory(IMemoryOwner<byte> memoryOwner)
{
_memoryOwner = memoryOwner;
AvailableMemory = memoryOwner.Memory;
}
public void SetOwnedMemory(byte[] arrayPoolBuffer)
{
_memoryOwner = arrayPoolBuffer;
AvailableMemory = arrayPoolBuffer;
}
public void ResetMemory()
{
Debug.Assert(_memoryOwner != null);
if (_memoryOwner is IMemoryOwner<byte> owner)
{
owner.Dispose();
}
else
{
byte[] poolArray = (byte[])_memoryOwner;
ArrayPool<byte>.Shared.Return(poolArray);
}
// Order of below field clears is significant as it clears in a sequential order
// https://github.com/dotnet/corefx/pull/35256#issuecomment-462800477
Next = null;
RunningIndex = 0;
Memory = default;
_memoryOwner = null;
_next = null;
_end = 0;
AvailableMemory = default;
}
// Exposed for testing
internal object? MemoryOwner => _memoryOwner;
public Memory<byte> AvailableMemory { get; private set; }
public int Length => End;
public int WritableBytes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => AvailableMemory.Length - End;
}
public void SetNext(BufferSegment segment)
{
Debug.Assert(segment != null);
Debug.Assert(Next == null);
NextSegment = segment;
segment = this;
while (segment.Next != null)
{
segment.NextSegment!.RunningIndex = segment.RunningIndex + segment.Length;
segment = segment.NextSegment;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long GetLength(BufferSegment startSegment, int startIndex, BufferSegment endSegment, int endIndex)
{
return (endSegment.RunningIndex + (uint)endIndex) - (startSegment.RunningIndex + (uint)startIndex);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long GetLength(long startPosition, BufferSegment endSegment, int endIndex)
{
return (endSegment.RunningIndex + (uint)endIndex) - startPosition;
}
}
|