jduncanator
jduncanator

Reputation: 2194

StructLayout to convert byte array to short

Sorry about the horrible title but I honestly have know idea what I want nor what is wrong...

Basically I have a struct (well I have 250+ structs but they all follow the same idea) that looks like this:

[StructLayout(LayoutKind.Explicit)]
public struct GenericPacket
{
    [FieldOffset(0)]
    public byte[] data;

    [FieldOffset(0)]
    public short packetid;
}

The issue is that a byte array is a reference type and a short is a value type and it won't allow the fieldoffset to be set to the same memory location...

I would really hate to have to remove all the structs I've written just to do it a different way. So here is my question, how can I use this in a way that works. Basically what I'm going to do is this:

socket.Receive(buff, 0, 0, SocketFlags.None);
GenericPacket packet = new GenericPacket();
packet.data = buff;
Console.WriteLine(packet.packetid);

It refuses to even compile as it doesn't want to generate the struct /:

Before anyone suggests other methods, the reason I am doing this is it needs ultra high speeds... I could use a ByteReader and other methods (eg. BitConverter) but it needs to be a tad faster than that...

I started with Bitwise shifts but I needed a more 'dynamic' way to do it because after I have a packet ID I then read it with another struct, for example:

[StructLayout(LayoutKind.Explicit)]
public struct _03k
{
    [FieldOffset(0)]
    byte[] data;

    [FieldOffset(0)]
    short packetid;
    [FieldOffset(2)]
    short @null;
    [FieldOffset(4)]
    int major;
    [FieldOffset(8)]
    int minor;
    [FieldOffset(12)]
    int build;
}

Instead of having to have a lot of inline "Bitwise shits" I simply want an easy AND very fast way of doing this... Seems I got the fast just not the easy /:

PLEASE HELP! Unsafe code is ok, but prefer a managed version too.

FAIL :(: Just remembered you can convert Value types to Reference types by boxing them (casting to type object). This does however remove the REAL return type and says it just is an object, is there anyway with XML Documentation that you can lie about the return type? DOESN'T WORK SADLY D:

UPDATE: OK, now I have:

public struct GenericPacket
{
    public short packetid;

    public static GenericPacket ReadUsingPointer(byte[] data)
    {
        unsafe
        {
            fixed (byte* packet = &data[0])
            {
                return *(GenericPacket*)packet;
            }
        }
    }
}

But its a bit annoying having to call a method everytime to convert it :( Anyone got any more suggestions?

Thanks, JD

Upvotes: 2

Views: 3428

Answers (4)

jduncanator
jduncanator

Reputation: 2194

What I finally settled on was a struct with an implicit conversion operator that called the ReadByPointer method.

public struct GenericPacket
{
    public short packetid;

    public static GenericPacket ReadUsingPointer(byte[] data)
    {
        unsafe
        {
            fixed (byte* packet = &data[0])
            {
                return *(GenericPacket*)packet;
            }
        }
    }

    static public implicit operator GenericPacket(byte[] value) 
    {
        return GenericPacket.ReadUsingPointer(value);
    }
}

This allows you to do the following:

GenericPacket packet = bufferFromReceive;

Thanks -jD

Upvotes: 0

Bartłomiej Szypelow
Bartłomiej Szypelow

Reputation: 2121

I've just found you can use fixed araays in structs:

[StructLayout(LayoutKind.Explicit)]
unsafe struct Union
{
    [FieldOffset(0)]
    public short x;
    [FieldOffset(0)]
    public fixed byte y[2];
}

The initialization:

var u = new Union();
byte[] y = new byte[2]; //your original array here
int len = y.Length;
unsafe
{
    fixed (byte* s= y)
    {
        byte* source = s;
        byte* dest = u.y;
        for (int i = 0; i < len; i++)
        {
            *dest++ = *source++;
        }
    }
}

Upvotes: 1

zmbq
zmbq

Reputation: 39013

You just want to convert the first two bytes of the array to a short, how does that pose any performance problem?

packetid = ((short)data[0]  << 8) | data[1];

Or the other way around if you want the other endianess.

EDIT:

So you want to parse multiple fields. Don't reinvent the wheel, then. Use Google's Protocol Buffers, it's very fast and efficient, and I doubt you'd encounter performance issues with it. Here's a .NET port.

Upvotes: 1

Bartłomiej Szypelow
Bartłomiej Szypelow

Reputation: 2121

In this particular example you could do this:

[StructLayout(LayoutKind.Explicit)]
struct byte_array
{
    [FieldOffset(0)]
    public byte byte1;

    [FieldOffset(1)]
    public byte byte2;

    [FieldOffset(0)]
    public short packetid;
}

But in general, Buffer.BlockCopy is probably a better solution.

Upvotes: 0

Related Questions