Search code examples
memory-leaks.net-coremarshallingmanaged

C# - convert any unsafe unmanaged type to managed type without any new memory allocation - keeping the same size in memory


I want to convert ushort array to short array, without any allocating to a new memory in C# core.

Also convert any type, such as byte[] to short[] or vice versa - The conversion need to keep the same size in memory for both source and destination

In C there was "union" syntax, which the type can be referred with several type-casting - I didn't find any equivalent for C# (that works only for non-primitive types).

One way is to code like this

ushort[] res = new ushort[1024 * 1024 * 1024];
short[] s = new short[1024 * 1024 * 1024];
Buffer.BlockCopy(s, 0, res, 0, s.Length * 2);

... but, I don't wont to allocate the 's' value - It's too much memory consuming, and may leads to memory leak.

Another alternative I use is using unsafe mode.

The code:

unsafe
{
    ushort[] res = new ushort[1024*1024*1024]; // large allocating
    fixed (ushort* ss = &res[0])
    {
        IntPtr ps = (IntPtr)ss;
        short[] s0 = (short[])Marshal.PtrToStructure(ps, typeof(short[]));
    }
}

run into exception.

No parameterless constructor defined for this object.

...

at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor)

at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean skipCheckThis, Boolean fillCache)

at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)

How can I eventually convert that big array of 1G bytes, without the need of allocating new memory?

Can I convert array type from unmanaged type to managed type?

I prefer using the unsafe mode, if ever possible.


After deep search, I found a solution, which I would participate (and editing this post), but it has a problem as describe as following:

That's code working very well, if the arrays are of the same size - There is a new question - How can I solve it for non same array base type size (i.e. short[] vs byte[]) ?

The only thing I cannot convert from short[] to byte[].

(Thanks, to some blog on the internet: https://social.msdn.microsoft.com/Forums/vstudio/en-US/06ac44b0-30d8-44a1-86a4-1716dc431c62/how-to-convert-an-intptr-to-an-object-in-c?forum=clr)

That works on C# core as well on C# .NET

First thing, create a class:

public static class MyConverter
{
    public static unsafe T GetInstance<T>(IntPtr pointer)
    {
        try
        {
            var fakeInstance = default(T);
            TypedReference typedReference = __makeref(fakeInstance);
            *(IntPtr*)(&typedReference) = pointer;
            T instance = (T) __refvalue(typedReference,T);
            return instance;
        }
        catch
        {
            return default(T);
        }
    }
}

and can use it in code like this:

      ushort[] x = new ushort[1024];
      GCHandle handle1 = GCHandle.Alloc(x);
      IntPtr px = (IntPtr)handle1;
      x[0] = 1;
      x[1] = 2;
      x[2] = 3;
      short[] s = MyConverter.GetInstance<short[]>(px);

That solution convert ushort[] to short[] without any waste of memory.

Thanks.


Solution

  • You can easily convert like this: (ushort[])(object)myShortArray. The CLR allows this.

    It's only allowed for primitive array element types that are the same size.

    In other cases you can use Span to treat the memory as a different type.

    Span<int> asInts = MemoryMarshal.Cast<byte, int>(asBytes);