Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/bitstring/bitarray_.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

298 statements  

1from __future__ import annotations 

2 

3import copy 

4import numbers 

5import re 

6from collections import abc 

7from typing import Union, List, Iterable, Any, Optional 

8from bitstring import utils 

9from bitstring.exceptions import CreationError, Error 

10from bitstring.bits import Bits, BitsType, TBits 

11 

12import bitstring.dtypes 

13common_helpers = bitstring.bitstore_common_helpers 

14 

15MutableBitStore = bitstring.bitstore.MutableBitStore 

16 

17 

18class BitArray(Bits): 

19 """A container holding a mutable sequence of bits. 

20 

21 Subclass of the immutable Bits class. Inherits all of its 

22 methods (except __hash__) and adds mutating methods. 

23 

24 Mutating methods: 

25 

26 append() -- Append a bitstring. 

27 byteswap() -- Change byte endianness in-place. 

28 clear() -- Remove all bits from the bitstring. 

29 insert() -- Insert a bitstring. 

30 invert() -- Flip bit(s) between one and zero. 

31 overwrite() -- Overwrite a section with a new bitstring. 

32 prepend() -- Prepend a bitstring. 

33 replace() -- Replace occurrences of one bitstring with another. 

34 reverse() -- Reverse bits in-place. 

35 rol() -- Rotate bits to the left. 

36 ror() -- Rotate bits to the right. 

37 set() -- Set bit(s) to 1 or 0. 

38 

39 Methods inherited from Bits: 

40 

41 all() -- Check if all specified bits are set to 1 or 0. 

42 any() -- Check if any of specified bits are set to 1 or 0. 

43 copy() -- Return a copy of the bitstring. 

44 count() -- Count the number of bits set to 1 or 0. 

45 cut() -- Create generator of constant sized chunks. 

46 endswith() -- Return whether the bitstring ends with a sub-string. 

47 find() -- Find a sub-bitstring in the current bitstring. 

48 findall() -- Find all occurrences of a sub-bitstring in the current bitstring. 

49 fromstring() -- Create a bitstring from a formatted string. 

50 join() -- Join bitstrings together using current bitstring. 

51 pp() -- Pretty print the bitstring. 

52 rfind() -- Seek backwards to find a sub-bitstring. 

53 split() -- Create generator of chunks split by a delimiter. 

54 startswith() -- Return whether the bitstring starts with a sub-bitstring. 

55 tobitarray() -- Return bitstring as a bitarray from the bitarray package. 

56 tobytes() -- Return bitstring as bytes, padding if needed. 

57 tofile() -- Write bitstring to file, padding if needed. 

58 unpack() -- Interpret bits using format string. 

59 

60 Special methods: 

61 

62 Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= 

63 in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^. 

64 

65 Properties: 

66 

67 [GENERATED_PROPERTY_DESCRIPTIONS] 

68 

69 len -- Length of the bitstring in bits. 

70 

71 """ 

72 

73 __slots__ = () 

74 

75 # As BitArray objects are mutable, we shouldn't allow them to be hashed. 

76 __hash__: None = None 

77 

78 def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, 

79 offset: Optional[int] = None, **kwargs) -> None: 

