0

I have a managed array provided by a third party library. The type of the array does not directly reflect the data stored in the array, but instead it's the data interpreted as integers.

So int[] data = Lib.GetData(); gives me an integer array, that I would like to cast into an array of DataStructure, that could look like this.

struct DataStructure {
    public int Id;
    public double Value;
}

Current I use Marshal.Copy (an implementation can been seen here), but it seems a bit excessive to copy the entire thing.

Does something like this exists:

int[] data = Lib.GetData();
DataStructure[] dataStructs = InterpretAs<DataStructure>(data);

where no copying is required, but access to dataStruct elements can be done like dataStruct[1].Id?

EDIT 2:
If I just want a single DataStructure, I can use

public static T ToStruct<T>(byte[] bytes) where T : struct
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T something = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
    handle.Free();
    return something;
}

where no copying is required.

EDIT:
The answers possible duplicate are currently about 7 years old, and they consists of either copying the data or implementing a hack.

Community
  • 1
  • 1
kasperhj
  • 10,052
  • 21
  • 63
  • 106
  • Such construct would be UB even in C (where also raw copy is implementation dependant). You can imagine in managed world with a strong type system... – Adriano Repetti Mar 07 '16 at 10:26
  • 1
    @kasperhj Are you sure that it causes performance problems? Do you really need to [make it faster](http://ericlippert.com/2012/12/17/performance-rant/)? Perhaps you can ask third party to provide more strongly typed data from their methods? At worst case you may try `unsafe` code (it should allow to do such things), but you should consider twice before doing so. – Eugene Podskal Mar 07 '16 at 10:28
  • @EugenePodskal No performance problems in my application, but it's a lot of copying simply to change the interpretation. – kasperhj Mar 07 '16 at 10:41
  • 1
    Possible duplicate of [What is the fastest way to convert a float\[\] to a byte\[\]?](http://stackoverflow.com/questions/619041/what-is-the-fastest-way-to-convert-a-float-to-a-byte) – Eugene Podskal Mar 07 '16 at 10:41
  • 1
    "No performance problems in my application, ..." - then don't solve problems you don't have. The conversion is probably just a drop in the bucket after that deceptively simple call to GetData(). – H H Mar 07 '16 at 10:49
  • @HenkHolterman I agree. In my case it's more an aesthetical issue. – kasperhj Mar 07 '16 at 10:55
  • 1
    @kasperhj *Marshal.PtrToStructure* and *where no copying is required.* False... *Marshal.PtrToStructure* makes a copy to the structure. The structure could be "blittable" (so a 1:1 copy is enough) or could require some marshaling (conversion). – xanatos Mar 08 '16 at 13:11
  • @xanatos You're right. Didn't read the docs correctly. – kasperhj Mar 09 '16 at 08:27

1 Answers1

3

There is actually a cheat, but it is an ugly unsafe totally unsafe cheat:

[StructLayout(LayoutKind.Sequential)]
//[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct DataStructure
{
    public int Id;
    public double Value;
}

[StructLayout(LayoutKind.Explicit)]
public struct DataStructureConverter
{
    [FieldOffset(0)]
    public int[] IntArray;

    [FieldOffset(0)]
    public DataStructure[] DataStructureArray;
}

and then you can convert it without problems:

var myarray = new int[8];
myarray[0] = 1;
myarray[3] = 2;
//myarray[4] = 2;

DataStructure[] ds = new DataStructureConverter { IntArray = myarray }.DataStructureArray;

int i1 = ds[0].Id;
int i2 = ds[1].Id;

Note that depending on the size of DataStructure (if it is 16 bytes or 12 bytes), you have to use Pack = 4 (if it is 12 bytes) or you don't need anything (see explanation (1) later)

I'll add that this technique is undocumented and totally unsafe. It even has a problem: ds.Length isn't the length of the DataStructure[] but is the length of the int[] (so in the example given it is 8, not 2)

The "technique" is the same I described here and originally described here.

explanation (1)

The sizeof(double) is 8 bytes, so Value is normally aligned on the 8 bytes boundary, so normally there is a "gap" between Id (that has sizeof(int) == 4) and Value of 4 bytes. So normally sizeof(DataStructure) == 16. Depending on how the DataStructure is built, there could not be this gap, so the Pack = 4 that forces alignment on the 4 byte boundary.

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • While I would be really reluctant to implement this in my production code, it does seem to do the trick. As there seems to be no other way of obtaining the same result, this _is_ the answer. Thanks. – kasperhj Mar 09 '16 at 08:27
  • 1
    @kasperhj For this there is a solution... (a solution that should be applied even if you want to use reflection) At the start of the program make some "sanity checks": test that the copy of the structure works as it should. In this way there won't be any "hidden problem". Like a unit test that is executed at start. – xanatos Mar 09 '16 at 09:29