简体   繁体   中英

Struct to byte array with bigEndian

Hi Im trying to find way how to convert struct to byte array (from little endian to big)

I know how to convert struct to byte array but question is how to do it with conversion from little to big endian.

Data in struct is in luttle endian but data of byte array must be in BigEndian because of Bus Standard...

I found more solution of conversion but not to much with endian conversion too.

For struct to byteArray Im using:

    public byte[] getBytes<T>(T str)
    {
        int size = Marshal.SizeOf(str);
        byte[] arr = new byte[size];

        IntPtr ptr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);
        return arr;
    } 

    public struct MyStruct
    {
        public UInt32 A1;
        public UInt16 A2;
        public Int16  A3;
        public Byte   B1;
    }

A solution is to declare the struct with fields in reverse order:

[StructLayout(LayoutKind.Sequential, Pack = 4)] // optional packing
public struct MyStruct
{
    public Byte   B1;
    public Int16  A3;
    public UInt16 A2;
    public UInt32 A1;
}

Or specify the layout to be in reverse order:

[StructLayout(LayoutKind.Explicit, Size = 9)] // optional packing
public struct MyStruct
{
    [FieldOffset(5)]
    public UInt32 A1;

    [FieldOffset(3)]
    public UInt16 A2;

    [FieldOffset(1)]
    public Int16 A3;

    [FieldOffset(0)]
    public Byte B1;
}

Then when converted to a byte array, to reverse the byte array:

public byte[] getBytes<T>(T str)
{
    int size = Marshal.SizeOf(str);
    byte[] arr = new byte[size];

    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(str, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
    Array.Reverse(arr);
    return arr;
}

Thus the fields will be in the correct position and the endianness will be inverted.

Have you tried your first approach with an struct that contains an array? That won't work because Marshal.SizeOf will return the size of the pointer to the int-array (x86=4, x64=8) and not the length of the int-array...

I don't know for sure how an array will be written to a byte array in C. But I assume the following:

new int[] { 10,11,12 }
should result in a 12 byte long byte-array: four bytes for the length and three times four bytes for each array-item. So on a little-endian-system it would look like
new byte[] { 3,0,0,0, 10,0,0,0, 11,0,0,0, 12,0,0,0 } .
I also don't know how this have to look on a big-endian system, but I assume that only the byte-blocks have to be reversed and not the order. So fo a big endian system it could like
new byte[] { 0,0,0,3, 0,0,0,10, 0,0,0,11, 0,0,0,12 } .
or, if the order of the items also has to be reversed, it might look like
new byte[] { 0,0,0,3, 0,0,0,12, 0,0,0,11, 0,0,0,10 } .

public static byte[] StructToArray<T>(T obj)
{
    var size = Marshal.SizeOf(obj);
    var mem = new MemoryStream(size);
    var fields = obj.GetType().GetFields();
    var little = BitConverter.IsLittleEndian;

    foreach(var field in fields)
    {
        var val = field.GetValue(obj);
        var type = val.GetType();

        if(type == typeof(int))
        {
            var raw = BitConverter.GetBytes((int)val);
            if (little) raw = raw.Reverse().ToArray();
            mem.Write(raw, 0, raw.Length);
        }
        else if(type == typeof(int[]))
        {
            var array = (int[])val;
            var length = BitConverter.GetBytes(array.Length);
            if (little) length = length.Reverse().ToArray();
            var raw = array.Select(x => BitConverter.GetBytes(x)).ToList();

            if (little) raw = raw.Select(x => x.Reverse().ToArray()).ToList();
            // Write the length...
            mem.Write(length, 0, length.Length);
            // ...and the items in "normal" order
            for (int i = 0; i < raw.Count; i++)
                mem.Write(raw[i], 0, raw[i].Length);                    
        }
    }
    return mem.ToArray();
}

Also keep in mind that the MSDN page of .GetFields() contains the following notice:

The GetFields method does not return fields in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which fields are returned, because that order varies.

Thats the reason why you should use an attribute to define the order and not to rely on the coincidence that the the framework version you're using returns them in declaration order.

// test case
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Test1
{
    class Program
    {
        public static byte[] getBytes<T>(T str)
        {
            int size = Marshal.SizeOf(str);
            byte[] arr = new byte[size];

            IntPtr ptr = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(str, ptr, true);
            Marshal.Copy(ptr, arr, 0, size);
            Marshal.FreeHGlobal(ptr);
            Array.Reverse(arr);
            return arr;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)] // optional packing
        public struct MyStruct
        {
            public UInt16 B;
            public UInt32 A;
        }
        static void Main(string[] args)
        {
            MyStruct x = new MyStruct();
            x.A = 0xa1a2a3a4;
            x.B = 0xb1b2;
            byte[] arr = getBytes<MyStruct>(x);
            MyStruct y = new MyStruct();
            y.A = BitConverter.ToUInt32(arr, 0);
            y.B = BitConverter.ToUInt16(arr, sizeof(UInt32));
            Console.ReadLine();
        }
    }
}

execution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM