// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; #pragma warning disable 8500 // sizeof of managed types namespace System.Runtime.InteropServices { /// <summary> /// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>, /// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>. /// </summary> public static partial class MemoryMarshal { /// <summary> /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes. /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <param name="span">The source slice, of type <typeparamref name="T"/>.</param> /// <exception cref="ArgumentException"> /// Thrown when <typeparamref name="T"/> contains pointers. /// </exception> /// <exception cref="OverflowException"> /// Thrown if the Length property of the new Span would exceed int.MaxValue. /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Span<byte> AsBytes<T>(Span<T> span) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); return new Span<byte>( ref Unsafe.As<T, byte>(ref GetReference(span)), checked(span.Length * sizeof(T))); } /// <summary> /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes. /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <param name="span">The source slice, of type <typeparamref name="T"/>.</param> /// <exception cref="ArgumentException"> /// Thrown when <typeparamref name="T"/> contains pointers. /// </exception> /// <exception cref="OverflowException"> /// Thrown if the Length property of the new Span would exceed int.MaxValue. /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ReadOnlySpan<byte> AsBytes<T>(ReadOnlySpan<T> span) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); return new ReadOnlySpan<byte>( ref Unsafe.As<T, byte>(ref GetReference(span)), checked(span.Length * sizeof(T))); } /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary> /// <param name="memory">The <see cref="ReadOnlyMemory{T}"/>.</param> /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns> /// <remarks> /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution. <see cref="ReadOnlyMemory{T}"/> is used /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to. The method exists to enable variables typed /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>. /// </remarks> public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> memory) => Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref memory); /// <summary> /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced. /// </summary> public static ref T GetReference<T>(Span<T> span) => ref span._reference; /// <summary> /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced. /// </summary> public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._reference; /// <summary> /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to fake non-null pointer. Such a reference can be used /// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref Unsafe.AsRef(in span._reference) : ref Unsafe.AsRef<T>((void*)1); /// <summary> /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference /// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref Unsafe.AsRef(in span._reference) : ref Unsafe.AsRef<T>((void*)1); /// <summary> /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <remarks> /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// </remarks> /// <param name="span">The source slice, of type <typeparamref name="TFrom"/>.</param> /// <exception cref="ArgumentException"> /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span) where TFrom : struct where TTo : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf<TFrom>(); uint toSize = (uint)Unsafe.SizeOf<TTo>(); uint fromLength = (uint)span.Length; int toLength; if (fromSize == toSize) { // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` // should be optimized to just `length` but the JIT doesn't do that today. toLength = (int)fromLength; } else if (fromSize == 1) { // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int` // and can't eliminate the checked cast. This also avoids a 32 bit specific issue, // the JIT can't eliminate long multiply by 1. toLength = (int)(fromLength / toSize); } else { // Ensure that casts are done in such a way that the JIT is able to "see" // the uint->ulong casts and the multiply together so that on 32 bit targets // 32x32to64 multiplication is used. ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize; toLength = checked((int)toLengthUInt64); } return new Span<TTo>( ref Unsafe.As<TFrom, TTo>(ref span._reference), toLength); } /// <summary> /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <remarks> /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// </remarks> /// <param name="span">The source slice, of type <typeparamref name="TFrom"/>.</param> /// <exception cref="ArgumentException"> /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span) where TFrom : struct where TTo : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); // Use unsigned integers - unsigned division by constant (especially by power of 2) // and checked casts are faster and smaller. uint fromSize = (uint)Unsafe.SizeOf<TFrom>(); uint toSize = (uint)Unsafe.SizeOf<TTo>(); uint fromLength = (uint)span.Length; int toLength; if (fromSize == toSize) { // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` // should be optimized to just `length` but the JIT doesn't do that today. toLength = (int)fromLength; } else if (fromSize == 1) { // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int` // and can't eliminate the checked cast. This also avoids a 32 bit specific issue, // the JIT can't eliminate long multiply by 1. toLength = (int)(fromLength / toSize); } else { // Ensure that casts are done in such a way that the JIT is able to "see" // the uint->ulong casts and the multiply together so that on 32 bit targets // 32x32to64 multiplication is used. ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize; toLength = checked((int)toLengthUInt64); } return new ReadOnlySpan<TTo>( ref Unsafe.As<TFrom, TTo>(ref GetReference(span)), toLength); } /// <summary> /// Creates a new span over a portion of a regular managed object. This can be useful /// if part of a managed object represents a "fixed array." This is dangerous because the /// <paramref name="length"/> is not checked. /// </summary> /// <param name="reference">A reference to data.</param> /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> /// <returns>A span representing the specified reference and length.</returns> /// <remarks> /// This method should be used with caution. It is dangerous because the length argument is not checked. /// Even though the ref is annotated as scoped, it will be stored into the returned span, and the lifetime /// of the returned span will not be validated for safety, even by span-aware languages. /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> CreateSpan<T>(scoped ref T reference, int length) => new Span<T>(ref Unsafe.AsRef(in reference), length); /// <summary> /// Creates a new read-only span over a portion of a regular managed object. This can be useful /// if part of a managed object represents a "fixed array." This is dangerous because the /// <paramref name="length"/> is not checked. /// </summary> /// <param name="reference">A reference to data.</param> /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> /// <returns>A read-only span representing the specified reference and length.</returns> /// <remarks> /// This method should be used with caution. It is dangerous because the length argument is not checked. /// Even though the ref is annotated as scoped, it will be stored into the returned span, and the lifetime /// of the returned span will not be validated for safety, even by span-aware languages. /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref readonly T reference, int length) => new ReadOnlySpan<T>(ref Unsafe.AsRef(in reference), length); /// <summary>Creates a new read-only span for a null-terminated string.</summary> /// <param name="value">The pointer to the null-terminated string of characters.</param> /// <returns>A read-only span representing the specified null-terminated string, or an empty span if the pointer is null.</returns> /// <remarks>The returned span does not include the null terminator.</remarks> /// <exception cref="ArgumentException">The string is longer than <see cref="int.MaxValue"/>.</exception> [CLSCompliant(false)] public static unsafe ReadOnlySpan<char> CreateReadOnlySpanFromNullTerminated(char* value) => value != null ? new ReadOnlySpan<char>(value, string.wcslen(value)) : default; /// <summary>Creates a new read-only span for a null-terminated UTF-8 string.</summary> /// <param name="value">The pointer to the null-terminated string of bytes.</param> /// <returns>A read-only span representing the specified null-terminated string, or an empty span if the pointer is null.</returns> /// <remarks>The returned span does not include the null terminator, nor does it validate the well-formedness of the UTF-8 data.</remarks> /// <exception cref="ArgumentException">The string is longer than <see cref="int.MaxValue"/>.</exception> [CLSCompliant(false)] public static unsafe ReadOnlySpan<byte> CreateReadOnlySpanFromNullTerminated(byte* value) => value != null ? new ReadOnlySpan<byte>(value, string.strlen(value)) : default; /// <summary> /// Get an array segment from the underlying memory. /// If unable to get the array segment, return false with a default array segment. /// </summary> public static bool TryGetArray<T>(ReadOnlyMemory<T> memory, out ArraySegment<T> segment) { object? obj = memory.GetObjectStartLength(out int index, out int length); // As an optimization, we skip the "is string?" check below if typeof(T) is not char, // as Memory<T> / ROM<T> can't possibly contain a string instance in this case. if (obj != null && !( (typeof(T) == typeof(char) && obj.GetType() == typeof(string)) )) { if (RuntimeHelpers.ObjectHasComponentSize(obj)) { // The object has a component size, which means it's variable-length, but we already // checked above that it's not a string. The only remaining option is that it's a T[] // or a U[] which is blittable to a T[] (e.g., int[] and uint[]). // The array may be prepinned, so remove the high bit from the start index in the line below. // The ArraySegment<T> ctor will perform bounds checking on index & length. segment = new ArraySegment<T>(Unsafe.As<T[]>(obj), index & ReadOnlyMemory<T>.RemoveFlagsBitMask, length); return true; } else { // The object isn't null, and it's not variable-length, so the only remaining option // is MemoryManager<T>. The ArraySegment<T> ctor will perform bounds checking on index & length. Debug.Assert(obj is MemoryManager<T>); if (Unsafe.As<MemoryManager<T>>(obj).TryGetArray(out ArraySegment<T> tempArraySegment)) { segment = new ArraySegment<T>(tempArraySegment.Array!, tempArraySegment.Offset + index, length); return true; } } } // If we got to this point, the object is null, or it's a string, or it's a MemoryManager<T> // which isn't backed by an array. We'll quickly homogenize all zero-length Memory<T> instances // to an empty array for the purposes of reporting back to our caller. if (length == 0) { segment = ArraySegment<T>.Empty; return true; } // Otherwise, there's *some* data, but it's not convertible to T[]. segment = default; return false; } /// <summary> /// Gets an <see cref="MemoryManager{T}"/> from the underlying read-only memory. /// If unable to get the <typeparamref name="TManager"/> type, returns false. /// </summary> /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrieve.</typeparam> /// <param name="memory">The memory to get the manager for.</param> /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param> /// <returns>A <see cref="bool"/> indicating if it was successful.</returns> public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, [NotNullWhen(true)] out TManager? manager) where TManager : MemoryManager<T> { TManager? localManager; // Use register for null comparison rather than byref manager = localManager = memory.GetObjectStartLength(out _, out _) as TManager; #pragma warning disable 8762 // "Parameter 'manager' may not have a null value when exiting with 'true'." return localManager != null; #pragma warning restore 8762 } /// <summary> /// Gets an <see cref="MemoryManager{T}"/> and <paramref name="start" />, <paramref name="length" /> from the underlying read-only memory. /// If unable to get the <typeparamref name="TManager"/> type, returns false. /// </summary> /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrieve.</typeparam> /// <param name="memory">The memory to get the manager for.</param> /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param> /// <param name="start">The offset from the start of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param> /// <param name="length">The length of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param> /// <returns>A <see cref="bool"/> indicating if it was successful.</returns> public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, [NotNullWhen(true)] out TManager? manager, out int start, out int length) where TManager : MemoryManager<T> { TManager? localManager; // Use register for null comparison rather than byref manager = localManager = memory.GetObjectStartLength(out start, out length) as TManager; Debug.Assert(length >= 0); if (localManager == null) { start = default; length = default; return false; } #pragma warning disable 8762 // "Parameter 'manager' may not have a null value when exiting with 'true'." return true; #pragma warning restore 8762 } /// <summary> /// Creates an <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /> to allow /// the <paramref name="memory" /> to be used in existing APIs that take an <see cref="IEnumerable{T}"/>. /// </summary> /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> /// <param name="memory">The ReadOnlyMemory to view as an <see cref="IEnumerable{T}"/></param> /// <returns>An <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /></returns> public static IEnumerable<T> ToEnumerable<T>(ReadOnlyMemory<T> memory) { object? obj = memory.GetObjectStartLength(out int index, out int length); // If the memory is empty, just return an empty array as the enumerable. if (length is 0 || obj is null) { return Array.Empty<T>(); } // If the object is a string, we can optimize. If it isn't a slice, just return the string as the // enumerable. Otherwise, return an iterator dedicated to enumerating the object; while we could // use the general one for any ReadOnlyMemory, that will incur a .Span access for every element. if (typeof(T) == typeof(char) && obj is string str) { return (IEnumerable<T>)(object)(index == 0 && length == str.Length ? str : FromString(str, index, length)); static IEnumerable<char> FromString(string s, int offset, int count) { for (int i = 0; i < count; i++) { yield return s[offset + i]; } } } // If the object is an array, we can optimize. If it isn't a slice, just return the array as the // enumerable. Otherwise, return an iterator dedicated to enumerating the object. if (RuntimeHelpers.ObjectHasComponentSize(obj)) // Same check as in TryGetArray to confirm that obj is a T[] or a U[] which is blittable to a T[]. { T[] array = Unsafe.As<T[]>(obj); index &= ReadOnlyMemory<T>.RemoveFlagsBitMask; // the array may be prepinned, so remove the high bit from the start index in the line below. return index == 0 && length == array.Length ? array : FromArray(array, index, length); static IEnumerable<T> FromArray(T[] array, int offset, int count) { for (int i = 0; i < count; i++) { yield return array[offset + i]; } } } // The ROM<T> wraps a MemoryManager<T>. The best we can do is iterate, accessing .Span on each MoveNext. return FromMemoryManager(memory); static IEnumerable<T> FromMemoryManager(ReadOnlyMemory<T> memory) { for (int i = 0; i < memory.Length; i++) { yield return memory.Span[i]; } } } /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary> /// <param name="memory">The memory that may be wrapping a <see cref="string"/> object.</param> /// <param name="text">The string.</param> /// <param name="start">The starting location in <paramref name="text"/>.</param> /// <param name="length">The number of items in <paramref name="text"/>.</param> /// <returns></returns> public static bool TryGetString(ReadOnlyMemory<char> memory, [NotNullWhen(true)] out string? text, out int start, out int length) { if (memory.GetObjectStartLength(out int offset, out int count) is string s) { Debug.Assert(offset >= 0); Debug.Assert(count >= 0); text = s; start = offset; length = count; return true; } else { text = null; start = 0; length = 0; return false; } } /// <summary> /// Reads a structure of type T out of a read-only span of bytes. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe T Read<T>(ReadOnlySpan<byte> source) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if (sizeof(T) > source.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } return Unsafe.ReadUnaligned<T>(ref GetReference(source)); } /// <summary> /// Reads a structure of type T out of a span of bytes. /// </summary> /// <returns>If the span is too small to contain the type T, return false.</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe bool TryRead<T>(ReadOnlySpan<byte> source, out T value) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if (sizeof(T) > (uint)source.Length) { value = default; return false; } value = Unsafe.ReadUnaligned<T>(ref GetReference(source)); return true; } /// <summary> /// Writes a structure of type T into a span of bytes. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Write<T>(Span<byte> destination, in T value) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if ((uint)sizeof(T) > (uint)destination.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } Unsafe.WriteUnaligned(ref GetReference(destination), value); } /// <summary> /// Writes a structure of type T into a span of bytes. /// </summary> /// <returns>If the span is too small to contain the type T, return false.</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe bool TryWrite<T>(Span<byte> destination, in T value) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if (sizeof(T) > (uint)destination.Length) { return false; } Unsafe.WriteUnaligned(ref GetReference(destination), value); return true; } /// <summary> /// Re-interprets a span of bytes as a reference to structure of type T. /// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <remarks> /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref T AsRef<T>(Span<byte> span) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if (sizeof(T) > (uint)span.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } return ref Unsafe.As<byte, T>(ref GetReference(span)); } /// <summary> /// Re-interprets a span of bytes as a reference to structure of type T. /// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// </summary> /// <remarks> /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref readonly T AsRef<T>(ReadOnlySpan<byte> span) where T : struct { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } if (sizeof(T) > (uint)span.Length) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } return ref Unsafe.As<byte, T>(ref GetReference(span)); } /// <summary> /// Creates a new memory over the portion of the pre-pinned target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// </summary> /// <param name="array">The pre-pinned target array.</param> /// <param name="start">The index at which to begin the memory.</param> /// <param name="length">The number of items in the memory.</param> /// <remarks>This method should only be called on an array that is already pinned and /// that array should not be unpinned while the returned Memory<typeparamref name="T"/> is still in use. /// Calling this method on an unpinned array could result in memory corruption.</remarks> /// <remarks>Returns default when <paramref name="array"/> is null.</remarks> /// <exception cref="ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Memory<T> CreateFromPinnedArray<T>(T[]? array, int start, int length) { if (array == null) { if (start != 0 || length != 0) ThrowHelper.ThrowArgumentOutOfRangeException(); return default; } if (!typeof(T).IsValueType && array.GetType() != typeof(T[])) ThrowHelper.ThrowArrayTypeMismatchException(); if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) ThrowHelper.ThrowArgumentOutOfRangeException(); // Before using _index, check if _index < 0, then 'and' it with RemoveFlagsBitMask return new Memory<T>((object)array, start | (1 << 31), length); } } } |