80 """Either specify an 'auto' initialiser: 

81 A string of comma separated tokens, an integer, a file object, 

82 a bytearray, a boolean iterable or another bitstring. 

83 

84 Or initialise via **kwargs with one (and only one) of: 

85 bin -- binary string representation, e.g. '0b001010'. 

86 hex -- hexadecimal string representation, e.g. '0x2ef' 

87 oct -- octal string representation, e.g. '0o777'. 

88 bytes -- raw data as a bytes object, for example read from a binary file. 

89 int -- a signed integer. 

90 uint -- an unsigned integer. 

91 float / floatbe -- a big-endian floating point number. 

92 bool -- a boolean (True or False). 

93 se -- a signed exponential-Golomb code. 

94 ue -- an unsigned exponential-Golomb code. 

95 sie -- a signed interleaved exponential-Golomb code. 

96 uie -- an unsigned interleaved exponential-Golomb code. 

97 floatle -- a little-endian floating point number. 

98 floatne -- a native-endian floating point number. 

99 bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. 

100 bfloatle -- a little-endian bfloat format 16-bit floating point number. 

101 bfloatne -- a native-endian bfloat format 16-bit floating point number. 

102 intbe -- a signed big-endian whole byte integer. 

103 intle -- a signed little-endian whole byte integer. 

104 intne -- a signed native-endian whole byte integer. 

105 uintbe -- an unsigned big-endian whole byte integer. 

106 uintle -- an unsigned little-endian whole byte integer. 

107 uintne -- an unsigned native-endian whole byte integer. 

108 filename -- the path of a file which will be opened in binary read-only mode. 

109 

110 Other keyword arguments: 

111 length -- length of the bitstring in bits, if needed and appropriate. 

112 It must be supplied for all integer and float initialisers. 

113 offset -- bit offset to the data. These offset bits are 

114 ignored and this is intended for use when 

115 initialising using 'bytes' or 'filename'. 

116 

117 """ 

118 pass 

119 

120 def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, 

121 offset: Optional[int] = None, **kwargs) -> TBits: 

122 x = super(Bits, cls).__new__(cls) 

123 if auto is None and not kwargs: 

124 # No initialiser so fill with zero bits up to length 

125 if length is not None: 

126 x._bitstore = MutableBitStore.from_zeros(length) 

127 else: 

128 x._bitstore = MutableBitStore() 

129 return x 

130 x._initialise(auto, length, offset, immutable=False, **kwargs) 

131 return x 

132 

133 @classmethod 

134 def fromstring(cls: TBits, s: str, /) -> TBits: 

135 """Create a new bitstring from a formatted string.""" 

136 x = super().__new__(cls) 

137 b = common_helpers.str_to_bitstore(s) 

138 x._bitstore = b._mutable_copy() 

139 return x 

140 

141 def copy(self: TBits) -> TBits: 

142 """Return a copy of the bitstring.""" 

143 return self.__copy__() 

144 

145 def _invert_all(self) -> None: 

146 """Invert every bit.""" 

147 self._bitstore.invert() 

148 

149 def __setattr__(self, attribute, value) -> None: 

150 try: 

151 # First try the ordinary attribute setter 

152 super().__setattr__(attribute, value) 

153 except AttributeError: 

154 dtype = bitstring.dtypes.Dtype(attribute) 

155 x = object.__new__(Bits) 

156 if (set_fn := dtype.set_fn) is None: 

157 raise AttributeError(f"Cannot set attribute '{attribute}' as it does not have a set_fn.") 

158 set_fn(x, value) 

159 if len(x) != dtype.bitlength: 

160 raise CreationError(f"Can't initialise with value of length {len(x)} bits, " 

161 f"as attribute has length of {dtype.bitlength} bits.") 

162 self._bitstore = x._bitstore 

163 return 

164 

165 def __iadd__(self, bs: BitsType) -> BitArray: 

166 """Append bs to current bitstring. Return self. 

167 

168 bs -- the bitstring to append. 

169 

170 """ 

171 self._append(bs) 

172 return self 

173 

174 def __copy__(self) -> BitArray: 

175 """Return a new copy of the BitArray.""" 

176 s_copy = BitArray() 

177 s_copy._bitstore = self._bitstore._mutable_copy() 

178 return s_copy 

179 

180 def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None: 

181 if isinstance(value, numbers.Integral): 

182 if value == 0: 

183 self._bitstore[key] = 0 

184 return 

185 if value in (1, -1): 

186 self._bitstore[key] = 1 

187 return 

188 raise ValueError(f"Cannot set a single bit with integer {value}.") 

