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