bits/bits/main.py

304 lines
7.5 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):
self.__value = 0
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):
ret = f'{self.__class__.__module__}.{self.__class__.__name__}'
ret += f'({bytes(self)})'
return ret
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] = bool(int(value))
self.__setvalue(l)
def __get__(self, instance, owner):
return self.bin()
def __setvalue(self, var):
self.__value = 0
if 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 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:]]
def list(self, pad=True, reverse=False):
ret = []
bits = self.bin(pad=pad, reverse=reverse)
for bit in bits:
ret.append(bool(int(bit)))
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)