using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace NahidaImpact.Kcp { /// /// The buffer rented and owned by KcpSharp. /// public readonly struct KcpRentedBuffer : IEquatable, IDisposable { private readonly object _owner; private readonly Memory _memory; internal object Owner => _owner; /// /// The rented buffer. /// public Memory Memory => _memory; /// /// The rented buffer. /// public Span Span => _memory.Span; /// /// Whether this struct contains buffer rented from the pool. /// public bool IsAllocated => _owner is not null; /// /// Whether this buffer contains no data. /// public bool IsEmpry => _memory.IsEmpty; internal KcpRentedBuffer(object owner, Memory buffer) { _owner = owner; _memory = buffer; } /// /// Create the buffer from the specified . /// /// The memory region of this buffer. /// The rented buffer. public static KcpRentedBuffer FromMemory(Memory memory) { return new KcpRentedBuffer(null, memory); } /// /// Create the buffer from the shared array pool. /// /// The minimum size of the buffer required. /// The rented buffer. public static KcpRentedBuffer FromSharedArrayPool(int size) { if (size < 0) { throw new ArgumentOutOfRangeException(nameof(size)); } byte[] buffer = ArrayPool.Shared.Rent(size); return new KcpRentedBuffer(ArrayPool.Shared, buffer); } /// /// Create the buffer from the specified array pool. /// /// The array pool to use. /// The byte array rented from the specified pool. /// The rented buffer. public static KcpRentedBuffer FromArrayPool(ArrayPool pool, byte[] buffer) { if (pool is null) { throw new ArgumentNullException(nameof(pool)); } if (buffer is null) { throw new ArgumentNullException(nameof(buffer)); } return new KcpRentedBuffer(pool, buffer); } /// /// Create the buffer from the specified array pool. /// /// The array pool to use. /// The byte array segment rented from the specified pool. /// The rented buffer. public static KcpRentedBuffer FromArrayPool(ArrayPool pool, ArraySegment arraySegment) { if (pool is null) { throw new ArgumentNullException(nameof(pool)); } return new KcpRentedBuffer(pool, arraySegment); } /// /// Create the buffer from the specified array pool. /// /// The array pool to use. /// The minimum size of the buffer required. /// The rented buffer. public static KcpRentedBuffer FromArrayPool(ArrayPool pool, int size) { if (pool is null) { throw new ArgumentNullException(nameof(pool)); } if (size < 0) { throw new ArgumentOutOfRangeException(nameof(size)); } return new KcpRentedBuffer(pool, pool.Rent(size)); } /// /// Create the buffer from the memory owner. /// /// The owner of this memory region. /// The rented buffer. public static KcpRentedBuffer FromMemoryOwner(IMemoryOwner memoryOwner) { if (memoryOwner is null) { throw new ArgumentNullException(nameof(memoryOwner)); } return new KcpRentedBuffer(memoryOwner, memoryOwner.Memory); } /// /// Create the buffer from the memory owner. /// /// The owner of this memory region. /// The memory region of the buffer. /// The rented buffer. public static KcpRentedBuffer FromMemoryOwner(IDisposable memoryOwner, Memory memory) { if (memoryOwner is null) { throw new ArgumentNullException(nameof(memoryOwner)); } return new KcpRentedBuffer(memoryOwner, memory); } /// /// Forms a slice out of the current buffer that begins at a specified index. /// /// The index at which to begin the slice. /// An object that contains all elements of the current instance from start to the end of the instance. public KcpRentedBuffer Slice(int start) { Memory memory = _memory; if ((uint)start > (uint)memory.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(nameof(start)); } return new KcpRentedBuffer(_owner, memory.Slice(start)); } /// /// Forms a slice out of the current memory starting at a specified index for a specified length. /// /// The index at which to begin the slice. /// The number of elements to include in the slice. /// An object that contains elements from the current instance starting at . public KcpRentedBuffer Slice(int start, int length) { Memory memory = _memory; if ((uint)start > (uint)memory.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(nameof(start)); } if ((uint)length > (uint)(memory.Length - start)) { ThrowHelper.ThrowArgumentOutOfRangeException(nameof(length)); } return new KcpRentedBuffer(_owner, memory.Slice(start, length)); } /// public void Dispose() { Debug.Assert(_owner is null || _owner is ArrayPool || _owner is IDisposable); if (_owner is null) { return; } if (_owner is ArrayPool arrayPool) { if (MemoryMarshal.TryGetArray(_memory, out ArraySegment arraySegment)) { arrayPool.Return(arraySegment.Array!); return; } } if (_owner is IDisposable disposable) { disposable.Dispose(); } } /// public bool Equals(KcpRentedBuffer other) => ReferenceEquals(_owner, other._owner) && _memory.Equals(other._memory); /// public override bool Equals(object obj) => obj is KcpRentedBuffer other && Equals(other); /// public override int GetHashCode() => _owner is null ? _memory.GetHashCode() : HashCode.Combine(RuntimeHelpers.GetHashCode(_owner), _memory); /// public override string ToString() => $"KcpSharp.KcpRentedBuffer[{_memory.Length}]"; } }