I wonder what's the recommended way to convert integer to/from little-endian in a portable way.
Is there any library for that?
We can use htonl and ntohl and then do another big-endian to (from) little-endian conversion, but it's not efficient.
I wonder what's the recommended way to convert integer to/from little-endian in a portable way.
Is there any library for that?
We can use htonl and ntohl and then do another big-endian to (from) little-endian conversion, but it's not efficient.
The portable way is to use bit shifts and masks into an appropriately sized string. Notice I say string, because this is really the only time you need to concern yourself with endianness -- when transferring bytes between systems.
If you want to avoid unnecessary conversions (e.g. converting to little-endian on a little-endian architecture), there is no completely portable way to do it at compile-time. But you can check at runtime to dynamically select the set of conversion functions.
This does have the disadvantage where the code can't be inlined. It might be more efficient to write the conversions in the portable way and use templates or inlining. Combined with semi-portable compile-time checks, that's about as good as you'll get.
Further reading: Detecting Endianness at compile-time.
This is a great question. It prompted me to see if there was any way to determine endianness at compile time using constexpr expression.
It turns out that without preprocessor tricks it's not possible, because there's no way to turn an integer into a sequence of bytes (via casts or unions) when evaluating in a constexpr context.
However it turns out that in gcc, a simple run-time check gets optimised away when compiled with -O2, so this is actually optimally efficient:
#include <cstdint>
#include <iostream>
constexpr bool is_little_endian()
{
union endian_tester {
std::uint16_t n;
std::uint8_t p[4];
};
constexpr endian_tester sample = {0x0102};
return sample.p[0] == 0x2;
}
template<class Int>
Int to_little_endian(Int in)
{
if (is_little_endian()) {
return in;
}
else {
Int out = 0;
std::uint8_t* p = reinterpret_cast<std::uint8_t*>(std::addressof(out));
for (std::size_t byte = 0 ; byte < sizeof(in) ; ++byte)
{
auto part = (in >> (byte * 8)) & 0xff;
*p++ = std::uint8_t(part);
}
return out;
}
}
int main()
{
auto x = to_little_endian(10);
std::cout << x << std::endl;
}
here's the assembler output when compiling on an intel (little-endian) platform:
main:
subq $8, %rsp
#
# here is the call to to_little_endian()
#
movl $10, %esi
#
# that was it - it's been completely optimised away
#
movl std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
movq %rax, %rdi
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xorl %eax, %eax
addq $8, %rsp
ret