C# access unmanaged array using Memory or ArraySegment?
C# access unmanaged array using Memory<T> or ArraySegment<T>?
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.
Memory
Span
ArraySegment
This latter requirement rules out Span
, which basically implemented exactly what I wanted: e.g.
Span
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
?
ArraySegment
Memory
byte
byte*
byte
Span
Memory
Marshal.GlobalHAlloc
IntPtr
byte
I'm using SkiaSharp to load images which I iterate over byte by byte. Currently I use a provided property which copies the data as
byte
to work with, but Skia also provides a native pointer to the unmanaged memory, which I want to explore because it saves me a memory copy.– Red Riding Hood
Sep 5 '18 at 17:33
byte
@RonBeyer
Memory<byte>
is actually perfect for this...– Marc Gravell♦
Sep 5 '18 at 18:49
Memory<byte>
@MarcGravell I agree now that I understand what the use case was, but I was unaware of the custom
MemoryManager<T>
, thanks!– Ron Beyer
Sep 5 '18 at 18:50
MemoryManager<T>
1 Answer
1
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...:
Memory<T>
MemoryManager<T>
/// <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)
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.
memory
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).
UnmanagedMemoryManager<byte>
.Slice(...)
.Memory
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.
Dispose()
Marshal
UnmanagedMemoryManager
- A legitimate use of a paradox if I've ever seen one.– AaronLS
Sep 5 '18 at 18:52
UnmanagedMemoryManager
@AaronLS it does seem somewhat an oxymoron, I agree
– Marc Gravell♦
Sep 5 '18 at 18:53
@AaronLS Maybe it needs a manager because it's "unmanaged"? ;-)
– Ahmed Abdelhameed
Sep 5 '18 at 19:00
@MarcGravell This is amazing. Is there anything special I need to do to use this? I get
The type or namespace name 'unmanaged' could not be found
. Already imported System.Buffers
and InteropServices
– Red Riding Hood
Sep 5 '18 at 19:42
The type or namespace name 'unmanaged' could not be found
System.Buffers
InteropServices
I removed the
unmanaged
interface and all the generics and it is now working.– Red Riding Hood
Sep 5 '18 at 20:03
unmanaged
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Why do you want to do this with a
Span
orMemory
? You canMarshal.GlobalHAlloc
and get anIntPtr
to work with a set of unmanaged memory directly. You may be able to somehow translate that into abyte
that can be passed into those objects. Are you wanting to create a new memory block to work on, or are you trying to access another process memory block?– Ron Beyer
Sep 5 '18 at 17:27