// 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.
#if NETSTANDARD2_1_OR_GREATER
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CommunityToolkit.HighPerformance.Helpers;
namespace CommunityToolkit.HighPerformance.Buffers.Internals;
///
/// A custom that can wrap arbitrary instances.
///
/// The type of elements in the target memory area.
internal sealed class RawObjectMemoryManager : MemoryManager
{
///
/// The target instance.
///
private readonly object instance;
///
/// The initial offset within .
///
private readonly IntPtr offset;
///
/// The length of the target memory area.
///
private readonly int length;
///
/// Initializes a new instance of the class.
///
/// The target instance.
/// The starting offset within .
/// The usable length within .
public RawObjectMemoryManager(object instance, IntPtr offset, int length)
{
this.instance = instance;
this.offset = offset;
this.length = length;
}
///
public override Span GetSpan()
{
ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset);
return MemoryMarshal.CreateSpan(ref r0, this.length);
}
///
public override unsafe MemoryHandle Pin(int elementIndex = 0)
{
if ((uint)elementIndex >= (uint)this.length)
{
ThrowArgumentOutOfRangeExceptionForInvalidElementIndex();
}
// Allocating a pinned handle for the array with fail and throw an exception
// if the array contains non blittable data. This is the expected behavior and
// the same happens when trying to pin a Memory instance obtained through
// traditional means (eg. via the implicit T[] array conversion), if T is a
// reference type or a type containing some references.
GCHandle handle = GCHandle.Alloc(this.instance, GCHandleType.Pinned);
ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset);
ref T r1 = ref Unsafe.Add(ref r0, (nint)(uint)elementIndex);
void* p = Unsafe.AsPointer(ref r1);
return new(p, handle);
}
///
public override void Unpin()
{
}
///
protected override void Dispose(bool disposing)
{
}
///
/// Throws an when the input index for is not valid.
///
private static void ThrowArgumentOutOfRangeExceptionForInvalidElementIndex()
{
throw new ArgumentOutOfRangeException("elementIndex", "The input element index was not in the valid range");
}
}
#endif