kain
kain

Reputation: 121

Convert structs of non-constant size to byte-array

I'm working on a library that communicated with a µController via UDP-Messages. For that I'm using a custom protocol which is basically a struct consisting of 2 elements: The header (some metadata + checksum) and the payload. The communication is done via the System.Net.Sockets.UDPClient Class. To convert my data I'm using the following function:

private List<byte> GetBytes(object 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.ToList();
}

I'm running into problems now if I want to send some payload which is not of a constant size, for example if I just want to write some data of variable length to the µController. One workaround that I am currently using is to just encapsulate my payload in a struct of a constant (maximum) size, but that seems not very efficient to me.

So, is there any way to convert a struct of a non constant-size to a Byte-Array with C#? For example this struct:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct PERIPHERY__PROTOCOL
{
    public PERIPHERY_HEADER strHeader;       
    public byte[] Data;
}

Upvotes: 0

Views: 236

Answers (2)

apocalypse
apocalypse

Reputation: 5884

Why you want to use a struct?

Use a class. This sample code lets you add new packet types and convert them to bytes. If you want mechanism which will convert it back (from bytes to classes), you need to add your own invention.

public abstract class Packet
{
    public int PacketType { get; }

    public Packet (int packetType)
    {
        PacketType = packetType;
    }

    protected abstract byte[] GetPayload ();

    private int CalculateChecksum ()
    {
        byte[] packetTypeBytes = BitConverter.GetBytes (PacketType);
        byte[] payloadBytes    = GetPayload ();
        byte[] lengthBytes     = BitConverter.GetBytes (payloadBytes.Length);

        return 0; // add some logic to calculate checksum from all bytes
    }

    public byte[] ToBytes ()
    {
        byte[] packetTypeBytes = BitConverter.GetBytes (PacketType);
        byte[] checksumBytes   = BitConverter.GetBytes (CalculateChecksum ());
        byte[] payloadBytes    = GetPayload ();
        byte[] lengthBytes     = BitConverter.GetBytes (payloadBytes.Length);

        return packetTypeBytes.Concat (lengthBytes).Concat (checksumBytes).Concat (payloadBytes).ToArray ();

    }
}



public sealed class ActionA : Packet
{
    public string Message { get; }

    public ActionA (string message) : base (0)
    {
        Message = message;
    }

    protected override byte[] GetPayload ()
    {
        return Encoding.ASCII.GetBytes (Message);
    }


}



public sealed class ActionB : Packet
{
    public int Value { get; }

    public ActionB (int value) : base (1)
    {
        Value = value;
    }

    protected override byte[] GetPayload ()
    {
        return BitConverter.GetBytes (Value);
    }
}

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1062560

In your PERIPHERY__PROTOCOL example, you aren't actually putting the data into the struct - you are creating a separate array and putting the data there. You can still do that, absolutely - but you'll have to pay the heap overheads. In that case, forget about Marshal - you'd just pass arr to the struct.

But if you mean "can I have variable length structs in .NET"; no - no you cannot.

Upvotes: 1

Related Questions