189 try: 

190 value = self._create_from_bitstype(value) 

191 except TypeError: 

192 raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") 

193 positive_key = key + len(self) if key < 0 else key 

194 if positive_key < 0 or positive_key >= len(self._bitstore): 

195 raise IndexError(f"Bit position {key} out of range.") 

196 self._bitstore[positive_key: positive_key + 1] = value._bitstore 

197 

198 def _setitem_slice(self, key: slice, value: BitsType) -> None: 

199 if isinstance(value, numbers.Integral): 

200 value = int(value) 

201 if key.step not in [None, -1, 1]: 

202 if value in [0, 1]: 

203 self.set(value, range(*key.indices(len(self)))) 

204 return 

205 else: 

206 raise ValueError("Can't assign an integer except 0 or 1 to a slice with a step value.") 

207 # To find the length we first get the slice 

208 s = self._bitstore.getslice(key.start, key.stop) 

209 length = len(s) 

210 # Now create an int of the correct length 

211 if value >= 0: 

212 value = self.__class__(uint=value, length=length) 

213 else: 

214 value = self.__class__(int=value, length=length) 

215 else: 

216 try: 

217 value = self._create_from_bitstype(value) 

218 except TypeError: 

219 raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") 

220 self._bitstore.__setitem__(key, value._bitstore) 

221 

222 def __setitem__(self, key: Union[slice, int], value: BitsType) -> None: 

223 if isinstance(key, numbers.Integral): 

224 self._setitem_int(int(key), value) 

225 else: 

226 self._setitem_slice(key, value) 

227 

228 def __delitem__(self, key: Union[slice, int]) -> None: 

229 """Delete item or range. 

230 

231 >>> a = BitArray('0x001122') 

232 >>> del a[8:16] 

233 >>> print a 

234 0x0022 

235 

236 """ 

237 self._bitstore.__delitem__(key) 

238 return 

239 

240 def __ilshift__(self: TBits, n: int) -> TBits: 

241 """Shift bits by n to the left in place. Return self. 

242 

243 n -- the number of bits to shift. Must be >= 0. 

244 

245 """ 

246 if n < 0: 

247 raise ValueError("Cannot shift by a negative amount.") 

248 if not len(self): 

249 raise ValueError("Cannot shift an empty bitstring.") 

250 if not n: 

251 return self 

252 n = min(n, len(self)) 

253 return self._ilshift(n) 

254 

255 def __irshift__(self: TBits, n: int) -> TBits: 

256 """Shift bits by n to the right in place. Return self. 

257 

258 n -- the number of bits to shift. Must be >= 0. 

259 

260 """ 

261 if n < 0: 

262 raise ValueError("Cannot shift by a negative amount.") 

263 if not len(self): 

264 raise ValueError("Cannot shift an empty bitstring.") 

265 if not n: 

266 return self 

267 n = min(n, len(self)) 

268 return self._irshift(n) 

269 

270 def __imul__(self: TBits, n: int) -> TBits: 

271 """Concatenate n copies of self in place. Return self. 

272 

273 Called for expressions of the form 'a *= 3'. 

274 n -- The number of concatenations. Must be >= 0. 

275 

276 """ 

277 if n < 0: 

278 raise ValueError("Cannot multiply by a negative integer.") 

279 return self._imul(n) 

280 

281 def __ior__(self: TBits, bs: BitsType) -> TBits: 

282 bs = self._create_from_bitstype(bs) 

283 self._bitstore |= bs._bitstore 

284 return self 

285 

286 def __iand__(self: TBits, bs: BitsType) -> TBits: 

287 bs = self._create_from_bitstype(bs) 

288 self._bitstore &= bs._bitstore 

289 return self 

290 

291 def __ixor__(self: TBits, bs: BitsType) -> TBits: 

292 bs = self._create_from_bitstype(bs) 

293 self._bitstore ^= bs._bitstore 

