C# StructLayout/FieldOffset and indexing in arrays

14,840

The problem is (from what I can see) that you've unioned the references of the arrays - so whichever array gets set last will win. Once there is an array, it is using the indexer (not byte offset) - so the size doesn't matter.

The way to do this "properly" (or improperly, as the case may be) would probably be with unsafe code - taking the pointer to the array - something like:

    IndexStruct s = new IndexStruct();
    s.data = new byte[] { 1, 0, 0, 0, 1, 1 };

    unsafe
    {
        fixed (short* data = s.idx16)
        {
            Console.WriteLine(data[0]); // should be 1 (little-endian)
            Console.WriteLine(data[1]); // should be 0
            Console.WriteLine(data[2]); // should be 257
        }
    }

Of course, I'm not sure I recommend it - but that seems to achieve what you want?

I also wonder whether you can drop the struct completely and just use unsafe access to a byte[] directly:

    byte[] raw = new byte[] { 1, 0, 0, 0, 1, 1 };
    unsafe
    {
        fixed (byte* addr = raw)
        {
            short* s = (short*)addr;
            Console.WriteLine(s[0]); // should be 1
            Console.WriteLine(s[1]); // should be 0
            Console.WriteLine(s[2]); // should be 257
        }
    }
Share:
14,840
Burre
Author by

Burre

A former game developer who nowadays make my living developing a mobile communication platform.

Updated on June 04, 2022

Comments

  • Burre
    Burre almost 2 years

    I'm having a bit of a problem using FieldOffset correctly with arrays. The code below is an example where it doesn't work correctly for me:

    [StructLayout(LayoutKind.Explicit)]
    public struct IndexStruct {
        [FieldOffset(0)]
        public byte[] data;
    
        [FieldOffset(0)]
        public short[] idx16;
    
        [FieldOffset(0)]
        public int[] idx32;
    }
    

    If I for example sets the array named "data" to a serialized byte array and then try to retrieve data as shorts using the "idx16" field the indexing is still aligned as a byte[]. Meaning that idx161 fetches the second byte in data, not the second 16bit word (byte 2 and 3). If I do the inverse I index shorts instead of bytes, meaning that the offset alignment is inherited from the source data. My question, is there a way to work around this? I know that I can compensate the index value by multiplying with the size of the element, but is there another way?

    Here is an answer I found here on StackOverflow, but when trying that code it turned out that it wasn't working properly. Tried it out using a Unit test in VS with the following code without any success:

    [TestMethod()]
    public void SumTest() {
        float[] fArr = {2.0f, 0.5f, 0.0f, 1.0f};
        MemoryStream ms = new MemoryStream();
        for (int i = 0; i < fArr.Length; i++) {
            ms.Write(BitConverter.GetBytes(fArr[i]), 0, sizeof(float));
        }
        byte[] buff = ms.ToArray();
        double expected = 3.5f;
        double actual = Sum(buff);
        Assert.AreEqual(expected, actual);
    }
    

    Many thanks in advance!