как изменить порядок байтов 128-битного числа - PullRequest
4 голосов
/ 04 ноября 2011

У меня есть массив беззнаковых символов, представляющих 128-битное число в порядке сетевых байтов.Как мне эффективно преобразовать это в порядок байтов хоста (в данном случае x86_64)?

Кажется, что в endian.h нет макросов, и моя попытка преобразовать старшие 64 бита и младшие64 бита независимо не работали.Единственный найденный мной метод, который определенно работает, - это цикл вроде:

unsigned __int128 num = 0;
for (int i = 0; i < 16; i++) {
    num = (num << 8) | byte[i];
}

В итоге я сделал следующее:

union {
    unsigned char b[MD5_DIGEST_LENGTH];
    uint64_t d[2];
    unsigned __int128 q;
} digest;
MD5((const unsigned char *)str, length, digest.b);
uint64_t tmp = digest.d[0];
digest.d[0] = be64toh(digest.d[1]);
digest.d[1] = be64toh(tmp);
/* digest.q is now in native byte order */

Ответы [ 5 ]

3 голосов
/ 04 ноября 2011

Если вы можете легко получить максимум и минимум __int64 с, то вы можете повернуть их вспять и поменять местами.В противном случае вам, вероятно, придется смотреть на каждый байт индивидуально.

2 голосов
/ 04 ноября 2011

Поскольку вы явно указали x86_64, вы можете привести к __m128i и использовать инструкцию PSHUFB или _mm_shuffle_epi8 соответственно.Позаботьтесь о правильном выравнивании ваших данных (хотя компилятор должен уже правильно выровнять __int128), чтобы компилятор мог использовать MOVDQA (gcc довольно хорош в этом, если это возможно, он просто сделает это).Вам нужно включить <tmmintrin.h> для _mm_shuffle_epi8.

Обратите внимание, что вам потребуется (возможно, медленная) резервная реализация, потому что некоторые очень ранние процессоры AMD64 примерно с 2003-2004 гг.есть поддержка SSE3.

2 голосов
/ 04 ноября 2011
union _128_as_32 {
    unsigned __int128 v;
    unsigned __int32 d[4];
} u1, u2;
u1.v = num;
u2.d[3] = ntohl(u1.d[0]);
u2.d[2] = ntohl(u1.d[1]);
u2.d[1] = ntohl(u1.d[2]);
u2.d[0] = ntohl(u1.d[3]);
// do something with u2.v

Если в вашей среде есть betoh64 / be64toh (linux / bsd endian.h), вы можете использовать

union _128_as_64 {
    unsigned __int128 v;
    unsigned __int64 q[2];
} u1, u2;
u1.v = num;
u2.q[1] = betoh64(u1.q[0]);
u2.q[0] = betoh64(u1.q[1]);
// do something with u2.v

Поскольку вы, вероятно, имеете дело с адресами IN6, у вас должно быть уже доступно семейство функций ntohl.

H.T.H.

1 голос
/ 04 ноября 2011

Если вы знаете шаблон изменения байтов, вы можете использовать для этого составной литерал. Сначала я объявил бы тип объединения, просто чтобы избежать некоторой типизации.

typedef union conv {
 uint128_t i;
 uint8_t c[16];
} conv;

Тогда вам нужно выражение монстра

#define swapped128(n)         \
  ((conv const){              \
    .c = {                    \
       [0] = 0xFF & (n >> xx) \
       ....                   \
       [15] = 0xFF & (n >> xx)\
     }                        \
    })

, где вам нужно заменить xx на значения, соответствующие значению сдвига для этого байта.

Теперь ваше замененное значение просто swapped(n).i, и хороший компилятор должен сам выяснить, какие инструкции ассемблера использовать для всего этого.

0 голосов
/ 04 ноября 2011

не так эффективно, но;

unsigned __int128 htonllll(unsigned __int128 v) {
        union { unsigned long lv[4]; unsigned __int128 llv; } u;
        u.lv[0] = htonl(v >> 96);
        u.lv[1] = htonl(v >> 64);  
        u.lv[2] = htonl(v >> 32);
        u.lv[3] = htonl(v & 0xFFFFFFFFULL);
        return u.llv;
    }

    unsigned __int128 ntohllll(unsigned __int128 v) {
        union { unsigned long lv[4]; unsigned __int128 llv; } u;
        u.llv = v;
        return ((unsigned __int128)ntohl(u.lv[0]) << 96) | (unsigned __int128)ntohl(u.lv[1]) << 64) | (unsigned __int128)ntohl(u.lv[2]) << 32) | (unsigned __int128)ntohl(u.lv[3]);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...