using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace KianaBH.KcpSharp.Base; /// /// The buffer rented and owned by KcpSharp. /// public readonly struct KcpRentedBuffer : IEquatable, IDisposable { private readonly Memory _memory; internal object? Owner { get; } /// /// 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)); var 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) { var 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) { var 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) { return ReferenceEquals(Owner, other.Owner) && _memory.Equals(other._memory); } /// public override bool Equals(object? obj) { return obj is KcpRentedBuffer other && Equals(other); } /// public override int GetHashCode() { return Owner is null ? _memory.GetHashCode() : HashCode.Combine(RuntimeHelpers.GetHashCode(Owner), _memory); } /// public override string ToString() { return $"KcpSharp.KcpRentedBuffer[{_memory.Length}]"; } }