• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Nic30 / pyMathBitPrecise / 1021c388-66bb-4d89-a469-3f139b55e4e2

26 Oct 2024 02:03PM UTC coverage: 76.055% (-1.6%) from 77.639%
1021c388-66bb-4d89-a469-3f139b55e4e2

push

circleci

Nic30
style(Bits3t): better err msgs

200 of 286 branches covered (69.93%)

Branch coverage included in aggregate %.

0 of 1 new or added line in 1 file covered. (0.0%)

36 existing lines in 1 file now uncovered.

737 of 946 relevant lines covered (77.91%)

0.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.33
/pyMathBitPrecise/bit_utils.py
1
#!/usr/bin/env python3
2
# -*- coding: UTF-8 -*-
3
from typing import List, Tuple, Generator, Union, Optional, Literal, Sequence
1✔
4

5
from pyMathBitPrecise.utils import grouper
1✔
6

7

8
def mask(bits: int) -> int:
1✔
9
    """
10
    Generate mask of specified size (sequence of '1')
11
    """
12
    return (1 << bits) - 1
1✔
13

14

15
def bit_field(_from: int, to: int) -> int:
1✔
16
    """
17
    Generate int which has bits '_from' to 'to' set to '1'
18

19
    :note: _from 0 to 1 -> '1'
20
    """
21
    w = to - _from
1✔
22
    return mask(w) << _from
1✔
23

24

25
def get_bit(val: int, bitNo: int) -> int:
1✔
26
    """
27
    Get bit from int
28
    """
29
    return (val >> bitNo) & 1
1✔
30

31

32
def get_bit_range(val: int, bitsStart: int, bitsLen: int) -> int:
1✔
33
    """
34
    Get sequence of bits from an int value
35
    """
36
    val >>= bitsStart
1✔
37
    return val & mask(bitsLen)
1✔
38

39

40
def clean_bit(val: int, bitNo: int) -> int:
1✔
41
    """
42
    Set a specified bit to '0'
43
    """
44
    return val & ~(1 << bitNo)
1✔
45

46

47
def set_bit(val: int, bitNo: int) -> int:
1✔
48
    """
49
    Set a specified bit to '1'
50
    """
51
    return val | (1 << bitNo)
1✔
52

53

54
def toggle_bit(val: int, bitNo: int) -> int:
1✔
55
    """
56
    Toggle specified bit in int
57
    """
58
    return val ^ (1 << bitNo)
1✔
59

60

61
def set_bit_range(val: int, bitStart: int, bitsLen: int, newBits: int) -> int:
1✔
62
    """
63
    Set specified range of bits in int to a specified value
64
    """
65
    _mask = mask(bitsLen)
1✔
66
    newBits &= _mask
1✔
67

68
    _mask <<= bitStart
1✔
69
    newBits <<= bitStart
1✔
70

71
    return (val & ~_mask) | newBits
1✔
72

73

74
def bit_set_to(val: int, bitNo: int, bitVal: int) -> int:
1✔
75
    """
76
    Set specified bit in int to a specified value
77
    """
78
    if bitVal == 0:
1✔
79
        return clean_bit(val, bitNo)
1✔
80
    elif bitVal == 1:
1!
81
        return set_bit(val, bitNo)
1✔
82
    else:
83
        raise ValueError(("Invalid value of bit to set", bitVal))
×
84

85

86
def apply_set_and_clear(val: int, set_flag: int, clear_flag: int):
1✔
87
    """
88
    :param val: an input value of the flag(s)
89
    :param set_flag: a mask of bits to set to 1
90
    :param clear_flag: a mask of bits to set to 0
91
    :note: set has higher priority
92

93
    :return: new value of the flag
94
    """
95
    return (val & ~clear_flag) | set_flag
1✔
96

97

98
def align(val: int, lowerBitCntToAlign: int) -> int:
1✔
99
    """
100
    Cut off lower bits to align a int value.
101
    """
102
    val = val >> lowerBitCntToAlign
1✔
103
    return val << lowerBitCntToAlign
1✔
104

105

106
def align_with_known_width(val, width: int, lowerBitCntToAlign: int):
1✔
107
    """
108
    Does same as :func:`~.align` just with the known width of val
109
    """
110
    return val & (mask(width - lowerBitCntToAlign) << lowerBitCntToAlign)
×
111

112

113
def iter_bits(val: int, length: int) -> Generator[Literal[0, 1], None, None]:
1✔
114
    """
115
    Iterate bits in int. LSB first.
116
    """
117
    for _ in range(length):
1✔
118
        yield val & 1
1✔
119
        val >>= 1
1✔
120

121

122
def iter_bits_sequences(val: int, length: int) -> Generator[Tuple[Literal[0, 1], int], None, None]:
1✔
123
    """
124
    Iter tuples (bitVal, number of same bits), lsb first
125
    """
126
    assert length > 0, length
×
127
    assert val >= 0
×
128
    # start of new bit seqence
