10

I'm looking for a way to reinterpret an array of type byte[] as a different type, say short[]. In C++ this would be achieved by a simple cast but in C# I haven't found a way to achieve this without resorting to duplicating the entire buffer.

Any ideas?

9 Answers 9

14

You can achieve this but this is a relatively bad idea. Raw memory access like this is not type-safe and can only be done under a full trust security environment. You should never do this in a properly designed managed application. If your data is masquerading under two different forms, perhaps you actually have two separate data sets?

In any case, here is a quick and simple code snippet to accomplish what you asked:

byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int byteCount = bytes.Length;

unsafe
{
    // By using the fixed keyword, we fix the array in a static memory location.
    // Otherwise, the garbage collector might move it while we are still using it!
    fixed (byte* bytePointer = bytes)
    {
        short* shortPointer = (short*)bytePointer;

        for (int index = 0; index < byteCount / 2; index++)
        {
            Console.WriteLine("Short {0}: {1}", index, shortPointer[index]);
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This solution is probably the most efficient approach if you're willing to use unverifiable code. However as you say, this is not a good idea.
9

There are four good answers to this question. Each has different downsides. Of course, beware of endianness and realize that all of these answers are holes in the type system, just not particularly treacherous holes. In short, don't do this a lot, and only when you really need to.

  1. Sander's answer. Use unsafe code to reinterpret pointers. This is the fastest solution, but it uses unsafe code. Not always an option.

  2. Leonidas' answer. Use StructLayout and FieldOffset(0) to turn a struct into a union. The downsides to this are that some (rare) environments don't support StructLayout (eg Flash builds in Unity3D) and that StructLayout cannot be used with generics.

  3. ljs' answer. Use BitConverter methods. This has the disadvantage that most of the methods allocate memory, which isn't great in low-level code. Also, there isn't a full suite of these methods, so you can't really use it generically.

  4. Buffer.BlockCopy two arrays of different types. The only downside is that you need two buffers, which is perfect when converting arrays, but a pain when casting a single value. Just beware that length is specified in bytes, not elements. Buffer.ByteLength helps. Also, it only works on primitives, like ints, floats and bools, not structs or enums.

But you can do some neat stuff with it.

public static class Cast {

    private static class ThreadLocalType<T> {

        [ThreadStatic]
        private static T[] buffer;

        public static T[] Buffer
        {
            get
            {
                if (buffer == null) {
                    buffer = new T[1];
                }
                return buffer;
            }
        }
    }

    public static TTarget Reinterpret<TTarget, TSource>(TSource source)
    {
        TSource[] sourceBuffer = ThreadLocalType<TSource>.Buffer;
        TTarget[] targetBuffer = ThreadLocalType<TTarget>.Buffer;

        int sourceSize = Buffer.ByteLength(sourceBuffer);
        int destSize = Buffer.ByteLength(targetBuffer);
        if (sourceSize != destSize) {
            throw new ArgumentException("Cannot convert " + typeof(TSource).FullName + " to " + typeof(TTarget).FullName + ". Data types are of different sizes.");
        }

        sourceBuffer[0] = source;
        Buffer.BlockCopy(sourceBuffer, 0, targetBuffer, 0, sourceSize);
        return targetBuffer[0];
    }
}

class Program {
    static void Main(string[] args)
    {
        Console.WriteLine("Float: " + Cast.Reinterpret<int, float>(100));
        Console.ReadKey();
    }
}

5 Comments

Should you not be using the sizeof operator rather than Marshal.SizeOf? See Eric Lippert's What's the difference? sizeof and Marshal.SizeOf. (But good addition to the existing answers nonetheless.)
Just noticed that. sizeof doesn't work for generics. Buffer.ByteLength seems an ideal solution.
Ah, right, I forgot about that. Agreed that Buffer.ByteLength is the way to go.
Good answer. On a separate topic: your profile pic. I had an 8-bit Atari once upon a time and I know that sprite! What game is it from? I thought it was Gateway to Apshai but no... something by Epyx?
Pitfall, probably the first platformer
4

You could wrap your shorts/bytes into a structure which allows you to access both values:

See also here: C++ union in C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace TestShortUnion {
    [StructLayout(LayoutKind.Explicit)]
    public struct shortbyte {
        public static implicit operator shortbyte(int input) {
            if (input > short.MaxValue)
                throw new ArgumentOutOfRangeException("input", "shortbyte only accepts values in the short-range");
            return new shortbyte((short)input);
        }

        public shortbyte(byte input) {
            shortval = 0;
            byteval = input;
        }

        public shortbyte(short input) {
            byteval = 0;
            shortval = input;
        }

        [FieldOffset(0)]
        public byte byteval;
        [FieldOffset(0)]
        public short shortval;
    }

    class Program {
        static void Main(string[] args) {
            shortbyte[] testarray = new shortbyte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1111 };

            foreach (shortbyte singleval in testarray) {
                Console.WriteLine("Byte {0}: Short {1}", singleval.byteval, singleval.shortval);
            }

            System.Console.ReadLine();
        }
    }
}

Comments

3

c# supports this so long as you are willing to use unsafe code but only on structs.

for example : (The framework provides this for you but you could extend this to int <-> uint conversion

public unsafe long DoubleToLongBits(double d)
{
    return *((long*) (void*) &d);
}

Since the arrays are reference types and hold their own metadata about their type you cannot reinterpret them without overwriting the metadata header on the instance as well (an operation likely to fail).

You can howveer take a foo* from a foo[] and cast that to a bar* (via the technique above) and use that to iterate over the array. Doing this will require you pin the original array for the lifetime of the reinterpreted pointer's use.

2 Comments

how are you going to interpret a pair of bytes using this method? I don't think it's possible.
you must reinterpret a pair of bytes into a short, ushort or other struct of size 2. Obviously they must be adjacent in memory for this.
3

You can use System.Memory to do this in a safe way.

public static TTo[] Cast<TFrom, TTo>(this TFrom[] source) where TTo : struct where TFrom : struct => 
  MemoryMarshal.Cast<TFrom, TTo>(source).ToArray();

private byte[] CastToBytes(int[] foo) => foo.Cast<int, byte>(foo);

3 Comments

For .NET Core 3 and above, this is probably the best answer.
Doesn't ToArray() create a second buffer? "Copies the contents of this span into a new array."
@MattKleinsmith Yes. As far as I know, there's no way to "retype" an array: you can only move the data from one array to another because of .NET's typing system. In C++, you don't have memory types per se - you just have memory, and pointers define the type. In C#, you don't allocate memory, you allocate types. The closest thing C# has to a pointer is a Span, which is exposed by the MemoryMarshal.Cast method (used in my example), if you really want that level of efficiency.
1

This kind of behaviour would result in C# being rather type-unsafe. You can easily achieve this in a type-safe manner, however (though of course you are copying the array in doing so).

If you want one byte to map to one short then it's simple using ConvertAll, e.g.:-

short[] shorts = Array.ConvertAll(bytes, b => (short)b);

If you want to simply map every 2 bytes to a short then the following should work:-

if (bytes.Length % 2 != 0)
{
    throw new ArgumentException("Byte array must have even rank.");
}

short[] shorts = new short[bytes.Length / 2];
for (int i = 0; i < bytes.Length / 2; ++i)
{
    shorts[i] = BitConverter.ToInt16(bytes, 2*i);
}

It may be possible to use the marshaller to do some weird bit-twiddling to achieve this, probably using an unsafe { ... } code block, though this would be prone to errors and make your code unverifiable.

I suspect what you're trying to do can be achieved better using a type-safe idiom rather than a type-unsafe C/C++ one!

Update: Updated to take into account comment.

3 Comments

Wrong. This will convert EACH byte to a short, not interpret the array as a short array (2 bytes per short).
Good point - However if he did mean to do so, then that is the solution though of course as you rightly point out that isn't what a reinterpret_cast would do. Have added code to do the re-interpreted conversion.
Well this is less efficient than Sander's unsafe version, however it is type-safe...!
0

Casting like this is fundamentally unsafe and not permitted in a managed language. That's also why C# doesn't support unions. Yes, the workaround is to use the Marshal class.

3 Comments

C# supports unions just fine - take a look at StructLayout and LayoutKind.Explict
Yes but that's hardly idiomatic is it?!
As can be seen in the answer by Sander, you can do this in C#. The trick/trap is that pointer arithmetic is restricted to within unsafe{} bocks.
0

Wouldn't it be possible to create a collection class that implements an interface for both bytes and shorts? Maybe implement both IList< byte > and IList< short >? Then you can have your underlying collection contain bytes, but implement IList< short > functions that work on byte pairs.

Comments

0

I used the code from FastArraySerializer to create a type converter to get from SByte[] to Double[]

public unsafe class ConvertArrayType
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)] public sbyte[] sbytes;
        [FieldOffset(0)] public double[] doubles;
    }

    private Union _union; 
    public double[] doubles {
        get { return _union.doubles; }
    }
    public sbyte[] sbytes
    {
        get { return _union.sbytes; }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    private readonly UIntPtr SBYTE_ARRAY_TYPE;
    private readonly UIntPtr DOUBLE_ARRAY_TYPE;

    public ConvertArrayType(Array ary, Type newType)
    {
        fixed (void* pBytes = new sbyte[1])
        fixed (void* pDoubles = new double[1])
        {
            SBYTE_ARRAY_TYPE = getHeader(pBytes)->type;
            DOUBLE_ARRAY_TYPE = getHeader(pDoubles)->type;
        }
        Type typAry = ary.GetType();
        if (typAry == newType)
            throw new Exception("No Type change specified");
        if (!(typAry == typeof(SByte[]) || typAry == typeof(double[])))
            throw new Exception("Type Not supported");

        if (newType == typeof(Double[]))
        {
            ConvertToArrayDbl((SByte[])ary);
        }
        else if (newType == typeof(SByte[]))
        {
            ConvertToArraySByte((Double[])ary);
        }
        else
        {
            throw new Exception("Type Not supported");
        }
    }

    private void ConvertToArraySByte(double[] ary)
    {
        _union = new Union { doubles = ary };
        toArySByte(_union.doubles);
    }
    private void ConvertToArrayDbl(sbyte[] ary)
    {
        _union = new Union { sbytes = ary };
        toAryDouble(_union.sbytes);
    }

    private ArrayHeader* getHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }

    private void toAryDouble(sbyte[] ary)
    {
        fixed (void* pArray = ary)
        {
            var pHeader = getHeader(pArray);
            pHeader->type = DOUBLE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(ary.Length / sizeof(double));
        }
    }

    private void toArySByte(double[] ary)
    {
        fixed (void* pArray = ary)
        {
            var pHeader = getHeader(pArray);
            pHeader->type = SBYTE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(ary.Length * sizeof(double));
        }
    }
} // ConvertArrayType{}

Here's the VB usage:

Dim adDataYgch As Double() = Nothing
Try
    Dim nGch As GCHandle = GetGch(myTag)
    If GCHandle.ToIntPtr(nGch) <> IntPtr.Zero AndAlso nGch.IsAllocated Then
        Dim asb As SByte()
        asb = CType(nGch.Target, SByte())
        Dim cvt As New ConvertArrayType(asb, GetType(Double()))
        adDataYgch = cvt.doubles
    End If
Catch ex As Exception
    Debug.WriteLine(ex.ToString)
End Try

1 Comment

The FastArraySerializer hack is corrupting the internal garbage collector data structures. It will cause intermittent crashes, data corruptions, and security bugs of the same class as use-after-free in C++. Hacking internal garbage collector data structures like this is absolutely not supported by the .NET runtime. github.com/HelloKitty/Reinterpret.Net/issues/1 has a long discussion about the crashes that this hack will lead to

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.