Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How get a Span<byte> view of a struct without the unsafe keyword

How can a Span<byte> view (reinterpret cast) be created from a single struct value with no copying, no allocations, and without the unsafe keyword.

I can currently only accomplish this using the unsafe keyword:

public unsafe Span<byte> AsSpan<T>(in T val) where T : unmanaged
{
    void* valPtr = Unsafe.AsPointer(ref Unsafe.AsRef(val));
    return new Span<byte>(valPtr, Marshal.SizeOf<T>());
}

// Alternatively, slightly easier when using 'ref' instead of 'in'
public unsafe Span<byte> AsSpan<T>(ref T val) where T : unmanaged
{
    void* valPtr = Unsafe.AsPointer(ref val);
    return new Span<byte>(valPtr, Marshal.SizeOf<T>());
}

When dealing with an array rather than a single value - this is easily and safely done using MemoryMarshal.Cast<TTo, TFrom>( ... ), for example:

public Span<byte> AsSpan<T>(Span<T> vals) where T : unmanaged
{
    return MemoryMarshal.Cast<T, byte>(vals);
}

Using netstandard2.0, latest language version C# 7.3, and latest RC packages for System.Memory and System.Runtime.CompilerServices.Unsafe:

<PropertyGroup>
   <TargetFramework>netstandard2.0</TargetFramework>
   <LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
   <PackageReference Include="System.Memory" Version="4.5.0" />
   <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup>

Edit: Concerning the replies about memory safety/corruption - the unmanaged generic constraint introduced in C# 7.3 can replace the struct generic constraint and allow this to be done in a memory safe way.

See: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

where T : unmanaged The type argument must not be a reference type and must not contain any reference type members at any level of nesting.

like image 718
zone117x Avatar asked May 11 '18 04:05

zone117x


1 Answers

Partial solution:

If targeting netcoreapp rather than netstandard2.0 then there is an API available in netcoreapp2.1 (available for download here as of this comment date).


Usage:

using System.Runtime.InteropServices;

public Span<byte> AsSpan<T>(ref T val) where T : unmanaged
{
    Span<T> valSpan = MemoryMarshal.CreateSpan(ref val, 1);
    return MemoryMarshal.Cast<T, byte>(valSpan);
}

This is not a solution to the question which asks for this capability in netstandard2.0. Nevertheless, this should be helpful to many who stumble across this.

like image 139
zone117x Avatar answered Oct 22 '22 01:10

zone117x