469 lines
11 KiB
Python
469 lines
11 KiB
Python
|
|
class Bit:
|
|
"""
|
|
Provide a flexible bit representation
|
|
"""
|
|
|
|
def __init__(self, var):
|
|
if not isinstance(var, (bool, int, str, Bit)):
|
|
raise TypeError("Bit must be one of type (int, bool, str)")
|
|
if isinstance(var, (int, Bit)):
|
|
if var < 0 or var > 1:
|
|
raise ValueError("Bit must be 0 or 1")
|
|
self.__bit = bool(var)
|
|
elif isinstance(var, str):
|
|
if var == "0" or var == "1":
|
|
self.__bit = bool(int(var))
|
|
else:
|
|
raise ValueError('Bit must be "0" or "1"')
|
|
elif isinstance(var, bool):
|
|
self.__bit = var
|
|
|
|
def __str__(self):
|
|
return str(int(self.__bit))
|
|
|
|
def __int__(self):
|
|
return int(self.__bit)
|
|
|
|
def __bool__(self):
|
|
return self.__bit
|
|
|
|
def __repr__(self):
|
|
return f'{self.__class__.__name__}({self.__bit!r})'
|
|
|
|
def __eq__(self, compare):
|
|
# Massage compare to bool
|
|
return bool(self) == bool(Bit(compare))
|
|
|
|
def __ne__(self, compare):
|
|
return bool(self) != bool(Bit(compare))
|
|
|
|
def __lt__(self, compare):
|
|
return bool(self) < bool(Bit(compare))
|
|
|
|
def __le__(self, compare):
|
|
return bool(self) <= bool(Bit(compare))
|
|
|
|
def __gt__(self, compare):
|
|
return bool(self) > bool(Bit(compare))
|
|
|
|
def __ge__(self, compare):
|
|
return bool(self) >= bool(Bit(compare))
|
|
|
|
def __get__(self, instance, owner):
|
|
return self.__bit
|
|
|
|
def __hash__(self):
|
|
return hash(self.__bit)
|
|
|
|
def toggle(self):
|
|
self.__bit = not self.__bit
|
|
|
|
@property
|
|
def lie(self):
|
|
"""
|
|
Return the opposite of bit, without changing the bit state
|
|
"""
|
|
return not self.__bit
|
|
|
|
|
|
class Bits:
|
|
"""
|
|
Provide bit operation helpers.
|
|
"""
|
|
|
|
def __init__(self, var=0, msb_last=False):
|
|
self.__value = 0
|
|
self.__r_to_l = bool(msb_last)
|
|
self.__setvalue(var)
|
|
|
|
def __bytes__(self):
|
|
return bytes([self.__value])
|
|
|
|
def __bool__(self):
|
|
return bool(int(self))
|
|
|
|
def __int__(self):
|
|
return self.__value
|
|
|
|
def __repr__(self):
|
|
return f'{self.__class__.__name__}({self.__value!r})'
|
|
|
|
def __str__(self):
|
|
return self.bin()
|
|
|
|
def __ord__(self):
|
|
return int(self)
|
|
|
|
def __index__(self):
|
|
return int(self)
|
|
|
|
def __len__(self):
|
|
return 8
|
|
|
|
def __eq__(self, compare):
|
|
return int(self) == int(Bits(compare))
|
|
|
|
def __ne__(self, compare):
|
|
return int(self) != int(Bits(compare))
|
|
|
|
def __lt__(self, compare):
|
|
return int(self) < int(Bits(compare))
|
|
|
|
def __le__(self, compare):
|
|
return int(self) <= int(Bits(compare))
|
|
|
|
def __gt__(self, compare):
|
|
return int(self) > int(Bits(compare))
|
|
|
|
def __ge__(self, compare):
|
|
return int(self) >= int(Bits(compare))
|
|
|
|
def __getitem__(self, index):
|
|
return self.list()[index]
|
|
|
|
def __setitem__(self, index, value):
|
|
# we'll take the easy way for now
|
|
l = self.list()
|
|
# str->int->bool->int : accept bool, str, int, return either "0" or "1"
|
|
l[index] = Bit(value)
|
|
self.__setvalue(l)
|
|
|
|
def __get__(self, instance, owner):
|
|
return self.bin()
|
|
|
|
def __add__(self, other):
|
|
return Bits(int(self) + int(Bits(other)))
|
|
|
|
def __sub__(self, other):
|
|
return Bits(int(self) - int(Bits(other)))
|
|
|
|
def __mul__(self, other):
|
|
return Bits(int(self) * int(Bits(other)))
|
|
|
|
def __matmul__(self, other):
|
|
return NotImplemented
|
|
|
|
def __truediv__(self, other):
|
|
return NotImplemented
|
|
|
|
def __floordiv__(self, other):
|
|
return Bits(int(self) // int(Bits(other)))
|
|
|
|
def __mod__(self, other):
|
|
return Bits(int(self) % int(Bits(other)))
|
|
|
|
def __divmod__(self, other):
|
|
return (self // Bits(other), self % Bits(other))
|
|
|
|
def __pow__(self, other, mod=None):
|
|
ret = Bits(0)
|
|
if mod is None:
|
|
ret = Bits(int(self) ** int(Bits(other)))
|
|
else:
|
|
ret = Bits(int(self) ** int(Bits(other)) % int(Bits(mod)))
|
|
return ret
|
|
|
|
def __lshift__(self, other):
|
|
return Bits(int(self) << int(Bits(other)))
|
|
|
|
def __rshift__(self, other):
|
|
return Bits(int(self) >> int(Bits(other)))
|
|
|
|
def __and__(self, other):
|
|
return Bits(int(self) & int(Bits(other)))
|
|
|
|
def __xor__(self, other):
|
|
return Bits(int(self) ^ int(Bits(other)))
|
|
|
|
def __or__(self, other):
|
|
return Bits(int(self) | int(Bits(other)))
|
|
|
|
def __radd__(self, other):
|
|
return Bits(other) + self
|
|
|
|
def __rsub__(self, other):
|
|
return Bits(other) - self
|
|
|
|
def __rmul__(self, other):
|
|
return Bits(other) * self
|
|
|
|
def __rmatmul__(self, other):
|
|
return NotImplemented
|
|
|
|
def __rtruediv__(self, other):
|
|
return NotImplemented
|
|
|
|
def __rfloordiv__(self, other):
|
|
return Bits(other) // self
|
|
|
|
def __rmod__(self, other):
|
|
return Bits(other) % self
|
|
|
|
def __rdivmod__(self, other):
|
|
return (Bits(other) // self, Bits(other) % self)
|
|
|
|
def __rpow__(self, other, mod=None):
|
|
ret = Bits(0)
|
|
if mod is None:
|
|
ret = Bits(other) ** self
|
|
else:
|
|
ret = Bits(other) ** self % Bits(mod)
|
|
return ret
|
|
|
|
def __rlshift__(self, other):
|
|
return Bits(other) << self
|
|
|
|
def __rrshift__(self, other):
|
|
return Bits(other) >> self
|
|
|
|
def __rand__(self, other):
|
|
return Bits(other) & self
|
|
|
|
def __rxor__(self, other):
|
|
return Bits(other) ^ self
|
|
|
|
def __ror__(self, other):
|
|
return Bits(other) | self
|
|
|
|
def __iadd__(self, other):
|
|
self = self + Bits(other)
|
|
return self
|
|
|
|
def __isub__(self, other):
|
|
self = self - Bits(other)
|
|
return self
|
|
|
|
def __imul__(self, other):
|
|
self = self * Bits(other)
|
|
return self
|
|
|
|
def __imatmul__(self, other):
|
|
return NotImplemented
|
|
|
|
def __itruediv__(self, other):
|
|
return NotImplemented
|
|
|
|
def __ifloordiv__(self, other):
|
|
self = self // Bits(other)
|
|
return self
|
|
|
|
def __imod__(self, other):
|
|
self = self % Bits(other)
|
|
return self
|
|
|
|
def __ipow__(self, other):
|
|
self = self ** Bits(other)
|
|
return self
|
|
|
|
def __ilshift__(self, other):
|
|
self = self << Bits(other)
|
|
return self
|
|
|
|
def __irshift__(self, other):
|
|
self = self >> Bits(other)
|
|
return self
|
|
|
|
def __iand__(self, other):
|
|
self = self & Bits(other)
|
|
return self
|
|
|
|
def __ixor__(self, other):
|
|
self = self ^ Bits(other)
|
|
return self
|
|
|
|
def __ior__(self, other):
|
|
self = self | Bits(other)
|
|
return self
|
|
|
|
def __contains__(self, item):
|
|
val = Bits(item) & self
|
|
return val == Bits(item)
|
|
|
|
def __setvalue(self, var):
|
|
self.__value = 0
|
|
if isinstance(var, Bits):
|
|
self.__value = int(var)
|
|
elif isinstance(var, int):
|
|
if var < 0 or var > 255:
|
|
raise ValueError("Integer must be between 0 and 255")
|
|
self.__value = var
|
|
elif isinstance(var, bytes):
|
|
for _byte in var:
|
|
self.__value += _byte
|
|
if ret > 255 or ret < 0:
|
|
raise ValueError("Sum value of bytes must be between 0"
|
|
" and 255")
|
|
elif (len(var) > 0) and (len(var) <=8):
|
|
for bit in range(0, len(var)):
|
|
self.__value += (int(bool(int(var[bit]))) *
|
|
(2 ** (len(var) - 1 - bit)))
|
|
else:
|
|
raise TypeError("Expected object with len <= 8")
|
|
|
|
def bin(self, pad=True, reverse=False):
|
|
bitcount = self.__value.bit_length()
|
|
ret = ""
|
|
if pad and ((bitcount % 8) > 0):
|
|
ret = "0" * (8 - (bitcount % 8))
|
|
ret += bin(self.__value)[2:]
|
|
if reverse:
|
|
ret = ret[::-1]
|
|
return ret
|
|
|
|
@property
|
|
def chr(self):
|
|
return chr(self.__value)
|
|
|
|
@property
|
|
def int(self):
|
|
return int(self)
|
|
|
|
@int.setter
|
|
def int(self, value):
|
|
self.__setvalue(value)
|
|
|
|
@property
|
|
def byte(self):
|
|
return bytes(self)
|
|
|
|
def reverse(self):
|
|
self.__setvalue(self.bin()[::-1])
|
|
|
|
@property
|
|
def msb(self):
|
|
return self.bin()[0]
|
|
|
|
@property
|
|
def lsb(self):
|
|
return self.bin()[7]
|
|
|
|
@property
|
|
def nibble(self):
|
|
return [self.bin()[:4], self.bin()[4:]]
|
|
|
|
@property
|
|
def rtl(self):
|
|
return self.__r_to_l
|
|
|
|
@rtl.setter
|
|
def rtl(self, msb_last):
|
|
self.__r_to_l = bool(msb_last)
|
|
|
|
def list(self, pad=True, reverse=False):
|
|
ret = []
|
|
bits = self.bin(pad=pad, reverse=reverse)
|
|
for b in bits:
|
|
ret.append(Bit(b))
|
|
return ret
|
|
|
|
|
|
class Bytes(bytearray):
|
|
|
|
def __init__(self, b, *args, **kwargs):
|
|
self.__raw = bytearray(b)
|
|
|
|
def __bytes__(self):
|
|
return bytes(self.__raw)
|
|
|
|
def __int__(self):
|
|
return self.int()
|
|
|
|
def __repr__(self):
|
|
ret = f'{self.__class__.__module__}.{self.__class__.__name__}'
|
|
ret += f'({bytes(self)})'
|
|
return ret
|
|
|
|
def __index__(self):
|
|
return self.int()
|
|
|
|
def __getitem__(self, index):
|
|
if not isinstance(index, int):
|
|
raise TypeError
|
|
return Bits(self.__raw[index])
|
|
|
|
def __setitem__(self, index, value):
|
|
if not isinstance(index, int):
|
|
raise TypeError
|
|
self.__raw[index] = int(value)
|
|
|
|
def __delitem__(self, index):
|
|
del self.__raw[index]
|
|
|
|
def __test_key(self, key):
|
|
if not isinstance(key, int):
|
|
raise TypeError
|
|
if key >= len(self.__raw):
|
|
raise IndexError
|
|
return True
|
|
|
|
def __eq__(self, compare):
|
|
return int(self) == int(compare)
|
|
|
|
def __ne__(self, compare):
|
|
return int(self) != int(compare)
|
|
|
|
def __lt__(self, compare):
|
|
return int(self) < int(compare)
|
|
|
|
def __le__(self, compare):
|
|
return int(self) <= int(compare)
|
|
|
|
def __gt__(self, compare):
|
|
return int(self) > int(compare)
|
|
|
|
def __ge__(self, compare):
|
|
return int(self) >= int(compare)
|
|
|
|
def __hash__(self):
|
|
return None
|
|
|
|
def __bool__(self):
|
|
return bool(int(self))
|
|
|
|
def __len__(self):
|
|
return len(self.__raw)
|
|
|
|
def __get__(self, instance, owner=None):
|
|
return self.__raw
|
|
|
|
def __str__(self):
|
|
return self.bin()
|
|
|
|
def int(self, _bytes=None, byteorder="big", signed=False):
|
|
if _bytes is None:
|
|
_bytes = bytes(self)
|
|
return int.from_bytes(bytes=_bytes, byteorder=byteorder,
|
|
signed=signed)
|
|
|
|
def bin(self, i=None, pad=True):
|
|
if i is None:
|
|
i = self.int()
|
|
bitcount = i.bit_length()
|
|
ret = ""
|
|
if pad and ((bitcount % 8) > 0):
|
|
ret = "0" * (8 - (bitcount % 8))
|
|
chop = 2
|
|
if i < 0:
|
|
# account for the sign character
|
|
chop += 1
|
|
return ret + bin(i)[chop:]
|
|
|
|
@property
|
|
def bytes(self):
|
|
return bytes(self)
|
|
|
|
@property
|
|
def bytearray(self):
|
|
return bytearray(bytes(self))
|
|
|
|
def from_int(self, i, byteorder="big"):
|
|
signed = False
|
|
if i < 0:
|
|
signed = True
|
|
length = (len(self.bin(i=i, pad=True)) / 8)
|
|
return i.to_bytes(length=length,
|
|
byteorder=byteorder,
|
|
signed=signed)
|
|
|