Integers need to be converted to a byte array of defined endianness to be reliably and consistently saved and transmitted, and converted back to be accurately received and read. The goal is to be as portable as possible, while maintaining concise and fast code.
#ifndef LE_h
#define LE_h
#include <stdint.h>
inline static uint16_t le16(const uint8_t b[const static 2]) {
return b[0]|(uint16_t)b[1]<<8;
}
inline static uint32_t le32(const uint8_t b[const static 4]) {
return le16(b)|(uint32_t)le16(b+2)<<16;
}
inline static uint64_t le64(const uint8_t b[const static 8]) {
return le32(b)|(uint64_t)le32(b+4)<<32;
}
inline static void le16b(uint8_t b[const static 2], const uint16_t n) {
b[0] = n;
b[1] = n>>8;
}
inline static void le32b(uint8_t b[const static 4], const uint32_t n) {
le16b(b, n);
le16b(b+2, n>>16);
}
inline static void le64b(uint8_t b[const static 8], const uint64_t n) {
le32b(b, n);
le32b(b+4, n>>32);
}
#endif /* LE_h */
1 Answer 1
The goal is to be as portable as possible, while maintaining concise and fast code.
Why little?
"to a byte array of defined endianness", is a good goal. I think little is more correct, yet big endian tends to be, though not universally, the preferred endian. I'd offer users both LE and BE routines.
Signed types?
Endian issues apply to signed types too. I'd expect le16signed()
and friends.
Note: Floating point has endian issues and a whole lot more encoding ones too.
Standard integer types?
For intercommunication, fixed width types are preferred, yet such a LE package would be nice if extended to include LE routine for short, int, ..., intmax_t
.
(u)intN_t
types are optional
A compliant compiler only needs to implement them if the corresponding type is available without padding. Consider the case of CHAR_BIT == 9
, or CHAR_BIT == 16
.
To port to such rare machines, the interface needs re-design. Perhaps use (u)int_leastN_t
types.
Or simply add a #if CHAR_BIT != 8 #error ...
.
Older compilers
b[const static 2]
is not valid on older compilers (of course then maybe not inline
). On such, could use *b
instead and/or no inline
.
At least consider gracefully detecting the required version and #error
when not met.
No UB seen
Good.
Pedantic warnings
Some compilers may warn about type narrowing:
b[0] = n;
// alternative
b[0] = (uint8_t) n;
Unneeded const
Style issue: the 2nd const
is not needed (and IMO distracting). Unsure about the usefulness of the first const
.
// vvvvv
inline static void le16b(uint8_t b[const static 2], const uint16_t n) {
Explore related questions
See similar questions with these tags.
htons
and friends ? \$\endgroup\$htons
family convert to/from big-endian. \$\endgroup\$to/from
- either way, those conversions are big-endian, and this is for little-endian. (E.g. on a big-endian platform, thehtons
family perform no conversion - the opposite of what's wanted here). \$\endgroup\$