/ src / Ryujinx.Common / Memory / MemoryOwner.cs
MemoryOwner.cs
  1  #nullable enable
  2  using System;
  3  using System.Buffers;
  4  using System.Diagnostics.CodeAnalysis;
  5  using System.Runtime.CompilerServices;
  6  using System.Runtime.InteropServices;
  7  using System.Threading;
  8  
  9  namespace Ryujinx.Common.Memory
 10  {
 11      /// <summary>
 12      /// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/>
 13      /// accessor, with memory allocated from <seealso cref="ArrayPool{T}.Shared"/>.
 14      /// </summary>
 15      /// <typeparam name="T">The type of item to store.</typeparam>
 16      public sealed class MemoryOwner<T> : IMemoryOwner<T>
 17      {
 18          private readonly int _length;
 19          private T[]? _array;
 20  
 21          /// <summary>
 22          /// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
 23          /// </summary>
 24          /// <param name="length">The length of the new memory buffer to use</param>
 25          private MemoryOwner(int length)
 26          {
 27              _length = length;
 28              _array = ArrayPool<T>.Shared.Rent(length);
 29          }
 30  
 31          /// <summary>
 32          /// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length.
 33          /// </summary>
 34          /// <param name="length">The length of the new memory buffer to use</param>
 35          /// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length</returns>
 36          /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
 37          [MethodImpl(MethodImplOptions.AggressiveInlining)]
 38          public static MemoryOwner<T> Rent(int length) => new(length);
 39  
 40          /// <summary>
 41          /// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length and the content cleared.
 42          /// </summary>
 43          /// <param name="length">The length of the new memory buffer to use</param>
 44          /// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length and the content cleared</returns>
 45          /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
 46          [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47          public static MemoryOwner<T> RentCleared(int length)
 48          {
 49              MemoryOwner<T> result = new(length);
 50  
 51              result._array.AsSpan(0, length).Clear();
 52  
 53              return result;
 54          }
 55  
 56          /// <summary>
 57          /// Creates a new <see cref="MemoryOwner{T}"/> instance with the content copied from the specified buffer.
 58          /// </summary>
 59          /// <param name="buffer">The buffer to copy</param>
 60          /// <returns>A <see cref="MemoryOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
 61          public static MemoryOwner<T> RentCopy(ReadOnlySpan<T> buffer)
 62          {
 63              MemoryOwner<T> result = new(buffer.Length);
 64  
 65              buffer.CopyTo(result._array);
 66  
 67              return result;
 68          }
 69  
 70          /// <summary>
 71          /// Gets the number of items in the current instance.
 72          /// </summary>
 73          public int Length
 74          {
 75              [MethodImpl(MethodImplOptions.AggressiveInlining)]
 76              get => _length;
 77          }
 78  
 79          /// <inheritdoc/>
 80          public Memory<T> Memory
 81          {
 82              [MethodImpl(MethodImplOptions.AggressiveInlining)]
 83              get
 84              {
 85                  T[]? array = _array;
 86  
 87                  if (array is null)
 88                  {
 89                      ThrowObjectDisposedException();
 90                  }
 91  
 92                  return new(array, 0, _length);
 93              }
 94          }
 95  
 96          /// <summary>
 97          /// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
 98          /// </summary>
 99          /// <remarks>
100          /// Uses a trick made possible by the .NET 6+ runtime array layout.
101          /// </remarks>
102          public Span<T> Span
103          {
104              [MethodImpl(MethodImplOptions.AggressiveInlining)]
105              get
106              {
107                  T[]? array = _array;
108  
109                  if (array is null)
110                  {
111                      ThrowObjectDisposedException();
112                  }
113  
114                  ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array);
115  
116                  return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
117              }
118          }
119  
120          /// <inheritdoc/>
121          public void Dispose()
122          {
123              T[]? array = Interlocked.Exchange(ref _array, null);
124  
125              if (array is not null)
126              {
127                  ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
128              }
129          }
130  
131          /// <summary>
132          /// Throws an <see cref="ObjectDisposedException"/> when <see cref="_array"/> is <see langword="null"/>.
133          /// </summary>
134          [DoesNotReturn]
135          private static void ThrowObjectDisposedException()
136          {
137              throw new ObjectDisposedException(nameof(MemoryOwner<T>), "The buffer has already been disposed.");
138          }
139      }
140  }