using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace KcpSharp
{
///
/// 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}]";
}
}