Completed
Pull Request — master (#86)
by Marc
03:04 queued 01:49
created

EnumSet::doRewindInt()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
crap 2
1
<?php
2
3
namespace MabeEnum;
4
5
use Countable;
6
use Iterator;
7
use InvalidArgumentException;
8
9
/**
10
 * This EnumSet is based on a bitset of a binary string.
11
 *
12
 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
13
 * @copyright Copyright (c) 2015 Marc Bennewitz
14
 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
15
 */
16
class EnumSet implements Iterator, Countable
17
{
18
    /**
19
     * The classname of the Enumeration
20
     * @var string
21
     */
22
    private $enumeration;
23
24
    /**
25
     * Ordinal number of current iterator position
26
     * @var int
27
     */
28
    private $ordinal = 0;
29
30
    /**
31
     * Highest possible ordinal number
32
     * @var int
33
     */
34
    private $ordinalMax;
35
36
    /**
37
     * Integer or binary (little endian) bitset used to handle attached enumerations.
38
     * @var int|string
39
     */
40
    private $bitset;
41
42
    /**#@+
43
     * Defined the private method names to be called dependend of
44
     * how the bitset type was set too.
45
     * ... Integer or binary bitset.
46
     * ... *Int or *Bin method
47
     * 
48
     * @var string
49
     */
50
    private $fnDoRewind            = 'doRewindBin';
51
    private $fnDoCount             = 'doCountBin';
52
    private $fnDoGetOrdinals       = 'doGetOrdinalsBin';
53
    private $fnDoGetBit            = 'doGetBitBin';
54
    private $fnDoSetBit            = 'doSetBitBin';
55
    private $fnDoUnsetBit          = 'doUnsetBitBin';
56
    private $fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeBin';
57
    private $fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeBin';
58
    /**#@-*/
59
60
    /**
61
     * Constructor
62
     *
63
     * @param string $enumeration The classname of the enumeration
64
     * @throws InvalidArgumentException
65
     */
66 49
    public function __construct($enumeration)
67
    {
68 49
        if (!is_subclass_of($enumeration, Enum::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \MabeEnum\Enum::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
69 1
            throw new InvalidArgumentException(sprintf(
70 1
                "%s can handle subclasses of '%s' only",
71 1
                static::class,
72
                Enum::class
73 1
            ));
74
        }
75
76 48
        $this->enumeration = $enumeration;
77 48
        $this->ordinalMax  = count($enumeration::getConstants());
78
79 48
        if ($this->ordinalMax > PHP_INT_SIZE * 8) {
80
            // init binary bitset with zeros
81 9
            $this->bitset = str_repeat("\0", ceil($this->ordinalMax / 8));
82 9
        } else {
83
            // init integer bitset
84 39
            $this->bitset                = 0;
85 39
            $this->fnDoRewind            = 'doRewindInt';
86 39
            $this->fnDoCount             = 'doCountInt';
87 39
            $this->fnDoGetOrdinals       = 'doGetOrdinalsInt';
88 39
            $this->fnDoGetBit            = 'doGetBitInt';
89 39
            $this->fnDoSetBit            = 'doSetBitInt';
90 39
            $this->fnDoUnsetBit          = 'doUnsetBitInt';
91 39
            $this->fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeInt';
92 39
            $this->fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeInt';
93
        }
94 48
    }
95
96
    /**
97
     * Get the classname of the enumeration
98
     * @return string
99
     */
100 1
    public function getEnumeration()
101
    {
102 1
        return $this->enumeration;
103
    }
104
105
    /**
106
     * Attach a new enumerator or overwrite an existing one
107
     * @param Enum|null|boolean|int|float|string $enumerator
108
     * @return void
109
     * @throws InvalidArgumentException On an invalid given enumerator
110
     */
111 34
    public function attach($enumerator)
112
    {
113 34
        $enumeration = $this->enumeration;
114 34
        $this->{$this->fnDoSetBit}($enumeration::get($enumerator)->getOrdinal());
115 34
    }
116
117
    /**
118
     * Detach the given enumerator
119
     * @param Enum|null|boolean|int|float|string $enumerator
120
     * @return void
121
     * @throws InvalidArgumentException On an invalid given enumerator
122
     */
123 6
    public function detach($enumerator)
124
    {
125 6
        $enumeration = $this->enumeration;
126 6
        $this->{$this->fnDoUnsetBit}($enumeration::get($enumerator)->getOrdinal());
127 6
    }
128
129
    /**
130
     * Test if the given enumerator was attached
131
     * @param Enum|null|boolean|int|float|string $enumerator
132
     * @return boolean
133
     */
134 9
    public function contains($enumerator)
135
    {
136 9
        $enumeration = $this->enumeration;
137 9
        return $this->{$this->fnDoGetBit}($enumeration::get($enumerator)->getOrdinal());
138
    }
139
140
    /* Iterator */
141
142
    /**
143
     * Get the current enumerator
144
     * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position
145
     */
146 9
    public function current()
147
    {
148 9
        if ($this->valid()) {
149 9
            $enumeration = $this->enumeration;
150 9
            return $enumeration::byOrdinal($this->ordinal);
151
        }
152
153 2
        return null;
154
    }
155
156
    /**
157
     * Get the ordinal number of the current iterator position
158
     * @return int
159
     */
160 6
    public function key()
161
    {
162 6
        return $this->ordinal;
163
    }
164
165
    /**
166
     * Go to the next valid iterator position.
167
     * If no valid iterator position is found the iterator position will be the last possible + 1.
168
     * @return void
169
     */
170 14
    public function next()
171
    {
172
        do {
173 14
            if (++$this->ordinal >= $this->ordinalMax) {
174 4
                $this->ordinal = $this->ordinalMax;
175 4
                return;
176
            }
177 14
        } while (!$this->{$this->fnDoGetBit}($this->ordinal));
178 14
    }
179
180
    /**
181
     * Go to the first valid iterator position.
182
     * If no valid iterator position was found the iterator position will be 0.
183
     * @return void
184
     * @see doRewindBin
185
     * @see doRewindInt
186
     */
187 8
    public function rewind()
188
    {
189 8
        $this->{$this->fnDoRewind}();
190 8
    }
191
192
    /**
193
     * Go to the first valid iterator position.
194
     * If no valid iterator position was found the iterator position will be 0.
195
     *
196
     * This is the binary bitset implementation.
197
     *
198
     * @return void
199
     * @see rewind
200
     * @see doRewindInt
201
     */
202 3
    private function doRewindBin()
203
    {
204 3
        if (trim($this->bitset, "\0") !== '') {
205 3
            $this->ordinal = -1;
206 3
            $this->next();
207 3
        } else {
208
            $this->ordinal = 0;
209
        }
210 3
    }
211
212
    /**
213
     * Go to the first valid iterator position.
214
     * If no valid iterator position was found the iterator position will be 0.
215
     *
216
     * This is the binary bitset implementation.
217
     *
218
     * @return void
219
     * @see rewind
220
     * @see doRewindBin
221
     */
222 5
    private function doRewindInt()
223
    {
224 5
        if ($this->bitset) {
225 5
            $this->ordinal = -1;
226 5
            $this->next();
227 5
        } else {
228 1
            $this->ordinal = 0;
229
        }
230 5
    }
231
232
    /**
233
     * Test if the iterator in a valid state
234
     * @return boolean
235
     */
236 10
    public function valid()
237
    {
238 10
        return $this->ordinal !== $this->ordinalMax && $this->{$this->fnDoGetBit}($this->ordinal);
239
    }
240
241
    /* Countable */
242
243
    /**
244
     * Count the number of elements
245
     *
246
     * @return int
247
     * @see doCountBin
248
     * @see doCountInt
249
     */
250 9
    public function count()
251
    {
252 9
        return $this->{$this->fnDoCount}();
253
    }
254
255
    /**
256
     * Count the number of elements.
257
     *
258
     * This is the binary bitset implementation.
259
     *
260
     * @return int
261
     * @see count
262
     * @see doCountInt
263
     */
264 4
    private function doCountBin()
265
    {
266 4
        $count = 0;
267 4
        $byteLen = strlen($this->bitset);
268 4
        for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) {
269 4
            if ($this->bitset[$bytePos] === "\0") {
270
                // fast skip null byte
271 4
                continue;
272
            }
273
274 4
            $byteOrd = ord($this->bitset[$bytePos]);
275 4
            for ($bitPos = 0; $bitPos < 8; ++$bitPos) {
276 4
                if ($byteOrd & (1 << $bitPos)) {
277 4
                    ++$count;
278 4
                }
279 4
            }
280 4
        }
281 4
        return $count;
282
    }
283
284
    /**
285
     * Count the number of elements.
286
     *
287
     * This is the integer bitset implementation.
288
     *
289
     * @return int
290
     * @see count
291
     * @see doCountBin
292
     */
293 5
    private function doCountInt()
294
    {
295 5
        $count = 0;
296 5
        $ord = 0;
297 5
        while ($ord !== $this->ordinalMax) {
298 4
            if ($this->bitset & (1 << $ord++)) {
299 4
                ++$count;
300 4
            }
301 4
        }
302 5
        return $count;
303
    }
304
305
    /**
306
     * Check if this EnumSet is the same as other
307
     * @param EnumSet $other
308
     * @return bool
309
     */
310 3
    public function isEqual(EnumSet $other)
311
    {
312 3
        return $this->enumeration === $other->enumeration
313 3
            && $this->bitset === $other->bitset;
314
    }
315
316
    /**
317
     * Check if this EnumSet is a subset of other
318
     * @param EnumSet $other
319
     * @return bool
320
     */
321 4 View Code Duplication
    public function isSubset(EnumSet $other)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
    {
323 4
        if ($this->enumeration !== $other->enumeration) {
324 1
            return false;
325
        }
326
327 3
        return ($this->bitset & $other->bitset) === $this->bitset;
328
    }
329
330
    /**
331
     * Check if this EnumSet is a superset of other
332
     * @param EnumSet $other
333
     * @return bool
334
     */
335 4 View Code Duplication
    public function isSuperset(EnumSet $other)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
    {
337 4
        if ($this->enumeration !== $other->enumeration) {
338 1
            return false;
339
        }
340
341 3
        return ($this->bitset | $other->bitset) === $this->bitset;
342
    }
343
344
    /**
345
     * Produce a new set with enumerators from both this and other (this | other)
346
     * @param EnumSet ...$other Other EnumSet(s) of the same enumeration to produce the union
347
     * @return EnumSet
348
     */
349 2 View Code Duplication
    public function union(EnumSet $other)
0 ignored issues
show
Unused Code introduced by
The parameter $other is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
350
    {
351 2
        $bitset = $this->bitset;
352 2
        foreach (func_get_args() as $other) {
353 2
            if (!$other instanceof self || $this->enumeration !== $other->enumeration) {
354 1
                throw new InvalidArgumentException(sprintf(
355 1
                    "Others should be an instance of %s of the same enumeration as this %s",
356 1
                    __CLASS__,
357 1
                    $this->enumeration
358 1
                ));
359
            }
360
361 1
            $bitset |= $other->bitset;
362 1
        }
363
364 1
        $clone = clone $this;
365 1
        $clone->bitset = $bitset;
366 1
        return $clone;
367
    }
368
369
    /**
370
     * Produce a new set with enumerators common to both this and other (this & other)
371
     * @param EnumSet ...$other Other EnumSet(s) of the same enumeration to produce the union
372
     * @return EnumSet
373
     */
374 2 View Code Duplication
    public function intersect(EnumSet $other)
0 ignored issues
show
Unused Code introduced by
The parameter $other is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
375
    {
376 2
        $bitset = $this->bitset;
377 2
        foreach (func_get_args() as $other) {
378 2
            if (!$other instanceof self || $this->enumeration !== $other->enumeration) {
379 1
                throw new InvalidArgumentException(sprintf(
380 1
                    "Others should be an instance of %s of the same enumeration as this %s",
381 1
                    __CLASS__,
382 1
                    $this->enumeration
383 1
                ));
384
            }
385
386 1
            $bitset &= $other->bitset;
387 1
        }
388
389 1
        $clone = clone $this;
390 1
        $clone->bitset = $bitset;
391 1
        return $clone;
392
    }
393
394
    /**
395
     * Produce a new set with enumerators in this but not in other (this - other)
396
     * @param EnumSet ...$other Other EnumSet(s) of the same enumeration to produce the union
397
     * @return EnumSet
398
     */
399 2 View Code Duplication
    public function diff(EnumSet $other)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
    {
401 2
        $bitset = $other->bitset;
402 2
        foreach (func_get_args() as $other) {
403 2
            if (!$other instanceof self || $this->enumeration !== $other->enumeration) {
404 1
                throw new InvalidArgumentException(sprintf(
405 1
                    "Others should be an instance of %s of the same enumeration as this %s",
406 1
                    __CLASS__,
407 1
                    $this->enumeration
408 1
                ));
409
            }
410
411 1
            $bitset |= $other->bitset;
412 1
        }
413
414 1
        $clone = clone $this;
415 1
        $clone->bitset = $this->bitset & ~$bitset;
416 1
        return $clone;
417
    }
418
419
    /**
420
     * Produce a new set with enumerators in either this and other but not in both (this ^ (other | other))
421
     * @param EnumSet ...$other Other EnumSet(s) of the same enumeration to produce the union
422
     * @return EnumSet
423
     */
424 2 View Code Duplication
    public function symDiff(EnumSet $other)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
425
    {
426 2
        $bitset = $other->bitset;
427 2
        foreach (func_get_args() as $other) {
428 2
            if (!$other instanceof self || $this->enumeration !== $other->enumeration) {
429 1
                throw new InvalidArgumentException(sprintf(
430 1
                    "Others should be an instance of %s of the same enumeration as this %s",
431 1
                    __CLASS__,
432 1
                    $this->enumeration
433 1
                ));
434
            }
435
436 1
            $bitset |= $other->bitset;
437 1
        }
438
439 1
        $clone = clone $this;
440 1
        $clone->bitset = $this->bitset ^ $bitset;
441 1
        return $clone;
442
    }
443
444
    /**
445
     * Get ordinal numbers of the defined enumerators as array
446
     * @return int[]
447
     */
448 12
    public function getOrdinals()
449
    {
450 12
        return $this->{$this->fnDoGetOrdinals}();
451
    }
452
453
    /**
454
     * Get ordinal numbers of the defined enumerators as array.
455
     *
456
     * This is the binary bitset implementation.
457
     *
458
     * @return int[]
459
     * @see getOrdinals
460
     * @see goGetOrdinalsInt
461
     */
462
    private function doGetOrdinalsBin()
463
    {
464
        $ordinals = array();
465
        $byteLen = strlen($this->bitset);
466
        for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) {
467
            if ($this->bitset[$bytePos] === "\0") {
468
                // fast skip null byte
469
                continue;
470
            }
471
472
            $byteOrd = ord($this->bitset[$bytePos]);
473
            for ($bitPos = 0; $bitPos < 8; ++$bitPos) {
474
                if ($byteOrd & (1 << $bitPos)) {
475
                    $ordinals[] = $bytePos * 8 + $bitPos;
476
                }
477
            }
478
        }
479
        return $ordinals;
480
    }
481
482
    /**
483
     * Get ordinal numbers of the defined enumerators as array.
484
     *
485
     * This is the integer bitset implementation.
486
     *
487
     * @return int[]
488
     * @see getOrdinals
489
     * @see doGetOrdinalsBin
490
     */
491 12
    private function doGetOrdinalsInt()
492
    {
493 12
        $ordinals = array();
494 12
        for ($ord = 0; $ord < $this->ordinalMax; ++$ord) {
495 12
            if ($this->bitset & (1 << $ord)) {
496 12
                $ordinals[] = $ord;
497 12
            }
498 12
        }
499 12
        return $ordinals;
500
    }
501
502
    /**
503
     * Get values of the defined enumerators as array
504
     * @return null[]|bool[]|int[]|float[]|string[]
505
     */
506 6
    public function getValues()
507
    {
508 6
        $enumeration = $this->enumeration;
509 6
        $values      = array();
510 6
        foreach ($this->getOrdinals() as $ord) {
511 6
            $values[] = $enumeration::byOrdinal($ord)->getValue();
512 6
        }
513 6
        return $values;
514
    }
515
516
    /**
517
     * Get names of the defined enumerators as array
518
     * @return string[]
519
     */
520 2
    public function getNames()
521
    {
522 2
        $enumeration = $this->enumeration;
523 2
        $names       = array();
524 2
        foreach ($this->getOrdinals() as $ord) {
525 2
            $names[] = $enumeration::byOrdinal($ord)->getName();
526 2
        }
527 2
        return $names;
528
    }
529
530
    /**
531
     * Get the defined enumerators as array
532
     * @return Enum[]
533
     */
534 2
    public function getEnumerators()
535
    {
536 2
        $enumeration = $this->enumeration;
537 2
        $enumerators = array();
538 2
        foreach ($this->getOrdinals() as $ord) {
539 2
            $enumerators[] = $enumeration::byOrdinal($ord);
540 2
        }
541 2
        return $enumerators;
542
    }
543
544
    /**
545
     * Get binary bitset in little-endian order
546
     * 
547
     * @return string
548
     */
549 3
    public function getBinaryBitsetLe()
550
    {
551 3
        return $this->{$this->fnDoGetBinaryBitsetLe}();
552
    }
553
554
    /**
555
     * Get binary bitset in little-endian order.
556
     *
557
     * This is the binary bitset implementation.
558
     *
559
     * @return string
560
     */
561 3
    private function doGetBinaryBitsetLeBin()
562
    {
563 3
        return $this->bitset;
564
    }
565
566
    /**
567
     * Get binary bitset in little-endian order.
568
     *
569
     * This is the integer bitset implementation.
570
     *
571
     * @return string
572
     */
573
    private function doGetBinaryBitsetLeInt()
574
    {
575
        $bin = pack(PHP_INT_SIZE === 8 ? 'P' : 'V', $this->bitset);
576
        return substr($bin, 0, ceil($this->ordinalMax / 8));
577
    }
578
579
    /**
580
     * Set binary bitset in little-endian order
581
     *
582
     * NOTE: It resets the current position of the iterator
583
     * 
584
     * @param string $bitset
585
     * @return void
586
     * @throws InvalidArgumentException On a non string is given as Parameter
587
     */
588 7
    public function setBinaryBitsetLe($bitset)
589
    {
590 7
        if (!is_string($bitset)) {
591 1
            throw new InvalidArgumentException('Bitset must be a string');
592
        }
593
594 6
        $this->{$this->fnDoSetBinaryBitsetLe}($bitset);
595
596
        // reset the iterator position
597 3
        $this->rewind();
598 3
    }
599
600
    /**
601
     * Set binary bitset in little-endian order
602
     *
603
     * NOTE: It resets the current position of the iterator
604
     *
605
     * @param string $bitset
606
     * @return void
607
     * @throws InvalidArgumentException On a non string is given as Parameter
608
     */
609 4
    private function doSetBinaryBitsetLeBin($bitset)
610
    {
611 4
        $size   = strlen($this->bitset);
612 4
        $sizeIn = strlen($bitset);
613
614 4
        if ($sizeIn < $size) {
615
            // add "\0" if the given bitset is not long enough
616 1
            $bitset .= str_repeat("\0", $size - $sizeIn);
617 4
        } elseif ($sizeIn > $size) {
618
            if (trim(substr($bitset, $size), "\0") !== '') {
619
                throw new InvalidArgumentException('Out-Of-Range bits detected');
620
            }
621
            $bitset = substr($bitset, 0, $size);
622
        }
623
624
        // truncate out-of-range bits of last byte
625 4
        $lastByteMaxOrd = $this->ordinalMax % 8;
626 4
        if ($lastByteMaxOrd !== 0) {
627 4
            $lastByte         = $bitset[$size - 1];
628 4
            $lastByteExpected = chr((1 << $lastByteMaxOrd) - 1) & $lastByte;
629 4
            if ($lastByte !== $lastByteExpected) {
630 1
                throw new InvalidArgumentException('Out-Of-Range bits detected');
631
            }
632
633 3
            $this->bitset = substr($bitset, 0, -1) . $lastByteExpected;
634 3
        }
635
636 3
        $this->bitset = $bitset;
637 3
    }
638
639
    /**
640
     * Set binary bitset in little-endian order
641
     *
642
     * NOTE: It resets the current position of the iterator
643
     *
644
     * @param string $bitset
645
     * @return void
646
     * @throws InvalidArgumentException On a non string is given as Parameter
647
     */
648 2
    private function doSetBinaryBitsetLeInt($bitset)
649
    {
650 2
        $len = strlen($bitset);
651 2
        $int = 0;
652 2
        for ($i = 0; $i < $len; ++$i) {
653 2
            $ord = ord($bitset[$i]);
654
655 2
            if ($ord && $i > PHP_INT_SIZE) {
656
                throw new InvalidArgumentException('Out-Of-Range bits detected');
657
            }
658
659 2
            $int |= $ord << (8 * $i);
660 2
        }
661
662 2
        if ($int & (~0 << $this->ordinalMax)) {
663 2
            throw new InvalidArgumentException('Out-Of-Range bits detected');
664
        }
665
666
        $this->bitset = $int;
667
    }
668
669
    /**
670
     * Get binary bitset in big-endian order
671
     * 
672
     * @return string
673
     */
674 1
    public function getBinaryBitsetBe()
675
    {
676 1
        return strrev($this->bitset);
677
    }
678
679
    /**
680
     * Set binary bitset in big-endian order
681
     *
682
     * NOTE: It resets the current position of the iterator
683
     * 
684
     * @param string $bitset
685
     * @return void
686
     * @throws InvalidArgumentException On a non string is given as Parameter
687
     */
688 2
    public function setBinaryBitsetBe($bitset)
689
    {
690 2
        if (!is_string($bitset)) {
691 1
            throw new InvalidArgumentException('Bitset must be a string');
692
        }
693 1
        $this->setBinaryBitsetLe(strrev($bitset));
694 1
    }
695
696
    /**
697
     * Get a bit at the given ordinal number
698
     *
699
     * @param int $ordinal Ordinal number of bit to get
700
     * @return boolean
701
     */
702
    public function getBit($ordinal)
703
    {
704
        if ($ordinal < 0 || $ordinal > $this->ordinalMax) {
705
            throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}");
706
        }
707
708
        return $this->{$this->fnDoGetBit}($ordinal);
709
    }