129
    w = 1
×
130
    valBit = val & 1
×
131
    val >>= 1
×
132
    foundBit = valBit
×
133
    for _ in range(length - 1):
×
134
        # extract single bit from val
135
        valBit = val & 1
×
136
        val >>= 1
×
137
        # check if it fits into current bit sequence
138
        if valBit == foundBit:
×
139
            w += 1
×
140
        else:
141
            # end of sequence of same bits
142
            yield (foundBit, w)
×
143
            foundBit = valBit
×
144
            w = 1
×
145

146
    if w != 0:
×
147
        yield (foundBit, w)
×
148

149

150
def to_signed(val: int, width: int) -> int:
1✔
151
    """
152
    Convert unsigned int to negative int which has same bits set (emulate sign overflow).
153

154
    :note: bits in value are not changed, just python int object
155
        has signed flag set properly. And number is in expected range.
156
    """
157
    if val > 0:
1✔
158
        msb = 1 << (width - 1)
1✔
159
        if val & msb:
1✔
160
            val -= mask(width) + 1
1✔
161
    return val
1✔
162

163

164
def to_unsigned(val, width) -> int:
1✔
165
    if val < 0:
1!
166
        return val & mask(width)
1✔
167
    else:
168
        return val
×
169

170

171
def mask_bytes(val: int, byte_mask: int, mask_bit_length: int) -> int:
1✔
172
    """
173
    Use each bit in byte_mask as a mask for each byte in val.
174

175
    :note: Useful for masking of value for HW interfaces where mask
176
        is represented by a vector of bits where each bit is mask
177
        for byte in data vector.
178
    """
179
    res = 0
1✔
180
    for i, m in enumerate(iter_bits(byte_mask, mask_bit_length)):
1✔
181
        if m:
1✔
182
            res |= (val & 0xff) << (i * 8)
1✔
183
        val >>= 8
1✔
184
    return res
1✔
185

186

187
INT_BASES = {
1✔
188
    "b": 2,
189
    "o": 8,
190
    "d": 10,
191
    "h": 16,
192
}
193

194

195
class ValidityError(ValueError):
1✔
196
    """
197
    Value is not fully defined and thus can not be used
198
    """
199

200

201
def normalize_slice(s: slice, obj_width: int) -> Tuple[int, int]:
1✔
202
    start, stop, step = s.start, s.stop, s.step
1✔
203
    if step is not None and step != -1:
1✔
204
        raise NotImplementedError(s.step)
205
    else:
206
        step = -1
1✔
207
    if stop is None:
1✔
208
        stop = 0
1✔
209
    else:
210
        stop = int(stop)
1✔
211

212
    if start is None:
1✔
213
        start = int(obj_width)
1✔
214
    else:
215
        start = int(start)
1✔
216
    # n...0
217
    if start <= stop:
1✔
218
        raise IndexError(s)
1✔
219
    firstBitNo = stop
1✔
220
    size = start - stop
1✔
221
    if start < 0 or stop < 0 or size < 0 or start > obj_width:
1✔
222
        raise IndexError(s)
1✔
223

224
    return firstBitNo, size
1✔
225

226

227
def reverse_bits(val: int, width: int):
1✔
228
    """
229
    Reverse bits in integer value of specified width
230
    """
231
    v = 0
1✔
232
    for i in range(width):
1✔
233
        v |= (get_bit(val, width - i - 1) << i)
1✔
234
    return v
1✔
235

236

237
def extend_to_size(collection: Sequence, items: int, pad=0):
1✔
238
    toAdd = items - len(collection)
1✔
239
    assert toAdd >= 0
1✔
240
    for _ in range(toAdd):
1✔
241
        collection.append(pad)
1✔
242

243
    return collection
1✔
244

245

246
def bit_list_reversed_endianity(bitList: List[Literal[0, 1]], extend=True):
1✔
247
    w = len(bitList)
1✔
248
    i = w
1✔
249

250
    items = []
1✔
251
    while i > 0:
1✔
252
        # take last 8 bytes or rest
253
        lower = max(i - 8, 0)
1✔
254
        b = bitList[lower:i]
1✔
255
        if extend:
1!
256
            extend_to_size(b, 8)
1✔
257
        items.extend(b)
1✔
258
        i -= 8
1✔
259

260
    return items
1✔
261

262

263
def bit_list_reversed_bits_in_bytes(bitList: List[Literal[0, 1]], extend=None):
1✔
264
    "Byte reflection  (0x0f -> 0xf0)"
265
    w = len(bitList)
1✔
266
    if extend is None:
1!
267
        assert w % 8 == 0
1✔
268
    tmp = []
1✔
269
    for db in grouper(8, bitList, padvalue=0):
1✔
270
        tmp.extend(reversed(db))
1✔
271

272
    if not extend and len(tmp) != w:
1!
273
        rem = w % 8
×
274
        # rm zeros from [0, 0, 0, 0, 0, d[2], d[1], d[0]] like
