// 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; using System.Buffers; using System.Diagnostics.Contracts; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CommunityToolkit.HighPerformance.Buffers.Internals; using CommunityToolkit.HighPerformance.Buffers.Internals.Interfaces; using MemoryStream = CommunityToolkit.HighPerformance.Streams.MemoryStream; namespace CommunityToolkit.HighPerformance; /// /// Helpers for working with the type. /// public static class ReadOnlyMemoryExtensions { #if NETSTANDARD2_1_OR_GREATER /// /// Returns a instance wrapping the underlying data for the given instance. /// /// The type of items in the input instance. /// The input instance. /// The height of the resulting 2D area. /// The width of each row in the resulting 2D area. /// The resulting instance. /// /// Thrown when one of the input parameters is out of range. /// /// /// Thrown when the requested area is outside of bounds for . /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlyMemory2D AsMemory2D(this ReadOnlyMemory memory, int height, int width) { return new(memory, height, width); } /// /// Returns a instance wrapping the underlying data for the given instance. /// /// The type of items in the input instance. /// The input instance. /// The initial offset within . /// The height of the resulting 2D area. /// The width of each row in the resulting 2D area. /// The pitch in the resulting 2D area. /// The resulting instance. /// /// Thrown when one of the input parameters is out of range. /// /// /// Thrown when the requested area is outside of bounds for . /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlyMemory2D AsMemory2D(this ReadOnlyMemory memory, int offset, int height, int width, int pitch) { return new(memory, offset, height, width, pitch); } #endif /// /// Casts a of one primitive type to of bytes. /// /// The type if items in the source . /// The source , of type . /// A of bytes. /// /// Thrown if the property of the new would exceed . /// /// Thrown when the data store of is not supported. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlyMemory AsBytes(this ReadOnlyMemory memory) where T : unmanaged { return Cast(memory); } /// /// Casts a of one primitive type to another primitive type . /// /// The type of items in the source . /// The type of items in the destination . /// The source , of type . /// A of type /// Thrown when the data store of is not supported. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlyMemory Cast(this ReadOnlyMemory memory) where TFrom : unmanaged where TTo : unmanaged { if (memory.IsEmpty) { return default; } if (typeof(TFrom) == typeof(char) && MemoryMarshal.TryGetString((ReadOnlyMemory)(object)memory, out string? text, out int start, out int length)) { return new StringMemoryManager(text!, start, length).Memory; } if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) { return new ArrayMemoryManager(segment.Array!, segment.Offset, segment.Count).Memory; } if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager? memoryManager, out start, out length)) { // If the memory manager is the one resulting from a previous cast, we can use it directly to retrieve // a new manager for the target type that wraps the original data store, instead of creating one that // wraps the current manager. This ensures that doing repeated casts always results in only up to one // indirection level in the chain of memory managers needed to access the target data buffer to use. if (memoryManager is IMemoryManager wrappingManager) { return wrappingManager.GetMemory(start, length); } return new ProxyMemoryManager(memoryManager!, start, length).Memory; } // Throws when the memory instance has an unsupported backing store static ReadOnlyMemory ThrowArgumentExceptionForUnsupportedMemory() { throw new ArgumentException("The input instance doesn't have a supported underlying data store."); } return ThrowArgumentExceptionForUnsupportedMemory(); } /// /// Returns a wrapping the contents of the given of instance. /// /// The input of instance. /// A wrapping the data within . /// /// Since this method only receives a instance, which does not track /// the lifetime of its underlying buffer, it is responsibility of the caller to manage that. /// In particular, the caller must ensure that the target buffer is not disposed as long /// as the returned is in use, to avoid unexpected issues. /// /// Thrown when has an invalid data store. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Stream AsStream(this ReadOnlyMemory memory) { return MemoryStream.Create(memory, true); } }