710
711
    /**
712
     * Get a bit at the given ordinal number.
713
     *
714
     * This is the binary bitset implementation.
715
     *
716
     * @param int $ordinal Ordinal number of bit to get
717
     * @return boolean
718
     * @see getBit
719
     * @see doGetBitInt
720
     */
721 6
    private function doGetBitBin($ordinal)
722
    {
723 6
        return (ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0;
724
    }
725
726
    /**
727
     * Get a bit at the given ordinal number.
728
     *
729
     * This is the integer bitset implementation.
730
     * 
731
     * @param int $ordinal Ordinal number of bit to get
732
     * @return boolean
733
     * @see getBit
734
     * @see doGetBitBin
735
     */
736 12
    private function doGetBitInt($ordinal)
737
    {
738 12
        return (bool)($this->bitset & (1 << $ordinal));
739
    }
740
741
    /**
742
     * Set a bit at the given ordinal number
743
     *
744
     * @param int $ordinal Ordnal number of bit to set
745
     * @param bool $bit    The bit to set
746
     * @return void
747
     * @see doSetBitBin
748
     * @see doSetBitInt
749
     * @see doUnsetBin
750
     * @see doUnsetInt
751
     */
752
    public function setBit($ordinal, $bit)
753
    {
754
        if ($ordinal < 0 || $ordinal > $this->ordinalMax) {
755
            throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}");
756
        }
757
758
        if ($bit) {
759
            $this->{$this->fnDoSetBit}($ordinal);
760
        } else {
761
            $this->{$this->fnDoUnsetBit}($ordinal);
762
        }
763
    }
