0

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;
    }
MacColl
  • 11
  • 1
  • If this is only about raw-types, I would do the conversion manually: Iterate the fields via reflection (you might need an aditional attribute to keep the order of the fields), create the byte-array and then convert them field by field. If I remember correctly, Jon Skeets `miscutils` http://jonskeet.uk/csharp/miscutil/ contains a `BitConverter` class where you can specify the endian'es - or you do the conversion yourself. – Michael Nov 25 '17 at 11:04
  • To do the conversion yourself, you could use the .NET BitConverter, ensure that you're on a little endian system, call the `.GetBytes()` method and then reverse the order of the result array. – Michael Nov 25 '17 at 11:07
  • UInt16 input = (UInt16)0xabdc; UInt16 output = (UInt16)(((input << 8) & 0xFF00) | ((input >> 8) & 0xFF)); – jdweng Nov 25 '17 at 12:52
  • Does this answer your question? [How to get little endian data from big endian in c# using bitConverter.ToInt32 method?](https://stackoverflow.com/questions/8241060/how-to-get-little-endian-data-from-big-endian-in-c-sharp-using-bitconverter-toin) – Orace Jul 06 '22 at 13:53

2 Answers2

1

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.

Orace
  • 7,822
  • 30
  • 45
PBOB
  • 11
  • 2
  • I believe this solves the problem. Why would this not work? – PBOB Jul 06 '22 at 14:30
  • Note the ARRAY.REVERSE and that the STRUCT is now reversed. Optionally packing can be explicit. – PBOB Jul 06 '22 at 14:37
  • Just ran it. works fine. See the test case. Can a Professional Code Licking critic be “completely wrong”? – PBOB Jul 06 '22 at 16:28
  • I was wrong. I hadn't read your reasoning, maybe because you hadn't written it. – Orace Jul 06 '22 at 17:01
0

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.

Michael
  • 1,931
  • 2
  • 8
  • 22