I'm implementing a class for handling arrays of bit values in Python. So far, here is what I did:
class Bitarray:
""" Representation of an array of bits.
:param bits: the list of boolean values (i.e. {False, True}) of the bitarray.
"""
def __init__(self, values:list[bool]):
self._bits:list[bool] = values
self._length:int = len(values)
@staticmethod
def from_bytes(data:bytes, byteorder:str = None):
def _access_bit(data, index):
""" Credits: https://stackoverflow.com/a/43787831/23022499
"""
base = int(index // 8)
shift = int(index % 8)
return (data[base] >> shift) & 0x1
if byteorder == None:
byteorder = sys.byteorder
elif byteorder != 'little' and byteorder != 'big':
raise ValueError('Param byteorder must be either "little" or "big".')
bin_data = [_access_bit(data, i) for i in range(len(data) * 8)]
bin_data = [bool(b) for b in bin_data]
return Bitarray(bin_data) if byteorder == 'big' else Bitarray(bin_data[::-1])
def __getitem__(self, index) -> bool:
return self._bits[index]
def __len__(self) -> int:
return self._length
# bit-wise operations
def __and__(self, other):
if type(other) != Bitarray:
raise TypeError("Unsupported operand type(s) for &: '{}' and '{}'".format(type(self), type(other)))
if self._length != len(other):
raise IndexError("The arguments for bitwise operations must have same length.")
return Bitarray([(a & b) for a, b in zip(self._bits, other._bits)])
def __or__(self, other):
if type(other) != Bitarray:
raise TypeError("Unsupported operand type(s) for &: '{}' and '{}'".format(type(self), type(other)))
if self._length != len(other):
raise IndexError("The arguments for bitwise operations must have same length.")
return Bitarray([(a | b) for a, b in zip(self._bits, other._bits)])
def __xor__(self, other):
if type(other) != Bitarray:
raise TypeError("Unsupported operand type(s) for &: '{}' and '{}'".format(type(self).__name__, type(other).__name__))
if self._length != len(other):
raise IndexError("The arguments for bitwise operations must have same length.")
return Bitarray([(a ^ b) for a, b in zip(self._bits, other._bits)])
# to string
def __str__(self):
return ''.join(str(int(b)) for b in self._bits)
If you are wondering about the usage, I want to generate random values using os.urandom() and then perform bitwise operations with these values. An example:
import os
import sys
a = Bitarray.from_bytes(os.urandom(16 // 8), sys.byteorder)
b = Bitarray.from_bytes(os.urandom(16 // 8), sys.byteorder)
print('XOR result: {}'.format(a ^ b))
By far, what I've done works. But I am pretty sure this is so inefficient that for those who are reading this and know way more about Python, I just committed some horrible sin. :P Jokes aside, iterating over boolean values for bitwise operations can't be good, right? Is there some more efficient way to do this?
P.S. For those who are curious, I am trying to build a cryptographic protocol which uses random keys and xor operations. I know about some modules like cryptography, bitarray and others but I thought it would be funnier to try to implement something on my own. Sorry for the lack of documentation and comments, I'll try to improve!
EDIT: Of course one may ask why do I need to use bit arrays if I could just perform bitwise operations using bytes. I need to access the single bit values and I would like my Bitarray class to be able to perform bitwise operations without having to move back to bytes everytime.
inefficient