1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* chdemko\BitArray\BitArray class |
5
|
|
|
* |
6
|
|
|
* @author Christophe Demko <[email protected]> |
7
|
|
|
* @copyright Copyright (C) 2012-2016 Christophe Demko. All rights reserved. |
8
|
|
|
* |
9
|
|
|
* @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html The CeCILL B license |
10
|
|
|
* |
11
|
|
|
* This file is part of the php-bitarray package https://github.com/chdemko/php-bitarray |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
// Declare chdemko\BitArray namespace |
15
|
|
|
namespace chdemko\BitArray; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Array of bits |
19
|
|
|
* |
20
|
|
|
* @package BitArray |
21
|
|
|
* |
22
|
|
|
* @property-read integer $count The number of bits set to true |
23
|
|
|
* @property-read integer $size The number of bits |
24
|
|
|
* |
25
|
|
|
* @since 1.0.0 |
26
|
|
|
*/ |
27
|
|
|
class BitArray implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* @var integer[] Number of bits for each value between 0 and 255 |
31
|
|
|
*/ |
32
|
|
|
private static $count = [ |
33
|
|
|
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, |
34
|
|
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, |
35
|
|
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, |
36
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
37
|
|
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, |
38
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
39
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
40
|
|
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, |
41
|
|
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, |
42
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
43
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
44
|
|
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, |
45
|
|
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
46
|
|
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, |
47
|
|
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, |
48
|
|
|
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 |
49
|
|
|
]; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var integer[] Mask for restricting complements |
53
|
|
|
*/ |
54
|
|
|
private static $restrict = [255, 1, 3, 7, 15, 31, 63, 127]; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var string Underlying data |
58
|
|
|
* |
59
|
|
|
* @since 1.0.0 |
60
|
|
|
*/ |
61
|
|
|
private $data; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var integer Size of the bit array |
65
|
|
|
* |
66
|
|
|
* @since 1.0.0 |
67
|
|
|
*/ |
68
|
|
|
private $size; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Create a new bit array of the given size |
72
|
|
|
* |
73
|
|
|
* @param integer $size The BitArray size |
74
|
|
|
* |
75
|
|
|
* @since 1.0.0 |
76
|
|
|
*/ |
77
|
|
|
protected function __construct($size) |
78
|
|
|
{ |
79
|
|
|
$this->size = (int) $size; |
80
|
|
|
$this->data = str_repeat("\0", ceil($this->size / 8)); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Clone a BitArray |
85
|
|
|
* |
86
|
|
|
* @return void |
87
|
|
|
* |
88
|
|
|
* @since 1.0.0 |
89
|
|
|
*/ |
90
|
|
|
public function __clone() |
91
|
|
|
{ |
92
|
|
|
$this->data = str_repeat($this->data, 1); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Convert the object to a string |
97
|
|
|
* |
98
|
|
|
* @return string String representation of this object |
99
|
|
|
* |
100
|
|
|
* @since 1.0.0 |
101
|
|
|
*/ |
102
|
|
|
public function __toString() |
103
|
|
|
{ |
104
|
|
|
$string = str_repeat('0', $this->size); |
105
|
|
|
|
106
|
|
|
for ($offset = 0; $offset < $this->size; $offset++) |
107
|
|
|
{ |
108
|
|
|
if (ord($this->data[(int) ($offset / 8)]) & (1 << $offset % 8)) |
109
|
|
|
{ |
110
|
|
|
$string[$offset] = '1'; |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $string; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Magic get method |
119
|
|
|
* |
120
|
|
|
* @param string $property The property |
121
|
|
|
* |
122
|
|
|
* @throws \RuntimeException If the property does not exist |
123
|
|
|
* |
124
|
|
|
* @return mixed The value associated to the property |
125
|
|
|
* |
126
|
|
|
* @since 1.0.0 |
127
|
|
|
*/ |
128
|
|
|
public function __get($property) |
129
|
|
|
{ |
130
|
|
|
switch ($property) |
131
|
|
|
{ |
132
|
|
|
case 'size': |
133
|
|
|
return $this->size; |
134
|
|
|
case 'count': |
135
|
|
|
return $this->count(); |
136
|
|
|
default: |
137
|
|
|
throw new \RuntimeException('Undefined property'); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Test the existence of an index |
143
|
|
|
* |
144
|
|
|
* @param integer $offset The offset |
145
|
|
|
* |
146
|
|
|
* @return boolean The truth value |
147
|
|
|
* |
148
|
|
|
* @since 1.0.0 |
149
|
|
|
*/ |
150
|
|
|
public function offsetExists($offset) |
151
|
|
|
{ |
152
|
|
|
return is_int($offset) && $offset >= 0 && $offset < $this->size; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Get the truth value for an index |
157
|
|
|
* |
158
|
|
|
* @param integer $offset The offset |
159
|
|
|
* |
160
|
|
|
* @return boolean The truth value |
161
|
|
|
* |
162
|
|
|
* @throw \OutOfRangeException Argument index must be an positive integer lesser than the size |
163
|
|
|
* |
164
|
|
|
* @since 1.0.0 |
165
|
|
|
*/ |
166
|
|
|
public function offsetGet($offset) |
167
|
|
|
{ |
168
|
|
|
if ($this->offsetExists($offset)) |
169
|
|
|
{ |
170
|
|
|
return (bool) (ord($this->data[(int) ($offset / 8)]) & (1 << $offset % 8)); |
171
|
|
|
} |
172
|
|
|
else |
173
|
|
|
{ |
174
|
|
|
throw new \OutOfRangeException('Argument offset must be a positive integer lesser than the size'); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Set the truth value for an index |
180
|
|
|
* |
181
|
|
|
* @param integer $offset The offset |
182
|
|
|
* @param boolean $value The truth value |
183
|
|
|
* |
184
|
|
|
* @return void |
185
|
|
|
* |
186
|
|
|
* @throw \OutOfRangeException Argument index must be an positive integer lesser than the size |
187
|
|
|
* |
188
|
|
|
* @since 1.0.0 |
189
|
|
|
*/ |
190
|
|
|
public function offsetSet($offset, $value) |
191
|
|
|
{ |
192
|
|
|
if ($this->offsetExists($offset)) |
193
|
|
|
{ |
194
|
|
|
$index = (int) ($offset / 8); |
195
|
|
|
|
196
|
|
|
if ($value) |
197
|
|
|
{ |
198
|
|
|
$this->data[$index] = chr(ord($this->data[$index]) | (1 << $offset % 8)); |
199
|
|
|
} |
200
|
|
|
else |
201
|
|
|
{ |
202
|
|
|
$this->data[$index] = chr(ord($this->data[$index]) & ~(1 << $offset % 8)); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
else |
206
|
|
|
{ |
207
|
|
|
throw new \OutOfRangeException('Argument index must be a positive integer lesser than the size'); |
208
|
|
|
} |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Unset the existence of an index |
213
|
|
|
* |
214
|
|
|
* @param integer $offset The index |
215
|
|
|
* |
216
|
|
|
* @return void |
217
|
|
|
* |
218
|
|
|
* @throw \RuntimeException Values cannot be unset |
219
|
|
|
* |
220
|
|
|
* @since 1.0.0 |
221
|
|
|
*/ |
222
|
|
|
public function offsetUnset($offset) |
223
|
|
|
{ |
224
|
|
|
throw new \RuntimeException('Values cannot be unset'); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Return the number of true bits |
229
|
|
|
* |
230
|
|
|
* @return integer The number of true bits |
231
|
|
|
* |
232
|
|
|
* @since 1.0.0 |
233
|
|
|
*/ |
234
|
|
|
public function count() |
235
|
|
|
{ |
236
|
|
|
$count = 0; |
237
|
|
|
|
238
|
|
|
for ($index = 0, $length = strlen($this->data); $index < $length; $index++) |
239
|
|
|
{ |
240
|
|
|
$count += self::$count[ord($this->data[$index])]; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
return $count; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Transform the object to an array |
248
|
|
|
* |
249
|
|
|
* @return array Array of values |
250
|
|
|
* |
251
|
|
|
* @since 1.1.0 |
252
|
|
|
*/ |
253
|
|
|
public function toArray() |
254
|
|
|
{ |
255
|
|
|
$array = []; |
256
|
|
|
|
257
|
|
|
for ($index = 0; $index < $this->size; $index++) |
258
|
|
|
{ |
259
|
|
|
$array[] = (bool) (ord($this->data[(int) ($index / 8)]) & (1 << $index % 8)); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
return $array; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Serialize the object |
267
|
|
|
* |
268
|
|
|
* @return array Array of values |
269
|
|
|
* |
270
|
|
|
* @since 1.0.0 |
271
|
|
|
*/ |
272
|
|
|
public function jsonSerialize() |
273
|
|
|
{ |
274
|
|
|
return $this->toArray(); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Get an iterator |
279
|
|
|
* |
280
|
|
|
* @return Iterator Iterator |
281
|
|
|
* |
282
|
|
|
* @since 1.0.0 |
283
|
|
|
*/ |
284
|
|
|
public function getIterator() |
285
|
|
|
{ |
286
|
|
|
return new Iterator($this); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Return the size |
291
|
|
|
* |
292
|
|
|
* @return integer The size |
293
|
|
|
* |
294
|
|
|
* @since 1.0.0 |
295
|
|
|
*/ |
296
|
|
|
public function size() |
297
|
|
|
{ |
298
|
|
|
return $this->size; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Copy bits directly from a BitArray |
303
|
|
|
* |
304
|
|
|
* @param BitArray $bits A BitArray to copy |
305
|
|
|
* @param int $index Starting index for destination |
306
|
|
|
* @param int $offset Starting index for copying |
307
|
|
|
* @param int $size Copy size |
308
|
|
|
* |
309
|
|
|
* @return BitArray This object for chaining |
310
|
|
|
* |
311
|
|
|
* @throw \OutOfRangeException Argument index must be an positive integer lesser than the size |
312
|
|
|
* |
313
|
|
|
* @since 1.1.0 |
314
|
|
|
*/ |
315
|
|
|
public function directCopy(BitArray $bits, $index = 0, $offset = 0, $size = 0) |
316
|
|
|
{ |
317
|
|
|
if ($offset > $index) |
318
|
|
|
{ |
319
|
|
View Code Duplication |
for ($i = 0; $i < $size; $i++) |
|
|
|
|
320
|
|
|
{ |
321
|
|
|
$this[$i + $index] = $bits[$i + $offset]; |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
else |
325
|
|
|
{ |
326
|
|
View Code Duplication |
for ($i = $size - 1; $i >= 0; $i--) |
|
|
|
|
327
|
|
|
{ |
328
|
|
|
$this[$i + $index] = $bits[$i + $offset]; |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return $this; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Copy bits from a BitArray |
337
|
|
|
* |
338
|
|
|
* @param BitArray $bits A BitArray to copy |
339
|
|
|
* @param int $index Starting index for destination. |
340
|
|
|
* If index is non-negative, the index parameter is used as it is, keeping its real value between 0 and size-1. |
341
|
|
|
* If index is negative, the index parameter starts from the end, keeping its real value between 0 and size-1. |
342
|
|
|
* @param int $offset Starting index for copying. |
343
|
|
|
* If offset is non-negative, the offset parameter is used as it is, keeping its positive value between 0 and size-1. |
344
|
|
|
* If offset is negative, the offset parameter starts from the end, keeping its real value between 0 and size-1. |
345
|
|
|
* @param mixed $size Copy size. |
346
|
|
|
* If size is given and is positive, then the copy will copy size elements. |
347
|
|
|
* If the bits argument is shorter than the size, then only the available elements will be copied. |
348
|
|
|
* If size is given and is negative then the copy starts from the end. |
349
|
|
|
* If it is omitted, then the copy will have everything from offset up until the end of the bits argument. |
350
|
|
|
* |
351
|
|
|
* @return BitArray This object for chaining |
352
|
|
|
* |
353
|
|
|
* @since 1.1.0 |
354
|
|
|
*/ |
355
|
|
|
public function copy(BitArray $bits, $index = 0, $offset = 0, $size = null) |
356
|
|
|
{ |
357
|
|
|
$index = $this->getRealOffset($index); |
358
|
|
|
$offset = $bits->getRealOffset($offset); |
359
|
|
|
$size = $bits->getRealSize($offset, $size); |
360
|
|
|
|
361
|
|
|
if ($size > $this->size - $index) |
362
|
|
|
{ |
363
|
|
|
$size = $this->size - $index; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
return $this->directCopy($bits, $index, $offset, $size); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Get the real offset using a positive or negative offset |
371
|
|
|
* |
372
|
|
|
* @param int $offset If offset is non-negative, the offset parameter is used as it is, keeping its real value between 0 and size-1. |
373
|
|
|
* If offset is negative, the offset parameter starts from the end, keeping its real value between 0 and size-1. |
374
|
|
|
* |
375
|
|
|
* @return int The real offset |
376
|
|
|
* |
377
|
|
|
* @since 1.1.0 |
378
|
|
|
*/ |
379
|
|
|
protected function getRealOffset($offset) |
380
|
|
|
{ |
381
|
|
|
$offset = (int) $offset; |
382
|
|
|
|
383
|
|
|
if ($offset < 0) |
384
|
|
|
{ |
385
|
|
|
// Start from the end |
386
|
|
|
$offset = $this->size + $offset; |
387
|
|
|
|
388
|
|
|
if ($offset < 0) |
389
|
|
|
{ |
390
|
|
|
$offset = 0; |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
elseif ($offset > $this->size) |
394
|
|
|
{ |
395
|
|
|
$offset = $this->size; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
return $offset; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* Get the real offset using a positive or negative offset |
403
|
|
|
* |
404
|
|
|
* @param int $offset The real offset. |
405
|
|
|
* @param mixed $size If size is given and is positive, then the real size will be between 0 and the current size-1. |
406
|
|
|
* If size is given and is negative then the real size starts from the end. |
407
|
|
|
* If it is omitted, then the size goes until the end of the BitArray. |
408
|
|
|
* |
409
|
|
|
* @return int The real size |
410
|
|
|
* |
411
|
|
|
* @since 1.1.0 |
412
|
|
|
*/ |
413
|
|
|
protected function getRealSize($offset, $size) |
414
|
|
|
{ |
415
|
|
|
if ($size === null) |
416
|
|
|
{ |
417
|
|
|
$size = $this->size - $offset; |
418
|
|
|
} |
419
|
|
|
else |
420
|
|
|
{ |
421
|
|
|
$size = (int) $size; |
422
|
|
|
|
423
|
|
|
if ($size < 0) |
424
|
|
|
{ |
425
|
|
|
$size = $this->size + $size - $offset; |
426
|
|
|
|
427
|
|
|
if ($size < 0) |
428
|
|
|
{ |
429
|
|
|
$size = 0; |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
elseif ($size > $this->size - $offset) |
433
|
|
|
{ |
434
|
|
|
$size = $this->size - $offset; |
435
|
|
|
} |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
return $size; |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Create a new BitArray from an integer |
443
|
|
|
* |
444
|
|
|
* @param integer $size Size of the BitArray |
445
|
|
|
* |
446
|
|
|
* @return BitArray A new BitArray |
447
|
|
|
* |
448
|
|
|
* @since 1.0.0 |
449
|
|
|
*/ |
450
|
|
|
public static function fromInteger($size) |
451
|
|
|
{ |
452
|
|
|
return new BitArray($size); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* Create a new BitArray from a traversable |
457
|
|
|
* |
458
|
|
|
* @param \Traversable $traversable A traversable and countable |
459
|
|
|
* |
460
|
|
|
* @return BitArray A new BitArray |
461
|
|
|
* |
462
|
|
|
* @since 1.0.0 |
463
|
|
|
*/ |
464
|
|
View Code Duplication |
public static function fromTraversable($traversable) |
465
|
|
|
{ |
466
|
|
|
$bits = new BitArray(count($traversable)); |
467
|
|
|
$offset = 0; |
468
|
|
|
$ord = 0; |
469
|
|
|
|
470
|
|
|
foreach ($traversable as $value) |
471
|
|
|
{ |
472
|
|
|
if ($value) |
473
|
|
|
{ |
474
|
|
|
$ord |= 1 << $offset % 8; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
if ($offset % 8 === 7) |
478
|
|
|
{ |
479
|
|
|
$bits->data[(int) ($offset / 8)] = chr($ord); |
480
|
|
|
$ord = 0; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
$offset++; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
if ($offset % 8 !== 0) |
487
|
|
|
{ |
488
|
|
|
$bits->data[(int) ($offset / 8)] = chr($ord); |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
return $bits; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Create a new BitArray from a bit string |
496
|
|
|
* |
497
|
|
|
* @param string $string A bit string |
498
|
|
|
* |
499
|
|
|
* @return BitArray A new BitArray |
500
|
|
|
* |
501
|
|
|
* @since 1.0.0 |
502
|
|
|
*/ |
503
|
|
View Code Duplication |
public static function fromString($string) |
504
|
|
|
{ |
505
|
|
|
$bits = new BitArray(strlen($string)); |
506
|
|
|
$ord = 0; |
507
|
|
|
|
508
|
|
|
for ($offset = 0; $offset < $bits->size; $offset++) |
509
|
|
|
{ |
510
|
|
|
if ($string[$offset] !== '0') |
511
|
|
|
{ |
512
|
|
|
$ord |= 1 << $offset % 8; |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
if ($offset % 8 === 7) |
516
|
|
|
{ |
517
|
|
|
$bits->data[(int) ($offset / 8)] = chr($ord); |
518
|
|
|
$ord = 0; |
519
|
|
|
} |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
if ($offset % 8 !== 0) |
523
|
|
|
{ |
524
|
|
|
$bits->data[(int) ($offset / 8)] = chr($ord); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
return $bits; |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
/** |
531
|
|
|
* Create a new BitArray from json |
532
|
|
|
* |
533
|
|
|
* @param string $json A json encoded value |
534
|
|
|
* |
535
|
|
|
* @return BitArray A new BitArray |
536
|
|
|
* |
537
|
|
|
* @since 1.0.0 |
538
|
|
|
*/ |
539
|
|
|
public static function fromJson($json) |
540
|
|
|
{ |
541
|
|
|
return self::fromTraversable(json_decode($json)); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
/** |
545
|
|
|
* Create a new BitArray using a slice |
546
|
|
|
* |
547
|
|
|
* @param BitArray $bits A BitArray to get the slice |
548
|
|
|
* @param int $offset If offset is non-negative, the slice will start at that offset in the bits argument. |
549
|
|
|
* If offset is negative, the slice will start from the end of the bits argument. |
550
|
|
|
* @param mixed $size If size is given and is positive, then the slice will have up to that many elements in it. |
551
|
|
|
* If the bits argument is shorter than the size, then only the available elements will be present. |
552
|
|
|
* If size is given and is negative then the slice will stop that many elements from the end of the bits argument. |
553
|
|
|
* If it is omitted, then the slice will have everything from offset up until the end of the bits argument. |
554
|
|
|
* |
555
|
|
|
* @return BitArray A new BitArray |
556
|
|
|
* |
557
|
|
|
* @since 1.1.0 |
558
|
|
|
*/ |
559
|
|
|
public static function fromSlice(BitArray $bits, $offset = 0, $size = null) |
560
|
|
|
{ |
561
|
|
|
$offset = $bits->getRealOffset($offset); |
562
|
|
|
$size = $bits->getRealSize($offset, $size); |
563
|
|
|
$slice = new BitArray($size); |
564
|
|
|
|
565
|
|
|
return $slice->directCopy($bits, 0, $offset, $size); |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
/** |
569
|
|
|
* Create a new BitArray using the concat operation |
570
|
|
|
* |
571
|
|
|
* @param BitArray $bits1 A BitArray |
572
|
|
|
* @param BitArray $bits2 A BitArray |
573
|
|
|
* |
574
|
|
|
* @return BitArray A new BitArray |
575
|
|
|
* |
576
|
|
|
* @since 1.1.0 |
577
|
|
|
*/ |
578
|
|
|
public static function fromConcat(BitArray $bits1, BitArray $bits2) |
579
|
|
|
{ |
580
|
|
|
$size = $bits1->size + $bits2->size; |
581
|
|
|
$concat = new BitArray($size); |
582
|
|
|
$concat->directCopy($bits1, 0, 0, $bits1->size); |
|
|
|
|
583
|
|
|
$concat->directCopy($bits2, $bits1->size, 0, $bits2->size); |
|
|
|
|
584
|
|
|
|
585
|
|
|
return $concat; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
/** |
589
|
|
|
* Complement the bit array |
590
|
|
|
* |
591
|
|
|
* @return BitArray This object for chaining |
592
|
|
|
* |
593
|
|
|
* @since 1.0.0 |
594
|
|
|
*/ |
595
|
|
|
public function applyComplement() |
596
|
|
|
{ |
597
|
|
|
$length = strlen($this->data); |
598
|
|
|
|
599
|
|
|
for ($index = 0; $index < $length; $index++) |
600
|
|
|
{ |
601
|
|
|
$this->data[$index] = chr(~ ord($this->data[$index])); |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
// Remove useless bits |
605
|
|
|
if ($length > 0) |
606
|
|
|
{ |
607
|
|
|
$this->data[$length - 1] = chr(ord($this->data[$length - 1]) & self::$restrict[$this->size % 8]); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
return $this; |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
/** |
614
|
|
|
* Or with an another bit array |
615
|
|
|
* |
616
|
|
|
* @param BitArray $bits A bit array |
617
|
|
|
* |
618
|
|
|
* @return BitArray This object for chaining |
619
|
|
|
* |
620
|
|
|
* @throw \InvalidArgumentException Argument must be of equal size |
621
|
|
|
* |
622
|
|
|
* @since 1.0.0 |
623
|
|
|
*/ |
624
|
|
View Code Duplication |
public function applyOr(BitArray $bits) |
625
|
|
|
{ |
626
|
|
|
if ($this->size == $bits->size) |
627
|
|
|
{ |
628
|
|
|
$length = strlen($this->data); |
629
|
|
|
|
630
|
|
|
for ($index = 0; $index < $length; $index++) |
631
|
|
|
{ |
632
|
|
|
$this->data[$index] = chr(ord($this->data[$index]) | ord($bits->data[$index])); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
return $this; |
636
|
|
|
} |
637
|
|
|
else |
638
|
|
|
{ |
639
|
|
|
throw new \InvalidArgumentException('Argument must be of equal size'); |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
/** |
644
|
|
|
* And with an another bit array |
645
|
|
|
* |
646
|
|
|
* @param BitArray $bits A bit array |
647
|
|
|
* |
648
|
|
|
* @return BitArray This object for chaining |
649
|
|
|
* |
650
|
|
|
* @throw \InvalidArgumentException Argument must be of equal size |
651
|
|
|
* |
652
|
|
|
* @since 1.0.0 |
653
|
|
|
*/ |
654
|
|
View Code Duplication |
public function applyAnd(BitArray $bits) |
655
|
|
|
{ |
656
|
|
|
if ($this->size == $bits->size) |
657
|
|
|
{ |
658
|
|
|
$length = strlen($this->data); |
659
|
|
|
|
660
|
|
|
for ($index = 0; $index < $length; $index++) |
661
|
|
|
{ |
662
|
|
|
$this->data[$index] = chr(ord($this->data[$index]) & ord($bits->data[$index])); |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
return $this; |
666
|
|
|
} |
667
|
|
|
else |
668
|
|
|
{ |
669
|
|
|
throw new \InvalidArgumentException('Argument must be of equal size'); |
670
|
|
|
} |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
/** |
674
|
|
|
* Xor with an another bit array |
675
|
|
|
* |
676
|
|
|
* @param BitArray $bits A bit array |
677
|
|
|
* |
678
|
|
|
* @return BitArray This object for chaining |
679
|
|
|
* |
680
|
|
|
* @throw \InvalidArgumentException Argument must be of equal size |
681
|
|
|
* |
682
|
|
|
* @since 1.0.0 |
683
|
|
|
*/ |
684
|
|
View Code Duplication |
public function applyXor(BitArray $bits) |
685
|
|
|
{ |
686
|
|
|
if ($this->size == $bits->size) |
687
|
|
|
{ |
688
|
|
|
$length = strlen($this->data); |
689
|
|
|
|
690
|
|
|
for ($index = 0; $index < $length; $index++) |
691
|
|
|
{ |
692
|
|
|
$this->data[$index] = chr(ord($this->data[$index]) ^ ord($bits->data[$index])); |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
return $this; |
696
|
|
|
} |
697
|
|
|
else |
698
|
|
|
{ |
699
|
|
|
throw new \InvalidArgumentException('Argument must be of equal size'); |
700
|
|
|
} |
701
|
|
|
} |
702
|
|
|
} |
703
|
|
|
|
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.