web-dev-qa-db-fra.com

Existe-t-il une fonction de type htonl "standard" pour les entiers 64 bits en C ++?

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:

  • Existe-t-il une fonction standard portable (Windows, Linux, AIX) pour ce faire?
  • S'il n'y a pas une telle fonction, comment la mettriez-vous en œuvre?

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;
    }
}
60
ereOn

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.

16
Brian R. Bondy
#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.

15
deltamind106

Vous pouvez essayer avec uint64_t htobe64(uint64_t Host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) pour vice-versa.

5
suresh m

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;
    }
}
4
pix0r

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
1
user3734255