1

I have a 1-byte pragma packed struct in C which I want to copy into a byte array for serialization purpose to be sent over a serial port.

#pragma pack(push, 1)

typedef struct {
    uint8_t ck_a;
    uint8_t ck_b;
} UBXChecksum_t ;

#pragma pack(pop)

What is the best way of serializing it into a byte array, should I just use memcpy()?

void writeStructToArray(const void* inStruct,
                        const uint16_t inLenStruct,
                        uint8_t* const outArray)
{
  memcpy(outArray, inStruct, inLenStruct);
}

or better use byte-by-byte copying doing pointer typecasting?

void writeStructToArray(const void* inStruct,
                        const uint16_t inLenStruct,
                        uint8_t* const outArray)
{
  for(uint16_t i = 0; i < inLenStruct; i++)
  {
    outArray[i] = ((uint8_t*)inStruct)[i];
  }
}
Akay
  • 1,092
  • 12
  • 32
  • 3
    You're doing the same (if `uint8_t` is `unsigned char` on your machine, etc.), only the `memcpy` is faster. – KamilCuk Feb 06 '19 at 11:36
  • If you show how you want to use the result of `writeStructToArray` we could provide better answers. Do you also have structures with data elements bigger than 8 bits? Do you already handle byte order and possible type size differences somewhere? – Bodo Feb 06 '19 at 12:39
  • Why do you need to copy the data? Double-buffering purposes? – Lundin Feb 06 '19 at 14:43

2 Answers2

5

As Kamil Cuk commented, your two proposals are nearly the same with some possible speed difference.

Another option would be to use a union:

typedef struct {
    uint8_t ck_a;
    uint8_t ck_b;
} UBXChecksum_t ;

union convert {
    UBXChecksum_t checksum;
    char buffer[sizeof UBXChecksum_t];
};

UBXChecksum_t checksum;

union convert converter;

converter.checksum = checksum;

passArrayToSomeFunction(converter.buffer, sizeof(converter.buffer));

You don't have to copy the data to convert it to an array. You could pass a pointer to the structure (if necessary casted to char* or void*) and the structure size to a function that sends the data to the serial port. Example:

typedef struct {
    uint8_t ck_a;
    uint8_t ck_b;
} UBXChecksum_t ;

int sendData(void *buf, size_t size);

UBXChecksum_t checksum;

/* ... */
int rc = sendData(&checksum, sizeof(checksum));

All these variants send the structure's internal representation as binary data. Normally "serializing" is understood as a way to convert the data into a platform-independent format.

Sending binary data structures works if the receiving system is of the same type and using the same compiler. You might get problems when the receiving system uses different byte order or different data type sizes.

In your case you have a structure of two uint8_t values, so the size is fixed and the byte order is not a problem.

It is OK to send binary data if the requirement for the structure is to match a specified binary data protocol and you are prepared to handle the byte order if necessary.

Bodo
  • 9,287
  • 1
  • 13
  • 29
  • yeah, I could do this, but I have more than 100 of these kinds of structs which I need to serialize, so in that case, this will involve a bit of work for creating unions for each of them, or maybe a master union for all of them! – Akay Feb 06 '19 at 12:18
  • 1
    @Akay You don't need the union and you don't need to copy the data. Depending on how you send the data you can simply cast the address to a `char*` or `void*` as necessary. Additionally you need the size of the structure, especially if you have some of them with different sizes. – Bodo Feb 06 '19 at 12:36
1

memcpy() will not consider endiannsess of the system. so if Sender is big endian and receiver is little endian then then will be a conflict in the receiver for the structure variable value.

With the second method you know how the byte stream is prepared at sender so at the receiving end also it can receive accordingly to make sure of the proper structure variable value.

If the endianness of the systems is same and endianness is not a concern then both the method will serve the purpose and memcpy() will be faster compare to the assigning the byte value in a loop.

Deepak
  • 86
  • 9
  • 1
    How does the endianess (ie. packing of bytes within integers) affect the second method? How will it differ? How will the first method and the second differ in the output, depending on the endianess of the system? Why and how need to be data prepared for the second method, while for the first there is no such requirement? Both methods copy byte by byte. Bytes in both methods are copied one after another - the order of bytes inside memory regions stays the same. – KamilCuk Feb 06 '19 at 12:28
  • Above answer is given in a generic manner without considering the data type of the variable inside the structure. Structure with int data type is used insteed of unt8_t, in 32 bit muchine and if the first variable is assigned with 0x12345678 in little endian and same variable value will be 0x78563412 in big endian machine. Receiver doing memcpy() to a structue variable pointer then value will be improper. In the second case receiver also have to write a code to make sure that the bytes read are converted to proper value by reversing the order. – Deepak Feb 06 '19 at 13:26
  • For sender both method will be same, answer is given by considering the receiver, usually we tend to use memcpy() in both sender and receiver, that will make out code machine dependent and not portable. So better to have our own logic where in sender and receiver will share the information in the initial hand shaking and then convert the byte stream accordingly. one of the sender or receiver can have logic to make sure of the data is received as intended. – Deepak Feb 06 '19 at 13:38
  • 1
    Let the protocol handler port worry about endianess, if applicable. What matters is _network_ endianess - the endianess used by the communication protocol. Not by the receiver. – Lundin Feb 06 '19 at 14:46
  • @Lundin: I am new on this network concept, if the sender is little endian and network is little endian and receiver is also big endian, then is this network take care of this handling? because in case of sending the structure with 2 int variable 8 byte will be sent to network as byte stream and network can not reverse whole 8 byte in this case to converts to big endian, it should consider first variable and second variable separately and then reverse 4 byte each and network will not know on structure declaration, how receiver will get proper value for the first and second variable? – Deepak Feb 07 '19 at 04:30
  • The network endianess is what the transmission protocol dictates. Both the transmitter and the receiver has to take care of this and write/read bytes in the appropriate order. – Lundin Feb 07 '19 at 07:46