如何更改128位数的字节顺序

时间:2011-11-04 04:09:42

标签: c gcc

我有一个unsigned char数组,表示网络字节顺序的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 个答案:

答案 0 :(得分:3)

如果您可以轻松获得高和低__int64 s,那么您可以反转并交换它们。否则,您可能需要单独查看每个字节。

答案 1 :(得分:2)

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。

答案 2 :(得分:2)

由于您明确说过x86_64,您可以转换为__m128i并分别使用PSHUFB指令或_mm_shuffle_epi8。注意正确对齐数据(虽然编译器应该已经正确对齐__int128),因此编译器可以使用MOVDQA(gcc非常擅长这一点,如果可能的话,它会这样做)。 您需要为<tmmintrin.h>添加_mm_shuffle_epi8

请注意,需要一个(可能很慢)的回退实现,因为2003 - 2004年左右的一些早期AMD64处理器没有SSE3支持。

答案 3 :(得分:1)

如果您知道重新移位字节的模式,则可以使用复合文字。首先,我要声明一个联合类型,只是为了避免一些打字。

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,一个好的编译器应该自己找出用于所有这些的汇编程序指令。

答案 4 :(得分:0)

效率不高;但

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