64-битный ntohl () в C ++? - PullRequest
       52

64-битный ntohl () в C ++?

58 голосов
/ 01 мая 2009

Справочные страницы для htonl() предполагают, что вы можете использовать его только для 32-битных значений. (На самом деле, ntohl() определено для длинного без знака, который на моей платформе составляет 32 бита. Я полагаю, если бы длинный без знака был 8 байтов, он работал бы для 64-битных целых).

Моя проблема заключается в том, что мне нужно преобразовать 64-битные целые числа (в моем случае это long без знака long) из старшего байта в младший. Прямо сейчас мне нужно сделать это конкретное преобразование. Но было бы еще лучше, если бы функция (например, ntohl()) НЕ преобразовывала мое 64-битное значение, если целевая платформа была бы с прямым порядком байтов. (Я бы предпочел не добавлять свою магию препроцессора для этого).

Что я могу использовать? Я хотел бы что-то стандартное, если оно существует, но я открыт для предложений по реализации. Я видел этот тип преобразования, сделанный в прошлом с помощью союзов. Я полагаю, у меня может быть союз с длинным без знака и символом [8]. Затем поменяйте местами байты соответственно. (Очевидно, что будет работать на платформах с большим порядком байтов).

Ответы [ 16 ]

2 голосов
/ 01 мая 2009

Мне нравится ответ профсоюза, довольно аккуратно. Обычно я просто сдвигаю бит для преобразования между прямым и младшим порядком байтов, хотя я думаю, что объединенное решение имеет меньше назначений и может быть быстрее:

//note UINT64_C_LITERAL is a macro that appends the correct prefix
//for the literal on that platform
inline void endianFlip(unsigned long long& Value)
{
   Value=
   ((Value &   UINT64_C_LITERAL(0x00000000000000FF)) << 56) |
   ((Value &   UINT64_C_LITERAL(0x000000000000FF00)) << 40) |
   ((Value &   UINT64_C_LITERAL(0x0000000000FF0000)) << 24) |
   ((Value &   UINT64_C_LITERAL(0x00000000FF000000)) << 8)  |
   ((Value &   UINT64_C_LITERAL(0x000000FF00000000)) >> 8)  | 
   ((Value &   UINT64_C_LITERAL(0x0000FF0000000000)) >> 24) |
   ((Value &   UINT64_C_LITERAL(0x00FF000000000000)) >> 40) |
   ((Value &   UINT64_C_LITERAL(0xFF00000000000000)) >> 56);
}

Затем, чтобы определить, нужно ли вам совершать свой переворот без макромагики, вы можете сделать что-то похожее на Pax, где, когда короткому назначению присваивается 0x0001, это будет 0x0100 в противоположной системе байтов.

Итак:

unsigned long long numberToSystemEndian
(
    unsigned long long In, 
    unsigned short SourceEndian
)
{
   if (SourceEndian != 1)
   {
      //from an opposite endian system
      endianFlip(In);
   }
   return In;
}

Итак, чтобы использовать это, вам нужно, чтобы SourceEndian был индикатором, указывающим порядковый номер входного числа. Это можно сохранить в файле (если это проблема сериализации) или передать по сети (если это проблема сериализации сети).

1 голос
/ 19 ноября 2017

htonl можно сделать, выполнив следующие шаги

  • Если система с прямым порядком байтов возвращает значение напрямую. Нет необходимости делать какие-либо преобразования. Если это система с прямым порядком байтов, нужно выполнить приведенное ниже преобразование.
  • Возьмите 32 бита LSB и примените 'htonl' и сдвиньте 32 раза.
  • Возьмите MSB 32 бита (сдвигая значение uint64_t 32 раза вправо) и примените 'htonl'
  • Теперь примените побитовое ИЛИ для значения, полученного на 2-м и 3-м шаге.

Аналогично для ntohll также

#define HTONLL(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)))
#define NTOHLL(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)))

Вы можете delcare выше 2 определения также как функции.

0 голосов
/ 17 апреля 2016

универсальная функция для любого размера значения.

template <typename T>
T swap_endian (T value)
{
    union {
        T src;
        unsigned char dst[sizeof(T)];
    } source, dest;

    source.src = value;
    for (size_t k = 0; k < sizeof(T); ++k)
        dest.dst[k] = source.dst[sizeof(T) - k - 1];

    return dest.src;
}
0 голосов
/ 07 сентября 2015

Предполагается, что вы кодируете в Linux с использованием 64-битной ОС; большинство систем имеют htole(x) или ntobe(x) и т. д., как правило, это макросы для различных bswap

#include <endian.h>
#include <byteswap.h>

unsigned long long htonll(unsigned long long val)
{
    if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
    else return __bswap_64(val);
}

unsigned long long ntohll(unsigned long long val)
{
    if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
    else return __bswap_64(val);
}

примечание стороны; это просто функции, которые нужно вызвать, чтобы поменять порядок байтов. Если вы используете метод с прямым порядком байтов, например, с сетью с прямым порядком байтов, но если вы используете кодировку с большим окончанием, то это излишне обратит порядок байтов, поэтому может потребоваться небольшая проверка "if __BYTE_ORDER == __LITTLE_ENDIAN", чтобы сделать ваш код более переносимым, в зависимости на ваши нужды.

Обновление: отредактировано, чтобы показать пример проверки порядка байтов

0 голосов
/ 08 апреля 2014
template <typename T>
static T ntoh_any(T t)
{
    static const unsigned char int_bytes[sizeof(int)] = {0xFF};
    static const int msb_0xFF = 0xFF << (sizeof(int) - 1) * CHAR_BIT;
    static bool host_is_big_endian = (*(reinterpret_cast<const int *>(int_bytes)) & msb_0xFF ) != 0;
    if (host_is_big_endian) { return t; }

    unsigned char * ptr = reinterpret_cast<unsigned char *>(&t);
    std::reverse(ptr, ptr + sizeof(t) );
    return t;
}

Работает на 2 байта, 4 байта, 8 байтов и 16 байтов (если у вас 128-битное целое число) Должен быть независимым от ОС / платформы.

0 голосов
/ 09 мая 2011

Как правило, нет необходимости знать порядковый номер машины, чтобы преобразовать целое число хоста в сетевой порядок. К сожалению, это справедливо только в том случае, если вы записали свое значение нетто-порядка в байтах, а не как другое целое число:

static inline void short_to_network_order(uchar *output, uint16_t in)
{
    output[0] = in>>8&0xff;
    output[1] = in&0xff;
}

(расширить при необходимости для больших чисел).

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

Недостаток, конечно, в том, что это не тот же стандартный интерфейс, что и у htonl () и друзей (что я не вижу в качестве недостатка, потому что дизайн htonl () был плохим выбором для imo).

...