Есть ли в C ++ «стандартная» htonl-подобная функция для 64-битных чисел? - PullRequest
60 голосов
/ 11 июня 2010

Я работаю над реализацией протокола memcache, который в некоторых моментах использует 64-битные целочисленные значения.Эти значения должны храниться в «сетевом порядке байтов».

Хотелось бы, чтобы была какая-то функция uint64_t htonll(uint64_t value) для внесения изменений, но, к сожалению, если она существует, я не смог бы ее найти.1005 * Итак, у меня есть 1 или 2 вопроса:

  • Существует ли стандартная функция portable (Windows, Linux, AIX) для этого?
  • Если естьнет такой функции, как бы вы ее реализовали?

Я имею в виду базовую реализацию, но я не знаю, как проверить порядок байтов во время компиляции, чтобы сделать код переносимым.Так что ваша помощь здесь более чем приветствуется;)

Спасибо.

Вот окончательное решение, которое я написал, благодаря решению Брайана.

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

Ответы [ 6 ]

16 голосов
/ 11 июня 2010

Вы, наверное, ищете bswap_64 Я думаю, что он поддерживается почти везде, но я бы не назвал его стандартным.

Вы можете легко проверить порядок байтов, создав int со значением 1, приведя адрес вашего int как char* и проверив значение первого байта.

Например:

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

Зная это, вы могли бы также сделать простую функцию, которая выполняет обмен.


Вы также можете всегда использовать boost, который содержит макросы с прямым порядком байтов, которые являются переносимыми кроссплатформенными.

15 голосов
/ 18 февраля 2015
#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))

Тест (1 == htonl (1)) просто определяет (к сожалению, во время выполнения), требует ли аппаратная архитектура замены байтов.Во время компиляции не существует каких-либо переносимых способов определения архитектуры, поэтому мы прибегаем к использованию «htonl», который является настолько переносимым, насколько это возможно в этой ситуации.Если требуется замена байтов, то мы заменяем 32 бита за раз, используя htonl (не забывая также поменять местами два 32-битных слова).


Вот еще один способ выполнить своп, который переносим черезбольшинство компиляторов и операционных систем, включая AIX, BSD, Linux и 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

Важной частью является использование __BIG_ENDIAN__ или __LITTLE_ENDIAN__;а не __BYTE_ORDER__, __ORDER_BIG_ENDIAN__ или __ORDER_LITTLE_ENDIAN__.Некоторым компиляторам и операционным системам не хватает __BYTE_ORDER__ и друзей.

4 голосов
/ 22 октября 2017

Вы можете попробовать с uint64_t htobe64(uint64_t host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) для наоборот.

4 голосов
/ 08 мая 2011

Это похоже на работу в C; я сделал что-то не так?

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;
    }
}
1 голос
/ 12 июня 2014

Чтобы уменьшить накладные расходы на "if num == ..." Использование препроцессора определяет:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
0 голосов
/ 11 июня 2010

РЕДАКТИРОВАТЬ: объединение двух (используется код Брайана):

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

Внимание: код не проверен!Пожалуйста, проверьте перед использованием.

...