2

I've seen a number of methods to copy a byte[] to a struct, and vise-versa. However, I was wondering if it was possible to cast the byte[] pointer to a struct (like you do in C)..

I want to be able to cast a byte[] to a struct, make changes to the struct, and have the changes automatically appear in the byte[].

Thanks, reza

reza
  • 1,329
  • 2
  • 22
  • 37
  • possible duplicate of [A C# equivalent of C's fread file i/o](http://stackoverflow.com/questions/1935851/a-c-sharp-equivalent-of-cs-fread-file-i-o) – Hans Passant Oct 21 '12 at 22:23

3 Answers3

1

You just cast the pointer (sometimes you need to go via void* in the middle):

struct Foo
{
    public int Bar;
}
static unsafe void Main()
{
    byte[] buffer = new byte[10];
    fixed (byte* untyped = buffer)
    {
        var typed = (Foo*)untyped;
        typed[0].Bar = 123;
    }
    // buffer has the changes
}

If you need to offset into the buffer, then use byte* untyped = &buffer[offset].

If you want a raw struct pointer, then:

fixed (byte* ptr = buffer)
{
    var typed = (Foo*)ptr;
    Foo* foo = &typed[0];
    foo->Bar = 123;
}

However, note that you can't pass a Foo* to methods expecting a Foo or ref Foo.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Ok, I see.. Is there some way to permanently fix a variable? I don't want to put fixed blocks in my code every time I want to access the struct. – reza Oct 21 '12 at 22:34
  • @reza you could just have a much larger `fixed` scope, and pass the `byte*` around somewhere. Warning though: this means pinning an object, and pinning an object for a long time impacts GC compaction. If you tell me more context, I might be able to advise more. `unsafe` code is pretty unusual when using C#. There are also ways of working voodoo such as you're not allocating any `byte[]` - for example, maybe you *actually* just need to `stackalloc` briefly to copy the raw data out. – Marc Gravell Oct 22 '12 at 06:39
  • My application interfaces with an embedded device I made. The embedded device's firmware is written in C. I have a struct with configuration options. I load the configuration options by reading in a sector off an SD card into a 512 uint8_t array, casting the array to the struct, then reading the configuration options (see http://pastebin.com/2PEZ8GDb for example). If I change an option, then uint8_t array automatically reflects it as it is the same memory. I then write the uint8_t[512] to the sector when I am done. It's fast and simple. I want to do the same thing in C#.[Continued] – reza Oct 22 '12 at 10:14
  • [From Previous] My application is also fairly simple and I don't care about garbage collection. I have been using some sample code that I've found that copies the byte[] to a struct, and vise-versa -- but that approach takes a number of liens of code and is aesthetically displeasing. If C# has access to pointers, I figure that I should be able to implement an equivalent system. Is it possible to make the fixed scope apply to the whole class? – reza Oct 22 '12 at 10:17
  • Oh, another thought - is it possible to do a union like http://pastebin.com/b9kzDhP1 – reza Oct 22 '12 at 10:19
0

I found this approach was the simplest to achieve what I wanted.. If you define the struct so that the byte[] overlaps with the elements, copies are effectively transparent between the struct and byte[] (assuming the endian-ness is what you expect; in my case, it is).

[StructLayout(LayoutKind.Explicit)]
    public unsafe struct ListEntry {
        [System.Runtime.InteropServices.FieldOffset(0)] public fixed byte raw[512];
        [System.Runtime.InteropServices.FieldOffset(0)] public byte version;
        [System.Runtime.InteropServices.FieldOffset(1)] public UInt16 magic;
        [System.Runtime.InteropServices.FieldOffset(3)] public UInt32 start_time;
        [System.Runtime.InteropServices.FieldOffset(7)] public UInt16 run_id;
        [System.Runtime.InteropServices.FieldOffset(9)] public UInt16 channels;
        [System.Runtime.InteropServices.FieldOffset(11)] public UInt16 sampling_rate;
        [System.Runtime.InteropServices.FieldOffset(13)] public UInt32 start_sector;
        [System.Runtime.InteropServices.FieldOffset(510)] public UInt16 checksum;
    }
reza
  • 1,329
  • 2
  • 22
  • 37
0

I would suggest that your best bet, if speed isn't critical, would probably be to have the data stored in a byte[], and have a class which holds an immutable reference to that byte[] and has properties whose get/set methods access the array. Property gets/sets would always reflect and be reflected in the state of the array, since the array itself would hold the state of the object. No "unsafe" code required.

Methods would probably look something like:

public static class IntPack
{  // All methods ssume unchecked arithmetic
    public static Int16 FetchI16LE(this byte[] dat, int offset)
    {
        return (Int16)(dat[offset] + (dat[offset + 1] << 8));
    }
    public static Int32 FetchI32LE(this byte[] dat, int offset)
    {
        return dat[offset] + (dat[offset + 1] << 8) +
               (dat[offset + 2] << 16) + (dat[offset + 3] << 24);
    }
    public static void StuffI16LE(this byte[] dat, int offset, int value)
    {
        dat[offset] = (byte)(value); 
        dat[offset+1] = (byte)(value >> 8);
    }
    public static void StuffI32LE(this byte[] dat, int offset, int value)
    {
        dat[offset] = (byte)(value);
        dat[offset + 1] = (byte)(value >> 8);
        dat[offset + 2] = (byte)(value >> 16);
        dat[offset + 3] = (byte)(value >> 24);
    }
}

The methods indicated assume little-endian ordering. One could easily write corresponding __BE methods methods for big-endian.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • the resulting get/set code would be ugly. I use pointers all the time (I do most of my coding in C). I don't consider using pointers unsafe if you know what you are doing. The only benefit would be if there was some implementation that was more aesthetically pleasing, but I've not found one. – reza Oct 23 '12 at 18:57
  • @reza: I have a family of utility methods which accept a byte array, an offset, and a value of some integer type, and store the item in the array, and another family to read such data from the array. Using extension methods, a property getter for a `int32_t` at offset 23 of `dat[]` could read as something like: `return dat.GetUint32(23);`. Doesn't seem too bad, and could be used in limited-trust environments. – supercat Oct 23 '12 at 19:02
  • it would be cool if you publish it someplace for me to check out. thnx. – reza Oct 23 '12 at 23:14