/usr/lib/python3/dist-packages/bitstruct.py is in python3-bitstruct 3.4.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | from __future__ import print_function
import re
import struct
__version__ = "3.4.0"
def _parse_format(fmt):
if fmt[-1] in [">", "<"]:
byte_order = fmt[-1]
fmt = fmt[:-1]
else:
byte_order = ">"
parsed_infos = re.findall(r'([<>]?)([a-zA-Z])(\d+)', fmt)
# Use big endian as default and use the endianness of the previous
# value if none is given for the current value.
infos = []
endianness = ">"
for info in parsed_infos:
if info[0] != "":
endianness = info[0]
infos.append((info[1], int(info[2]), endianness))
return infos, byte_order
def _pack_integer(size, arg):
value = int(arg)
if value < 0:
value = ((1 << size) + value)
return '{{:0{}b}}'.format(size).format(value)
def _pack_boolean(size, arg):
value = bool(arg)
return _pack_integer(size, int(value))
def _pack_float(size, arg):
value = float(arg)
if size == 32:
value = struct.pack('>f', value)
elif size == 64:
value = struct.pack('>d', value)
else:
raise ValueError('expected float size of 32 of 64 bits (got {})'.format(
size))
return ''.join('{:08b}'.format(b)
for b in bytearray(value))
def _pack_bytearray(size, arg):
value = bytearray(arg)
bits = ''.join('{:08b}'.format(b)
for b in value)
return bits[0:size]
def _pack_text(size, arg):
value = arg.encode('utf-8')
return _pack_bytearray(size, bytearray(value))
def _unpack_integer(type_, bits):
value = int(bits, 2)
if type_ == 's':
if bits[0] == '1':
value -= (1 << len(bits))
return value
def _unpack_boolean(bits):
value = _unpack_integer('u', bits)
return bool(value)
def _unpack_float(size, bits):
packed = _unpack_bytearray(size, bits)
if size == 32:
value = struct.unpack('>f', packed)[0]
elif size == 64:
value = struct.unpack('>d', packed)[0]
else:
raise ValueError('expected float size of 32 of 64 bits (got {})'.format(
size))
return value
def _unpack_bytearray(size, bits):
value = bytearray()
for i in range(size // 8):
value.append(int(bits[8*i:8*i+8], 2))
rest = size % 8
if rest > 0:
value.append(int(bits[size-rest:], 2) << (8-rest))
return value
def _unpack_text(size, bits):
return _unpack_bytearray(size, bits).decode('utf-8')
def pack(fmt, *args):
"""Return a byte string containing the values v1, v2, ... packed
according to the given format. If the total number of bits are not
a multiple of 8, padding will be added at the end of the last
byte.
:param fmt: Bitstruct format string. See format description below.
:param args: Variable argument list of values to pack.
:returns: A byte string of the packed values.
`fmt` is a string of bitorder-type-length groups, and optionally a
byteorder identifier afer the groups. Bitorder and byteorder may
be omitted.
Bitorder is either ">" or "<", where ">" means MSB first and "<"
means LSB first. If bitorder is omitted, the previous values'
bitorder is used for the current value. For example, in the format
string "u1<u2u3" u1 is MSB first and both u2 and u3 are LSB first.
Byteorder is either ">" or "<", where ">" means most significant
byte first and "<" means least significant byte first. If
byteorder is omitted, most significant byte first is used.
There are seven types; 'u', 's', 'f', 'b', 't', 'r' and 'p'.
- 'u' -- unsigned integer
- 's' -- signed integer
- 'f' -- floating point number of 32 or 64 bits
- 'b' -- boolean
- 't' -- text (ascii or utf-8)
- 'r' -- raw, bytes
- 'p' -- padding, ignore
Length is the number of bits to pack the value into.
Example format string with default bit and byte ordering: 'u1u3p7s16'
Same format string, but with least significant byte first:
'u1u3p7s16<'
Same format string, but with LSB first ('<' prefix) and least
significant byte first ('<' suffix): '<u1u3p7s16<'
"""
bits = ''
infos, byte_order = _parse_format(fmt)
i = 0
# Sanity check of the number of arguments.
number_of_arguments = 0
for info in infos:
if info[0] != 'p':
number_of_arguments += 1
if number_of_arguments > len(args):
raise ValueError("pack expected {} item(s) for packing "
"(got {})".format(number_of_arguments, len(args)))
for type_, size, endianness in infos:
if type_ == 'p':
bits += size * '0'
else:
if type_ in 'us':
value_bits = _pack_integer(size, args[i])
elif type_ == 'f':
value_bits = _pack_float(size, args[i])
elif type_ == 'b':
value_bits = _pack_boolean(size, args[i])
elif type_ == 't':
value_bits = _pack_text(size, args[i])
elif type_ == 'r':
value_bits = _pack_bytearray(size, bytearray(args[i]))
else:
raise ValueError("bad type '{}' in format".format(type_))
# reverse the bit order in little endian values
if endianness == "<":
value_bits = value_bits[::-1]
# reverse bytes order for least significant byte first
if byte_order == ">":
bits += value_bits
else:
aligned_offset = len(value_bits) - (8 - (len(bits) % 8))
while aligned_offset > 0:
bits += value_bits[aligned_offset:]
value_bits = value_bits[:aligned_offset]
aligned_offset -= 8
bits += value_bits
i += 1
# padding of last byte
tail = len(bits) % 8
if tail != 0:
bits += (8 - tail) * '0'
return bytes(bytearray([int(''.join(bits[i:i+8]), 2)
for i in range(0, len(bits), 8)]))
def unpack(fmt, data):
"""Unpack `data` (byte string, bytearray or list of integers)
according to the given format. The result is a tuple even if it
contains exactly one item.
:param fmt: Bitstruct format string. See pack() for details.
:param data: Byte string of values to unpack.
:returns: A tuple of the unpacked values.
"""
bits = ''.join(['{:08b}'.format(b) for b in bytearray(data)])
infos, byte_order = _parse_format(fmt)
# Sanity check.
number_of_bits_to_unpack = sum([size for _, size, _ in infos])
if number_of_bits_to_unpack > len(bits):
raise ValueError("unpack requires at least {} bits to unpack "
"(got {})".format(number_of_bits_to_unpack,
len(bits)))
res = []
offset = 0
for type_, size, endianness in infos:
if type_ == 'p':
pass
else:
# reverse bytes order for least significant byte first
if byte_order == ">":
value_bits = bits[offset:offset+size]
else:
value_bits_tmp = bits[offset:offset+size]
aligned_offset = (size - ((offset + size) % 8))
value_bits = ''
while aligned_offset > 0:
value_bits += value_bits_tmp[aligned_offset:aligned_offset+8]
value_bits_tmp = value_bits_tmp[:aligned_offset]
aligned_offset -= 8
value_bits += value_bits_tmp
# reverse the bit order in little endian values
if endianness == "<":
value_bits = value_bits[::-1]
if type_ in 'us':
value = _unpack_integer(type_, value_bits)
elif type_ == 'f':
value = _unpack_float(size, value_bits)
elif type_ == 'b':
value = _unpack_boolean(value_bits)
elif type_ == 't':
value = _unpack_text(size, value_bits)
elif type_ == 'r':
value = bytes(_unpack_bytearray(size, value_bits))
else:
raise ValueError("bad type '{}' in format".format(type_))
res.append(value)
offset += size
return tuple(res)
def calcsize(fmt):
"""Calculate the number of bits in given format.
:param fmt: Bitstruct format string.
:returns: Number of bits in format string.
"""
return sum([size for _, size, _ in _parse_format(fmt)[0]])
def byteswap(fmt, data, offset = 0):
"""Swap bytes in `data` according to `fmt`, starting at byte
`offset`. `fmt` must be an iterable, iterating over number of
bytes to swap. For example, the format string "24" applied to the
byte string b'\x00\x11\x22\x33\x44\x55' will produce the result
b'\x11\x00\x55\x44\x33\x22'.
:param fmt: Swap format string.
:param data: Byte string of data to swap.
:param offset: Start offset into `data`.
:returns: Byte string of swapped bytes.
"""
i = offset
data_swapped = b''
for f in fmt:
length = int(f)
value = data[i:i+length]
data_swapped += value[::-1]
i += length
return data_swapped
|