New utility Number::constrainInteger() (#15663)
* New utility Number::constrainInteger() Fixes a bug with Number::unsignedAsSigned() and implements signed support as well. * cleanup * Apply fixes from StyleCI * Remove default cases --------- Co-authored-by: StyleCI Bot <bot@styleci.io>
This commit is contained in:
parent
b4a61636c1
commit
6bea8cffa6
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* IntegerType.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2023 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Enum;
|
||||
|
||||
enum IntegerType
|
||||
{
|
||||
case int8;
|
||||
case int16;
|
||||
case int32;
|
||||
case int64;
|
||||
case uint8;
|
||||
case uint16;
|
||||
case uint32;
|
||||
case uint64;
|
||||
|
||||
public function maxValue(): int
|
||||
{
|
||||
return match ($this) {
|
||||
self::int8 => 127,
|
||||
self::int16 => 32767,
|
||||
self::int32 => 2147483647,
|
||||
self::int64 => 4611686018427387903,
|
||||
self::uint8 => 255,
|
||||
self::uint16 => 65535,
|
||||
self::uint32 => 4294967295,
|
||||
self::uint64 => 9223372036854775807,
|
||||
};
|
||||
}
|
||||
|
||||
public function isSigned(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::int8,self::int16,self::int32,self::int64 => true,
|
||||
self::uint8,self::uint16,self::uint32,self::uint64 => false,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ use App\Models\PortVdsl;
|
|||
use App\Observers\ModuleModelObserver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\DB\SyncsModels;
|
||||
use LibreNMS\Enum\IntegerType;
|
||||
use LibreNMS\Interfaces\Data\DataStorageInterface;
|
||||
use LibreNMS\Interfaces\Module;
|
||||
use LibreNMS\OS;
|
||||
|
@ -139,7 +140,7 @@ class Xdsl implements Module
|
|||
if (isset($data[$oid])) {
|
||||
if ($oid == 'adslAtucCurrOutputPwr') {
|
||||
// workaround Cisco Bug CSCvj53634
|
||||
$data[$oid] = Number::unsignedAsSigned($data[$oid]);
|
||||
$data[$oid] = Number::constrainInteger($data[$oid], IntegerType::int32);
|
||||
}
|
||||
$data[$oid] = $data[$oid] / 10;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use LibreNMS\Enum\IntegerType;
|
||||
|
||||
class Number
|
||||
{
|
||||
public static function formatBase($value, $base = 1000, $round = 2, $sf = 3, $suffix = 'B')
|
||||
|
@ -176,21 +178,36 @@ class Number
|
|||
return (int) ($number * (1024 ** $exponent));
|
||||
}
|
||||
|
||||
public static function unsignedAsSigned(int $unsignedValue, int $bitLength = 32): int
|
||||
public static function constrainInteger(int $value, IntegerType $integerSize): int
|
||||
{
|
||||
// Maximum representable value for signed integer
|
||||
$maxSignedValue = pow(2, $bitLength - 1) - 1;
|
||||
if ($integerSize->isSigned()) {
|
||||
$maxSignedValue = $integerSize->maxValue();
|
||||
|
||||
// Check if the unsigned value is greater than the maximum representable unsigned value
|
||||
// If so, convert it to its two's complement representation
|
||||
if ($unsignedValue > $maxSignedValue) {
|
||||
if ($unsignedValue > 2 ** $bitLength - 1) {
|
||||
throw new \InvalidArgumentException('Unsigned value exceeds the maximum representable value of the give bit length: ' . $bitLength);
|
||||
if ($value > $maxSignedValue) {
|
||||
$signedValue = $value - $maxSignedValue * 2 - 2;
|
||||
|
||||
// if conversion was successfull, the number will still be in the valid range
|
||||
if ($signedValue > $maxSignedValue) {
|
||||
throw new \InvalidArgumentException('Unsigned value exceeds the maximum representable value of ' . $integerSize->name);
|
||||
}
|
||||
|
||||
return $signedValue;
|
||||
}
|
||||
|
||||
return $unsignedValue - ($maxSignedValue + 1) * 2;
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $unsignedValue;
|
||||
// unsigned check if value is negative
|
||||
if ($value < 0) {
|
||||
$unsignedValue = $value + $integerSize->maxValue() - 1;
|
||||
|
||||
if ($unsignedValue < 0) {
|
||||
throw new \InvalidArgumentException('Unsigned value exceeds the minimum representable value of ' . $integerSize->name);
|
||||
}
|
||||
|
||||
return $unsignedValue;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
namespace LibreNMS\Tests;
|
||||
|
||||
use LibreNMS\Device\YamlDiscovery;
|
||||
use LibreNMS\Enum\IntegerType;
|
||||
use LibreNMS\Util\Number;
|
||||
use LibreNMS\Util\Time;
|
||||
|
||||
|
@ -129,18 +130,26 @@ class FunctionsTest extends TestCase
|
|||
|
||||
public function testNumberAsUnsigned()
|
||||
{
|
||||
$this->assertSame(42, Number::unsignedAsSigned('42')); /** @phpstan-ignore-line */
|
||||
$this->assertSame(2147483647, Number::unsignedAsSigned(2147483647));
|
||||
$this->assertSame(-2147483648, Number::unsignedAsSigned(2147483648));
|
||||
$this->assertSame(-2147483647, Number::unsignedAsSigned(2147483649));
|
||||
$this->assertSame(-1, Number::unsignedAsSigned(4294967295));
|
||||
$this->assertSame(42, Number::constrainInteger('42', IntegerType::int32)); /** @phpstan-ignore-line */
|
||||
$this->assertSame(2147483647, Number::constrainInteger(2147483647, IntegerType::int32));
|
||||
$this->assertSame(-2147483648, Number::constrainInteger(2147483648, IntegerType::int32));
|
||||
$this->assertSame(-2147483647, Number::constrainInteger(2147483649, IntegerType::int32));
|
||||
$this->assertSame(-1, Number::constrainInteger(4294967295, IntegerType::int32));
|
||||
$this->assertSame(-3757, Number::constrainInteger(61779, IntegerType::int16));
|
||||
$this->assertSame(0, Number::constrainInteger(0, IntegerType::uint32));
|
||||
$this->assertSame(42, Number::constrainInteger(42, IntegerType::uint32));
|
||||
$this->assertSame(4294967252, Number::constrainInteger(-42, IntegerType::uint32));
|
||||
$this->assertSame(2147483648, Number::constrainInteger(-2147483646, IntegerType::uint32));
|
||||
$this->assertSame(2147483647, Number::constrainInteger(-2147483647, IntegerType::uint32));
|
||||
$this->assertSame(2147483646, Number::constrainInteger(-2147483648, IntegerType::uint32));
|
||||
$this->assertSame(2147483645, Number::constrainInteger(-2147483649, IntegerType::uint32));
|
||||
}
|
||||
|
||||
public function testNumberAsUnsignedValueExceedsMaxUnsignedValue()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
// Exceeds the maximum representable value for a 32-bit unsigned integer
|
||||
Number::unsignedAsSigned(4294967296, 32);
|
||||
// Exceeds the maximum representable value for a 16-bit unsigned integer
|
||||
Number::constrainInteger(4294967296, IntegerType::int16);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue