CIDR 按位操作 - 我可以更明智一点吗?
我正在构建一个类来表示 IPv4 子网。我将网络地址和子网掩码存储为4字节二进制字符串,这些字符串是在基于参数的构造函数期间构建的。我希望构造函数接受的表示形式之一是 CIDR 表示法。
我的按位操作有点生锈,我遇到困难的地方是将子网掩码的十进制整数CIDR表示形式转换为4字节二进制字符串,反之亦然。我还发现我无法在琴弦上执行左/右移位 - 我确定我以前已经成功做到了吗?
我已经设法将转换为二进制字符串以使用以下代码:
// An example input value.
$mask = 24; // 255.255.255.0
if ($mask < 0 || $mask > 32) {
// Invalid prefix size
throw new RangeException('Invalid CIDR prefix size');
} else if ($mask === 0) {
// Handle 0
$mask = "\x00\x00\x00\x00";
} else {
// Left-pad a 4-byte string with $mask set bits
$mask = pack('N', (0x01 << 31) >> ($mask - 1));
}
我不喜欢这种逻辑,原因有二:
- 我不喜欢被视为特例
0
- 我不喜欢右移后左移
我确信有一种方法可以更有效地做到这一点,这种方式可以正确处理,而不会将其视为特例。0
将二进制字符串转换回CIDR前缀大小的十进制表示形式时,我当前正在使用下面的代码。在验证以其他格式提供的子网掩码时,我还有另一个非常相似的代码块,以确保设置的位是连续的。
// An example input value.
$mask = "\xff\xff\xff\x00"; // /24
// Convert the binary string to an int so bit shifts will work
$mask = current(unpack('N', $mask));
// A counter to represent the CIDR
$cidr = 0;
// Loop and check each bit
for ($i = 31; $i > 0; $i--) {
if (($mask >> $i) & 0x01) {
$cidr++;
} else {
break;
}
}
// Return the result
return $cidr;
我不喜欢这个,因为循环 - 我觉得有一个更智能的按位方法来做到这一点。
有没有更智能的方法来完成这些任务中的任何一个?
想法/建议/一般滥用请...
编辑:
任何解决方案都需要在 PHP 4.3.10 及更高版本上运行,并且必须同时在 32 位和 64 位平台上工作。请记住,PHP中的所有整数都是有符号的,在32位平台上,任何东西都将存储为双精度值(因此在按位操作中不会很好)。>= 0x80000000