Skip to content

Commit

Permalink
V2 (#27)
Browse files Browse the repository at this point in the history
* V2

* Refactored, removed indexed and associative

* Fixed set mask
  • Loading branch information
yaroslavche authored Oct 30, 2022
1 parent f405275 commit 248ca2a
Show file tree
Hide file tree
Showing 32 changed files with 397 additions and 918 deletions.
16 changes: 3 additions & 13 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest]
php-version: ['7.4', '8.0', '8.1', '8.2']
php-version: ['8.1', '8.2']
name: PHP ${{ matrix.php-version }}
steps:
- name: Checkout
Expand All @@ -29,17 +29,7 @@ jobs:
- name: Install package
run: |
composer install
- name: Phpstan rules for PHP ${{ matrix.php-version }}
if: ${{ matrix.php-version == '7.4' || matrix.php-version == '8.0' }}
run: cp phpstan_below_81.neon phpstan.neon
- name: Main checks
- name: CI pack
run: |
composer phpcs
composer phpstan
composer phpunit
- name: Infection and coverage
if: ${{ matrix.php-version == '8.1' || matrix.php-version == '8.2' }}
run: |
composer infection
composer coverage
composer ci:pack
bash <(curl -s https://codecov.io/bash)
78 changes: 19 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,30 @@ define('READ', 1 << 0);
define('WRITE', 1 << 1);
define('EXECUTE', 1 << 2);
$mask = READ | WRITE | EXECUTE;
// read: 1 write: 2 execute: 4 mask: 7
echo sprintf('read: %d write: %d execute: %d mask: %d', READ, WRITE, EXECUTE, $mask);
// read: 1 write: 2 execute: 4 mask: 7
if ($mask & READ) {
// $mask have a READ
}
```

But you can try other way with this package:

```php
use BitMask\BitMask;
use BitMask\Util\Bits;

$bitmask = new BitMask();
$bitmask = new BitMask(0, 2); // no bits set, but only three bits allowed: 1, 2, 4
$bitmask->set(0b111); // 7, 1 << 0 | 1 << 1 | 1 << 2

// get value and check if single bit or mask is set
$integerMask = $bitmask->get(); // int 7
$boolIsSetBit = $bitmask->isSetBit(4); // bool true
$boolIsSetBit = $bitmask->isSetBitByShiftOffset(2); // true
$boolIsSetMask = $bitmask->isSet(6); // bool true
$boolIsSetBit = $bitmask->isSetBits(1, 2, 4); // bool true, variadic arguments
$boolIsSetBit = $bitmask->isSetBitByShiftOffset(2); // bool true, for single MSB
$boolIsSetMask = $bitmask->isSet(6); // bool true, single mask

// get some info about bits
$integerMostSignificantBit = Bits::getMostSignificantBit($bitmask->get()); // int 3
$integerMostSignificantBit = Bits::getMostSignificantBit($bitmask->get()); // int 7
$arraySetBits = Bits::getSetBits($bitmask->get()); // array:3 [1, 2, 4]
$arraySetBitsIndexes = Bits::getSetBitsIndexes($bitmask->get()); // array:3 [0, 1, 2]
$string = Bits::toString($bitmask->get()); // string "111"
Expand All @@ -46,49 +47,15 @@ $integerIndex = Bits::bitToIndex(65536); // int 16
$boolIsSingleBit = Bits::isSingleBit(8); // true

// change mask
$bitmask->unsetBit(4);
$bitmask->unsetBitByShiftOffset(2);
$bitmask->setBit(8);
$bitmask->unsetBits(4); // or $bitmask->unsetBitByShiftOffset(2);
Bits::getSetBits($bitmask->get()); // array:3 [1, 2]

Bits::getSetBits($bitmask->get()); // array:3 [1, 2, 8]
$bitmask->setBits(0b1000); // throws OutOfRangeException
```

Some examples can be found in [BitMaskInterface](/src/BitMaskInterface.php) and in [tests](/tests)

Exists `IndexedBitMask` and `AssociativeBitMask` helper classes:
```php
use BitMask\IndexedBitMask;
use BitMask\AssociativeBitMask;

// Indexed are extended BitMask with one extra method: getByIndex
// For instance, mask 0b110 would have following "index:value": 0:false, 1:true, 2:true
// Indexes are RTL, starts from 0. Equals to mask left shift offset.
$indexed = new IndexedBitMask(1 << 1 | 1 << 2); // 0b110
$indexed->getByIndex(2); // true
$indexed->getByIndex(0); // false

// Associative are extended Indexed. In addition to the mask you must also specify the number of bits and the array of key strings.
// Each key will have a bitmask property with the same name and a method named 'is{Key}'.
$bitmask = new AssociativeBitMask(5, 3, ['readable', 'writable', 'executable']); //
$bitmask->getByKey('readable'); // bool(true)
/** __call */
$boolReadable = $bitmask->isReadable(); // bool(true)
$boolWritable = $bitmask->isWritable(); // bool(true)
$boolExecutable = $bitmask->isExecutable(); // bool(true)
$result = $bitmask->isUnknownKey(); // BitMask\Exception\UnknownKeyException
/** __get */
$boolReadable = $bitmask->readable; // bool true
$boolWritable = $bitmask->writable; // bool false
$boolExecutable = $bitmask->executable; // bool true
$result = $bitmask->unknownKey; // BitMask\Exception\UnknownKeyException
/** __set */
$bitmask->readable = false;
$bitmask->writable = true;
$bitmask->executable = false;
$bitmask->unknownKey = true; // BitMask\Exception\UnknownKeyException
```

With PHP ^8.1 `EnumBitMask` can be used:
`EnumBitMask` is extended BitMask

```php
enum Permissions {
Expand All @@ -99,20 +66,13 @@ enum Permissions {

use BitMask\EnumBitMask;

// First argument is required and expects FQCN of the enum
// Second argument is a variadic UnitEnum, and acts like setter of the bits
$bitmask = new EnumBitMask(Permissions::class, Permissions::Read, Permissions::Execute);
// previous statement is equals to following lines:
// $bitmask = new EnumBitMask(Permissions::class);
// $bitmask->set(Permissions::Read, Permissions::Execute); // set, isSet and unset also have variadic args

$bitmask->isSet(Permissions::Read); // true
$bitmask->isSet(Permissions::Write); // false
$bitmask->isSet(Permissions::Execute); // true
$bitmask->set(Permissions::Write);
$bitmask->isSet(Permissions::Write, Permissions::Read); // true
$bitmask->unset(Permissions::Write);
$bitmask->isSet(Permissions::Write); // false
$bitmask = new EnumBitMask(Permissions::class);
$bitmask->setEnumBits(Permissions::Read, Permissions::Execute);
$bitmask->isSetEnumBits(Permissions::Read); // true
$bitmask->isSetEnumBits(Permissions::Write); // false
$bitmask->setEnumBits(Permissions::Write);
$bitmask->isSetEnumBits(Permissions::Read, Permissions::Write, Permissions::Execute); // true
$bitmask->unsetEnumBits(Permissions::Write);
```

## Installing
Expand Down Expand Up @@ -146,7 +106,7 @@ $ ./vendor/bin/phpbench run benchmarks --report=default
##### PHPStan
```bash
$ composer phpstan
$ ./vendor/bin/phpstan analyse src/ -c phpstan.neon --level=8 --no-progress -vvv --memory-limit=1024M
$ ./vendor/bin/phpstan analyse src/ -c phpstan.neon --level=9 --no-progress -vvv --memory-limit=1024M
```
##### PHP-CS
###### Code style check
Expand Down
18 changes: 8 additions & 10 deletions benchmarks/EvenOddBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Yaroslavche\Benchmarks;

use Generator;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use PhpBench\Attributes\ParamProviders;
use PhpBench\Attributes\Revs;

class EvenOddBench
{
Expand All @@ -18,20 +18,18 @@ public function numberProvider(): Generator
yield [PHP_INT_MAX];
}

/**
* @Revs(1000000)
* @ParamProviders({"numberProvider"})
*/
/** @param int[] $number */
#[Revs(1000000)]
#[ParamProviders('numberProvider')]
public function benchEvenOdd1(array $number): void
{
$this->isEven1($number[0]);
$this->isOdd1($number[0]);
}

/**
* @Revs(1000000)
* @ParamProviders({"numberProvider"})
*/
/** @param int[] $number */
#[Revs(1000000)]
#[ParamProviders('numberProvider')]
public function benchEvenOdd2(array $number): void
{
$this->isEven2($number[0]);
Expand Down
26 changes: 12 additions & 14 deletions benchmarks/GetSetBitsIndexBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

use BitMask\Util\Bits as BitUtils;
use Generator;
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\ParamProviders;
use PhpBench\Attributes\Revs;

class GetSetBitsIndexBench
{
Expand All @@ -19,21 +19,19 @@ public function maskProvider(): Generator
yield [1 << 32];
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"maskProvider"})
*/
/** @param int[] $mask */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('maskProvider')]
public function benchGetSetBitsIndex1(array $mask): void
{
$this->getSetBitsIndex1($mask[0]);
}

/**
* @Revs(10000)
* @Iterations(5)
* @ParamProviders({"maskProvider"})
*/
/** @param int[] $mask */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('maskProvider')]
public function benchGetSetBitsIndex2(array $mask): void
{
$this->getSetBitsIndex2($mask[0]);
Expand All @@ -56,7 +54,7 @@ private function getSetBitsIndex2(int $mask): array
{
$bitIndexes = [];
foreach (BitUtils::getSetBits($mask) as $index => $bit) {
$bitIndexes[$index] = (int)log($bit, 2);
$bitIndexes[$index] = BitUtils::getMostSignificantBit($bit);
}
return $bitIndexes;
}
Expand Down
24 changes: 11 additions & 13 deletions benchmarks/IndexToBitBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Yaroslavche\Benchmarks;

use Generator;
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\ParamProviders;
use PhpBench\Attributes\Revs;

class IndexToBitBench
{
Expand All @@ -18,21 +18,19 @@ public function indexProvider(): Generator
yield [10000];
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"indexProvider"})
*/
/** @param int[] $index */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('indexProvider')]
public function benchIndexToBit1(array $index): void
{
$this->indexToBit1($index[0]);
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"indexProvider"})
*/
/** @param int[] $index */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('indexProvider')]
public function benchIndexToBit2(array $index): void
{
$this->indexToBit2($index[0]);
Expand Down
39 changes: 16 additions & 23 deletions benchmarks/IsSingleBitBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

use BitMask\Util\Bits;
use Generator;
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\ParamProviders;
use PhpBench\Attributes\Revs;

class IsSingleBitBench
{
Expand All @@ -20,31 +20,28 @@ public function maskProvider(): Generator
yield [1 << 32];
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"maskProvider"})
*/
/** @param int[] $mask */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('maskProvider')]
public function benchIsSingleBit1(array $mask): void
{
$this->isSingleBit1($mask[0]);
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"maskProvider"})
*/
/** @param int[] $mask */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('maskProvider')]
public function benchIsSingleBit2(array $mask): void
{
$this->isSingleBit2($mask[0]);
}

/**
* @Revs(100000)
* @Iterations(5)
* @ParamProviders({"maskProvider"})
*/
/** @param int[] $mask */
#[Revs(100000)]
#[Iterations(5)]
#[ParamProviders('maskProvider')]
public function benchIsSingleBit3(array $mask): void
{
$this->isSingleBit3($mask[0]);
Expand All @@ -62,10 +59,6 @@ private function isSingleBit2(int $mask): bool

private function isSingleBit3(int $mask): bool
{
$shift = Bits::getMostSignificantBit($mask) - 1;
if ($shift < 0) {
return false;
}
return 1 << $shift === $mask;
return 1 << Bits::getMostSignificantBit($mask) === $mask;
}
}
Loading

0 comments on commit 248ca2a

Please sign in to comment.