294 return self 

295 

296 def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int: 

297 if bytealigned is None: 

298 bytealigned = bitstring.options.bytealigned 

299 # First find all the places where we want to do the replacements 

300 starting_points: List[int] = [] 

301 for x in self.findall(old, start, end, bytealigned=bytealigned): 

302 if not starting_points: 

303 starting_points.append(x) 

304 elif x >= starting_points[-1] + len(old): 

305 # Can only replace here if it hasn't already been replaced! 

306 starting_points.append(x) 

307 if count != 0 and len(starting_points) == count: 

308 break 

309 if not starting_points: 

310 return 0 

311 replacement_list = [self._bitstore.getslice(0, starting_points[0])] 

312 for i in range(len(starting_points) - 1): 

313 replacement_list.append(new._bitstore) 

314 replacement_list.append( 

315 self._bitstore.getslice(starting_points[i] + len(old), starting_points[i + 1])) 

316 # Final replacement 

317 replacement_list.append(new._bitstore) 

318 replacement_list.append(self._bitstore.getslice(starting_points[-1] + len(old), None)) 

319 if bitstring.options.lsb0: 

320 # Addition of bitarray is always on the right, so assemble from other end 

321 replacement_list.reverse() 

322 self._bitstore.clear() 

323 for r in replacement_list: 

324 self._bitstore += r 

325 return len(starting_points) 

326 

327 def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None, 

328 count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int: 

329 """Replace all occurrences of old with new in place. 

330 

331 Returns number of replacements made. 

332 

333 old -- The bitstring to replace. 

334 new -- The replacement bitstring. 

335 start -- Any occurrences that start before this will not be replaced. 

336 Defaults to 0. 

337 end -- Any occurrences that finish after this will not be replaced. 

338 Defaults to len(self). 

339 count -- The maximum number of replacements to make. Defaults to 

340 replace all occurrences. 

341 bytealigned -- If True replacements will only be made on byte 

342 boundaries. 

343 

344 Raises ValueError if old is empty or if start or end are 

345 out of range. 

346 

347 """ 

348 if count == 0: 

349 return 0 

350 old = self._create_from_bitstype(old) 

351 new = self._create_from_bitstype(new) 

352 if len(old) == 0: 

353 raise ValueError("Empty bitstring cannot be replaced.") 

354 start, end = self._validate_slice(start, end) 

355 

356 if new is self: 

357 # Prevent self assignment woes 

358 new = copy.copy(self) 

359 return self._replace(old, new, start, end, 0 if count is None else count, bytealigned) 

360 

361 def insert(self, bs: BitsType, pos: int) -> None: 

362 """Insert bs at bit position pos. 

363 

364 bs -- The bitstring to insert. 

365 pos -- The bit position to insert at. 

366 

367 Raises ValueError if pos < 0 or pos > len(self). 

368 

369 """ 

370 bs = self._create_from_bitstype(bs) 

371 if len(bs) == 0: 

372 return 

373 if bs is self: 

374 bs = self._copy() 

375 if pos < 0: 

376 pos += len(self) 

377 if not 0 <= pos <= len(self): 

378 raise ValueError("Invalid insert position.") 

379 self._insert(bs, pos) 

380 

381 def overwrite(self, bs: BitsType, pos: int) -> None: 

382 """Overwrite with bs at bit position pos. 

383 

384 bs -- The bitstring to overwrite with. 

385 pos -- The bit position to begin overwriting from. 

386 

387 Raises ValueError if pos < 0 or pos > len(self). 

388 

389 """ 

390 bs = self._create_from_bitstype(bs) 

391 if len(bs) == 0: 

392 return 

393 if pos < 0: 

394 pos += len(self) 

395 if pos < 0 or pos > len(self): 

396 raise ValueError("Overwrite starts outside boundary of bitstring.") 

397 self._overwrite(bs, pos) 

398 

