I'm using .NET Core 2.1 and language standard 7.3. I wish to reference a fixed buffer without obtaining a pointer to it. Is it currently possible?
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
//return a span referencing the private field without a fixed statement
}
}
}
I know Span is currently able to track a managed array through garbage collections, so I don't see anything preventing it from tracking fixed buffers in a similar fashion.
If its not possible, what would happen if I did use a fixed statement like such:
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
fixed (byte* ptr = dataField)
{
return new Span<byte>(ptr, 32);
}
}
}
}
Would the garbage collector become a problem if the struct is wrapped inside an object or class on the heap?
You can use Span<T> to wrap an entire array. Because it supports slicing, it can not only point to the first element of the array, but any contiguous range of elements within the array. foreach (int i in slice) Console. WriteLine($"{i} ");
A Span<T> represents a contiguous region of arbitrary memory. A Span<T> instance is often used to hold the elements of an array or a portion of an array. Unlike an array, however, a Span<T> instance can point to managed memory, native memory, or memory managed on the stack.
A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. Fixed size buffers are only permitted in struct declarations and can only occur in unsafe contexts.
Span<T> is a new value type at the heart of . NET. It enables the representation of contiguous regions of arbitrary memory, regardless of whether that memory is associated with a managed object, is provided by native code via interop, or is on the stack.
So, I've done some research in the form of ILSpy'ing .NET Assemblies, and some testing on .NET Core 2.1. My Test results are as follows:
interface ITest
{
Span<byte> Data { get; }
}
unsafe struct TestStruct : ITest
{
fixed byte dataField[8];
public Span<byte> Data
{
get
{
//Unsafe.AsPointer() to avoid the fixed expression :-)
return new Span<byte>(Unsafe.AsPointer(ref dataField[0]), 8);
}
}
}
class Program
{
//Note: This test is done in Debug mode to make sure the string allocation isn't ommited
static void Main(string[] args)
{
new string('c', 200);
//Boxes the struct onto the heap.
//The object is allocated after the string to ensure it will be moved during GC compacting
ITest HeapAlloc = new TestStruct();
Span<byte> span1, span2;
span1 = HeapAlloc.Data; //Creates span to old location
GC.Collect(2, GCCollectionMode.Forced, true, true); //Force a compacting garbage collection
span2 = HeapAlloc.Data; //Creates span to new location
//Ensures that if the pointer to span1 wasn't updated, that there wouldn't be heap corruption
//Write to Span2
span2[0] = 5;
//Read from Span1
Console.WriteLine(span1[0] == 5); //Prints true in .NET Core 2.1, span1's pointer is updated
}
}
What I've learned from my research into the IL, please forgive me if I'm not explaining this correctly:
.NET Core's 2 Field Span:
//Note, this is not the complete declaration, just the fields
public ref readonly struct Span<T>
{
internal readonly ByReference<T> _pointer;
private readonly int _length;
}
.NET Framework's 3 Field Span:
//Same note as 2 Field Span
public ref readonly struct Span<T>
{
private readonly Pinnable<T> _pinnable;
private readonly IntPtr _byteOffset;
private readonly int _length;
}
.Net Core is using the 2 field model of Span. Due to the .NET Framework using the 3 field model, It's pointer will not be updated. The reason? The Span<T>(void* pointer, int length)
constructor (which I am using for this) for the 3 field span sets the _byteOffset
field with the pointer
argument. The pointer in the 3 field span that would be updated by the GC is the _pinnable
field. With the 2 field Span, they are the same.
So, the answer to my question is, yes I can have a Span point to a fixed buffer with or without a fixed statement, but its dangerous to do this at all when not using .NET Core's 2 field Span Model. Correct me if I'm wrong about .NET Framework's current Span model.
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