Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/bitstring/bitstore.py: 45%
119 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1from __future__ import annotations
3import bitarray
4from bitstring.exceptions import CreationError
5from typing import Union, Iterable, Optional, overload
8def _offset_slice_indices_lsb0(key: slice, length: int, offset: int) -> slice:
9 # First convert slice to all integers
10 # Length already should take account of the offset
11 start, stop, step = key.indices(length)
12 new_start = length - stop - offset
13 new_stop = length - start - offset
14 # For negative step we sometimes get a negative stop, which can't be used correctly in a new slice
15 return slice(new_start, None if new_stop < 0 else new_stop, step)
18def _offset_slice_indices_msb0(key: slice, length: int, offset: int) -> slice:
19 # First convert slice to all integers
20 # Length already should take account of the offset
21 start, stop, step = key.indices(length)
22 start += offset
23 stop += offset
24 # For negative step we sometimes get a negative stop, which can't be used correctly in a new slice
25 return slice(start, None if stop < 0 else stop, step)
28class BitStore(bitarray.bitarray):
29 """A light wrapper around bitarray that does the LSB0 stuff"""
31 __slots__ = ('modified', 'length', 'offset', 'filename', 'immutable')
33 def __init__(self, *args, immutable: bool = False, frombytes: Optional[Union[bytes, bytearray]] = None,
34 offset: int = 0, length: Optional[int] = None, filename: str = '',
35 **kwargs) -> None:
36 if frombytes is not None:
37 self.frombytes(frombytes)
38 self.immutable = immutable
39 self.offset = offset
40 self.filename = filename
41 # Here 'modified' means that it isn't just the underlying bitarray. It could have a different start and end, and be from a file.
42 # This also means that it shouldn't be changed further, so setting deleting etc. are disallowed.
43 self.modified = offset != 0 or length is not None or filename != ''
44 if self.modified:
45 assert immutable is True
46 # These class variable only exist if modified is True.
48 self.length = super().__len__() - self.offset if length is None else length
50 if self.length < 0:
51 raise CreationError("Can't create bitstring with a negative length.")
52 if self.length + self.offset > super().__len__():
53 self.length = super().__len__() - self.offset
54 raise CreationError(
55 f"Can't create bitstring with a length of {self.length} and an offset of {self.offset} from {super().__len__()} bits of data.")
57 @classmethod
58 def _setlsb0methods(cls, lsb0: bool = False) -> None:
59 if lsb0:
60 cls.__setitem__ = cls.setitem_lsb0
61 cls.__delitem__ = cls.delitem_lsb0
62 cls.getindex = cls.getindex_lsb0
63 cls.getslice = cls.getslice_lsb0
64 cls.invert = cls.invert_lsb0
65 else:
66 cls.__setitem__ = super().__setitem__
67 cls.__delitem__ = super().__delitem__
68 cls.getindex = cls.getindex_msb0
69 cls.getslice = cls.getslice_msb0
70 cls.invert = cls.invert_msb0
72 def __new__(cls, *args, **kwargs) -> bitarray.bitarray:
73 # Just pass on the buffer keyword, not the length, offset, filename and frombytes
74 new_kwargs = {'buffer': kwargs.get('buffer', None)}
75 return bitarray.bitarray.__new__(cls, *args, **new_kwargs)
77 def __add__(self, other: bitarray.bitarray) -> BitStore:
78 assert not self.immutable
79 return BitStore(super().__add__(other))
81 def __iter__(self) -> Iterable[bool]:
82 for i in range(len(self)):
83 yield self.getindex(i)
85 def copy(self) -> BitStore:
86 x = BitStore(self.getslice(slice(None, None, None)))
87 return x
89 def __getitem__(self, item: Union[int, slice]) -> Union[int, BitStore]:
90 # Use getindex or getslice instead
91 raise NotImplementedError
93 def getindex_msb0(self, index: int) -> bool:
94 if self.modified and index >= 0:
95 index += self.offset
96 return bool(super().__getitem__(index))
98 def getslice_msb0(self, key: slice) -> BitStore:
99 if self.modified:
100 key = _offset_slice_indices_msb0(key, len(self), self.offset)
101 return BitStore(super().__getitem__(key))
103 def getindex_lsb0(self, index: int) -> bool:
104 if self.modified and index >= 0:
105 index += self.offset
106 return bool(super().__getitem__(-index - 1))
108 def getslice_lsb0(self, key: slice) -> BitStore:
109 if self.modified:
110 key = _offset_slice_indices_lsb0(key, len(self), self.offset)
111 else:
112 key = _offset_slice_indices_lsb0(key, len(self), 0)
113 return BitStore(super().__getitem__(key))
115 @overload
116 def setitem_lsb0(self, key: int, value: int) -> None:
117 ...
119 @overload
120 def setitem_lsb0(self, key: slice, value: BitStore) -> None:
121 ...
123 def setitem_lsb0(self, key: Union[int, slice], value: Union[int, BitStore]) -> None:
124 assert not self.immutable
125 if isinstance(key, slice):
126 new_slice = _offset_slice_indices_lsb0(key, len(self), 0)
127 super().__setitem__(new_slice, value)
128 else:
129 super().__setitem__(-key - 1, value)
131 def delitem_lsb0(self, key: Union[int, slice]) -> None:
132 assert not self.immutable
133 if isinstance(key, slice):
134 new_slice = _offset_slice_indices_lsb0(key, len(self), 0)
135 super().__delitem__(new_slice)
136 else:
137 super().__delitem__(-key - 1)
139 def invert_msb0(self, index: Optional[int] = None) -> None:
140 assert not self.immutable
141 if index is not None:
142 super().invert(index)
143 else:
144 super().invert()
146 def invert_lsb0(self, index: Optional[int] = None) -> None:
147 assert not self.immutable
148 if index is not None:
149 super().invert(-index - 1)
150 else:
151 super().invert()
153 def any_set(self) -> bool:
154 if self.modified:
155 return super().__getitem__(slice(self.offset, self.offset + self.length, None)).any()
156 else:
157 return super().any()
159 def all_set(self) -> bool:
160 if self.modified:
161 return super().__getitem__(slice(self.offset, self.offset + self.length, None)).all()
162 else:
163 return super().all()
165 def __len__(self) -> int:
166 if self.modified:
167 return self.length
168 return super().__len__()
170 # Default to the MSB0 methods (mainly to stop mypy from complaining)
171 getslice = getslice_msb0
172 getindex = getindex_msb0
173 __setitem__ = bitarray.bitarray.__setitem__
174 __delitem__ = bitarray.bitarray.__delitem__