diff options
Diffstat (limited to 'bits.py')
| -rw-r--r-- | bits.py | 158 |
1 files changed, 88 insertions, 70 deletions
@@ -1,12 +1,86 @@ -empty = (0, 0) +class Bitfield(object): + def __init__(self, val, size): + self.val = val + self.size = size + + def __repr__(self): + return "Bitfield(0x{:x}, {})".format(self.val, self.size) + + def __str__(self): + return "0x{:x}'{}".format(self.val, 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 0xf'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 0xf'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 not isinstance(other, Bitfield): + raise NotImplementedError() + + return Bitfield(self.val << other.size | other.val, self.size + other.size) + +empty = Bitfield(0, 0) def u(num, bits): """ parse an unsigned integer into a bitfield. >>> u(0x08, 8) - (8, 8) + Bitfield(0x8, 8) >>> u(0xff, 8) - (255, 8) + Bitfield(0xff, 8) >>> u(0xf0, 7) Traceback (most recent call last): @@ -24,19 +98,19 @@ def u(num, bits): raise ValueError("value {} (u{}) too large for u{} field" .format(num, num.bit_length(), bits)) - return (num, bits) + return Bitfield(num, bits) def i(num, bits): """ parse a signed integer into a bitfield. >>> i(8, 8) - (8, 8) + Bitfield(0x8, 8) >>> i(-4, 8) - (252, 8) - >>> i(127, 8) - (127, 8) + Bitfield(0xfc, 8) + >>> i(0x7f, 8) + Bitfield(0x7f, 8) >>> i(-128, 8) - (128, 8) + Bitfield(0x80, 8) >>> i(128, 8) Traceback (most recent call last): @@ -62,67 +136,11 @@ def i(num, bits): def from_part(part): """ parse a size-tagged subv part into a bit. - >>> from_part((34, 2)) - (34, 2) - >>> from_part((34, 2, 'extra')) - (34, 2) + >>> from_part((0x12, 2)) + Bitfield(0x12, 2) + >>> from_part((0x12, 2, 'extra')) + Bitfield(0x12, 2) """ val = int(part[0]) size = part[1] - return (val, int(size)) - -def concat(*parts): - """ concatenate multiple bitfields. - - >>> concat((0b10, 2), (0b00, 2)) - (2, 4) - >>> concat((0b1, 1), (0b0110, 4), (0b110, 3)) - (205, 8) - """ - val, size = 0, 0 - for (pval, psize) in parts: - val = val | pval << size - size += psize - return (val, size) - -def slice(bits, hi, lo, allow_empty=False): - """ slice a bitfield given hi and lo bit indices. - - >>> slice((0x7f, 8), 7, 4) - (7, 4) - >>> slice((0b100, 3), 2, 2) - (1, 1) - >>> slice((0x12345678, 32), 7, 0) - (120, 8) - >>> slice((0x12345678, 32), 15, 8) - (86, 8) - >>> slice((0x12345678, 32), 23, 16) - (52, 8) - - >>> slice((0xf, 4), 4, 0) - Traceback (most recent call last): - ... - ValueError: slice [4:0] out of range (4 bit value) - >>> slice((0xf, 4), 2, 3) - Traceback (most recent call last): - ... - ValueError: cant slice reverse range - >>> slice((0xf, 4), 2, 3, allow_empty=True) - (0, 0) - >>> slice((0xf, 4), 3, -1) - Traceback (most recent call last): - ... - ValueError: slice [3:-1] out of range (4 bit value) - """ - (val, size) = bits - - if hi < lo: - if allow_empty: - return empty - raise ValueError("cant slice reverse range") - elif lo < 0 or hi >= size: - raise ValueError("slice [{}:{}] out of range ({} bit value)".format(hi, lo, size)) - - size = hi - lo + 1 - val = (val >> lo) & ((1 << size) - 1) - return (val, size) + return Bitfield(val, int(size)) |
