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

1from __future__ import annotations 

2 

3import bitarray 

4from bitstring.exceptions import CreationError 

5from typing import Union, Iterable, Optional, overload 

6 

7 

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) 

16 

17 

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) 

26 

27 

28class BitStore(bitarray.bitarray): 

29 """A light wrapper around bitarray that does the LSB0 stuff""" 

30 

31 __slots__ = ('modified', 'length', 'offset', 'filename', 'immutable') 

32 

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. 

47 

48 self.length = super().__len__() - self.offset if length is None else length 

49 

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.") 

56 

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 

71 

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) 

76 

77 def __add__(self, other: bitarray.bitarray) -> BitStore: 

78 assert not self.immutable 

79 return BitStore(super().__add__(other)) 

80 

81 def __iter__(self) -> Iterable[bool]: 

82 for i in range(len(self)): 

83 yield self.getindex(i) 

84 

85 def copy(self) -> BitStore: 

86 x = BitStore(self.getslice(slice(None, None, None))) 

87 return x 

88 

89 def __getitem__(self, item: Union[int, slice]) -> Union[int, BitStore]: 

90 # Use getindex or getslice instead 

91 raise NotImplementedError 

92 

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)) 

97 

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)) 

102 

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)) 

107 

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)) 

114 

115 @overload 

116 def setitem_lsb0(self, key: int, value: int) -> None: 

117 ... 

118 

119 @overload 

120 def setitem_lsb0(self, key: slice, value: BitStore) -> None: 

121 ... 

122 

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) 

130 

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) 

138 

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() 

145 

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() 

152 

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() 

158 

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() 

164 

165 def __len__(self) -> int: 

166 if self.modified: 

167 return self.length 

168 return super().__len__() 

169 

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__