bits/bits/main.py

514 lines
13 KiB
Python
Raw Normal View History

2020-10-06 18:30:52 -05:00
2020-10-10 00:42:18 -05:00
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
2020-10-06 18:30:52 -05:00
2020-10-10 00:42:18 -05:00
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.
"""
2020-10-13 01:32:50 -05:00
def __init__(self, var=0, msb_last=False):
2020-10-10 00:42:18 -05:00
self.__value = 0
2020-10-13 01:32:50 -05:00
self.__r_to_l = bool(msb_last)
2020-10-10 00:42:18 -05:00
self.__setvalue(var)
2020-10-03 20:35:46 -05:00
def __bytes__(self):
2020-10-06 18:30:52 -05:00
return bytes([self.__value])
2020-10-03 20:35:46 -05:00
2020-10-06 23:29:55 -05:00
def __bool__(self):
return bool(int(self))
2020-10-03 20:35:46 -05:00
def __int__(self):
2020-10-06 18:30:52 -05:00
return self.__value
2020-10-03 20:35:46 -05:00
2020-10-06 18:30:52 -05:00
def __repr__(self):
2020-10-13 01:32:50 -05:00
return f'{self.__class__.__name__}({self.__value!r})'
2020-10-06 18:30:52 -05:00
def __str__(self):
return self.bin()
2020-10-10 00:42:18 -05:00
def __ord__(self):
return int(self)
2020-10-06 18:30:52 -05:00
def __index__(self):
return int(self)
def __len__(self):
return 8
2020-10-06 23:29:55 -05:00
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))
2020-10-06 18:30:52 -05:00
def __getitem__(self, index):
2020-10-10 00:42:18 -05:00
return self.list()[index]
2020-10-06 18:30:52 -05:00
def __setitem__(self, index, value):
# we'll take the easy way for now
2020-10-10 00:42:18 -05:00
l = self.list()
2020-10-06 18:30:52 -05:00
# str->int->bool->int : accept bool, str, int, return either "0" or "1"
2020-10-13 01:32:50 -05:00
l[index] = Bit(value)
2020-10-10 00:42:18 -05:00
self.__setvalue(l)
2020-10-06 18:30:52 -05:00
def __get__(self, instance, owner):
return self.bin()
2020-10-13 01:32:50 -05:00
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)
2020-10-10 00:42:18 -05:00
def __setvalue(self, var):
2020-10-06 18:30:52 -05:00
self.__value = 0
2020-10-13 01:32:50 -05:00
if isinstance(var, Bits):
self.__value = int(var)
elif isinstance(var, int):
2020-10-10 00:42:18 -05:00
if var < 0 or var > 255:
2020-10-06 18:30:52 -05:00
raise ValueError("Integer must be between 0 and 255")
2020-10-10 00:42:18 -05:00
self.__value = var
elif isinstance(var, bytes):
2020-10-14 02:55:47 -05:00
if len(var) == 1:
self.__value = ord(var)
elif len(var) > 1:
raise ValueError("bytes must be single byte with integer"
" value between 0 and 255")
else:
self.__value = 0
2020-10-10 00:42:18 -05:00
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)))
2020-10-06 18:30:52 -05:00
else:
raise TypeError("Expected object with len <= 8")
2020-10-10 00:42:18 -05:00
def bin(self, pad=True, reverse=False):
2020-10-14 02:55:47 -05:00
from math import ceil
2020-10-06 18:30:52 -05:00
bitcount = self.__value.bit_length()
2020-10-06 23:29:55 -05:00
ret = ""
2020-10-06 18:30:52 -05:00
if pad and ((bitcount % 8) > 0):
ret = "0" * (8 - (bitcount % 8))
2020-10-10 00:42:18 -05:00
ret += bin(self.__value)[2:]
if reverse:
ret = ret[::-1]
return ret
2020-10-06 18:30:52 -05:00
@property
def chr(self):
return chr(self.__value)
2020-10-13 01:32:50 -05:00
@property
def int(self):
return int(self)
@int.setter
def int(self, value):
self.__setvalue(value)
2020-10-06 18:30:52 -05:00
@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:]]
2020-10-13 01:32:50 -05:00
@property
def rtl(self):
return self.__r_to_l
@rtl.setter
def rtl(self, msb_last):
self.__r_to_l = bool(msb_last)
2020-10-10 00:42:18 -05:00
def list(self, pad=True, reverse=False):
ret = []
bits = self.bin(pad=pad, reverse=reverse)
2020-10-13 01:32:50 -05:00
for b in bits:
ret.append(Bit(b))
2020-10-10 00:42:18 -05:00
return ret
2020-10-06 18:30:52 -05:00
2020-10-14 23:07:34 -05:00
class Bytes:
"""
A colletion of Bits with convenient properties for working with binary data
"""
2020-10-06 18:30:52 -05:00
2020-10-14 23:07:34 -05:00
def __init__(self, var=None, byteorder="big"):
self.__raw = bytearray(b'')
self.__small = False
if byteorder.lower() in ["small", "little"]:
self.__small = True
if var is not None:
self.__raw = self.__to_bytearray(var)
2020-10-06 18:30:52 -05:00
def __bytes__(self):
return bytes(self.__raw)
def __int__(self):
return self.int()
2020-10-03 20:35:46 -05:00
2020-10-04 13:11:46 -05:00
def __repr__(self):
2020-10-06 18:30:52 -05:00
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
2020-10-03 20:35:46 -05:00
def __eq__(self, compare):
2020-10-06 18:30:52 -05:00
return int(self) == int(compare)
2020-10-03 20:35:46 -05:00
def __ne__(self, compare):
2020-10-06 18:30:52 -05:00
return int(self) != int(compare)
2020-10-03 20:35:46 -05:00
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)
2020-10-06 18:30:52 -05:00
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()
2020-10-14 23:07:34 -05:00
def __to_bytearray(self, var):
retvalue = bytearray(b'')
byteorder="little" if self.__small else "big"
if isinstance(var, int):
retvalue = bytearray(self.from_int(var, byteorder))
elif isinstance(var, bytes):
retvalue = bytearray(var)
elif isinstance(var, str):
if len(var) % 8 > 0:
var = ("0" * (8 - (len(var) % 8))) + var
bitelist = []
for i in range(0, len(var), 8):
bitelist.append(Bits(var[i:i+8]))
retvalue = bytearray(bitelist)
elif isinstance(var, bytearray):
retvalue = var
elif isinstance(var, Bytes):
retvalue = bytearray(var)
elif isinstance(var, list):
# test first item in list to see if it is Bit
if len(var) > 0 and isinstance(var[0], (bool, Bit)):
bitstring = ""
for bit in var:
bitstring = bitstring + str(Bit(bit))
retvalue += bytearray(Bytes(bitstring))
else:
for item in var:
if isinstance(item, (int, Bits, Bytes)):
retvalue += bytearray(self.from_int(int(item),
byteorder))
elif isinstance(item, bytes):
retvalue += bytearray(item)
return retvalue
2020-10-06 18:30:52 -05:00
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)