3

Sep

da una Struct a byte[] e ritorno, sequenzialmente, ed arbitrariamente

Ciao a tutti

 

come sappiamo, le variabili in memoria sono solo bytes

un int (a 32 bit) è appunto 4 byte, uno short 2 byte, etc…

 

ci sono casi in cui dobbiamo/vogliamo convertire direttamente degli oggetti in bytes senza voler usare la serializzazione

infatti anche il BinaryFormatter in realtà produce dello sporco aggiungendo alcune informazioni (tipo il full-name del tipo)

i casi specifici sono ad esempio in programmazione di rete, quando implementiamo delle comunicazioni a basso livello in TCP o anche in Seriale

 

si parte tutto da una struttura in memoria di tipo ValueType, quindi composta dall’insieme dei bytes che compongono i suoi campi

vi ricordate le “struct”??? quelle che assomigliano alle classi ma che hanno meno funzionalità e che praticamente non si usano mai a parte per lavorare a bytes e per superare la barriera managed/unmanaged?!?!?!? Sorriso

 

ecco, le struct sono il nostro punto di inizio. ad esempio:

public struct MyStruct
{
    [BinaryPosition(0)]
    public Int16 Code;
    [BinaryPosition(2)]
    public Int16 Height;
    [BinaryPosition(4)]
    public Int16 Length;
}

 

a questo in msdn l’esempio tipico è questo: usare la classe Marshal, un helper utilizzato in genere appunto per trasportare un istanza managed/unmanaged

 

public static class MarshalConverter
{
    public static byte[] ToBytes(T arg)
        where T : struct
    {
        var size = Marshal.SizeOf(new T());
        var arr = new byte[size];
        var ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(arg, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);

        return arr;
    }

    public static T FromBytes(byte[] arr)
        where T : struct
    {
        var size = Marshal.SizeOf(new T());
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(arr, 0, ptr, size);

        var str = (T)Marshal.PtrToStructure(ptr, typeof(T));
        Marshal.FreeHGlobal(ptr);

        return str;
    }

}

 

uso:

var value = new MyStruct { Code = 1012, Height = 0, Length = 0 };
var marshalBytes = MarshalConverter.ToBytes(value);
var marshalFromBytes = MarshalConverter.FromBytes(marshalBytes);

oppure, si potrebbe usare una classe autocostruita, con l’ausilio di un attributo autocostruito, che ci permette di specificare come se fossimo in un flat-file, come costruire la nostra conversione in bytes

 

public static class BinaryConverter
{
    public static byte[] ToByte(T arg)
        where T : struct
    {
        var fields = (
            from p in typeof(T).GetFields()
            where p.FieldType.IsPrimitive
            let attribute = p.GetCustomAttributes(typeof(BinaryPositionAttribute), false).OfType().FirstOrDefault()
            where attribute != null
            let size = BitConverter.GetBytes((dynamic)Activator.CreateInstance(p.FieldType)).Length
            select new
            {
                Info = p,
                attribute.BeginPosition,
                Size = size,
            }).ToArray();

        var bytes = new byte[fields.Max(p => p.BeginPosition + p.Size)];

        foreach (var p in fields)
        {
            var value = p.Info.GetValue(arg);
            var valueBytes = BitConverter.GetBytes((dynamic)value);

            int j = 0;
            foreach (var b in valueBytes)
                bytes[p.BeginPosition + j++] = b;
        }

        return bytes;
    }

    public static T FromBytes(byte[] bytes)
        where T : struct
    {
        var fields = (
            from p in typeof(T).GetFields()
            where p.FieldType.IsPrimitive
            let attribute = p.GetCustomAttributes(typeof(BinaryPositionAttribute), false).OfType().FirstOrDefault()
            where attribute != null
            let size = BitConverter.GetBytes((dynamic)Activator.CreateInstance(p.FieldType)).Length
            select new
            {
                Info = p,
                attribute.BeginPosition,
                Size = size,
            }).ToArray();

        var risp = (object)new T();

        foreach (var p in fields)
            if (p.Info.FieldType.Equals(typeof(Int16)))
                p.Info.SetValue(risp, BitConverter.ToInt16(bytes, p.BeginPosition));

        return (T)risp;
    }
}

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class BinaryPositionAttribute : Attribute
{
    public int BeginPosition { get; set; }

    public BinaryPositionAttribute(int beginPosition)
    {
        BeginPosition = beginPosition;
    }
}

 

uso:

var value = new MyStruct { Code = 1012, Height = 0, Length = 0 };
var bytes = BinaryConverter.ToByte(value);
var again = BinaryConverter.FromBytes(bytes);

 

 

 

un saluto a tutti

by Antonio Esposito on 9/3/2013
Post archive