import numpy as np
from struct import pack, unpack_from
from warnings import warn
class Bit:
def __init__(self, value):
if isinstance(value, bytes):
self._len = 8 * len(value)
self._data = value
else:
if isinstance(value, str):
value = [v != '0' for v in value]
else:
value = np.asarray(value)
if value.dtype != np.bool:
# skip warning for result of np.unpackbits
if value.dtype != np.uint8 or np.any(value > 1):
warn('expected elements to be boolean', stacklevel=2)
value = value.astype(bool)
if value.ndim != 1:
raise ValueError('expected ndim to be 1')
self._len = len(value)
self._data = np.packbits(value).tobytes()
def __repr__(self):
return f'Bit({self.to_text()})'
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._len == other._len and self._data == other._data
return False
def to_list(self):
return self.to_numpy().tolist()
def to_numpy(self):
return np.unpackbits(np.frombuffer(self._data, dtype=np.uint8), count=self._len).astype(bool)
def to_text(self):
return ''.join(format(v, '08b') for v in self._data)[:self._len]
def to_binary(self):
return pack('>i', self._len) + self._data
@classmethod
def from_text(cls, value):
return cls(str(value))
@classmethod
def from_binary(cls, value):
if not isinstance(value, bytes):
raise ValueError('expected bytes')
bit = cls.__new__(cls)
bit._len = unpack_from('>i', value)[0]
bit._data = value[4:]
return bit
@classmethod
def _to_db(cls, value):
if not isinstance(value, cls):
raise ValueError('expected bit')
return value.to_text()
@classmethod
def _to_db_binary(cls, value):
if not isinstance(value, cls):
raise ValueError('expected bit')
return value.to_binary()