imported mlocati/ip-lib version 1.14.0

This commit is contained in:
El RIDO
2021-05-22 09:21:01 +02:00
parent 194b27e685
commit 89f6f0051d
20 changed files with 3048 additions and 8 deletions

View File

@@ -0,0 +1,118 @@
<?php
namespace IPLib\Service;
/**
* Helper class to work with unsigned binary integers.
*/
class BinaryMath
{
/**
* Trim the leading zeroes from a non-negative integer represented in binary form.
*
* @param string $value
*
* @return string
*/
public function reduce($value)
{
$value = ltrim($value, '0');
return $value === '' ? '0' : $value;
}
/**
* Compare two non-negative integers represented in binary form.
*
* @param string $a
* @param string $b
*
* @return int 1 if $a is greater than $b, -1 if $b is greater than $b, 0 if they are the same
*/
public function compare($a, $b)
{
list($a, $b) = $this->toSameLength($a, $b);
return $a < $b ? -1 : ($a > $b ? 1 : 0);
}
/**
* Add 1 to a non-negative integer represented in binary form.
*
* @param string $value
*
* @return string
*/
public function increment($value)
{
$lastZeroIndex = strrpos($value, '0');
if ($lastZeroIndex === false) {
return '1' . str_repeat('0', strlen($value));
}
return ltrim(substr($value, 0, $lastZeroIndex), '0') . '1' . str_repeat('0', strlen($value) - $lastZeroIndex - 1);
}
/**
* Calculate the bitwise AND of two non-negative integers represented in binary form.
*
* @param string $operand1
* @param string $operand2
*
* @return string
*/
public function andX($operand1, $operand2)
{
$operand1 = $this->reduce($operand1);
$operand2 = $this->reduce($operand2);
$numBits = min(strlen($operand1), strlen($operand2));
$operand1 = substr(str_pad($operand1, $numBits, '0', STR_PAD_LEFT), -$numBits);
$operand2 = substr(str_pad($operand2, $numBits, '0', STR_PAD_LEFT), -$numBits);
$result = '';
for ($index = 0; $index < $numBits; $index++) {
$result .= $operand1[$index] === '1' && $operand2[$index] === '1' ? '1' : '0';
}
return $this->reduce($result);
}
/**
* Calculate the bitwise OR of two non-negative integers represented in binary form.
*
* @param string $operand1
* @param string $operand2
*
* @return string
*/
public function orX($operand1, $operand2)
{
list($operand1, $operand2, $numBits) = $this->toSameLength($operand1, $operand2);
$result = '';
for ($index = 0; $index < $numBits; $index++) {
$result .= $operand1[$index] === '1' || $operand2[$index] === '1' ? '1' : '0';
}
return $result;
}
/**
* Zero-padding of two non-negative integers represented in binary form, so that they have the same length.
*
* @param string $num1
* @param string $num2
*
* @return string[],int[] The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits
*/
private function toSameLength($num1, $num2)
{
$num1 = $this->reduce($num1);
$num2 = $this->reduce($num2);
$numBits = max(strlen($num1), strlen($num2));
return array(
str_pad($num1, $numBits, '0', STR_PAD_LEFT),
str_pad($num2, $numBits, '0', STR_PAD_LEFT),
$numBits,
);
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace IPLib\Service;
use IPLib\Address\AddressInterface;
use IPLib\Factory;
use IPLib\Range\Subnet;
/**
* Helper class to calculate the subnets describing all (and only all) the addresses between two bouundaries.
*/
class RangesFromBounradyCalculator
{
/**
* The BinaryMath instance to be used to perform bitwise poerations.
*
* @var \IPLib\Service\BinaryMath
*/
private $math;
/**
* The number of bits used to represent addresses.
*
* @var int
*
* @example 32 for IPv4, 128 for IPv6
*/
private $numBits;
/**
* The bit masks for every bit index.
*
* @var string[]
*/
private $masks;
/**
* The bit unmasks for every bit index.
*
* @var string[]
*/
private $unmasks;
/**
* Initializes the instance.
*
* @param int $numBits the number of bits used to represent addresses (32 for IPv4, 128 for IPv6)
*/
public function __construct($numBits)
{
$this->math = new BinaryMath();
$this->setNumBits($numBits);
}
/**
* Calculate the subnets describing all (and only all) the addresses between two bouundaries.
*
* @param \IPLib\Address\AddressInterface $from
* @param \IPLib\Address\AddressInterface $to
*
* @return \IPLib\Range\Subnet[]|null return NULL if the two addresses have an invalid number of bits (that is, different from the one passed to the constructor of this class)
*/
public function getRanges(AddressInterface $from, AddressInterface $to)
{
if ($from->getNumberOfBits() !== $this->numBits || $to->getNumberOfBits() !== $this->numBits) {
return null;
}
if ($from->getComparableString() > $to->getComparableString()) {
list($from, $to) = array($to, $from);
}
$result = array();
$this->calculate($this->math->reduce($from->getBits()), $this->math->reduce($to->getBits()), $this->numBits, $result);
return $result;
}
/**
* Set the number of bits used to represent addresses (32 for IPv4, 128 for IPv6).
*
* @param int $numBits
*/
private function setNumBits($numBits)
{
$numBits = (int) $numBits;
$masks = array();
$unmasks = array();
for ($bit = 0; $bit < $numBits; $bit++) {
$masks[$bit] = str_repeat('1', $numBits - $bit) . str_repeat('0', $bit);
$unmasks[$bit] = $bit === 0 ? '0' : str_repeat('1', $bit);
}
$this->numBits = $numBits;
$this->masks = $masks;
$this->unmasks = $unmasks;
}
/**
* Calculate the subnets.
*
* @param string $start the start address (represented in reduced bit form)
* @param string $end the end address (represented in reduced bit form)
* @param int $position the number of bits in the mask we are comparing at this cycle
* @param \IPLib\Range\Subnet[] $result found ranges will be added to this variable
*/
private function calculate($start, $end, $position, array &$result)
{
if ($start === $end) {
$result[] = $this->subnetFromBits($start, $this->numBits);
return;
}
for ($index = $position - 1; $index >= 0; $index--) {
$startMasked = $this->math->andX($start, $this->masks[$index]);
$endMasked = $this->math->andX($end, $this->masks[$index]);
if ($startMasked !== $endMasked) {
$position = $index;
break;
}
}
if ($startMasked === $start && $this->math->andX($this->math->increment($end), $this->unmasks[$position]) === '0') {
$result[] = $this->subnetFromBits($start, $this->numBits - 1 - $position);
return;
}
$middleAddress = $this->math->orX($start, $this->unmasks[$position]);
$this->calculate($start, $middleAddress, $position, $result);
$this->calculate($this->math->increment($middleAddress), $end, $position, $result);
}
/**
* Create an address instance starting from its bits.
*
* @param string $bits the bits of the address (represented in reduced bit form)
*
* @return \IPLib\Address\AddressInterface
*/
private function addressFromBits($bits)
{
$bits = str_pad($bits, $this->numBits, '0', STR_PAD_LEFT);
$bytes = array();
foreach (explode("\n", trim(chunk_split($bits, 8, "\n"))) as $byteBits) {
$bytes[] = bindec($byteBits);
}
return Factory::addressFromBytes($bytes);
}
/**
* Create an range instance starting from the bits if the address and the length of the network prefix.
*
* @param string $bits the bits of the address (represented in reduced bit form)
* @param int $networkPrefix the length of the network prefix
*
* @return \IPLib\Range\Subnet
*/
private function subnetFromBits($bits, $networkPrefix)
{
$address = $this->addressFromBits($bits);
return new Subnet($address, $address, $networkPrefix);
}
}