Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Portable way of splitting n-byte integer into single bytes

The problem is simple: Take an 32-bit or 64-bit integer and split it up to send over an (usually)1-byte interface like uart, spi or i2c.

To do this I can easily use bit masking and shifting to get what I want. However, I want this to be portable that will work on big and little endian, but also make it work for platforms that don't discard bits but rotate through carry(masking gets rid of excess bits right?).

Example code:

uint32_t value;
uint8_t buffer[4];
buffer[0] = (value >> 24) & 0xFF;
buffer[1] = (value >> 16) & 0xFF;
buffer[2] = (value >> 8) & 0xFF;
buffer[3] = value & 0xFF;

I want to guarantee this works on any platform that supports 32 bit integers or more. I don't know if this is correct.

like image 502
Tryphon Avatar asked Mar 05 '23 16:03

Tryphon


1 Answers

The code you presented is the most portable way of doing it. You convert a single unsigned integer value with 32 bits width into an array of unsigned integer values of exactly 8 bits width. The resulting bytes in the buffer array are in big endian order.

The masking is not needed. From C11 6.5.7p5:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has
a signed type and a nonnegative value, the value of the result is the integral part of the quotient of
E1 / 2^E2.

and casting to an integer with 8 bits width is (to the value) equal to masking 8 bits. So (result >> 24) & 0xff is equal to (uint8_t)(result >> 24) (to the value). As you assign to uint8_t variable the masking is not needed. Anyway I would safely assume it will be optimized out by a sane compiler.

I can recommend to take a look at one implementation that I remembered, that I guess has implemented in a really safe manner all the possible variants of splitting and composing fixed-width integers up to 64 bits from bytes and back, that is at gpsd bits.h.

like image 169
KamilCuk Avatar answered Mar 27 '23 20:03

KamilCuk