Casting a byte array to a managed structure

Solution 1:

//I have found this at: http://code.cheesydesign.com/?p=572 (I have not tested yet, but // at first sight it will work well.)

    /// <summary>
    /// Reads in a block from a file and converts it to the struct
    /// type specified by the template parameter
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="reader"></param>
    /// <returns></returns>
    private static T FromBinaryReader<T>(BinaryReader reader)
    {

        // Read in a byte array
        byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));

        // Pin the managed memory while, copy it out the data, then unpin it
        GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        handle.Free();

        return theStructure;
    }

Solution 2:

I'd turn the byte array into a memory stream. Then instantiate a binary reader on that stream. And then define helper functions that take a binary reader and parse a single class.

The built in BinaryReader class always uses little endian.

I'd use classes instead of structs here.

class PacketHeader 
{
    uint16_t magic;
    uint16_t packet_size;
    uint32_t unknown1;
    uint32_t unknown2;
    uint32_t unknown3;
    uint32_t unknown4;
    uint16_t unknown5;
    uint16_t unknown6;
    uint32_t unknown7;
    uint32_t unknown8;
    string packet_type; // replaced with a real string
};

PacketHeader ReadPacketHeader(BinaryReader reader)
{
  var result=new PacketHeader();
  result.magic = reader.ReadInt16();
  ...
  result.packet_type=ReadCString();//Some helper function you might need to define yourself
  return result;
}

Solution 3:

This is how i did:

using System;
using System.Runtime.InteropServices;
public static object GetObjectFromBytes(byte[] buffer, Type objType)
{
    object obj = null;
    if ((buffer != null) && (buffer.Length > 0))
    {
        IntPtr ptrObj = IntPtr.Zero;
        try
        {
            int objSize = Marshal.SizeOf(objType);
            if (objSize > 0)
            {
                if (buffer.Length < objSize)
                    throw new Exception(String.Format("Buffer smaller than needed for creation of object of type {0}", objType));
                ptrObj = Marshal.AllocHGlobal(objSize);
                if (ptrObj != IntPtr.Zero)
                {
                    Marshal.Copy(buffer, 0, ptrObj, objSize);
                    obj = Marshal.PtrToStructure(ptrObj, objType);
                }
                else
                    throw new Exception(String.Format("Couldn't allocate memory to create object of type {0}", objType));
            }
        }
        finally
        {
            if (ptrObj != IntPtr.Zero)
                Marshal.FreeHGlobal(ptrObj);
        }
    }
    return obj;
}

And in the struct definition I didn't use any fixed region, instead I used the MarshalAs attribute if the standard marshalling didn't worked. This is what you will probally need for the string.

You would use this function like this:

PacketHeader ph = (PacketHeader)GetObjectFromBytes(buffer, typeof(PacketHeader));

Edit: I didn´t see your BigEndian "restriction" in the code example. This solution will only work if the bytes are LittleEndian.

Edit 2: In the string of your example you would decorate it with:

[MarshalAs(UnmanagedType.LPStr)]

In the arrays I would go with something like this for a n-sized array:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = n)]

Solution 4:

For those who have access to C# 7.3 features, I use this piece of unsafe code to "serialize" to bytes:

public static class Serializer
{
    public static unsafe byte[] Serialize<T>(T value) where T : unmanaged
    {
        byte[] buffer = new byte[sizeof(T)];

        fixed (byte* bufferPtr = buffer)
        {
            Buffer.MemoryCopy(&value, bufferPtr, sizeof(T), sizeof(T));
        }

        return buffer;
    }

    public static unsafe T Deserialize<T>(byte[] buffer) where T : unmanaged
    {
        T result = new T();

        fixed (byte* bufferPtr = buffer)
        {
            Buffer.MemoryCopy(bufferPtr, &result, sizeof(T), sizeof(T));
        }

        return result;
    }
}

A unmanaged type can be a struct (simple struct without reference types, those a considered managed structs) or a native type such as int, short, etc.

Solution 5:

If you want fast code without copy, this is the solution. We are working here on raw byte[], simply casting the pointer in unsafe code, as you would in native C / C++. So there is no overhead calling into expensive framework methods, making copies etc. etc.

Any change to the unmanaged struct will reflect in managed byte[], and vice versa.

//FOR DEBUG/TEST ONLY
using System.Runtime.InteropServices;
namespace ByteStructCast1
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        unsafe struct StructTest//4B
        {
            [MarshalAs(UnmanagedType.U2)]
            public ushort item1; //2B
            public fixed byte item2[2]; //2B =2x 1B
        }
        static void Main(string[] args)
        {
            //managed byte array
            byte[] DB1 = new byte[7]; //7B more than we need. byte buffer usually is greater.
            DB1[0] = 2;//test data |> LITTLE ENDIAN
            DB1[1] = 0;//test data |
            DB1[2] = 3;//test data
            DB1[3] = 4;//test data
            unsafe //we'll now pin unmanaged struct over managed byte array
            {
                fixed(byte* db1 = DB1) //db1 is pinned pointer to DB1 byte[] array
                {
                    //StructTest t1 = *(StructTest*)db1;    //does not change DB1/db1
                    //t1.item1 = 11;                        //does not change DB1/db1
                    db1[0] = 22;                            //does CHANGE DB1/db1
                    DB1[0] = 33;                            //does CHANGE DB1/db1
                    StructTest* ptest = (StructTest*)db1;   //does CHANGE DB1/db1
                    ptest->item1 = 44;                      //does CHANGE DB1/db1
                    ptest->item2[0]++;                      //does CHANGE DB1/db1
                    ptest->item2[1]--;                      //does CHANGE DB1/db1
                }
            }
        }
    }
}

This can also be used when you are working with primitive-typed fixed-size buffers, and need to work on elements thereof as structs with members, e.g. ulong to MyStruct, both 64 bits long.