#nullable enable
using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Common.Memory
{
///
/// An implementation with an embedded length and fast
/// accessor, with memory allocated from .
///
/// The type of item to store.
public sealed class MemoryOwner : IMemoryOwner
{
private readonly int _length;
private T[]? _array;
///
/// Initializes a new instance of the class with the specified parameters.
///
/// The length of the new memory buffer to use
private MemoryOwner(int length)
{
_length = length;
_array = ArrayPool.Shared.Rent(length);
}
///
/// Creates a new instance with the specified length.
///
/// The length of the new memory buffer to use
/// A instance of the requested length
/// Thrown when is not valid
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MemoryOwner Rent(int length) => new(length);
///
/// Creates a new instance with the specified length and the content cleared.
///
/// The length of the new memory buffer to use
/// A instance of the requested length and the content cleared
/// Thrown when is not valid
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MemoryOwner RentCleared(int length)
{
MemoryOwner result = new(length);
result._array.AsSpan(0, length).Clear();
return result;
}
///
/// Creates a new instance with the content copied from the specified buffer.
///
/// The buffer to copy
/// A instance with the same length and content as
public static MemoryOwner RentCopy(ReadOnlySpan buffer)
{
MemoryOwner result = new(buffer.Length);
buffer.CopyTo(result._array);
return result;
}
///
/// Gets the number of items in the current instance.
///
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _length;
}
///
public Memory Memory
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
T[]? array = _array;
if (array is null)
{
ThrowObjectDisposedException();
}
return new(array, 0, _length);
}
}
///
/// Gets a wrapping the memory belonging to the current instance.
///
///
/// Uses a trick made possible by the .NET 6+ runtime array layout.
///
public Span Span
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
T[]? array = _array;
if (array is null)
{
ThrowObjectDisposedException();
}
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array);
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
}
}
///
public void Dispose()
{
T[]? array = Interlocked.Exchange(ref _array, null);
if (array is not null)
{
ArrayPool.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences());
}
}
///
/// Throws an when is .
///
[DoesNotReturn]
private static void ThrowObjectDisposedException()
{
throw new ObjectDisposedException(nameof(MemoryOwner), "The buffer has already been disposed.");
}
}
}