399 def append(self, bs: BitsType) -> None: 

400 """Append a bitstring to the current bitstring. 

401 

402 bs -- The bitstring to append. 

403 

404 """ 

405 self._append(bs) 

406 

407 def prepend(self, bs: BitsType) -> None: 

408 """Prepend a bitstring to the current bitstring. 

409 

410 bs -- The bitstring to prepend. 

411 

412 """ 

413 self._prepend(bs) 

414 

415 def _append_msb0(self, bs: BitsType) -> None: 

416 self._addright(self._create_from_bitstype(bs)) 

417 

418 def _append_lsb0(self, bs: BitsType) -> None: 

419 bs = self._create_from_bitstype(bs) 

420 self._addleft(bs) 

421 

422 def reverse(self, start: Optional[int] = None, end: Optional[int] = None) -> None: 

423 """Reverse bits in-place. 

424 

425 start -- Position of first bit to reverse. Defaults to 0. 

426 end -- One past the position of the last bit to reverse. 

427 Defaults to len(self). 

428 

429 Using on an empty bitstring will have no effect. 

430 

431 Raises ValueError if start < 0, end > len(self) or end < start. 

432 

433 """ 

434 start, end = self._validate_slice(start, end) 

435 if start == 0 and end == len(self): 

436 self._bitstore.reverse() 

437 return 

438 s = self._slice(start, end) 

439 s._bitstore.reverse() 

440 self[start:end] = s 

441 

442 def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]] = None) -> None: 

443 """Set one or many bits to 1 or 0. 

444 

445 value -- If bool(value) is True bits are set to 1, otherwise they are set to 0. 

446 pos -- Either a single bit position or an iterable of bit positions. 

447 Negative numbers are treated in the same way as slice indices. 

448 Defaults to the entire bitstring. 

449 

450 Raises IndexError if pos < -len(self) or pos >= len(self). 

451 

452 """ 

453 if pos is None: 

454 # Set all bits to either 1 or 0 

455 self._bitstore.__setitem__(slice(None), bool(value)) 

456 return 

457 if not isinstance(pos, abc.Iterable): 

458 pos = (pos,) 

459 v = 1 if value else 0 

460 if isinstance(pos, range): 

461 self._bitstore.__setitem__(slice(pos.start, pos.stop, pos.step), v) 

462 return 

463 for p in pos: 

464 self._bitstore[p] = v 

465 

466 def invert(self, pos: Optional[Union[Iterable[int], int]] = None) -> None: 

467 """Invert one or many bits from 0 to 1 or vice versa. 

468 

469 pos -- Either a single bit position or an iterable of bit positions. 

470 Negative numbers are treated in the same way as slice indices. 

471 

472 Raises IndexError if pos < -len(self) or pos >= len(self). 

473 

474 """ 

475 if pos is None: 

476 self._invert_all() 

477 return 

478 if not isinstance(pos, abc.Iterable): 

479 pos = (pos,) 

480 length = len(self) 

481 

482 for p in pos: 

483 if p < 0: 

484 p += length 

485 if not 0 <= p < length: 

486 raise IndexError(f"Bit position {p} out of range.") 

487 self._invert(p) 

488 

