5
5
use Iterator ;
6
6
use Countable ;
7
7
use InvalidArgumentException ;
8
- use OutOfRangeException ;
9
8
10
9
/**
11
10
* EnumSet implementation in base of an integer bit set
@@ -23,10 +22,10 @@ class EnumSet implements Iterator, Countable
23
22
private $ enumeration ;
24
23
25
24
/**
26
- * BitSet of all attached enumerations
27
- * @var int
25
+ * BitSet of all attached enumerations in little endian
26
+ * @var string
28
27
*/
29
- private $ bitset = 0 ;
28
+ private $ bitset ;
30
29
31
30
/**
32
31
* Ordinal number of current iterator position
@@ -54,19 +53,12 @@ public function __construct($enumeration)
54
53
__NAMESPACE__ . '\Enum '
55
54
));
56
55
}
57
-
56
+
58
57
$ this ->enumeration = $ enumeration ;
59
58
$ this ->ordinalMax = count ($ enumeration ::getConstants ());
60
-
61
- if (PHP_INT_SIZE * 8 < $ this ->ordinalMax ) {
62
- throw new OutOfRangeException (sprintf (
63
- "Your system can handle up to %u enumerators within an EnumSet "
64
- . " but the given enumeration '%s' has defined %u enumerators " ,
65
- PHP_INT_SIZE * 8 ,
66
- $ enumeration ,
67
- $ this ->ordinalMax
68
- ));
69
- }
59
+
60
+ // init the bitset with zeros
61
+ $ this ->bitset = str_repeat ("\0" , ceil ($ this ->ordinalMax / 8 ));
70
62
}
71
63
72
64
/**
@@ -97,7 +89,7 @@ public function getEnumeration()
97
89
public function attach ($ enumerator )
98
90
{
99
91
$ enumeration = $ this ->enumeration ;
100
- $ this ->bitset |= 1 << $ enumeration ::get ($ enumerator )->getOrdinal ();
92
+ $ this ->setBit ( $ enumeration ::get ($ enumerator )->getOrdinal () );
101
93
}
102
94
103
95
/**
@@ -109,7 +101,7 @@ public function attach($enumerator)
109
101
public function detach ($ enumerator )
110
102
{
111
103
$ enumeration = $ this ->enumeration ;
112
- $ this ->bitset &= ~( 1 << $ enumeration ::get ($ enumerator )->getOrdinal ());
104
+ $ this ->unsetBit ( $ enumeration ::get ($ enumerator )->getOrdinal ());
113
105
}
114
106
115
107
/**
@@ -120,7 +112,7 @@ public function detach($enumerator)
120
112
public function contains ($ enumerator )
121
113
{
122
114
$ enumeration = $ this ->enumeration ;
123
- return ( bool )( $ this ->bitset & ( 1 << $ enumeration ::get ($ enumerator )->getOrdinal () ));
115
+ return $ this ->getBit ( $ enumeration ::get ($ enumerator )->getOrdinal ());
124
116
}
125
117
126
118
/* Iterator */
@@ -156,11 +148,11 @@ public function key()
156
148
public function next ()
157
149
{
158
150
do {
159
- if (++$ this ->ordinal >= $ this ->ordinalMax ) {
151
+ if (++ $ this ->ordinal >= $ this ->ordinalMax ) {
160
152
$ this ->ordinal = $ this ->ordinalMax ;
161
153
return ;
162
154
}
163
- } while (!( $ this ->bitset & ( 1 << $ this ->ordinal ) ));
155
+ } while (!$ this ->getBit ( $ this ->ordinal ));
164
156
}
165
157
166
158
/**
@@ -170,7 +162,7 @@ public function next()
170
162
*/
171
163
public function rewind ()
172
164
{
173
- if ($ this ->bitset ) {
165
+ if (trim ( $ this ->bitset , "\0" ) !== '' ) {
174
166
$ this ->ordinal = -1 ;
175
167
$ this ->next ();
176
168
} else {
@@ -184,7 +176,7 @@ public function rewind()
184
176
*/
185
177
public function valid ()
186
178
{
187
- return $ this ->bitset & ( 1 << $ this ->ordinal ) && $ this ->ordinal !== $ this ->ordinalMax ;
179
+ return $ this ->ordinal !== $ this ->ordinalMax && $ this ->getBit ( $ this ->ordinal ) ;
188
180
}
189
181
190
182
/* Countable */
@@ -198,11 +190,88 @@ public function count()
198
190
$ cnt = 0 ;
199
191
$ ord = 0 ;
200
192
do {
201
- if ($ this ->bitset & ( 1 << $ ord ++)) {
193
+ if ($ this ->getBit ( $ ord ++)) {
202
194
++$ cnt ;
203
195
}
204
196
} while ($ ord !== $ this ->ordinalMax );
205
197
206
198
return $ cnt ;
207
199
}
200
+
201
+ /**
202
+ * Get the binary bitset
203
+ *
204
+ * @return string Returns the binary bitset in big-endian order
205
+ */
206
+ public function getBitset ()
207
+ {
208
+ return strrev ($ this ->bitset );
209
+ }
210
+
211
+ /**
212
+ * Set the bitset.
213
+ * NOTE: It resets the current position of the iterator
214
+ *
215
+ * @param string $bitset The binary bitset in big-endian order
216
+ * @return void
217
+ * @throws InvalidArgumentException On a non string is given as Parameter
218
+ */
219
+ public function setBitset ($ bitset )
220
+ {
221
+ if (! is_string ($ bitset )) {
222
+ throw new InvalidArgumentException ("bitset must be a string " );
223
+ }
224
+
225
+ $ bitset = strrev ($ bitset );
226
+ $ size = ceil ($ this ->ordinalMax / 8 );
227
+ $ sizeIn = strlen ($ bitset );
228
+
229
+ if ($ sizeIn < $ size ) {
230
+ // add "\0" if the given bitset is not long enough
231
+ $ bitset .= str_repeat ("\0" , $ size - $ sizeIn );
232
+ } elseif ($ sizeIn > $ size ) {
233
+ $ bitset = substr ($ bitset , 0 , $ size );
234
+ }
235
+
236
+ $ this ->bitset = $ bitset ;
237
+
238
+ $ this ->rewind ();
239
+ }
240
+
241
+ /**
242
+ * get a bit at the given ordinal
243
+ *
244
+ * @param $ordinal int Number of bit to get
245
+ * @return boolean
246
+ */
247
+ private function getBit ($ ordinal )
248
+ {
249
+ return (ord ($ this ->bitset [(int ) ($ ordinal / 8 )]) & 1 << ($ ordinal % 8 )) !== 0 ;
250
+ }
251
+
252
+ /**
253
+ * set a bit at the given ordinal
254
+ *
255
+ * @param $ordinal int
256
+ * number of bit to manipulate
257
+ * @return void
258
+ */
259
+ private function setBit ($ ordinal )
260
+ {
261
+ $ byte = (int ) ($ ordinal / 8 );
262
+ $ this ->bitset [$ byte ] = $ this ->bitset [$ byte ] | chr (1 << ($ ordinal % 8 ));
263
+ }
264
+
265
+ /**
266
+ * reset a bit at the given ordinal
267
+ *
268
+ * @param $ordinal int
269
+ * number of bit to set to false
270
+ * @return void
271
+ */
272
+ private function unsetBit ($ ordinal )
273
+ {
274
+ $ byte = (int ) ($ ordinal / 8 );
275
+ $ this ->bitset [$ byte ] = $ this ->bitset [$ byte ] & chr (~ (1 << ($ ordinal % 8 )));
276
+ }
208
277
}
0 commit comments