HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Structure to ByteArray Extension

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
bytearraystructureextension

Problem

I have a need to turn various structures into byte arrays to be sent over serial port to another machine. I created generic extensions to turn any structure into a byte array and from a byte array back into a given structure.

I've not used the Marshal class often, and I'm uncomfortable manipulating memory on this level.

  • Do I need to explicitly free any memory?



  • Is this an efficient implementation? I'll be calling this a lot, it's important that it's efficient in both terms of memory and speed.



public static class StructureExtensions
{
    public static byte[] ToByteArray(this T structure) where T : struct
    {
        var bufferSize = Marshal.SizeOf(structure);
        var byteArray = new byte[bufferSize];

        IntPtr handle = Marshal.AllocHGlobal(bufferSize);
        Marshal.StructureToPtr(structure, handle, true);
        Marshal.Copy(handle, byteArray, 0, bufferSize);

        return byteArray;
    }

    public static T ToStructure(this byte[] byteArray) where T : struct
    {
        var packet = new T();
        var bufferSize = Marshal.SizeOf(packet);
        IntPtr handle = Marshal.AllocHGlobal(bufferSize);
        Marshal.Copy(byteArray, 0, handle, bufferSize);

        return Marshal.PtrToStructure(handle);
    }
}



Example Call:

var point = new Point(10,5);
byte[] serialized = point.ToByteArray(); 

Point deserialized = serialized.ToStructure();

Solution

I haven't used it, however all of the documentation seems to suggest that you need explicitly free the memory allocated through:

IntPtr handle = Marshal.AllocHGlobal(bufferSize);


With a paired call to:

Marshal.FreeHGlobal(handle);


You don't seem to need the handle after you've performed the marshalling, so it could be as simple as updating the function to:

public static byte[] ToByteArray(this T structure) where T : struct
{
    var bufferSize = Marshal.SizeOf(structure);
    var byteArray = new byte[bufferSize];

    IntPtr handle = Marshal.AllocHGlobal(bufferSize);
    try
    {
        Marshal.StructureToPtr(structure, handle, true);
        Marshal.Copy(handle, byteArray, 0, bufferSize);

    }
    finally
    {
        Marshal.FreeHGlobal(handle);
    }
    return byteArray;
}


Depending on the overhead of The AllocHGlobal / FreeHGlobal and if you're going to be marshalling the same structures over and over again you might be better off going with something like:

public sealed class Marshaller : IDisposable where T : struct 
{
    readonly IntPtr _handle;
    readonly int _bufferSize;

    public Marshaller()
    {
        _bufferSize = Marshal.SizeOf(typeof(T));
        _handle = Marshal.AllocHGlobal(_bufferSize);
    }

    public byte[] ToByteArray(T structure)
    {
        var byteArray = new byte[_bufferSize];

        Marshal.StructureToPtr(structure, _handle, true);
        Marshal.Copy(_handle, byteArray, 0, _bufferSize);

        return byteArray;
    }

    public T ToStructure(byte[] byteArray)
    {
        var packet = new T();

        Marshal.Copy(byteArray, 0, _handle, _bufferSize);

        return Marshal.PtrToStructure(_handle);
    }

    #region IDisposable Support
    private bool disposedValue = false;

    ~Marshaller() {
       Dispose();
    }

    public void Dispose()
    {
        if (!disposedValue)
        {
            Marshal.FreeHGlobal(_handle);

            disposedValue = true;
        }

        GC.SuppressFinalize(this);
    }
    #endregion

}

Code Snippets

IntPtr handle = Marshal.AllocHGlobal(bufferSize);
Marshal.FreeHGlobal(handle);
public static byte[] ToByteArray<T>(this T structure) where T : struct
{
    var bufferSize = Marshal.SizeOf(structure);
    var byteArray = new byte[bufferSize];

    IntPtr handle = Marshal.AllocHGlobal(bufferSize);
    try
    {
        Marshal.StructureToPtr(structure, handle, true);
        Marshal.Copy(handle, byteArray, 0, bufferSize);

    }
    finally
    {
        Marshal.FreeHGlobal(handle);
    }
    return byteArray;
}
public sealed class Marshaller<T> : IDisposable where T : struct 
{
    readonly IntPtr _handle;
    readonly int _bufferSize;

    public Marshaller()
    {
        _bufferSize = Marshal.SizeOf(typeof(T));
        _handle = Marshal.AllocHGlobal(_bufferSize);
    }

    public byte[] ToByteArray(T structure)
    {
        var byteArray = new byte[_bufferSize];

        Marshal.StructureToPtr(structure, _handle, true);
        Marshal.Copy(_handle, byteArray, 0, _bufferSize);

        return byteArray;
    }

    public T ToStructure(byte[] byteArray)
    {
        var packet = new T();

        Marshal.Copy(byteArray, 0, _handle, _bufferSize);

        return Marshal.PtrToStructure<T>(_handle);
    }


    #region IDisposable Support
    private bool disposedValue = false;

    ~Marshaller() {
       Dispose();
    }

    public void Dispose()
    {
        if (!disposedValue)
        {
            Marshal.FreeHGlobal(_handle);

            disposedValue = true;
        }

        GC.SuppressFinalize(this);
    }
    #endregion

}

Context

StackExchange Code Review Q#136007, answer score: 9

Revisions (0)

No revisions yet.