489 def ror(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: 

490 """Rotate bits to the right in-place. 

491 

492 bits -- The number of bits to rotate by. 

493 start -- Start of slice to rotate. Defaults to 0. 

494 end -- End of slice to rotate. Defaults to len(self). 

495 

496 Raises ValueError if bits < 0. 

497 

498 """ 

499 if not len(self): 

500 raise Error("Cannot rotate an empty bitstring.") 

501 if bits < 0: 

502 raise ValueError("Cannot rotate by negative amount.") 

503 self._ror(bits, start, end) 

504 

505 def _ror_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: 

506 start, end = self._validate_slice(start, end) # the _slice deals with msb0/lsb0 

507 bits %= (end - start) 

508 if not bits: 

509 return 

510 rhs = self._slice(end - bits, end) 

511 self._delete(bits, end - bits) 

512 self._insert(rhs, start) 

513 

514 def rol(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: 

515 """Rotate bits to the left in-place. 

516 

517 bits -- The number of bits to rotate by. 

518 start -- Start of slice to rotate. Defaults to 0. 

519 end -- End of slice to rotate. Defaults to len(self). 

520 

521 Raises ValueError if bits < 0. 

522 

523 """ 

524 if not len(self): 

525 raise Error("Cannot rotate an empty bitstring.") 

526 if bits < 0: 

527 raise ValueError("Cannot rotate by negative amount.") 

528 self._rol(bits, start, end) 

529 

530 def _rol_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None): 

531 start, end = self._validate_slice(start, end) 

532 bits %= (end - start) 

533 if bits == 0: 

534 return 

535 lhs = self._slice(start, start + bits) 

536 self._delete(bits, start) 

537 self._insert(lhs, end - bits) 

538 

539 def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: Optional[int] = None, 

540 end: Optional[int] = None, repeat: bool = True) -> int: 

541 """Change the endianness in-place. Return number of repeats of fmt done. 

542 

543 fmt -- A compact structure string, an integer number of bytes or 

544 an iterable of integers. Defaults to 0, which byte reverses the 

545 whole bitstring. 

546 start -- Start bit position, defaults to 0. 

547 end -- End bit position, defaults to len(self). 

548 repeat -- If True (the default) the byte swapping pattern is repeated 

549 as much as possible. 

550 

551 """ 

552 start_v, end_v = self._validate_slice(start, end) 

553 if fmt is None or fmt == 0: 

554 # reverse all of the whole bytes. 

555 bytesizes = [(end_v - start_v) // 8] 

556 elif isinstance(fmt, numbers.Integral): 

557 if fmt < 0: 

558 raise ValueError(f"Improper byte length {fmt}.") 

559 bytesizes = [fmt] 

560 elif isinstance(fmt, str): 

561 if not (m := utils.BYTESWAP_STRUCT_PACK_RE.match(fmt)): 

562 raise ValueError(f"Cannot parse format string {fmt}.") 

563 # Split the format string into a list of 'q', '4h' etc. 

564 formatlist = re.findall(utils.STRUCT_SPLIT_RE, m.group('fmt')) 

565 # Now deal with multiplicative factors, 4h -> hhhh etc. 

566 bytesizes = [] 

567 for f in formatlist: 

568 if len(f) == 1: 

569 bytesizes.append(utils.PACK_CODE_SIZE[f]) 

570 else: 

571 bytesizes.extend([utils.PACK_CODE_SIZE[f[-1]]] * int(f[:-1])) 

572 elif isinstance(fmt, abc.Iterable): 

573 bytesizes = fmt 

574 for bytesize in bytesizes: 

575 if not isinstance(bytesize, numbers.Integral) or bytesize < 0: 

576 raise ValueError(f"Improper byte length {bytesize}.") 

577 else: 

578 raise TypeError("Format must be an integer, string or iterable.") 

579 

580 repeats = 0 

581 totalbitsize: int = 8 * sum(bytesizes) 

582 if not totalbitsize: 

583 return 0 

584 if repeat: 

585 # Try to repeat up to the end of the bitstring. 

586 finalbit = end_v 

587 else: 

588 # Just try one (set of) byteswap(s). 

589 finalbit = start_v + totalbitsize 

590 for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize): 

591 bytestart = patternend - totalbitsize 

592 for bytesize in bytesizes: 

593 byteend = bytestart + bytesize * 8 

594 self._reversebytes(bytestart, byteend) 

595 bytestart += bytesize * 8 

596 repeats += 1 

597 return repeats 

598 

599 def clear(self) -> None: 

600 """Remove all bits, reset to zero length.""" 

601 self._clear()