Files
controller-wisetopdataserver/python/biopro/util/bit.py
T
2021-12-20 14:52:55 +08:00

169 lines
3.7 KiB
Python

"""bit operating."""
from typing import Union, List, Tuple
def shift_list(data: Union[bytes, List[int]], shift: int) -> List[int]:
"""shift all the data *shift* bits in buffer *data*. the overflow part will go to its neighbor.
:param data: data array
:param shift: shift bits. value range from -8 to +8.
:return: new reference
"""
if len(data) == 0:
return []
if shift == 0 or shift == 8 or shift == -8:
if isinstance(data, bytes):
return [v for v in data]
else:
return data
elif 0 < shift < 8:
ret = []
ls = shift
rs = 8 - ls
v = data[0]
a = (v & 0xFF) >> rs
if a > 0:
ret.append(a)
a = (v << ls) & 0xFF
for v in data[1:]:
ret.append(a | ((v >> rs) & 0xFF))
a = (v << ls) & 0xFF
if a > 0:
ret.append(a)
return ret
elif -8 < shift < 0:
ret = []
rs = -shift
ls = 8 - rs
v = data[0]
a = (v & 0xFF) >> rs
if a > 0:
ret.append(a)
a = (v << ls) & 0xFF
for v in data[1:]:
ret.append(a | ((v >> rs) & 0xFF))
a = (v << ls) & 0xFF
if a > 0:
ret.append(a)
return ret
else:
raise RuntimeError('shift value too big : ' + str(shift))
def endian_switch(value: int, bit: int) -> int:
"""endian switch for *value* with *bit* width
:param value:
:param bit: bit width
:return:
"""
ret = 0
for _ in range(bit):
ret = (ret << 1) | (value & 1)
value >>= 1
return ret
def append_buffer(buffer: List[int], shift: int, width: int, value: int, little_endian=False) -> int:
"""append *width* bit-width *value* into the last byte of the *buffer*
:param buffer: data buffer
:param shift: start from bits
:param width: *value* bit-width
:param value:
:param little_endian: endian
:return: next *shift* value
"""
if width == 0:
return shift % 8
if len(buffer) == 0:
buffer.append(0)
if little_endian:
value = endian_switch(value, width)
while shift >= 8:
buffer.append(0)
shift -= 8
while shift + width >= 8:
# high
offset = 8 - shift
mask = (1 << offset) - 1
buffer[-1] |= (value >> (width - offset)) & mask
if shift + width == 8:
return 8
# low
buffer.append(0)
shift = 0
width -= offset
value &= (1 << width) - 1
offset = 8 - (shift + width)
mask = (1 << width) - 1
buffer[-1] |= (value & mask) << offset
return (shift + width) % 8
def parse_buffer(buffer: List[int], offset: int, shift: int, width: int, little_endian=False) -> Tuple[int, int, int]:
"""extra value from *buffer*.
:param buffer: data buffer
:param offset: reading index offset for *buffer*
:param shift: reading bit offset for *buffer*
:param width: value bit width
:param little_endian:
:return: tuple of extra value, new *offset*, new *shift* value
"""
while shift >= 8:
offset += 1
shift -= 8
value = buffer[offset]
if width == 0:
return 0, offset, shift
if shift > 0:
value &= ((1 << (8 - shift)) - 1)
if shift + width == 8:
shift = 0
offset += 1
elif shift + width < 8:
shift += width
value >>= (8 - shift)
else:
w = width + shift - 8
v, offset, shift = parse_buffer(buffer, offset + 1, 0, w, False)
value = (value << w) | v
if little_endian:
value = endian_switch(value, width)
return value, offset, shift