#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."); } } }