Writing to unaligned storage

Viewed 352

I have some values that I want to write into a raw byte array, in exactly defined locations, to send them off over the network.

//These are computed somewhere else in the program (and converted to little-endian regardless of platform):
using float32_t = float; static_assert(sizeof(float32_t) == 4);
uint32_t someInteger = 42;
float32_t someFloat = 4.2f;
bool someBoolean = true;
uint16_t someIndex = 42, someCRC1 = 42;

My first solution worked and produced the right result, but the memory sanitizer warned me about unaligned writes:

char* data = someBuffer.data(); //points directly into allocated raw bytes
(*reinterpret_cast<uint32_t *>(data     )) = someInteger;
(*reinterpret_cast<bool     *>(data +  4)) = someBoolean;
(*reinterpret_cast<float32_t*>(data +  5)) = someFloat; //unaligned
(*reinterpret_cast<uint16_t *>(data +  9)) = someIndex; //unaligned
(*reinterpret_cast<uint16_t *>(data + 11)) = someCRC;   //unaligned

So I came up with this second solution by reordering some values and inserting a 1-byte buffer for alignment in between.

char* data = someBuffer.data(); //points directly into allocated raw bytes
(*reinterpret_cast<uint32_t *>(data     )) = someInteger;
(*reinterpret_cast<float32_t*>(data +  4)) = someFloat;
(*reinterpret_cast<bool     *>(data +  8)) = someBoolean;
(*reinterpret_cast<char     *>(data +  9)) = '\0'; //buffer for memory alignment
(*reinterpret_cast<uint16_t *>(data + 10)) = someIndex;
(*reinterpret_cast<uint16_t *>(data + 12)) = someCRC; //this has to be the last value!

However I would much rather have the first ordering and no alignment bytes. Is that possible to do independently of platform or do I have to rely on having support for unaligned store instructions? I've heard that maybe using std::memcpy may be able to do this but I am not sure if that's true and how I would use it.

1 Answers

Use std::memcpy, as in std::memcpy(data + 5, &someFloat, sizeof someFloat);. It is defined by the rules of the C++ standard, unlike using reinterpret_cast in the way shown in the question, and a good compiler will optimize it to simple load and store instructions if appropriate.

Note that the sizes of the objects being copied are dependent on the C++ implementation; it will be your responsibility to ensure they match the sizes as laid out in the buffer being sent. Also, the representations of objects are largely implementation-dependent, particularly the order in which their bytes are stored in memory, and so it is also your responsibility to make sure the representation matches what is expected in the message sent over the network. In some C++ implementations, you may need to reverse the bytes within objects. And you must ensure that the sending and receiving systems use the same format for float, and so on.

Related