tests passing; improve doc

This commit is contained in:
2020-11-23 07:51:45 +00:00
parent 97f7d58dd7
commit 66aa7ee915
2 changed files with 317 additions and 57 deletions

View File

@@ -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 = ""