We can cast Span<T>
and ReadOnlySpan<T>
to another using MemoryMarshal.Cast method overloads. Like :
Span<byte> span = stackalloc byte[4];
var singleIntSpan = MemoryMarshal.Cast<byte, int>(span);
But is there any way to cast Memory<T>
to another? for example cast Memory<byte>
to Memory<ushort>
.
You can't do it directly; however, if you really need, you can create a custom MemoryManager<T>
(presumably actually a MyMemoryManager<TFrom, TTo> : MemoryManager<TTo>
that performs the cast as part of the GetSpan()
override. This is slightly non-trivial, and demands another allocation - unlike a Span<T>
cast, which is allocation-free.
If you need a concrete example of that, I can whip one up (I actually do exactly this in some existing code), but: to be honest, you probably want to reconsider the scenario instead.
Edit: something like this:
using System;
using System.Buffers;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
Memory<byte> bytes = new byte[1024];
Memory<ushort> typed = Utils.Cast<byte, ushort>(bytes);
Console.WriteLine(typed.Length); // 512
// note CPU endianness matters re the layout
typed.Span[0] = 0x5432;
Console.WriteLine(bytes.Span[0]); // 50 = 0x32
Console.WriteLine(bytes.Span[1]); // 84 = 0x54
}
}
static class Utils
{
public static Memory<TTo> Cast<TFrom, TTo>(Memory<TFrom> from)
where TFrom : unmanaged
where TTo : unmanaged
{
// avoid the extra allocation/indirection, at the cost of a gen-0 box
if (typeof(TFrom) == typeof(TTo)) return (Memory<TTo>)(object)from;
return new CastMemoryManager<TFrom, TTo>(from).Memory;
}
private sealed class CastMemoryManager<TFrom, TTo> : MemoryManager<TTo>
where TFrom : unmanaged
where TTo : unmanaged
{
private readonly Memory<TFrom> _from;
public CastMemoryManager(Memory<TFrom> from) => _from = from;
public override Span<TTo> GetSpan()
=> MemoryMarshal.Cast<TFrom, TTo>(_from.Span);
protected override void Dispose(bool disposing) { }
public override MemoryHandle Pin(int elementIndex = 0)
=> throw new NotSupportedException();
public override void Unpin()
=> throw new NotSupportedException();
}
}
If you really want to support pin/unpin, that should be possible - you'll just need to compute the relative ranges and offsets from the competing TFrom
/TTo
, though - presumably using Unsafe.SizeOf<T>
etc, and using MemoryMarshal.TryGetMemoryManager
to get the underlying memory manager (if one - note that naked arrays don't have a memory manager). Unless you're going to extensively test that option, throwing is probably safer than getting it wrong.
I don't think you can, however I guess you can return a span from it, though i doubt it will help
Memory.Span Property
Returns a span from the current instance.
var array = new int[4];
var mem = array.AsMemory();
var span = MemoryMarshal.Cast<int, byte>(mem.Span);
You can use Unsafe.As
to cast Memory<TFrom>
to Memory<TTo>
but you would need to take care of the correct length of Memory<TTo>
:
using System.Runtime.CompilerServices;
// test data
var floatData = new float[5];
var floatMemory = floatData.AsMemory();
floatMemory.Span.Fill(1.99f);
// cast
var resizeFactor = sizeof(double) / sizeof(float);
var doubleMemory = Unsafe
.As<Memory<float>, Memory<double>>(ref floatMemory)
.Slice(0, floatMemory.Length / resizeFactor);
Edit: this works as long as the byte size of TTo
is larger or equal to TFrom
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