github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/pypy/_struct.py (about) 1 # 2 # This module is a pure Python version of pypy.module.struct. 3 # It is only imported if the vastly faster pypy.module.struct is not 4 # compiled in. For now we keep this version for reference and 5 # because pypy.module.struct is not ootype-backend-friendly yet. 6 # 7 8 """Functions to convert between Python values and C structs. 9 Python strings are used to hold the data representing the C struct 10 and also as format strings to describe the layout of data in the C struct. 11 12 The optional first format char indicates byte order, size and alignment: 13 @: native order, size & alignment (default) 14 =: native order, std. size & alignment 15 <: little-endian, std. size & alignment 16 >: big-endian, std. size & alignment 17 !: same as > 18 19 The remaining chars indicate types of args and must match exactly; 20 these can be preceded by a decimal repeat count: 21 x: pad byte (no data); 22 c:char; 23 b:signed byte; 24 B:unsigned byte; 25 h:short; 26 H:unsigned short; 27 i:int; 28 I:unsigned int; 29 l:long; 30 L:unsigned long; 31 f:float; 32 d:double. 33 Special cases (preceding decimal count indicates length): 34 s:string (array of char); p: pascal string (with count byte). 35 Special case (only available in native format): 36 P:an integer type that is wide enough to hold a pointer. 37 Special case (not in native mode unless 'long long' in platform C): 38 q:long long; 39 Q:unsigned long long 40 Whitespace between formats is ignored. 41 42 The variable struct.error is an exception raised on errors.""" 43 44 import math 45 import sys 46 47 # TODO: XXX Find a way to get information on native sizes and alignments 48 49 50 class StructError(Exception): 51 pass 52 error = StructError 53 54 bytes = str 55 56 57 def unpack_int(data, index, size, le): 58 _bytes = [b for b in data[index:index + size]] 59 if le == 'little': 60 _bytes.reverse() 61 number = 0 62 for b in _bytes: 63 number = number << 8 | b 64 return int(number) 65 66 67 def unpack_signed_int(data, index, size, le): 68 number = unpack_int(data, index, size, le) 69 max = (1 << (size * 8)) 70 if number > (1 << (size * 8 - 1)) - 1: 71 number = int(-1 * (max - number)) 72 return number 73 74 INFINITY = 1e200 * 1e200 75 NAN = INFINITY / INFINITY 76 77 78 def unpack_char(data, index, size, le): 79 return data[index:index + size] 80 81 82 def pack_int(number, size, le): 83 x = number 84 res = [] 85 for i in range(size): 86 res.append(x & 0xff) 87 x = x >> 8 88 if le == 'big': 89 res.reverse() 90 return ''.join(chr(x) for x in res) 91 92 93 def pack_signed_int(number, size, le): 94 if not isinstance(number, int): 95 raise StructError("argument for i,I,l,L,q,Q,h,H must be integer") 96 if number > (1 << (8 * size - 1)) - 1 or number < -1 * (1 << (8 * size - 1)): 97 raise OverflowError("Number:%i too large to convert" % number) 98 return pack_int(number, size, le) 99 100 101 def pack_unsigned_int(number, size, le): 102 if not isinstance(number, int): 103 raise StructError("argument for i,I,l,L,q,Q,h,H must be integer") 104 if number < 0: 105 raise TypeError("can't convert negative long to unsigned") 106 if number > (1 << (8 * size)) - 1: 107 raise OverflowError("Number:%i too large to convert" % number) 108 return pack_int(number, size, le) 109 110 111 def pack_char(char, size, le): 112 return str(char) 113 114 115 def isinf(x): 116 return x != 0.0 and x / 2 == x 117 118 119 def isnan(v): 120 return v != v * 1.0 or (v == 1.0 and v == 2.0) 121 122 123 def pack_float(x, size, le): 124 unsigned = float_pack(x, size) 125 result = [] 126 for i in range(8): 127 result.append((unsigned >> (i * 8)) & 0xFF) 128 if le == "big": 129 result.reverse() 130 return ''.join(chr(x) for x in result) 131 132 133 def unpack_float(data, index, size, le): 134 binary = [data[i] for i in range(index, index + 8)] 135 if le == "big": 136 binary.reverse() 137 unsigned = 0 138 for i in range(8): 139 # unsigned |= binary[i] << (i * 8) 140 unsigned |= ord(binary[i]) << (i * 8) 141 return float_unpack(unsigned, size, le) 142 143 144 def round_to_nearest(x): 145 """Python 3 style round: round a float x to the nearest int, but 146 unlike the builtin Python 2.x round function: 147 148 - return an int, not a float 149 - do round-half-to-even, not round-half-away-from-zero. 150 151 We assume that x is finite and nonnegative; except wrong results 152 if you use this for negative x. 153 154 """ 155 int_part = int(x) 156 frac_part = x - int_part 157 if frac_part > 0.5 or frac_part == 0.5 and int_part & 1 == 1: 158 int_part += 1 159 return int_part 160 161 162 def float_unpack(Q, size, le): 163 """Convert a 32-bit or 64-bit integer created 164 by float_pack into a Python float.""" 165 166 if size == 8: 167 MIN_EXP = -1021 # = sys.float_info.min_exp 168 MAX_EXP = 1024 # = sys.float_info.max_exp 169 MANT_DIG = 53 # = sys.float_info.mant_dig 170 BITS = 64 171 elif size == 4: 172 MIN_EXP = -125 # C's FLT_MIN_EXP 173 MAX_EXP = 128 # FLT_MAX_EXP 174 MANT_DIG = 24 # FLT_MANT_DIG 175 BITS = 32 176 else: 177 raise ValueError("invalid size value") 178 179 if Q >> BITS: 180 raise ValueError("input out of range") 181 182 # extract pieces 183 sign = Q >> BITS - 1 184 exp = (Q & ((1 << BITS - 1) - (1 << MANT_DIG - 1))) >> MANT_DIG - 1 185 mant = Q & ((1 << MANT_DIG - 1) - 1) 186 187 if exp == MAX_EXP - MIN_EXP + 2: 188 # nan or infinity 189 result = float('nan') if mant else float('inf') 190 elif exp == 0: 191 # subnormal or zero 192 result = math.ldexp(float(mant), MIN_EXP - MANT_DIG) 193 else: 194 # normal 195 mant += 1 << MANT_DIG - 1 196 result = math.ldexp(float(mant), exp + MIN_EXP - MANT_DIG - 1) 197 return -result if sign else result 198 199 200 def float_pack(x, size): 201 """Convert a Python float x into a 64-bit unsigned integer 202 with the same byte representation.""" 203 204 if size == 8: 205 MIN_EXP = -1021 # = sys.float_info.min_exp 206 MAX_EXP = 1024 # = sys.float_info.max_exp 207 MANT_DIG = 53 # = sys.float_info.mant_dig 208 BITS = 64 209 elif size == 4: 210 MIN_EXP = -125 # C's FLT_MIN_EXP 211 MAX_EXP = 128 # FLT_MAX_EXP 212 MANT_DIG = 24 # FLT_MANT_DIG 213 BITS = 32 214 else: 215 raise ValueError("invalid size value") 216 217 sign = math.copysign(1.0, x) < 0.0 218 if math.isinf(x): 219 mant = 0 220 exp = MAX_EXP - MIN_EXP + 2 221 elif math.isnan(x): 222 mant = 1 << (MANT_DIG - 2) # other values possible 223 exp = MAX_EXP - MIN_EXP + 2 224 elif x == 0.0: 225 mant = 0 226 exp = 0 227 else: 228 m, e = math.frexp(abs(x)) # abs(x) == m * 2**e 229 exp = e - (MIN_EXP - 1) 230 if exp > 0: 231 # Normal case. 232 mant = round_to_nearest(m * (1 << MANT_DIG)) 233 mant -= 1 << MANT_DIG - 1 234 else: 235 # Subnormal case. 236 if exp + MANT_DIG - 1 >= 0: 237 mant = round_to_nearest(m * (1 << exp + MANT_DIG - 1)) 238 else: 239 mant = 0 240 exp = 0 241 242 # Special case: rounding produced a MANT_DIG-bit mantissa. 243 assert 0 <= mant <= 1 << MANT_DIG - 1 244 if mant == 1 << MANT_DIG - 1: 245 mant = 0 246 exp += 1 247 248 # Raise on overflow (in some circumstances, may want to return 249 # infinity instead). 250 if exp >= MAX_EXP - MIN_EXP + 2: 251 raise OverflowError("float too large to pack in this format") 252 253 # check constraints 254 assert 0 <= mant < 1 << MANT_DIG - 1 255 assert 0 <= exp <= MAX_EXP - MIN_EXP + 2 256 assert 0 <= sign <= 1 257 return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant 258 259 260 big_endian_format = { 261 'x': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None}, 262 'b': {'size': 1, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int}, 263 'B': {'size': 1, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int}, 264 'c': {'size': 1, 'alignment': 0, 'pack': pack_char, 'unpack': unpack_char}, 265 's': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None}, 266 'p': {'size': 1, 'alignment': 0, 'pack': None, 'unpack': None}, 267 'h': {'size': 2, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int}, 268 'H': {'size': 2, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int}, 269 'i': {'size': 4, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int}, 270 'I': {'size': 4, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int}, 271 'l': {'size': 4, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int}, 272 'L': {'size': 4, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int}, 273 'q': {'size': 8, 'alignment': 0, 'pack': pack_signed_int, 'unpack': unpack_signed_int}, 274 'Q': {'size': 8, 'alignment': 0, 'pack': pack_unsigned_int, 'unpack': unpack_int}, 275 'f': {'size': 4, 'alignment': 0, 'pack': pack_float, 'unpack': unpack_float}, 276 'd': {'size': 8, 'alignment': 0, 'pack': pack_float, 'unpack': unpack_float}, 277 } 278 default = big_endian_format 279 formatmode = {'<': (default, 'little'), 280 '>': (default, 'big'), 281 '!': (default, 'big'), 282 '=': (default, sys.byteorder), 283 '@': (default, sys.byteorder) 284 } 285 286 287 def getmode(fmt): 288 try: 289 formatdef, endianness = formatmode[fmt[0]] 290 index = 1 291 except (IndexError, KeyError): 292 formatdef, endianness = formatmode['@'] 293 index = 0 294 return formatdef, endianness, index 295 296 297 def getNum(fmt, i): 298 num = None 299 cur = fmt[i] 300 while ('0' <= cur) and (cur <= '9'): 301 if num == None: 302 num = int(cur) 303 else: 304 num = 10 * num + int(cur) 305 i += 1 306 cur = fmt[i] 307 return num, i 308 309 310 def calcsize(fmt): 311 """calcsize(fmt) -> int 312 Return size of C struct described by format string fmt. 313 See struct.__doc__ for more on format strings.""" 314 315 formatdef, endianness, i = getmode(fmt) 316 num = 0 317 result = 0 318 while i < len(fmt): 319 num, i = getNum(fmt, i) 320 cur = fmt[i] 321 try: 322 format = formatdef[cur] 323 except KeyError: 324 raise StructError("%s is not a valid format" % cur) 325 if num != None: 326 result += num * format['size'] 327 else: 328 result += format['size'] 329 num = 0 330 i += 1 331 return result 332 333 334 def pack(fmt, *args): 335 """pack(fmt, v1, v2, ...) -> string 336 Return string containing values v1, v2, ... packed according to fmt. 337 See struct.__doc__ for more on format strings.""" 338 formatdef, endianness, i = getmode(fmt) 339 args = list(args) 340 n_args = len(args) 341 result = [] 342 while i < len(fmt): 343 num, i = getNum(fmt, i) 344 cur = fmt[i] 345 try: 346 format = formatdef[cur] 347 except KeyError: 348 raise StructError("%s is not a valid format" % cur) 349 if num == None: 350 num_s = 0 351 num = 1 352 else: 353 num_s = num 354 355 if cur == 'x': 356 result += [b'\0' * num] 357 elif cur == 's': 358 if isinstance(args[0], bytes): 359 padding = num - len(args[0]) 360 result += [args[0][:num] + b'\0' * padding] 361 args.pop(0) 362 else: 363 raise StructError("arg for string format not a string") 364 elif cur == 'p': 365 if isinstance(args[0], bytes): 366 padding = num - len(args[0]) - 1 367 368 if padding > 0: 369 result += [bytes([len(args[0])]) + args[0] 370 [:num - 1] + b'\0' * padding] 371 else: 372 if num < 255: 373 result += [bytes([num - 1]) + args[0][:num - 1]] 374 else: 375 result += [bytes([255]) + args[0][:num - 1]] 376 args.pop(0) 377 else: 378 raise StructError("arg for string format not a string") 379 380 else: 381 if len(args) < num: 382 raise StructError("insufficient arguments to pack") 383 for var in args[:num]: 384 result += [format['pack'](var, format['size'], endianness)] 385 args = args[num:] 386 num = None 387 i += 1 388 if len(args) != 0: 389 raise StructError("too many arguments for pack format") 390 return b''.join(result) 391 392 393 def unpack(fmt, data): 394 """unpack(fmt, string) -> (v1, v2, ...) 395 Unpack the string, containing packed C structure data, according 396 to fmt. Requires len(string)==calcsize(fmt). 397 See struct.__doc__ for more on format strings.""" 398 formatdef, endianness, i = getmode(fmt) 399 j = 0 400 num = 0 401 result = [] 402 length = calcsize(fmt) 403 if length != len(data): 404 raise StructError("unpack str size does not match format") 405 while i < len(fmt): 406 num, i = getNum(fmt, i) 407 cur = fmt[i] 408 i += 1 409 try: 410 format = formatdef[cur] 411 except KeyError: 412 raise StructError("%s is not a valid format" % cur) 413 414 if not num: 415 num = 1 416 417 if cur == 'x': 418 j += num 419 elif cur == 's': 420 result.append(data[j:j + num]) 421 j += num 422 elif cur == 'p': 423 n = data[j] 424 if n >= num: 425 n = num - 1 426 result.append(data[j + 1:j + n + 1]) 427 j += num 428 else: 429 for n in range(num): 430 result += [format['unpack'](data, j, format['size'], endianness)] 431 j += format['size'] 432 433 return tuple(result) 434 435 436 def pack_into(fmt, buf, offset, *args): 437 data = pack(fmt, *args) 438 buffer(buf)[offset:offset + len(data)] = data 439 440 441 def unpack_from(fmt, buf, offset=0): 442 size = calcsize(fmt) 443 data = buffer(buf)[offset:offset + size] 444 if len(data) != size: 445 raise error("unpack_from requires a buffer of at least %d bytes" 446 % (size,)) 447 return unpack(fmt, data) 448 449 450 def _clearcache(): 451 "Clear the internal cache." 452 # No cache in this implementation