문제

I'm working on an implementation of the memcache protocol which, at some points, uses 64 bits integer values. These values must be stored in "network byte order".

I wish there was some uint64_t htonll(uint64_t value) function to do the change, but unfortunately, if it exist, I couldn't find it.

So I have 1 or 2 questions:

  • Is there any portable (Windows, Linux, AIX) standard function to do this ?
  • If there is no such function, how would you implement it ?

I have in mind a basic implementation but I don't know how to check the endianness at compile-time to make the code portable. So your help is more than welcome here ;)

Thank you.


Here is the final solution I wrote, thanks to Brian's solution.

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;
    }
}
도움이 되었습니까?

해결책

You are probably looking for bswap_64 I think it is supported pretty much everywhere but I wouldn't call it standard.

You can easily check the endianness by creating an int with a value of 1, casting your int's address as a char* and checking the value of the first byte.

For example:

int num = 42;
if(*(char *)&num == 42)
{
   //Little Endian
}
else
{
   //Big Endian
} 

Knowing this you could also make a simple function that does the swapping.


You could also always use boost which contains endian macros which are portable cross platform.

다른 팁

#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))

The test (1==htonl(1)) simply determines (at runtime sadly) if the hardware architecture requres byte swapping. There aren't any portable ways to determine at compile-time what the architecture is, so we resort to using "htonl", which is as portable as it gets in this situation. If byte-swapping is required, then we swap 32 bits at a time using htonl (remembering to swap the two 32 bit words as well).


Here's another way to perform the swap that is portable across most compilers and operating systems, including AIX, BSDs, Linux, and 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

The important part is to use __BIG_ENDIAN__ or __LITTLE_ENDIAN__; and not __BYTE_ORDER__, __ORDER_BIG_ENDIAN__ or __ORDER_LITTLE_ENDIAN__. Some compilers and operating systems lack __BYTE_ORDER__ and friends.

You can try with uint64_t htobe64(uint64_t host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) for vice-versa.

This seems to work in C; did I do anything wrong?

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

To reduce the overhead of the "if num == ..." Use the pre-processor defines:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif

EDIT: combining the two (used Brian's code):

uint64_t htonll(uint64_t value)
{
     int num = 42;
     if(*(char *)&num == 42)
          return (htonl(value & 0xFFFFFFFF) << 32LL) | htonl(value >> 32);
     else 
          return value;
}

Warning: untested code! Please test before using.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top