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

Nic30 / pyMathBitPrecise / 2bf0944d-4bd7-490c-b58f-4459c23eef14

19 Oct 2024 11:20AM UTC coverage: 77.639% (-2.6%) from 80.19%
2bf0944d-4bd7-490c-b58f-4459c23eef14

push

circleci

Nic30
feat:  Bits3t.__rshift__ fast case

200 of 276 branches covered (72.46%)

Branch coverage included in aggregate %.

7 of 9 new or added lines in 1 file covered. (77.78%)

102 existing lines in 2 files now uncovered.

734 of 927 relevant lines covered (79.18%)

0.79 hits per line

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

69.71
/pyMathBitPrecise/bit_utils.py
1
#!/usr/bin/env python3
2
# -*- coding: UTF-8 -*-
3
from typing import List, Tuple, Generator, Union, Optional
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:
UNCOV
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
    """
UNCOV
110
    return val & (mask(width - lowerBitCntToAlign) << lowerBitCntToAlign)
×
111

112

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

146
    if w != 0:
×
UNCOV
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:
UNCOV
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, width):
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, items, 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, 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[int], 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!
UNCOV
273
        rem = w % 8
×
274
        # rm zeros from [0, 0, 0, 0, 0, d[2], d[1], d[0]] like
UNCOV
275
        tmp = tmp[:w - rem] + tmp[-rem:]
×
276

277
    return tmp
1✔
278

279

280
def byte_list_to_be_int(_bytes: List[int]):
1✔
281
    """
282
    In input list LSB first, in result little endian ([1, 0] -> 0x0001)
283
    """
UNCOV
284
    return int_list_to_int(_bytes, 8)
×
285

286

287
def bit_list_to_int(bitList: List[int]):
1✔
288
    """
289
    In input list LSB first, in result little endian ([0, 1] -> 0b10)
290
    """
291
    res = 0
1✔
292
    for i, r in enumerate(bitList):
1✔
293
        res |= (r & 0x1) << i
1✔
294
    return res
1✔
295

296

297
def int_list_to_int(il: List[int], item_width: int):
1✔
298
    """
299
    [0x0201, 0x0403] -> 0x04030201
300
    """
301
    v = 0
1✔
302
    for i, b in enumerate(il):
1✔
303
        v |= b << (i * item_width)
1✔
304

305
    return v
1✔
306

307

308
def int_to_int_list(v: int, item_width: int, number_of_items: int):
1✔
309
    """
310
    opposite of :func:`~.int_list_to_int`
311
    """
312
    item_mask = mask(item_width)
1✔
313
    res = []
1✔
314
    for _ in range(number_of_items):
1✔
315
        res.append(v & item_mask)
1✔
316
        v >>= item_width
1✔
317

318
    assert v == 0, ("there should be nothing left, the value is larger", v)
1✔
319
    return res
1✔
320

321

322
def reverse_byte_order(val: "Bits3val"):
1✔
323
    """
324
    Reverse byteorder (littleendian/bigendian) of signal or value
325
    """
326
    w = val._dtype.bit_length()
×
327
    i = w
×
UNCOV
328
    items = []
×
329

UNCOV
330
    while i > 0:
×
331
        # take last 8 bytes or rest
332
        lower = max(i - 8, 0)
×
333
        items.append(val[i:lower])
×
UNCOV
334
        i -= 8
×
335
    
336
    # Concat(*items)
337
    top = None
×
338
    for s in items:
×
339
        if top is None:
×
UNCOV
340
            top = s
×
341
        else:
342
            top = top._concat(s)
×
UNCOV
343
    return top
×
344

345
def is_power_of_2(v: Union["Bits3val", int]):
1✔
UNCOV
346
    return ~(v & (v - 1))
×
347

348
def next_power_of_2(v: Union["Bits3val", int], width:Optional[int]=None):
1✔
349
    # depend on the fact that v < 2^width
UNCOV
350
    v = v - 1
×
UNCOV
351
    if isinstance(v, int):
×
UNCOV
352
        v = to_unsigned(v, width)
×
353
    else:
UNCOV
354
        width = v._dtype.bit_length()
×
355

UNCOV
356
    i = 1
×
UNCOV
357
    while True:
×
UNCOV
358
        v |= (v >> i) # 1, 2, 4, 8, 16 for 32b
×
UNCOV
359
        i <<= 1
×
UNCOV
360
        if i > width // 2:
×
UNCOV
361
            break
×
362
    
UNCOV
363
    v = v + 1
×
364

UNCOV
365
    if isinstance(v, int):
×
UNCOV
366
        v &= mask(width)
×
UNCOV
367
    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