169 lines
3.7 KiB
Python
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
|