tests passing; improve doc
This commit is contained in:
268
src/bits.py
268
src/bits.py
@@ -18,7 +18,7 @@ False. This class provides methods to perform binary comparisons and
|
||||
conversions between different types that can be used to represent a single
|
||||
binary value.
|
||||
|
||||
Byte
|
||||
Bits
|
||||
====
|
||||
A Byte is the representation of 8 Bits. The Byte class provides methods to
|
||||
compare a single binary Byte to other data types. The Class also provides
|
||||
@@ -290,7 +290,234 @@ class Bit:
|
||||
|
||||
class Bits:
|
||||
"""
|
||||
Provide bit operation helpers.
|
||||
=====
|
||||
Bits
|
||||
=====
|
||||
|
||||
A single byte of data with helper methods and properties.
|
||||
|
||||
A Bits object provides many helpful methods for working with a byte of
|
||||
information. The backing object is a single int, from which all operations
|
||||
and conversions are performed. A Bits object can be converted and compared
|
||||
to *bytes*, *int*, *str*, *list(of Bit)*, *bool* and other *Bits* objects.
|
||||
The custom object *Bytes* can also be cast to a Bits object, as long as the
|
||||
*Bytes* object has an *int* value >= **0** and <= **255**.
|
||||
|
||||
When comparing or operating on *Bits* objects with objects of a different
|
||||
type, the *operand* object will first be cast to a Bits object. In this
|
||||
way, any object which can be used to create a *Bits* object can also be
|
||||
used to perform operations with *Bits*.
|
||||
|
||||
----------------
|
||||
Type Conversion
|
||||
----------------
|
||||
The objects that can be converted to and from *Bits* include *bytes*,
|
||||
*int*, *str*, *list*, *bool* and other *Bits* objects. When using a list,
|
||||
the length of the list must be 8 or less, each list item must be castable
|
||||
to *int*, and each item will be evaluated as either *True* or *False*.
|
||||
|
||||
Convert *bool* to *Bits*:
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits(True)
|
||||
/>/>/> my_bits
|
||||
Bits(1)
|
||||
/>/>/>
|
||||
|
||||
Convert *bytes* to *Bits*:
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits(b'\\xab')
|
||||
/>/>/> my_bits
|
||||
Bits(171)
|
||||
/>/>/> my_bits = Bits(bytes([210]))
|
||||
/>/>/> my_bits
|
||||
Bits(210)
|
||||
/>/>/>
|
||||
|
||||
Convert *int* to *Bits*:
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits(234)
|
||||
/>/>/> my_bits
|
||||
Bits(234)
|
||||
/>/>/>
|
||||
|
||||
Convert *str* to *Bits*:
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits("01010101")
|
||||
/>/>/> my_bits
|
||||
Bits(85)
|
||||
/>/>/> my_bits = Bits("11")
|
||||
/>/>/> my_bits
|
||||
Bits(3)
|
||||
/>/>/>
|
||||
|
||||
Convert *list* to *Bits*:
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits(["0", "1", "0", "1", "0", "1", "0", "1"])
|
||||
/>/>/> my_bits
|
||||
Bits(85)
|
||||
/>/>/> my_bits = Bits([0, 1, 0, 1, 0, 1, 0, 1])
|
||||
/>/>/> my_bits
|
||||
Bits(85)
|
||||
/>/>/> my_bits = Bits([False, True, False, True, False, True])
|
||||
/>/>/> my_bits
|
||||
Bits(21)
|
||||
/>/>/> my_bits = Bits([True, True])
|
||||
/>/>/> my_bits
|
||||
Bits(3)
|
||||
/>/>/> my_bits = Bits([0, "1", False, True, "0", Bit(1), Bit(0), 127])
|
||||
/>/>/> my_bits
|
||||
Bits(85)
|
||||
|
||||
.. note:: Each item in a list will be evaluated to either True or False.
|
||||
|
||||
|
||||
-------------
|
||||
Type Casting
|
||||
-------------
|
||||
Bits objects support casting to multiple different types.
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits(16)
|
||||
/>/>/> str(my_bits)
|
||||
'00010000'
|
||||
/>/>/> int(my_bits)
|
||||
16
|
||||
/>/>/> bin(my_bits)
|
||||
'0b10000'
|
||||
/>/>/> bool(my_bits)
|
||||
True
|
||||
/>/>/> list(my_bits)
|
||||
[Bit(0), Bit(0), Bit(0), Bit(1), Bit(0), Bit(0), Bit(0), Bit(0)]
|
||||
/>/>/> bytes(my_bits)
|
||||
b'\x10'
|
||||
/>/>/> bytearray([my_bits])
|
||||
bytearray(b'\x10')
|
||||
/>/>/>
|
||||
|
||||
|
||||
-------------
|
||||
Subscripting
|
||||
-------------
|
||||
Subscripting can be used to query or modify each Bit.
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits('01101110')
|
||||
/>/>/> my_bits[3]
|
||||
Bit(0)
|
||||
/>/>/> my_bits[3] = True
|
||||
/>/>/> my_bits[3]
|
||||
Bit(1)
|
||||
/>/>/> str(my_bits)
|
||||
'01111110')
|
||||
/>/>/> list(my_bits)
|
||||
[Bit(0), Bit(1), Bit(1), Bit(1), Bit(1), Bit(1), Bit(1), Bit(0)]
|
||||
/>/>/>
|
||||
|
||||
|
||||
------------
|
||||
Comparisons
|
||||
------------
|
||||
Bits objects can be compared to any object which can be cast to a Bits
|
||||
object. This goes both ways.
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits('01101110')
|
||||
/>/>/> my_bits
|
||||
Bits(110)
|
||||
/>/>/> 110 == my_bits
|
||||
True
|
||||
/>/>/> 111 == my_bits
|
||||
False
|
||||
/>/>/> 111 > my_bits
|
||||
True
|
||||
/>/>/> my_bits > 112
|
||||
False
|
||||
/>/>/> b'n' == my_bits
|
||||
True
|
||||
/>/>/> my_bits >= b'#'
|
||||
True
|
||||
/>/>/> my_bits >= '10000000'
|
||||
False
|
||||
/>/>/> '10000000' >= my_bits
|
||||
True
|
||||
/>/>/>
|
||||
|
||||
|
||||
--------------------
|
||||
Bit-Masks and Flags
|
||||
--------------------
|
||||
Bits objects support bitmask and flag operations.
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits('01101110')
|
||||
/>/>/> my_bits
|
||||
Bits(110)
|
||||
/>/>/> 110 in my_bits
|
||||
True
|
||||
/>/>/> Bits('00000010') in my_bits
|
||||
True
|
||||
/>/>/> pow_2 = [0, 1, 2, 4, 8, 16, 32, 64, 128]
|
||||
/>/>/> for p2 in pow_2:
|
||||
... print(str(Bits(p2)))
|
||||
...
|
||||
00000000
|
||||
00000001
|
||||
00000010
|
||||
00000100
|
||||
00001000
|
||||
00010000
|
||||
00100000
|
||||
01000000
|
||||
10000000
|
||||
/>/>/> for p2 in pow_2:
|
||||
... print(str(p2) + ' in Bits(110): ' + p2 in my_bits)
|
||||
...
|
||||
0 in Bits(110): True
|
||||
1 in Bits(110): False
|
||||
2 in Bits(110): True
|
||||
4 in Bits(110): True
|
||||
8 in Bits(110): True
|
||||
16 in Bits(110): False
|
||||
32 in Bits(110): True
|
||||
64 in Bits(110): True
|
||||
128 in Bits(110): False
|
||||
/>/>/> str(Bits(14))
|
||||
'00001110'
|
||||
/>/>/> 14 in my_bits
|
||||
True
|
||||
/>/>/> (0 + 2 + 4 + 8) in my_bits
|
||||
True
|
||||
/>/>/>
|
||||
|
||||
|
||||
-------------------
|
||||
Bitwise operations
|
||||
-------------------
|
||||
Binary bitwise operations are supported.
|
||||
.. code-block: python
|
||||
|
||||
/>/>/> my_bits = Bits('01101110')
|
||||
/>/>/> my_bits
|
||||
Bits(110)
|
||||
/>/>/> str(my_bits & '0011')
|
||||
'00000010'
|
||||
/>/>/> str(my_bits >> 3)
|
||||
'00001101'
|
||||
/>/>/> str(my_bits << 1)
|
||||
'11011100'
|
||||
/>/>/> str(my_bits|'10111110')
|
||||
'11111110'
|
||||
/>/>/> str(my_bits^'10101010')
|
||||
'11000100'
|
||||
/>/>/> str(~my_bits)
|
||||
'10010001'
|
||||
/>/>/>
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, var=0, msb_last=False):
|
||||
@@ -430,6 +657,14 @@ class Bits:
|
||||
def __or__(self, other):
|
||||
return Bits(int(self) | int(Bits(other)))
|
||||
|
||||
def __invert__(self):
|
||||
# There's probably a better way to do this...
|
||||
inv = str(self).replace('0', 'x').replace('1', '0').replace('x', '1')
|
||||
return Bits(inv)
|
||||
|
||||
def __neg__(self):
|
||||
return ~ self
|
||||
|
||||
def __radd__(self, other):
|
||||
return Bits(other) + self
|
||||
|
||||
@@ -552,27 +787,18 @@ class Bits:
|
||||
set the value of self to int(var)
|
||||
"""
|
||||
self.__value = 0
|
||||
if isinstance(var, Bits):
|
||||
self.__value = int(var)
|
||||
elif isinstance(var, int):
|
||||
if var < 0 or var > 255:
|
||||
if isinstance(var, (int, bool, Bits, Bytes, Bit)):
|
||||
var_i = int(var)
|
||||
if var_i < 0 or var_i > 255:
|
||||
raise ValueError("Integer must be between 0 and 255")
|
||||
self.__value = var
|
||||
self.__value = var_i
|
||||
elif isinstance(var, bytes):
|
||||
if len(var) == 1:
|
||||
self.__value = ord(var)
|
||||
self.__setvalue(ord(var))
|
||||
elif len(var) > 1:
|
||||
raise ValueError("bytes must be single byte with integer"
|
||||
" value between 0 and 255")
|
||||
else:
|
||||
self.__value = 0
|
||||
elif isinstance(var, Bytes):
|
||||
if int(var) < 256:
|
||||
self.__value = int(var)
|
||||
else:
|
||||
raise ValueError("Bytes object must be < 256")
|
||||
return list(var)
|
||||
elif (len(var) > 0) and (len(var) <=8):
|
||||
elif isinstance(var, (list, str)) and (len(var) <=8):
|
||||
# handles: "01010101"
|
||||
# ["0", "1", "0", "1", "0", "1", "0", "1"]
|
||||
# [0, 1, 0, 1, 0, 1, 0, 1]
|
||||
@@ -583,7 +809,7 @@ class Bits:
|
||||
self.__value += (int(bool(int(var[bit]))) *
|
||||
(2 ** (len(var) - 1 - bit)))
|
||||
else:
|
||||
raise TypeError("Expected object with len <= 8")
|
||||
raise TypeError("Expected compatible object")
|
||||
|
||||
def bin(self, pad=True, reverse=False):
|
||||
"""
|
||||
@@ -591,6 +817,12 @@ class Bits:
|
||||
pad: True to include leading zeros, False to strip leading zeroes
|
||||
reverse: True to return binary string representation of self as stiB.
|
||||
"""
|
||||
if self.__value == 0:
|
||||
if pad:
|
||||
return "0" * 8
|
||||
else:
|
||||
return "0"
|
||||
|
||||
from math import ceil
|
||||
bitcount = self.__value.bit_length()
|
||||
ret = ""
|
||||
|
Reference in New Issue
Block a user