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))