764
765
    /**
766
     * Set a bit at the given ordinal number.
767
     *
768
     * This is the binary bitset implementation.
769
     * 
770
     * @param int $ordinal Ordnal number of bit to set
771
     * @return void
772
     * @see setBit
773
     * @see doSetBitInt
774
     */
775 3
    private function doSetBitBin($ordinal)
776
    {
777 3
        $byte = (int) ($ordinal / 8);
778 3
        $this->bitset[$byte] = $this->bitset[$byte] | chr(1 << ($ordinal % 8));
779 3
    }
780
781
    /**
782
     * Set a bit at the given ordinal number.
783
     *
784
     * This is the binary bitset implementation.
785
     *
786
     * @param int $ordinal Ordnal number of bit to set
787
     * @return void
788
     * @see setBit
789
     * @see doSetBitBin
790
     */
791 31
    private function doSetBitInt($ordinal)
792
    {
793 31
        $this->bitset = $this->bitset | (1 << $ordinal);
794 31
    }
795
796
    /**
797
     * Unset a bit at the given ordinal number.
798
     *
799
     * This is the binary bitset implementation.
800
     *
801
     * @param int $ordinal Ordinal number of bit to unset
802
     * @return void
803
     * @see setBit
804
     * @see doUnsetBitInt
805
     */
806 2
    private function doUnsetBitBin($ordinal)
807
    {
808 2
        $byte = (int) ($ordinal / 8);
809 2
        $this->bitset[$byte] = $this->bitset[$byte] & chr(~(1 << ($ordinal % 8)));
810 2
    }
811
812
    /**
813
     * Unset a bit at the given ordinal number.
814
     *
815
     * This is the integer bitset implementation.
816
     *
817
     * @param int $ordinal Ordinal number of bit to unset
818
     * @return void
819
     * @see setBit
820
     * @see doUnsetBitBin
821
     */
822 4
    private function doUnsetBitInt($ordinal)
823
    {
824 4
        $this->bitset = $this->bitset & ~(1 << $ordinal);
825 4
    }
826
}
827