views:

195

answers:

2

Hi,

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;
    }
}
+2  A: 

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.

Brian R. Bondy
@Brian R. Bondy: Thanks ;) The `boost` macros seem interesting. Do you have link ?
ereOn
Check out http://www.boost.org/doc/libs/1_37_0/boost/detail/endian.hpp
Brian R. Bondy
A: 

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.

Pavel Radzivilovsky
Does the wrong thing on Big Endian systems.
Ben Voigt
thanks, fixed. Used brian's code.
Pavel Radzivilovsky
@Pavel Still doesn't work. `htonl()` returns a 32 bits value on which you **cannot** call `<< 32LL` (because you cannot shift 32 bits left on an only 32 bits value).
ereOn
I think shifting by 32LL will do promotion of the left side, no?
Pavel Radzivilovsky
@Pavel: no, the bit-size of the shift value doesn't change anything. gcc emits a warning: "left shift count >= width of type". And your function says that htonll(0x0102030405060708ULL) == 0xc070605.
bstpierre
Change it to `return ((uint64_t)htonl(value ` and it works right.
bstpierre