Je travaille sur une implémentation du protocole memcache qui, à certains moments, utilise des valeurs entières 64 bits. Ces valeurs doivent être stockées dans "l'ordre des octets réseau".
J'aimerais qu'il y ait une fonction uint64_t htonll(uint64_t value)
pour faire le changement, mais malheureusement, si elle existe, je ne l'ai pas trouvée.
J'ai donc 1 ou 2 questions:
Je pense à une implémentation de base mais je ne sais pas comment vérifier l'endianité au moment de la compilation pour rendre le code portable. Votre aide est donc plus que bienvenue ici;)
Je vous remercie.
Voici la solution finale que j'ai écrite, grâce à la solution de Brian.
uint64_t htonll(uint64_t value)
{
// The answer is 42
static const int num = 42;
// Check the endianness
if (*reinterpret_cast<const char*>(&num) == num)
{
const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32));
const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL));
return (static_cast<uint64_t>(low_part) << 32) | high_part;
} else
{
return value;
}
}
Vous recherchez probablement bswap_64
Je pense qu'il est pris en charge à peu près partout mais je n'appellerais pas cela standard.
Vous pouvez facilement vérifier l'endianité en créant un int avec une valeur de 1, en convertissant l'adresse de votre int en char*
et en vérifiant la valeur du premier octet.
Par exemple:
int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}
Sachant cela, vous pouvez également créer une fonction simple qui effectue l'échange.
Vous pouvez également toujours utiliser boost qui contient des macros endiennes qui sont des plates-formes portables.
#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))
Le test (1 == htonl (1)) détermine simplement (malheureusement au moment de l'exécution) si l'architecture matérielle nécessite un échange d'octets. Il n'y a aucun moyen portable de déterminer au moment de la compilation ce qu'est l'architecture, nous avons donc recours à "htonl", qui est aussi portable que possible dans cette situation. Si l'échange d'octets est requis, nous échangeons 32 bits à la fois en utilisant htonl (en pensant également à échanger les deux mots de 32 bits).
Voici une autre façon d'effectuer l'échange qui est portable sur la plupart des compilateurs et des systèmes d'exploitation, y compris AIX, BSD, Linux et 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
L'important est d'utiliser __BIG_ENDIAN__
ou __LITTLE_ENDIAN__
; et pas __BYTE_ORDER__
, __ORDER_BIG_ENDIAN__
ou __ORDER_LITTLE_ENDIAN__
. Certains compilateurs et systèmes d'exploitation manquent de __BYTE_ORDER__
et amis.
Vous pouvez essayer avec uint64_t htobe64(uint64_t Host_64bits)
& uint64_t be64toh(uint64_t big_endian_64bits)
pour vice-versa.
Cela semble fonctionner en C; ai-je fait quelque chose de mal?
uint64_t htonll(uint64_t value) {
int num = 42;
if (*(char *)&num == 42) {
uint32_t high_part = htonl((uint32_t)(value >> 32));
uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
return (((uint64_t)low_part) << 32) | high_part;
} else {
return value;
}
}
Pour réduire les frais généraux du "if num == ..." Utilisez le pré-processeur définit:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif