// 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)); } } }