diff --git a/bits/__init__.py b/bits/__init__.py index 8b5eac5..0d58038 100644 --- a/bits/__init__.py +++ b/bits/__init__.py @@ -1 +1 @@ -from .main import Bits +from .main import Bits, Bytes diff --git a/bits/main.py b/bits/main.py index 8b6e9d2..93cd046 100644 --- a/bits/main.py +++ b/bits/main.py @@ -1,36 +1,129 @@ -"""The Bits class simplifies bit level operations. -(c) 2020 S Groesz -""" -class Bits: - def __init__(self, data=None): - if data is None: - self.__raw = b'' - else: - self.__raw = bytes(data) +class Bits(int): + + def __init__(self, byte): + self.__setvalue(byte) def __bytes__(self): - return self.__raw + return bytes([self.__value]) def __int__(self): - #return int.from_bytes(bytes(self), "big") # alternative Py > 3.1 - i = 0 - for b in bytes(self): - i = i * 256 + int(b) # Python 2: int(b) -> ord(b) - return i - - def __hash__(self): - return hash(bytes(self)) + return self.__value def __repr__(self): - return (f'{self.__class__.__name__}({bytes(self)})') - #return "{:08b}".format(int(self)) + ret = f'{self.__class__.__module__}.{self.__class__.__name__}' + ret += f'({bytes(self)})' + return ret + + def __str__(self): + return self.bin() + + def __index__(self): + return int(self) + + def __len__(self): + return 8 + + def __getitem__(self, index): + return bool(int(self.bin()[index])) #self.bool(key) + + def __setitem__(self, index, value): + # we'll take the easy way for now + s = list(self.bin()) + # str->int->bool->int : accept bool, str, int, return either "0" or "1" + s[index] = str(int(bool(int(value)))) + self.__setvalue("".join(s)) + + def __get__(self, instance, owner): + return self.bin() + + def __setvalue(self, value): + self.__value = 0 + if isinstance(value, int): + if value < 0 or value > 255: + raise ValueError("Integer must be between 0 and 255") + self.__value = value + elif (len(value) > 0) and (len(value) <=8): + for bit in range(0, len(value)): + self.__value += (int(bool(int(value[bit]))) * + (2 ** (len(value) - 1 - bit))) + else: + raise TypeError("Expected object with len <= 8") + + def bin(self, pad=True): + bitcount = self.__value.bit_length() + if pad and ((bitcount % 8) > 0): + ret = "0" * (8 - (bitcount % 8)) + return ret + bin(self.__value)[2:] + + @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:]] + + +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 hash(self) == hash(compare) + return int(self) == int(compare) def __ne__(self, compare): - return hash(self) != hash(compare) + return int(self) != int(compare) def __lt__(self, compare): return int(self) < int(compare) @@ -44,3 +137,54 @@ class Bits: 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) +