有没有什么“标准”? C++ 中类似 htonl 的 64 位整数函数?

发布于 2024-09-05 02:41:40 字数 917 浏览 11 评论 0原文

我正在研究 memcache 协议的实现,该协议在某些时候使用 64 位整数值。这些值必须以“网络字节顺序”存储。

我希望有一些 uint64_t htonll(uint64_t value) 函数来进行更改,但不幸的是,如果它存在,我找不到它。

所以我有 1 或 2 个问题:

  • 是否有任何可移植(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;
    }
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

给不了的爱 2024-09-12 02:41:41

减少“if num == ...”的开销
使用预处理器定义:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif

To reduce the overhead of the "if num == ..."
Use the pre-processor defines:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
杯别 2024-09-12 02:41:41

好吧,我认为最好在编译时尽可能使用字节序切换,但我更喜欢使用函数而不是宏,因为在宏中参数只是被实参替换;因此,如果参数在宏中多次出现(如前面提供的一些解决方案中所做的那样),则可以对参数进行多次求值并创建奇怪的结果。

uint64_t htonll(uint64_t x)
{
#if __BIG_ENDIAN__
    return x;
#else
    return ((uint64_t)htonl((x) & 0xFFFFFFFFLL) << 32) | htonl((x) >> 32);
#endif
}

uint64_t ntohll(uint64_t x)
{
#if __BIG_ENDIAN__
    return x;
#else
    return ((uint64_t)ntohl((x) & 0xFFFFFFFFLL) << 32) | ntohl((x) >> 32);
#endif
}

因此,这允许调用 htonll(x++) 而无需多次增加 x,就像使用之前的宏一样。

Well, I think it's better to use the endian switching at compile time as much as possible, but I prefer to use function instead of macro, because in a macro the parameters are just replaced by the arguments; so arguments can be evaluated multiple times and create weird result if they are present multiple times in the macro (as done in some of the previous provided solutions).

uint64_t htonll(uint64_t x)
{
#if __BIG_ENDIAN__
    return x;
#else
    return ((uint64_t)htonl((x) & 0xFFFFFFFFLL) << 32) | htonl((x) >> 32);
#endif
}

uint64_t ntohll(uint64_t x)
{
#if __BIG_ENDIAN__
    return x;
#else
    return ((uint64_t)ntohl((x) & 0xFFFFFFFFLL) << 32) | ntohl((x) >> 32);
#endif
}

So this allow calling htonll(x++) without increment x several time like it will be done using the previous macros.

可遇━不可求 2024-09-12 02:41:41

我建议使用 endian.h

由于网络字节顺序按照惯例是大端字节序,因此您只需从主机调用适当的函数即可“be”。它包括 16、32 和 64 位版本。

我创建了以下单个 ntoh 模板化函数,它们适用于所有整数类型,因此我不必担心要调用哪个函数。

template <typename T> std::enable_if_t<sizeof(T) == 1, T> ntoh(T v) { return v; }
template <typename T> std::enable_if_t<sizeof(T) == 2, T> ntoh(T v) { return be16toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> ntoh(T v) { return be32toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> ntoh(T v) { return be64toh(v); }

template <typename T> std::enable_if_t<sizeof(T) == 1, T> hton(T v) { return v; }
template <typename T> std::enable_if_t<sizeof(T) == 2, T> hton(T v) { return htobe16(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> hton(T v) { return htobe32(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> hton(T v) { return htobe64(v); }

在 Linux 上,对该函数的调用没有任何损失,因为不需要进行任何转换。
在 Windows 上,谁关心 Windows ;-P

希望它能帮助别人。

I suggest to use the functions which are defined in endian.h

As network byte order is big endian by convention you just have to call the appropriate function from host to "be". It includes 16, 32 and 64 bits versions.

I've created the following single ntoh templated fuctions which work for all the integral types so I don't have to worry about which function to call.

template <typename T> std::enable_if_t<sizeof(T) == 1, T> ntoh(T v) { return v; }
template <typename T> std::enable_if_t<sizeof(T) == 2, T> ntoh(T v) { return be16toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> ntoh(T v) { return be32toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> ntoh(T v) { return be64toh(v); }

template <typename T> std::enable_if_t<sizeof(T) == 1, T> hton(T v) { return v; }
template <typename T> std::enable_if_t<sizeof(T) == 2, T> hton(T v) { return htobe16(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> hton(T v) { return htobe32(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> hton(T v) { return htobe64(v); }

On Linux, the call to that function has no penalty as no transformation need to be done.
On Windows, who cares about windows ;-P

Hope it helps someone.

半透明的墙 2024-09-12 02:41:41

编辑:将两者结合起来(使用布莱恩的代码):

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

警告:未经测试的代码!使用前请先测试。

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.

暮凉 2024-09-12 02:41:40
#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”,它在这种情况下是可移植的。如果需要字节交换,那么我们使用 htonl 一次交换 32 位(记住也要交换两个 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__ 和朋友。

#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))

The test (1==htonl(1)) simply determines (at runtime sadly) if the hardware architecture requires byte swapping. There aren't any portable ways to determine at compile-time what the architecture is, so we resort to using "htonl", which is as portable as it gets in this situation. If byte-swapping is required, then we swap 32 bits at a time using htonl (remembering to swap the two 32 bit words as well).


Here's another way to perform the swap that is portable across most compilers and operating systems, including AIX, BSDs, Linux, and 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

The important part is to use __BIG_ENDIAN__ or __LITTLE_ENDIAN__; and not __BYTE_ORDER__, __ORDER_BIG_ENDIAN__ or __ORDER_LITTLE_ENDIAN__. Some compilers and operating systems lack __BYTE_ORDER__ and friends.

南城追梦 2024-09-12 02:41:40

您可能正在寻找 bswap_64 我认为它几乎在所有地方都受支持,但我不会将其称为标准。

您可以通过创建值为 1 的 int、将 int 的地址转换为 char* 并检查第一个字节的值来轻松检查字节顺序。

例如:

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

知道了这一点,您还可以创建一个简单的函数来进行交换。


您还可以始终使用包含可移植跨平台的字节序宏的 boost。

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.

活雷疯 2024-09-12 02:41:40

您可以尝试使用 uint64_t htobe64(uint64_t host_64bits) &
uint64_t be64toh(uint64_t big_endian_64bits) 反之亦然。

You can try with uint64_t htobe64(uint64_t host_64bits) &
uint64_t be64toh(uint64_t big_endian_64bits) for vice-versa.

心是晴朗的。 2024-09-12 02:41:40

这似乎在 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;
    }
}

This seems to work in C; did I do anything wrong?

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文