275
        tmp = tmp[:w - rem] + tmp[-rem:]
×
276

277
    return tmp
1✔
278

279

280
def bytes_to_bit_list_lower_bit_first(bytes_: bytes) -> List[Literal[0, 1]]:
1✔
281
    """
282
    b'\x01' to [1, 0, 0, 0, 0, 0, 0, 0]
283
    """
284
    result: List[Literal[0, 1]] = []
×
UNCOV
285
    for byte in bytes_:
×
UNCOV
286
        for _ in range(8):
×
UNCOV
287
            result.append(byte & 0b1)
×
UNCOV
288
            byte >>= 1
×
UNCOV
289
    return result
×
290

291
def bytes_to_bit_list_upper_bit_first(bytes_: bytes) -> List[Literal[0, 1]]:
1✔
292
    """
293
    b'\x01' to [0, 0, 0, 0, 0, 0, 0, 1]
294
    """
UNCOV
295
    result: List[Literal[0, 1]] = []
×
UNCOV
296
    for byte in bytes_:
×
UNCOV
297
        for _ in range(8):
×
UNCOV
298
            result.append((byte & 0x80) >> 7)
×
UNCOV
299
            byte <<= 1
×
UNCOV
300
    return result
×
301

302

303
def byte_list_to_be_int(_bytes: List[Literal[0, 1, 2, 3, 4, 5, 6, 7]]):
1✔
304
    """
305
    In input list LSB first, in result little endian ([1, 0] -> 0x0001)
306
    """
UNCOV
307
    return int_list_to_int(_bytes, 8)
×
308

309

310
def bit_list_to_int(bitList: List[Literal[0, 1]]):
1✔
311
    """
312
    In input list LSB first, in result little endian ([0, 1] -> 0b10)
313
    """
314
    res = 0
1✔
315
    for i, r in enumerate(bitList):
1✔
316
        res |= (r & 0x1) << i
1✔
317
    return res
1✔
318

319
def bit_list_to_bytes(bitList: List[Literal[0, 1]]) -> bytes:
1✔
UNCOV
320
    byteCnt = len(bitList) // 8
×
UNCOV
321
    if len(bitList) % 8:
×
UNCOV
322
        byteCnt += 1
×
UNCOV
323
    return bit_list_to_int(bitList).to_bytes(byteCnt, 'big')
×
324

325
def int_list_to_int(il: List[int], item_width: int):
1✔
326
    """
327
    [0x0201, 0x0403] -> 0x04030201
328
    """
329
    v = 0
1✔
330
    for i, b in enumerate(il):
1✔
331
        v |= b << (i * item_width)
1✔
332

333
    return v
1✔
334

335

336
def int_to_int_list(v: int, item_width: int, number_of_items: int):
1✔
337
    """
338
    opposite of :func:`~.int_list_to_int`
339
    """
340
    item_mask = mask(item_width)
1✔
341
    res = []
1✔
342
    for _ in range(number_of_items):
1✔
343
        res.append(v & item_mask)
1✔
344
        v >>= item_width
1✔
345

346
    assert v == 0, ("there should be nothing left, the value is larger", v)
1✔
347
    return res
1✔
348

349

350
def reverse_byte_order(val: "Bits3val"):
1✔
351
    """
352
    Reverse byteorder (littleendian/bigendian) of signal or value
353
    """
354
    w = val._dtype.bit_length()
×
UNCOV
355
    i = w
×
356
    items = []
×
357

358
    while i > 0:
×
359
        # take last 8 bytes or rest
360
        lower = max(i - 8, 0)
×
361
        items.append(val[i:lower])
×
UNCOV
362
        i -= 8
×
363

364
    # Concat(*items)
365
    top = None
×
366
    for s in items:
×
367
        if top is None:
×
UNCOV
368
            top = s
×
369
        else:
UNCOV
370
            top = top._concat(s)
×
UNCOV
371
    return top
×
372

373

374
def is_power_of_2(v: Union["Bits3val", int]):
1✔
UNCOV
375
    return ~(v & (v - 1))
×
376

377

378
def next_power_of_2(v: Union["Bits3val", int], width:Optional[int]=None):
1✔
379
    # depend on the fact that v < 2^width
UNCOV
380
    v = v - 1
×
UNCOV
381
    if isinstance(v, int):
×
UNCOV
382
        v = to_unsigned(v, width)
×
383
    else:
UNCOV
384
        width = v._dtype.bit_length()
×
385

UNCOV
386
    i = 1
×
UNCOV
387
    while True:
×
UNCOV
388
        v |= (v >> i)  # 1, 2, 4, 8, 16 for 32b
×
UNCOV
389
        i <<= 1
×
UNCOV
390
        if i > width // 2:
×
UNCOV
391
            break
×
392

UNCOV
393
    v = v + 1
×
394

UNCOV
395
    if isinstance(v, int):
×
UNCOV
396
        v &= mask(width)
×
UNCOV
397
    return v
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc