class Bitfield(object):
def __init__(self, val, size, extra=()):
if val < 0:
min = -(1 << (size - 1))
max = -1 - min
if val > max or val < min:
raise ValueError(
"value {} out of i{} range [{};{}]".format(val, size, min, max)
)
if val < 0:
val = (1 << size) + val
self.val = val
self.size = size
self.extra = extra
def __repr__(self):
"""format for debugging.
>>> Bitfield(0, 8)
Bitfield(0x0, 8)
>>> Bitfield(16, 8)
Bitfield(0x10, 8)
>>> Bitfield(-16, 8)
Bitfield(0xf0, 8)
>>> Bitfield(3, 5)
Bitfield(0x3, 5)
>>> Bitfield(3, 4, extra=('hello',))
Bitfield(0x3, 4, extra=('hello',))
"""
extra = ""
if self.extra:
extra = ", extra={!r}".format(self.extra)
return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra)
def __str__(self):
"""format as a word.
>>> str(Bitfield(0, 8))
'00/8'
>>> str(Bitfield(16, 8))
'10/8'
>>> str(Bitfield(3, 8))
'03/8'
>>> str(Bitfield(3, 5))
'03/5'
>>> str(Bitfield(3, 4))
'3/4'
>>> str(Bitfield(3, 4, extra=('hello',)))
'3/hello/4'
"""
digits = 1 + ((self.size - 1) // 4)
base = "{:x}".format(self.val)
base = (digits - len(base)) * "0" + base
return "/".join([base, *self.extra, str(self.size)])
def __getitem__(self, range):
"""slice a bitfield given hi and lo bit indices.
>>> Bitfield(0x7f, 8)[7:4]
Bitfield(0x7, 4)
>>> Bitfield(0b100, 3)[2:2]
Bitfield(0x1, 1)
>>> Bitfield(0x12345678, 32)[7:0]
Bitfield(0x78, 8)
>>> Bitfield(0x12345678, 32)[15:8]
Bitfield(0x56, 8)
>>> Bitfield(0x12345678, 32)[23:16]
Bitfield(0x34, 8)
>>> Bitfield(0xf, 4)[4:0]
Traceback (most recent call last):
...
ValueError: slice [4:0] out of range of f/4
>>> Bitfield(0xf, 4)[2:3]
Traceback (most recent call last):
...
ValueError: cant slice reverse range
>>> Bitfield(0xf, 4)[3:-1]
Traceback (most recent call last):
...
ValueError: slice [3:-1] out of range of f/4
"""
if not isinstance(range, slice):
range = slice(range, range)
hi, lo = range.start, range.stop
if hi < lo:
raise ValueError("cant slice reverse range")
elif lo < 0 or hi >= self.size:
raise ValueError(
"slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
)
size = hi - lo + 1
val = (self.val >> lo) & ((1 << size) - 1)
return Bitfield(val, size)
def slice_allowempty(self, range):
if not isinstance(range, slice):
range = slice(range, range)
hi, lo = range.start, range.stop
if hi < lo:
return empty
return self[range]
def __and__(self, other):
"""concatenate multiple bitfields.
>>> Bitfield(0b00, 2) & Bitfield(0b10, 2)
Bitfield(0x2, 4)
>>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1)
Bitfield(0xcd, 8)
"""
if isinstance(other, Bitfield):
return Bitfield(self.val << other.size | other.val, self.size + other.size)
raise NotImplementedError()
global_slice = slice
empty = Bitfield(0, 0)
def u(num, bits):
"""parse an unsigned integer into a bitfield.
>>> u(0x08, 8)
Bitfield(0x8, 8)
>>> u(0xff, 8)
Bitfield(0xff, 8)
>>> u(0xf0, 7)
Traceback (most recent call last):
...
ValueError: value 240 (u8) too large for u7 field
>>> u(-1, 8)
Traceback (most recent call last):
...
ValueError: negative value not allowed: -1
"""
if num < 0:
raise ValueError("negative value not allowed: {}".format(num))
if num.bit_length() > bits:
raise ValueError(
"value {} (u{}) too large for u{} field".format(num, num.bit_length(), bits)
)
return Bitfield(num, bits)
def i(num, bits):
"""parse a signed integer into a bitfield.
>>> i(8, 8)
Bitfield(0x8, 8)
>>> i(-4, 8)
Bitfield(0xfc, 8)
>>> i(0x7f, 8)
Bitfield(0x7f, 8)
>>> i(-128, 8)
Bitfield(0x80, 8)
>>> i(256, 8)
Traceback (most recent call last):
...
ValueError: value 256 (u9) too large for u8 field
>>> i(-129, 8)
Traceback (most recent call last):
...
ValueError: value -129 (i9) too small for i8 field (min -128)
"""
min = -(1 << (bits - 1))
if num < min:
raise ValueError(
"value {} (i{}) too small for i{} field (min {})".format(
num, num.bit_length() + 1, bits, min
)
)
if num < 0:
num = (1 << bits) + num
return u(num, bits)
def from_part(part):
"""parse a size-tagged subv part into a bit.
>>> from_part((0x12, 2))
Bitfield(0x12, 2)
>>> from_part((0x12, 2, 'extra'))
Bitfield(0x12, 2)
"""
val = int(part[0])
size = part[1]
return Bitfield(val, int(size))