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

Nic30 / pyMathBitPrecise / 2fd037fb-a15f-4333-8cc6-71ed98b25546

20 Nov 2024 08:42PM UTC coverage: 73.307% (-2.7%) from 76.055%
2fd037fb-a15f-4333-8cc6-71ed98b25546

push

circleci

Nic30
fix(Bits3t): repr and __int__ of negative signed numbers

200 of 296 branches covered (67.57%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

81 existing lines in 2 files now uncovered.

742 of 989 relevant lines covered (75.03%)

0.75 hits per line

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

61.79
/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 rotate_right(v: int, width: int, shAmount:int):
1✔
247
    # https://www.geeksforgeeks.org/rotate-bits-of-an-integer/
UNCOV
248
    assert v >= 0, v
×
UNCOV
249
    assert width > 0, width
×
UNCOV
250
    assert shAmount >= 0, shAmount
×
UNCOV
251
    return (v >> shAmount) | ((v << (width - shAmount)) & mask(width))
×
252

253

254
def rotate_left(v: int, width: int, shAmount:int):
1✔
255
    # https://www.geeksforgeeks.org/rotate-bits-of-an-integer/
UNCOV
256
    assert v >= 0, v
×
UNCOV
257
    assert width > 0, width
×
UNCOV
258
    assert shAmount >= 0, shAmount
×
UNCOV
259
    return ((v << shAmount) & mask(width)) | (v >> (width - shAmount))
×
260

261

262
def bit_list_reversed_endianity(bitList: List[Literal[0, 1]], extend=True):
1✔
263
    w = len(bitList)
1✔
264
    i = w
1✔
265

266
    items = []
1✔
267
    while i > 0:
1✔
268
        # take last 8 bytes or rest
269
        lower = max(i - 8, 0)
1✔
270
        b = bitList[lower:i]
1✔
271
        if extend:
1!
272
            extend_to_size(b, 8)
1✔
273
        items.extend(b)
1✔
274
        i -= 8
1✔
275

276
    return items
1✔
277

278

279
def bit_list_reversed_bits_in_bytes(bitList: List[Literal[0, 1]], extend=None):
1✔
280
    "Byte reflection  (0x0f -> 0xf0)"
281
    w = len(bitList)
1✔
282
    if extend is None:
1!
283
        assert w % 8 == 0
1✔
284
    tmp = []
1✔
285
    for db in grouper(8, bitList, padvalue=0):
1✔
286
        tmp.extend(reversed(db))
1✔
287

288
    if not extend and len(tmp) != w:
1!
289
        rem = w % 8
×
290
        # rm zeros from [0, 0, 0, 0, 0, d[2], d[1], d[0]] like
UNCOV
291
        tmp = tmp[:w - rem] + tmp[-rem:]
×
292

293
    return tmp
1✔
294

295

296
def bytes_to_bit_list_lower_bit_first(bytes_: bytes) -> List[Literal[0, 1]]:
1✔
297
    """
298
    b'\x01' to [1, 0, 0, 0, 0, 0, 0, 0]
299
    """
300
    result: List[Literal[0, 1]] = []
×
UNCOV
301
    for byte in bytes_:
×
UNCOV
302
        for _ in range(8):
×
UNCOV
303
            result.append(byte & 0b1)
×
UNCOV
304
            byte >>= 1
×
UNCOV
305
    return result
×
306

307

308
def bytes_to_bit_list_upper_bit_first(bytes_: bytes) -> List[Literal[0, 1]]:
1✔
309
    """
310
    b'\x01' to [0, 0, 0, 0, 0, 0, 0, 1]
311
    """
UNCOV
312
    result: List[Literal[0, 1]] = []
×
UNCOV
313
    for byte in bytes_:
×
UNCOV
314
        for _ in range(8):
×
UNCOV
315
            result.append((byte & 0x80) >> 7)
×
UNCOV
316
            byte <<= 1
×
UNCOV
317
    return result
×
318

319

320
def byte_list_to_be_int(_bytes: List[Literal[0, 1, 2, 3, 4, 5, 6, 7]]):
1✔
321
    """
322
    In input list LSB first, in result little endian ([1, 0] -> 0x0001)
323
    """
UNCOV
324
    return int_list_to_int(_bytes, 8)
×
325

326

327
def bit_list_to_int(bitList: List[Literal[0, 1]]):
1✔
328
    """
329
    In input list LSB first, in result little endian ([0, 1] -> 0b10)
330
    """
331
    res = 0
1✔
332
    for i, r in enumerate(bitList):
1✔
333
        res |= (r & 0x1) << i
1✔
334
    return res
1✔
335

336

337
def bit_list_to_bytes(bitList: List[Literal[0, 1]]) -> bytes:
1✔
UNCOV
338
    byteCnt = len(bitList) // 8
×
UNCOV
339
    if len(bitList) % 8:
×
UNCOV
340
        byteCnt += 1
×
UNCOV
341
    return bit_list_to_int(bitList).to_bytes(byteCnt, 'big')
×
342

343

344
def int_list_to_int(il: List[int], item_width: int):
1✔
345
    """
346
    [0x0201, 0x0403] -> 0x04030201
347
    """
348
    v = 0
1✔
349
    for i, b in enumerate(il):
1✔
350
        v |= b << (i * item_width)
1✔
351

352
    return v
1✔
353

354

355
def int_to_int_list(v: int, item_width: int, number_of_items: int):
1✔
356
    """
357
    opposite of :func:`~.int_list_to_int`
358
    """
359
    item_mask = mask(item_width)
1✔
360
    res = []
1✔
361
    for _ in range(number_of_items):
1✔
362
        res.append(v & item_mask)
1✔
363
        v >>= item_width
1✔
364

365
    assert v == 0, ("there should be nothing left, the value is larger", v)
1✔
366
    return res
1✔
367

368

369
def reverse_byte_order(val: "Bits3val"):
1✔
370
    """
371
    Reverse byteorder (littleendian/bigendian) of signal or value
372
    """
UNCOV
373
    w = val._dtype.bit_length()
×
UNCOV
374
    i = w
×
375
    items = []
×
376

UNCOV
377
    while i > 0:
×
378
        # take last 8 bytes or rest
UNCOV
379
        lower = max(i - 8, 0)
×
380
        items.append(val[i:lower])
×
381
        i -= 8
×
382

383
    # Concat(*items)
384
    top = None
×
UNCOV
385
    for s in items:
×
386
        if top is None:
×
387
            top = s
×
388
        else:
389
            top = top._concat(s)
×
390
    return top
×
391

392

393
def is_power_of_2(v: Union["Bits3val", int]):
1✔
UNCOV
394
    return ~(v & (v - 1))
×
395

396

397
def next_power_of_2(v: Union["Bits3val", int], width:Optional[int]=None):
1✔
398
    # depend on the fact that v < 2^width
UNCOV
399
    v = v - 1
×
UNCOV
400
    if isinstance(v, int):
×
UNCOV
401
        v = to_unsigned(v, width)
×
402
    else:
UNCOV
403
        width = v._dtype.bit_length()
×
404

UNCOV
405
    i = 1
×
UNCOV
406
    while True:
×
UNCOV
407
        v |= (v >> i)  # 1, 2, 4, 8, 16 for 32b
×
UNCOV
408
        i <<= 1
×
UNCOV
409
        if i > width // 2:
×
UNCOV
410
            break
×
411

UNCOV
412
    v = v + 1
×
413

UNCOV
414
    if isinstance(v, int):
×
UNCOV
415
        v &= mask(width)
×
UNCOV
416
    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