Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialization/Deserialization of a struct to a char* in C

I have a struct

struct Packet {
    int senderId;
    int sequenceNumber;
    char data[MaxDataSize];

    char* Serialize() {
        char *message = new char[MaxMailSize];
        message[0] = senderId;
        message[1] = sequenceNumber;
        for (unsigned i=0;i<MaxDataSize;i++)
            message[i+2] = data[i];
        return message;
    }

    void Deserialize(char *message) {
        senderId = message[0];
        sequenceNumber = message[1];
        for (unsigned i=0;i<MaxDataSize;i++)
            data[i] = message[i+2];
    }

};

I need to convert this to a char* , maximum length MaxMailSize > MaxDataSize for sending over network and then deserialize it at the other end

I can't use tpl or any other library.

Is there any way to make this better I am not that comfortable with this, or is this the best we can do.

like image 579
Ankur Chauhan Avatar asked Oct 31 '09 07:10

Ankur Chauhan


2 Answers

since this is to be sent over a network, i strongly advise you to convert those data into network byte order before transmitting, and back into host byte order when receiving. this is because the byte ordering is not the same everywhere, and once your bytes are not in the right order, it may become very difficult to reverse them (depending on the programming language used on the receiving side). byte ordering functions are defined along with sockets, and are named htons(), htonl(), ntohs() and ntohl(). (in those name: h means 'host' or your computer, n means 'network', s means 'short' or 16bit value, l means 'long' or 32 bit value).

then you are on your own with serialization, C and C++ have no automatic way to perform it. some softwares can generate code to do it for you, like the ASN.1 implementation asn1c, but they are difficult to use because they involve much more than just copying data over the network.

like image 116
Adrien Plisson Avatar answered Oct 24 '22 08:10

Adrien Plisson


Depending if you have enough place or not... you might simply use the streams :)

std::string Serialize() {
  std::ostringstream out;
  char version = '1';
  out << version << senderId << '|' << sequenceNumber << '|' << data;
  return out.str();
}

void Deserialize(const std::string& iString)
{
  std::istringstream in(iString);
  char version = 0, check1 = 0, check2 = 0;
  in >> version;
  switch(version)
  {
  case '1':
    senderId >> check1 >> sequenceNumber >> check2 >> data;
    break;
  default:
    // Handle
  }
  // You can check here than 'check1' and 'check2' both equal to '|'
}

I readily admit it takes more place... or that it might.

Actually, on a 32 bits architecture an int usually cover 4 bytes (4 char). Serializing them using streams only take more than 4 'char' if the value is superior to 9999, which usually gives some room.

Also note that you should probably include some guards in your stream, just to check when you get it back that it's alright.

Versioning is probably a good idea, it does not cost much and allows for unplanned later development.

like image 22
Matthieu M. Avatar answered Oct 24 '22 07:10

Matthieu M.