Solución:
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
La prueba (1 == htonl (1)) simplemente determina (en tiempo de ejecución, lamentablemente) si la arquitectura de hardware requiere intercambio de bytes. No existen formas portátiles de determinar en tiempo de compilación cuál es la arquitectura, por lo que recurrimos al uso de “htonl”, que es lo más portátil posible en esta situación. Si se requiere el intercambio de bytes, entonces intercambiamos 32 bits a la vez usando htonl (recordando intercambiar las dos palabras de 32 bits también).
Aquí hay otra forma de realizar el intercambio que es portátil en la mayoría de los compiladores y sistemas operativos, incluidos AIX, BSD, Linux y Solaris.
#if __BIG_ENDIAN__
# define htonll(x) (x)
# define ntohll(x) (x)
#else
# define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
# define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif
La parte importante es usar __BIG_ENDIAN__
o __LITTLE_ENDIAN__
; y no __BYTE_ORDER__
, __ORDER_BIG_ENDIAN__
o __ORDER_LITTLE_ENDIAN__
. Algunos compiladores y sistemas operativos carecen __BYTE_ORDER__
y amigos.
Probablemente estas buscando bswap_64
Creo que se admite prácticamente en todas partes, pero no lo llamaría estándar.
Puede verificar fácilmente el endianness creando un int con un valor de 1, lanzando la dirección de su int como un char*
y comprobar el valor del primer byte.
Por ejemplo:
int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}
Sabiendo esto, también podría hacer una función simple que realice el intercambio.
También puede usar boost que contiene macros endian que son portátiles multiplataforma.
Puedes probar con uint64_t htobe64(uint64_t host_64bits)
Y
uint64_t be64toh(uint64_t big_endian_64bits)
para viceversa.