Coverage for src/bluetooth_sig/gatt/characteristics/utils/bit_field_utils.py: 97%
183 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""Bit field manipulation and flag handling utilities."""
3from __future__ import annotations
6class BitPositions: # pylint: disable=too-few-public-methods
7 """Common bit position constants for flag manipulation."""
9 # Common flag bit positions
10 BIT_0 = 1 << 0
11 BIT_1 = 1 << 1
12 BIT_2 = 1 << 2
13 BIT_3 = 1 << 3
14 BIT_4 = 1 << 4
15 BIT_5 = 1 << 5
16 BIT_6 = 1 << 6
17 BIT_7 = 1 << 7
20class BitFieldUtils: # pylint: disable=too-many-public-methods
21 """Utility class for bit field manipulation and flag handling."""
23 # Common bit operation constants
24 SINGLE_BIT_MASK = 1
25 DEFAULT_BIT_WIDTH = 32
27 @staticmethod
28 def extract_bit_field(value: int, start_bit: int, num_bits: int) -> int:
29 """Extract a bit field from an integer value."""
30 if num_bits <= 0 or start_bit < 0:
31 raise ValueError("Invalid bit field parameters")
32 mask = (1 << num_bits) - 1
33 return (value >> start_bit) & mask
35 @staticmethod
36 def set_bit_field(value: int, field_value: int, start_bit: int, num_bits: int) -> int:
37 """Set a bit field in an integer value."""
38 if num_bits <= 0 or start_bit < 0:
39 raise ValueError("Invalid bit field parameters")
40 mask = (BitFieldUtils.SINGLE_BIT_MASK << num_bits) - 1
41 if field_value > mask:
42 raise ValueError(f"Field value {field_value} exceeds {num_bits}-bit capacity")
43 # Clear the field and set new value
44 value &= ~(mask << start_bit)
45 value |= (field_value & mask) << start_bit
46 return value
48 @staticmethod
49 def create_bitmask(start_bit: int, num_bits: int) -> int:
50 """Create a bitmask for a specific bit field range."""
51 if num_bits <= 0 or start_bit < 0:
52 raise ValueError("Invalid bitmask parameters")
53 mask = (1 << num_bits) - 1
54 return mask << start_bit
56 @staticmethod
57 def test_bit(value: int, bit_position: int) -> bool:
58 """Test if a specific bit is set in the value."""
59 if bit_position < 0:
60 raise ValueError("Bit position must be non-negative")
61 return bool(value & (BitFieldUtils.SINGLE_BIT_MASK << bit_position))
63 @staticmethod
64 def test_bits_any(value: int, bitmask: int) -> bool:
65 """Test if any bits in the bitmask are set in the value."""
66 return bool(value & bitmask)
68 @staticmethod
69 def test_bits_all(value: int, bitmask: int) -> bool:
70 """Test if all bits in the bitmask are set in the value."""
71 return (value & bitmask) == bitmask
73 @staticmethod
74 def set_bit(value: int, bit_position: int) -> int:
75 """Set a specific bit in the value."""
76 if bit_position < 0:
77 raise ValueError("Bit position must be non-negative")
78 return value | (BitFieldUtils.SINGLE_BIT_MASK << bit_position)
80 @staticmethod
81 def clear_bit(value: int, bit_position: int) -> int:
82 """Clear a specific bit in the value."""
83 if bit_position < 0:
84 raise ValueError("Bit position must be non-negative")
85 return value & ~(BitFieldUtils.SINGLE_BIT_MASK << bit_position)
87 @staticmethod
88 def toggle_bit(value: int, bit_position: int) -> int:
89 """Toggle a specific bit in the value."""
90 if bit_position < 0:
91 raise ValueError("Bit position must be non-negative")
92 return value ^ (BitFieldUtils.SINGLE_BIT_MASK << bit_position)
94 @staticmethod
95 def extract_bits(value: int, bitmask: int) -> int:
96 """Extract bits from value using a bitmask."""
97 return value & bitmask
99 @staticmethod
100 def set_bits(value: int, bitmask: int) -> int:
101 """Set all bits specified in the bitmask."""
102 return value | bitmask
104 @staticmethod
105 def clear_bits(value: int, bitmask: int) -> int:
106 """Clear all bits specified in the bitmask."""
107 return value & ~bitmask
109 @staticmethod
110 def toggle_bits(value: int, bitmask: int) -> int:
111 """Toggle all bits specified in the bitmask."""
112 return value ^ bitmask
114 @staticmethod
115 def count_set_bits(value: int) -> int:
116 """Count the number of set bits in the value."""
117 count = 0
118 while value:
119 count += value & 1
120 value >>= 1
121 return count
123 @staticmethod
124 def get_bit_positions(value: int) -> list[int]:
125 """Get a list of positions of all set bits in the value."""
126 positions: list[int] = []
127 bit_pos = 0
128 while value:
129 if value & 1:
130 positions.append(bit_pos)
131 value >>= 1
132 bit_pos += 1
133 return positions
135 @staticmethod
136 def find_first_set_bit(value: int) -> int | None:
137 """Find the position of the first (least significant) set bit."""
138 if value == 0:
139 return None
140 position = 0
141 while (value & 1) == 0:
142 value >>= 1
143 position += 1
144 return position
146 @staticmethod
147 def find_last_set_bit(value: int) -> int | None:
148 """Find the position of the last (most significant) set bit."""
149 if value == 0:
150 return None
151 position = 0
152 while value > 1:
153 value >>= 1
154 position += 1
155 return position
157 @staticmethod
158 def reverse_bits(value: int, bit_width: int = DEFAULT_BIT_WIDTH) -> int:
159 """Reverse the bits in a value within the specified bit width."""
160 result = 0
161 for i in range(bit_width):
162 if value & (1 << i):
163 result |= 1 << (bit_width - 1 - i)
164 return result
166 @staticmethod
167 def calculate_parity(value: int) -> int:
168 """Calculate the parity (even/odd) of set bits.
170 Returns 0 for even, 1 for odd.
171 """
172 parity = 0
173 while value:
174 parity ^= value & 1
175 value >>= 1
176 return parity
178 @staticmethod
179 def validate_bit_field_range(start_bit: int, num_bits: int, total_bits: int = DEFAULT_BIT_WIDTH) -> bool:
180 """Validate that a bit field range is within bounds."""
181 return start_bit >= 0 and num_bits > 0 and start_bit + num_bits <= total_bits
183 @staticmethod
184 def copy_bit_field(source: int, dest: int, source_start: int, dest_start: int, num_bits: int) -> int:
185 """Copy a bit field from source to destination."""
186 field_value = BitFieldUtils.extract_bit_field(source, source_start, num_bits)
187 return BitFieldUtils.set_bit_field(dest, field_value, dest_start, num_bits)
189 @staticmethod
190 def shift_bit_field_left(value: int, start_bit: int, num_bits: int, shift_amount: int) -> int:
191 """Shift a bit field left within the value."""
192 if shift_amount <= 0:
193 return value
194 field_value = BitFieldUtils.extract_bit_field(value, start_bit, num_bits)
195 # Clear the original field
196 value = BitFieldUtils.set_bit_field(value, 0, start_bit, num_bits)
197 # Set the shifted field
198 new_start = start_bit + shift_amount
199 return BitFieldUtils.set_bit_field(value, field_value, new_start, num_bits)
201 @staticmethod
202 def shift_bit_field_right(value: int, start_bit: int, num_bits: int, shift_amount: int) -> int:
203 """Shift a bit field right within the value."""
204 if shift_amount <= 0:
205 return value
206 field_value = BitFieldUtils.extract_bit_field(value, start_bit, num_bits)
207 # Clear the original field
208 value = BitFieldUtils.set_bit_field(value, 0, start_bit, num_bits)
209 # Set the shifted field
210 new_start = max(0, start_bit - shift_amount)
211 return BitFieldUtils.set_bit_field(value, field_value, new_start, num_bits)
213 @staticmethod
214 def merge_bit_fields(*fields: tuple[int, int, int]) -> int:
215 """Merge multiple bit fields into a single value.
217 Args:
218 fields: Tuples of (field_value, start_bit, num_bits)
220 """
221 result = 0
222 for field_value, start_bit, num_bits in fields:
223 result = BitFieldUtils.set_bit_field(result, field_value, start_bit, num_bits)
224 return result
226 @staticmethod
227 def split_bit_field(value: int, *field_specs: tuple[int, int]) -> list[int]:
228 """Split a value into multiple bit fields.
230 Args:
231 value: The value to split
232 field_specs: Tuples of (start_bit, num_bits) for each field
234 Returns:
235 List of extracted field values
237 """
238 return [BitFieldUtils.extract_bit_field(value, start_bit, num_bits) for start_bit, num_bits in field_specs]
240 @staticmethod
241 def compare_bit_fields(value1: int, value2: int, start_bit: int, num_bits: int) -> int:
242 """Compare bit fields between two values.
244 Returns -1, 0, or 1.
245 """
246 field1 = BitFieldUtils.extract_bit_field(value1, start_bit, num_bits)
247 field2 = BitFieldUtils.extract_bit_field(value2, start_bit, num_bits)
248 if field1 < field2:
249 return -1
250 if field1 > field2:
251 return 1
252 return 0
254 @staticmethod
255 def rotate_left(value: int, positions: int, bit_width: int = DEFAULT_BIT_WIDTH) -> int:
256 """Rotate bits left by the specified number of positions."""
257 positions = positions % bit_width
258 if positions == 0:
259 return value
260 mask = (1 << bit_width) - 1
261 value &= mask
262 return ((value << positions) | (value >> (bit_width - positions))) & mask
264 @staticmethod
265 def rotate_right(value: int, positions: int, bit_width: int = DEFAULT_BIT_WIDTH) -> int:
266 """Rotate bits right by the specified number of positions."""
267 positions = positions % bit_width
268 if positions == 0:
269 return value
270 mask = (1 << bit_width) - 1
271 value &= mask
272 return ((value >> positions) | (value << (bit_width - positions))) & mask
274 @staticmethod
275 def extract_bit_field_from_mask(value: int, mask: int, shift: int) -> int:
276 """Extract a bit field using a mask and shift amount.
278 Args:
279 value: The value to extract from
280 mask: The base mask (e.g., 0x0F for 4 bits)
281 shift: How many bits to shift the mask left
283 Returns:
284 The extracted bit field value
286 """
287 shifted_mask = mask << shift
288 return BitFieldUtils.extract_bits(value, shifted_mask) >> shift