Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize a byte array to a struct

I get a transmission over the network that's an array of chars/bytes. It contains a header and some data. I'd like to map the header onto a struct. Here's an example:

#pragma pack(1)

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < sizeof(Header); x++)
        msgBuffer[x] = 0x01; // assign some values

    memcpy(&header, msgBuffer.data(), sizeof(Header));

    system("PAUSE");    

    return 0;
}

Will this always work assuming the structure never contains any variable length fields? Is there a platform independent / idiomatic way of doing this?

Note:

I have seen quite a few libraries on the internet that let you serialize/deserialize, but I get the impression that they can only deserialize something if it has ben previously serialized with the same library. Well, I have no control over the format of the transmission. I'm definitely going to get a byte/char array where all the values just follow upon each other.

like image 968
drby Avatar asked Feb 06 '09 11:02

drby


4 Answers

Just plain copying is very likely to break, at least if the data can come from a different architecture (or even just compiler) than what you are on. This is for reasons of:

  • Endianness
  • Structure packing

That second link is GCC-specific, but this applies to all compilers.

I recommend reading the fields byte-by-byte, and assembling larger field (ints, etc) from those bytes. This gives you control of endianness and padding.

like image 51
unwind Avatar answered Oct 11 '22 06:10

unwind


Some processors require that certain types are properly aligned. They will not accept the specified packing and generate a hardware trap.

And even on common x86 packed structures can cause the code to run more slowly.

Also you will have to take care when working with different endianness platforms.

By the way, if you want a simple and platform-independent communication mechanism with bindings to many programming languages, then have a look at YAMI.

like image 42
Anonymous Avatar answered Oct 11 '22 05:10

Anonymous


The #pragma pack(1) directive should work on most compilers but you can check by working out how big your data structure should be (10 in your case if my maths is correct) and using printf("%d", sizeof(Header)); to check that the packing is being done.

As others have said you still need to be wary of Endianness if you're going between architectures.

like image 38
Mark Pim Avatar answered Oct 11 '22 04:10

Mark Pim


I strongly disagree with the idea of reading byte by byte. If you take care of the structure packing in the struct declaration, you can copy into the struct without a problem. For the endiannes problem again reading byte by byte solves the problem but does not give you a generic solution. That method is very lame. I have done something like this before for a similar job and it worked allright without a glitch.

Think about this. I have a structure, I also have a corresponding definition of that structure. You may construct this by hand but I have had written a parser for this and used it for other things as well.

For example, the definition of the structure you gave above is "s i s s". ( s = short , i = int ) Then I give the struct address , this definition and structure packing option of this struct to a special function that deals with the endiannes thing and voila it is done.

SwitchEndianToBig(&header, "s i s s", 4); // 4 = structure packing option

like image 38
Malkocoglu Avatar answered Oct 11 '22 04:10

Malkocoglu