Reputation: 49
I am sending data over a network. To send that data over a network I am packing it into a byte array.
Right now the data type consist of a byte and 3 floats making it 13 bytes total. To read this I need to know the size of the structure. Using Marshal's sizeOf does not return the "correct" size of my serialized byte array.
Now I know what is happening. Marshal is returning me the managed aligned size of my struct. I also know if I flag it
[StructLayout(LayoutKind.Sequential,
Pack = 1)]
then it will return the "correct" size. My question is what is the best approach? I know the reason it adds in the padding bits is because the processor likes everything to be aligned (if I'm not mistaken, feel free to correct me!)
Is the performance penalty worth doing this so I can use sizeof to deserialize the array, or would it be better to use a constant that I calculate in size myself to read from the array, or take a size penalty of a few bytes in my array by padding it (my least favorite option.)
If there is something I'm misunderstanding about why Marshal wants to align the memory or anything else please feel free to enlighten me and give me suggestions as to which approach is the b est.
Upvotes: 0
Views: 172
Reputation: 155578
I am sending data over a network. To send that data over a network I am packing it into a byte array.
Don't do this.
Marshal
and StructLayout
is intended for native interop use, not for network IO and seralization.
The correct way to serialize an object instance for IO is to write it manually, that way you get precise control over the serialized representation and can guarantee the correct format (right down the endianness of multi-byte values). Use a BinaryWriter
(or better yet, Jon Skeet's MiscUtil. EndianBinaryReader
: http://jonskeet.uk/csharp/miscutil/ ).
struct Foo {
public const Int32 Size = sizeof(Byte) + (sizeof(Single) * 3);
public Byte B;
public Single F1;
public Single F2;
public Single F3;
public void Serialize(EndianBinaryWriter wtr) {
wtr.Write( Size );
wtr.Write( B );
wtr.Write( F1 );
wtr.Write( F2 );
wtr.Write( F3 );
}
public static Foo Deserialize(EndianBinaryReader rdr) {
if( rdr.ReadInt32() != Size ) throw new ...
Foo f;
f.B = rdr.ReadByte();
f.F1 = rdr.ReadSingle();
f.F2 = rdr.ReadSingle();
f.F3 = rdr.ReadSingle();
return f;
}
}
Think of this as a kind-of ISerializable
-lite. You would use this in a network program by having a Stream
object that corresponds to your open network socket connection then wrapping it around the EndianBinaryWriter|Reader
objects and doing all IO operations through those objects. As a bonus, you get disk serialization for free: just use a FileStream
instead of a network stream.
Upvotes: 2