// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Diagnostics;
using Internal.Runtime.CompilerServices;
namespace System.Runtime.InteropServices
{
///
/// Provides a collection of methods for interoperating with , ,
/// , and .
///
public static partial class MemoryMarshal
{
///
/// Get an array segment from the underlying memory.
/// If unable to get the array segment, return false with a default array segment.
///
public static bool TryGetArray(ReadOnlyMemory memory, out ArraySegment segment)
{
object obj = memory.GetObjectStartLength(out int index, out int length);
if (index < 0)
{
Debug.Assert(length >= 0);
if (((MemoryManager)obj).TryGetArray(out ArraySegment arraySegment))
{
segment = new ArraySegment(arraySegment.Array, arraySegment.Offset + (index & ReadOnlyMemory.RemoveFlagsBitMask), length);
return true;
}
}
else if (obj is T[] arr)
{
segment = new ArraySegment(arr, index, length & ReadOnlyMemory.RemoveFlagsBitMask);
return true;
}
if ((length & ReadOnlyMemory.RemoveFlagsBitMask) == 0)
{
segment = ArraySegment.Empty;
return true;
}
segment = default;
return false;
}
///
/// Gets an from the underlying read-only memory.
/// If unable to get the type, returns false.
///
/// The element type of the .
/// The type of to try and retrive.
/// The memory to get the manager for.
/// The returned manager of the .
/// A indicating if it was successful.
public static bool TryGetMemoryManager(ReadOnlyMemory memory, out TManager manager)
where TManager : MemoryManager
{
TManager localManager; // Use register for null comparison rather than byref
manager = localManager = memory.GetObjectStartLength(out _, out _) as TManager;
return manager != null;
}
///
/// Gets an and , from the underlying read-only memory.
/// If unable to get the type, returns false.
///
/// The element type of the .
/// The type of to try and retrive.
/// The memory to get the manager for.
/// The returned manager of the .
/// The offset from the start of the that the represents.
/// The length of the that the represents.
/// A indicating if it was successful.
public static bool TryGetMemoryManager(ReadOnlyMemory memory, out TManager manager, out int start, out int length)
where TManager : MemoryManager
{
TManager localManager; // Use register for null comparison rather than byref
manager = localManager = memory.GetObjectStartLength(out start, out length) as TManager;
start &= ReadOnlyMemory.RemoveFlagsBitMask;
Debug.Assert(length >= 0);
if (manager == null)
{
start = default;
length = default;
return false;
}
return true;
}
///
/// Creates an view of the given to allow
/// the to be used in existing APIs that take an .
///
/// The element type of the .
/// The ReadOnlyMemory to view as an
/// An view of the given
public static IEnumerable ToEnumerable(ReadOnlyMemory memory)
{
for (int i = 0; i < memory.Length; i++)
yield return memory.Span[i];
}
/// Attempts to get the underlying from a .
/// The memory that may be wrapping a object.
/// The string.
/// The starting location in .
/// The number of items in .
///
public static bool TryGetString(ReadOnlyMemory memory, 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;
}
}
///
/// Reads a structure of type T out of a read-only span of bytes.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Read(ReadOnlySpan source)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf() > source.Length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
return Unsafe.ReadUnaligned(ref GetReference(source));
}
///
/// Reads a structure of type T out of a span of bytes.
/// If the span is too small to contain the type T, return false.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead(ReadOnlySpan source, out T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf() > (uint)source.Length)
{
value = default;
return false;
}
value = Unsafe.ReadUnaligned(ref GetReference(source));
return true;
}
///
/// Writes a structure of type T into a span of bytes.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write(Span destination, ref T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if ((uint)Unsafe.SizeOf() > (uint)destination.Length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
Unsafe.WriteUnaligned(ref GetReference(destination), value);
}
///
/// Writes a structure of type T into a span of bytes.
/// If the span is too small to contain the type T, return false.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryWrite(Span destination, ref T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf() > (uint)destination.Length)
{
return false;
}
Unsafe.WriteUnaligned(ref GetReference(destination), value);
return true;
}
///
/// 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.
///
///
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef(Span span)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf() > (uint)span.Length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
return ref Unsafe.As(ref GetReference(span));
}
///
/// 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.
///
///
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T AsRef(ReadOnlySpan span)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences())
{
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf() > (uint)span.Length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
return ref Unsafe.As(ref GetReference(span));
}
///
/// Creates a new memory over the portion of the pre-pinned target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
///
/// The pre-pinned target array.
/// The index at which to begin the memory.
/// The number of items in the memory.
/// This method should only be called on an array that is already pinned and
/// that array should not be unpinned while the returned Memory is still in use.
/// Calling this method on an unpinned array could result in memory corruption.
/// Returns default when is null.
/// Thrown when is covariant and array's type is not exactly T[].
///
/// Thrown when the specified or end index is not in the range (<0 or >=Length).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory CreateFromPinnedArray(T[] array, int start, int length)
{
if (array == null)
{
if (start != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
return default;
}
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
// Before using _length, check if _length < 0, then 'and' it with RemoveFlagsBitMask
return new Memory((object)array, start, length | (1 << 31));
}
}
}