5
5
namespace MabeEnum ;
6
6
7
7
use Countable ;
8
- use Iterator ;
9
8
use InvalidArgumentException ;
9
+ use Iterator ;
10
+ use IteratorAggregate ;
11
+ use OutOfBoundsException ;
10
12
11
13
/**
12
14
* A set of enumerators of the given enumeration (EnumSet<T>)
16
18
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
17
19
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
18
20
*/
19
- class EnumSet implements Iterator , Countable
21
+ class EnumSet implements IteratorAggregate , Countable
20
22
{
21
23
/**
22
24
* The classname of the Enumeration
@@ -25,16 +27,10 @@ class EnumSet implements Iterator, Countable
25
27
private $ enumeration ;
26
28
27
29
/**
28
- * Ordinal number of current iterator position
30
+ * Number of enumerators defined in the enumeration
29
31
* @var int
30
32
*/
31
- private $ ordinal = 0 ;
32
-
33
- /**
34
- * Highest possible ordinal number
35
- * @var int
36
- */
37
- private $ ordinalMax ;
33
+ private $ enumerationCount ;
38
34
39
35
/**
40
36
* Integer or binary (little endian) bitset
@@ -49,7 +45,7 @@ class EnumSet implements Iterator, Countable
49
45
*
50
46
* @var string
51
47
*/
52
- private $ fnDoRewind = 'doRewindInt ' ;
48
+ private $ fnDoGetIterator = 'doGetIteratorInt ' ;
53
49
private $ fnDoCount = 'doCountInt ' ;
54
50
private $ fnDoGetOrdinals = 'doGetOrdinalsInt ' ;
55
51
private $ fnDoGetBit = 'doGetBitInt ' ;
@@ -75,18 +71,18 @@ public function __construct(string $enumeration)
75
71
));
76
72
}
77
73
78
- $ this ->enumeration = $ enumeration ;
79
- $ this ->ordinalMax = \count ($ enumeration ::getConstants ());
74
+ $ this ->enumeration = $ enumeration ;
75
+ $ this ->enumerationCount = \count ($ enumeration ::getConstants ());
80
76
81
77
// By default the bitset is initialized as integer bitset
82
78
// in case the enumeraton has more enumerators then integer bits
83
79
// we will switch this into a binary bitset
84
- if ($ this ->ordinalMax > \PHP_INT_SIZE * 8 ) {
80
+ if ($ this ->enumerationCount > \PHP_INT_SIZE * 8 ) {
85
81
// init binary bitset with zeros
86
- $ this ->bitset = \str_repeat ("\0" , (int )\ceil ($ this ->ordinalMax / 8 ));
82
+ $ this ->bitset = \str_repeat ("\0" , (int )\ceil ($ this ->enumerationCount / 8 ));
87
83
88
84
// switch internal binary bitset functions
89
- $ this ->fnDoRewind = 'doRewindBin ' ;
85
+ $ this ->fnDoGetIterator = 'doGetIteratorBin ' ;
90
86
$ this ->fnDoCount = 'doCountBin ' ;
91
87
$ this ->fnDoGetOrdinals = 'doGetOrdinalsBin ' ;
92
88
$ this ->fnDoGetBit = 'doGetBitBin ' ;
@@ -138,106 +134,79 @@ public function contains($enumerator): bool
138
134
return $ this ->{$ this ->fnDoGetBit }(($ this ->enumeration )::get ($ enumerator )->getOrdinal ());
139
135
}
140
136
141
- /* Iterator */
142
-
143
- /**
144
- * Get the current enumerator
145
- * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position
146
- */
147
- public function current (): ?Enum
148
- {
149
- if ($ this ->valid ()) {
150
- return ($ this ->enumeration )::byOrdinal ($ this ->ordinal );
151
- }
152
-
153
- return null ;
154
- }
137
+ /* IteratorAggregate */
155
138
156
139
/**
157
- * Get the ordinal number of the current iterator position
158
- * @return int
140
+ * Get a new iterator
141
+ * @return Iterator
142
+ * @uses doGetIteratorInt()
143
+ * @uses doGetIteratorBin()
159
144
*/
160
- public function key (): int
145
+ public function getIterator (): Iterator
161
146
{
162
- return $ this ->ordinal ;
163
- }
147
+ return $ this ->{$ this ->fnDoGetIterator }();
164
148
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
- public function next (): void
171
- {
172
- do {
173
- if (++$ this ->ordinal >= $ this ->ordinalMax ) {
174
- $ this ->ordinal = $ this ->ordinalMax ;
175
- return ;
149
+ /*
150
+ $ordinal = 0;
151
+ $enumerationCount = $this->enumerationCount;
152
+ $fnDoGetBit = $this->fnDoGetBit;
153
+ for (; $ordinal < $enumerationCount; ++$ordinal) {
154
+ if ($this->{$fnDoGetBit}($ordinal)) {
155
+ yield $ordinal => ($this->enumeration)::byOrdinal($ordinal);
176
156
}
177
- } while (!$ this ->{$ this ->fnDoGetBit }($ this ->ordinal ));
178
- }
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
- * @uses doRewindBin()
185
- * @uses doRewindInt()
186
- */
187
- public function rewind (): void
188
- {
189
- $ this ->{$ this ->fnDoRewind }();
157
+ }
158
+ */
190
159
}
191
160
192
161
/**
193
- * Go to the first valid iterator position.
194
- * If no valid iterator position was found the iterator position will be 0.
162
+ * Get a new Iterator.
195
163
*
196
164
* This is the binary bitset implementation.
197
165
*
198
- * @return void
199
- * @see rewind ()
200
- * @see doRewindInt ()
166
+ * @return Iterator
167
+ * @see getIterator ()
168
+ * @see goGetIteratorInt ()
201
169
*/
202
- private function doRewindBin (): void
170
+ private function doGetIteratorBin (): Iterator
203
171
{
204
- if (\ltrim ($ this ->bitset , "\0" ) !== '' ) {
205
- $ this ->ordinal = -1 ;
206
- $ this ->next ();
207
- } else {
208
- $ this ->ordinal = 0 ;
172
+ $ bitset = $ this ->bitset ;
173
+ $ byteLen = \strlen ($ bitset );
174
+ for ($ bytePos = 0 ; $ bytePos < $ byteLen ; ++$ bytePos ) {
175
+ if ($ bitset [$ bytePos ] === "\0" ) {
176
+ // fast skip null byte
177
+ continue ;
178
+ }
179
+
180
+ $ ord = \ord ($ bitset [$ bytePos ]);
181
+ for ($ bitPos = 0 ; $ bitPos < 8 ; ++$ bitPos ) {
182
+ if ($ ord & (1 << $ bitPos )) {
183
+ $ ordinal = $ bytePos * 8 + $ bitPos ;
184
+ yield $ ordinal => ($ this ->enumeration )::byOrdinal ($ ordinal );
185
+ }
186
+ }
209
187
}
210
188
}
211
189
212
190
/**
213
- * Go to the first valid iterator position.
214
- * If no valid iterator position was found the iterator position will be 0.
191
+ * Get a new Iterator.
215
192
*
216
- * This is the binary bitset implementation.
193
+ * This is the integer bitset implementation.
217
194
*
218
- * @return void
219
- * @see rewind ()
220
- * @see doRewindBin ()
195
+ * @return Iterator
196
+ * @see getIterator ()
197
+ * @see doGetIteratorBin ()
221
198
*/
222
- private function doRewindInt (): void
199
+ private function doGetIteratorInt (): Iterator
223
200
{
224
- if ($ this ->bitset ) {
225
- $ this ->ordinal = -1 ;
226
- $ this ->next ();
227
- } else {
228
- $ this ->ordinal = 0 ;
201
+ $ count = $ this ->enumerationCount ;
202
+ $ bitset = $ this ->bitset ;
203
+ for ($ ordinal = 0 ; $ ordinal < $ count ; ++$ ordinal ) {
204
+ if ($ bitset & (1 << $ ordinal )) {
205
+ yield $ ordinal => ($ this ->enumeration )::byOrdinal ($ ordinal );
206
+ }
229
207
}
230
208
}
231
209
232
- /**
233
- * Test if the iterator is in a valid state
234
- * @return bool
235
- */
236
- public function valid (): bool
237
- {
238
- return $ this ->ordinal !== $ this ->ordinalMax && $ this ->{$ this ->fnDoGetBit }($ this ->ordinal );
239
- }
240
-
241
210
/* Countable */
242
211
243
212
/**
@@ -499,12 +468,12 @@ private function doGetOrdinalsBin(): array
499
468
*/
500
469
private function doGetOrdinalsInt (): array
501
470
{
502
- $ ordinals = [];
503
- $ ordinalMax = $ this ->ordinalMax ;
504
- $ bitset = $ this ->bitset ;
505
- for ($ ord = 0 ; $ ord < $ ordinalMax ; ++$ ord ) {
506
- if ($ bitset & (1 << $ ord )) {
507
- $ ordinals [] = $ ord ;
471
+ $ ordinals = [];
472
+ $ count = $ this ->enumerationCount ;
473
+ $ bitset = $ this ->bitset ;
474
+ for ($ ordinal = 0 ; $ ordinal < $ count ; ++$ ordinal ) {
475
+ if ($ bitset & (1 << $ ordinal )) {
476
+ $ ordinals [] = $ ordinal ;
508
477
}
509
478
}
510
479
return $ ordinals ;
@@ -590,7 +559,7 @@ private function doGetBinaryBitsetLeBin(): string
590
559
private function doGetBinaryBitsetLeInt (): string
591
560
{
592
561
$ bin = \pack (\PHP_INT_SIZE === 8 ? 'P ' : 'V ' , $ this ->bitset );
593
- return \substr ($ bin , 0 , (int )\ceil ($ this ->ordinalMax / 8 ));
562
+ return \substr ($ bin , 0 , (int )\ceil ($ this ->enumerationCount / 8 ));
594
563
}
595
564
596
565
/**
@@ -607,9 +576,6 @@ private function doGetBinaryBitsetLeInt(): string
607
576
public function setBinaryBitsetLe (string $ bitset ): void
608
577
{
609
578
$ this ->{$ this ->fnDoSetBinaryBitsetLe }($ bitset );
610
-
611
- // reset the iterator position
612
- $ this ->rewind ();
613
579
}
614
580
615
581
/**
@@ -639,7 +605,7 @@ private function doSetBinaryBitsetLeBin(string $bitset): void
639
605
}
640
606
641
607
// truncate out-of-range bits of last byte
642
- $ lastByteMaxOrd = $ this ->ordinalMax % 8 ;
608
+ $ lastByteMaxOrd = $ this ->enumerationCount % 8 ;
643
609
if ($ lastByteMaxOrd !== 0 ) {
644
610
$ lastByte = $ bitset [-1 ];
645
611
$ lastByteExpected = \chr ((1 << $ lastByteMaxOrd ) - 1 ) & $ lastByte ;
@@ -678,7 +644,7 @@ private function doSetBinaryBitsetLeInt(string $bitset): void
678
644
$ int |= $ ord << (8 * $ i );
679
645
}
680
646
681
- if ($ int & (~0 << $ this ->ordinalMax )) {
647
+ if ($ int & (~0 << $ this ->enumerationCount )) {
682
648
throw new InvalidArgumentException ('out-of-range bits detected ' );
683
649
}
684
650
@@ -720,8 +686,8 @@ public function setBinaryBitsetBe(string $bitset): void
720
686
*/
721
687
public function getBit (int $ ordinal ): bool
722
688
{
723
- if ($ ordinal < 0 || $ ordinal > $ this ->ordinalMax ) {
724
- throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->ordinalMax }" );
689
+ if ($ ordinal < 0 || $ ordinal > $ this ->enumerationCount ) {
690
+ throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->enumerationCount }" );
725
691
}
726
692
727
693
return $ this ->{$ this ->fnDoGetBit }($ ordinal );
@@ -771,8 +737,8 @@ private function doGetBitInt(int $ordinal): bool
771
737
*/
772
738
public function setBit (int $ ordinal , bool $ bit ): void
773
739
{
774
- if ($ ordinal < 0 || $ ordinal > $ this ->ordinalMax ) {
775
- throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->ordinalMax }" );
740
+ if ($ ordinal < 0 || $ ordinal > $ this ->enumerationCount ) {
741
+ throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->enumerationCount }" );
776
742
}
777
743
778
744
if ($ bit ) {
0 commit comments