With the introduction of Memory
, Span
and ArraySegment
in C# 7.2, I was wondering if I could represent an unmanaged array as an enumerable object, that lives on the heap.
This latter requirement rules out Span
, which basically implemented exactly what I wanted: e.g.
unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width);
Is it possible to do the same with ArraySegment
or Memory
? Their constructors only accept byte[]
, maybe there some way to trick C# into passing a byte*
instead of byte[]
?
Yes for Memory<T>
, but you need to create your own MemoryManager<T>
. Don't worry - this isn't as scary as it sounds - here's one I wrote earlier...:
/// <summary>
/// A MemoryManager over a raw pointer
/// </summary>
/// <remarks>The pointer is assumed to be fully unmanaged, or externally pinned - no attempt will be made to pin this data</remarks>
public sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T>
where T : unmanaged
{
private readonly T* _pointer;
private readonly int _length;
/// <summary>
/// Create a new UnmanagedMemoryManager instance at the given pointer and size
/// </summary>
/// <remarks>It is assumed that the span provided is already unmanaged or externally pinned</remarks>
public UnmanagedMemoryManager(Span<T> span)
{
fixed (T* ptr = &MemoryMarshal.GetReference(span))
{
_pointer = ptr;
_length = span.Length;
}
}
/// <summary>
/// Create a new UnmanagedMemoryManager instance at the given pointer and size
/// </summary>
public UnmanagedMemoryManager(T* pointer, int length)
{
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
_pointer = pointer;
_length = length;
}
/// <summary>
/// Obtains a span that represents the region
/// </summary>
public override Span<T> GetSpan() => new Span<T>(_pointer, _length);
/// <summary>
/// Provides access to a pointer that represents the data (note: no actual pin occurs)
/// </summary>
public override MemoryHandle Pin(int elementIndex = 0)
{
if (elementIndex < 0 || elementIndex >= _length)
throw new ArgumentOutOfRangeException(nameof(elementIndex));
return new MemoryHandle(_pointer + elementIndex);
}
/// <summary>
/// Has no effect
/// </summary>
public override void Unpin() { }
/// <summary>
/// Releases all resources associated with this object
/// </summary>
protected override void Dispose(bool disposing) { }
}
Now you can use:
var mgr = new UnmanagedMemoryManager((byte*)ptr + (index * Width), Width);
Memory<byte> memory = mgr.Memory;
and memory
can be stored on the heap.
However, to minimize allocations you probably want to create a single UnmanagedMemoryManager<byte>
that covers the entire region - once only - and then use .Slice(...)
on the .Memory
that represents the entire region. That way you have a single object and lots of slices (the slices are structs, not objects).
Note this implementation assumes that you're going to control the lifetime of the memory elsewhere - the Dispose()
here does not attempt to release the memory via Marshal
etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With