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) 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(128, 8) Traceback (most recent call last): ... ValueError: value 128 (i9) too large for i8 field [-128;127] >>> i(-129, 8) Traceback (most recent call last): ... ValueError: value -129 (i9) too large for i8 field [-128;127] """ min = -(1 << (bits - 1)) max = -1 - min if num > max or num < min: raise ValueError("value {} (i{}) too large for i{} field [{};{}]" .format(num, num.bit_length()+1, bits, min, max)) 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))