如何在 C/C++ 中减去两个 IPv6 地址(128 位数字)?
How can I subtract two IPv6 addresses (128bit numbers) in C/C++?
我将 IP 地址存储在 sockaddr_in6
中,它支持四个 32 位的数组,addr[4]。本质上是一个 128 位数。
我正在尝试计算给定 IPv6 范围内的 IP 数量(中间有多少 IP)。所以这是一个使用两个长度为四的数组从另一个中减去一个的问题。
问题是因为没有128bit数据类型,我不能转换成十进制。
非常感谢!
您可以使用某种 big-int 库(如果您可以容忍 LGPL,则可以选择 GMP)。幸运的是,如有必要,128 位减法很容易手动模拟。下面是计算 (a-b) 绝对值的快速演示,对于 128 位值:
#include <iostream>
#include <iomanip>
struct U128
{
unsigned long long hi;
unsigned long long lo;
};
bool subtract(U128& a, U128 b)
{
unsigned long long carry = b.lo > a.lo;
a.lo -= b.lo;
unsigned long long carry2 = b.hi > a.hi || a.hi == b.hi && carry;
a.hi -= carry;
a.hi -= b.hi;
return carry2 != 0;
}
int main()
{
U128 ipAddressA = { 45345, 345345 };
U128 ipAddressB = { 45345, 345346 };
bool carry = subtract(ipAddressA, ipAddressB);
// Carry being set means that we underflowed; that ipAddressB was > ipAddressA.
// Lets just compute 0 - ipAddressA as a means to calculate the negation
// (0-x) of our current value. This gives us the absolute value of the
// difference.
if (carry)
{
ipAddressB = ipAddressA;
ipAddressA = { 0, 0 };
subtract(ipAddressA, ipAddressB);
}
// Print gigantic hex string of the 128-bit value
std::cout.fill ('0');
std::cout << std::hex << std::setw(16) << ipAddressA.hi << std::setw(16) << ipAddressA.lo << std::endl;
}
这为您提供了差异的绝对值。如果范围不是很大(64 位或更少),那么 ipAddressA.lo
可以作为简单的 unsigned long long
.
如果您有性能方面的顾虑,您可以利用编译器内部函数来利用某些架构,例如 amd64,如果您希望它在该处理器上是最佳的。 _subborrow_u64
是用于必要减法工作的 amd64 内在函数。
in6_addr
结构以网络字节顺序存储地址 - 或 'big endian' - 最高有效字节 @ s6_addr[0]
。您不能指望其他工会成员的命名或定义一致。即使您通过(不可移植的)uint32_t
字段访问联合,这些值也必须使用 ntohl
进行转换。因此,便携式 查找差异的方法需要一些工作。
您可以将 in6_addr
转换为 uint64_t[2]
。坚持典型的 'bignum' 约定,我们使用 [0] 表示低 64 位,[1] 表示高 64 位:
static inline void
in6_to_u64 (uint64_t dst[2], const struct in6_addr *src)
{
uint64_t hi = 0, lo = 0;
for (unsigned int i = 0; i < 8; i++)
{
hi = (hi << 8) | src->s6_addr[i];
lo = (lo << 8) | src->s6_addr[i + 8];
}
dst[0] = lo, dst[1] = hi;
}
和区别:
static inline unsigned int
u64_diff (uint64_t d[2], const uint64_t x[2], const uint64_t y[2])
{
unsigned int b = 0, bi;
for (unsigned int i = 0; i < 2; i++)
{
uint64_t di, xi, yi, tmp;
xi = x[i], yi = y[i];
tmp = xi - yi;
di = tmp - b, bi = tmp > xi;
d[i] = di, b = bi | (di > tmp);
}
return b; /* borrow flag = (x < y) */
}
我将 IP 地址存储在 sockaddr_in6
中,它支持四个 32 位的数组,addr[4]。本质上是一个 128 位数。
我正在尝试计算给定 IPv6 范围内的 IP 数量(中间有多少 IP)。所以这是一个使用两个长度为四的数组从另一个中减去一个的问题。
问题是因为没有128bit数据类型,我不能转换成十进制。
非常感谢!
您可以使用某种 big-int 库(如果您可以容忍 LGPL,则可以选择 GMP)。幸运的是,如有必要,128 位减法很容易手动模拟。下面是计算 (a-b) 绝对值的快速演示,对于 128 位值:
#include <iostream>
#include <iomanip>
struct U128
{
unsigned long long hi;
unsigned long long lo;
};
bool subtract(U128& a, U128 b)
{
unsigned long long carry = b.lo > a.lo;
a.lo -= b.lo;
unsigned long long carry2 = b.hi > a.hi || a.hi == b.hi && carry;
a.hi -= carry;
a.hi -= b.hi;
return carry2 != 0;
}
int main()
{
U128 ipAddressA = { 45345, 345345 };
U128 ipAddressB = { 45345, 345346 };
bool carry = subtract(ipAddressA, ipAddressB);
// Carry being set means that we underflowed; that ipAddressB was > ipAddressA.
// Lets just compute 0 - ipAddressA as a means to calculate the negation
// (0-x) of our current value. This gives us the absolute value of the
// difference.
if (carry)
{
ipAddressB = ipAddressA;
ipAddressA = { 0, 0 };
subtract(ipAddressA, ipAddressB);
}
// Print gigantic hex string of the 128-bit value
std::cout.fill ('0');
std::cout << std::hex << std::setw(16) << ipAddressA.hi << std::setw(16) << ipAddressA.lo << std::endl;
}
这为您提供了差异的绝对值。如果范围不是很大(64 位或更少),那么 ipAddressA.lo
可以作为简单的 unsigned long long
.
如果您有性能方面的顾虑,您可以利用编译器内部函数来利用某些架构,例如 amd64,如果您希望它在该处理器上是最佳的。 _subborrow_u64
是用于必要减法工作的 amd64 内在函数。
in6_addr
结构以网络字节顺序存储地址 - 或 'big endian' - 最高有效字节 @ s6_addr[0]
。您不能指望其他工会成员的命名或定义一致。即使您通过(不可移植的)uint32_t
字段访问联合,这些值也必须使用 ntohl
进行转换。因此,便携式 查找差异的方法需要一些工作。
您可以将 in6_addr
转换为 uint64_t[2]
。坚持典型的 'bignum' 约定,我们使用 [0] 表示低 64 位,[1] 表示高 64 位:
static inline void
in6_to_u64 (uint64_t dst[2], const struct in6_addr *src)
{
uint64_t hi = 0, lo = 0;
for (unsigned int i = 0; i < 8; i++)
{
hi = (hi << 8) | src->s6_addr[i];
lo = (lo << 8) | src->s6_addr[i + 8];
}
dst[0] = lo, dst[1] = hi;
}
和区别:
static inline unsigned int
u64_diff (uint64_t d[2], const uint64_t x[2], const uint64_t y[2])
{
unsigned int b = 0, bi;
for (unsigned int i = 0; i < 2; i++)
{
uint64_t di, xi, yi, tmp;
xi = x[i], yi = y[i];
tmp = xi - yi;
di = tmp - b, bi = tmp > xi;
d[i] = di, b = bi | (di > tmp);
}
return b; /* borrow flag = (x < y) */
}