diff --git a/lib/assets/Lib/_struct.py b/lib/assets/Lib/_struct.py new file mode 100644 --- /dev/null +++ b/lib/assets/Lib/_struct.py @@ -0,0 +1,448 @@ +# +# This module is a pure Python version of pypy.module.struct. +# It is only imported if the vastly faster pypy.module.struct is not +# compiled in. For now we keep this version for reference and +# because pypy.module.struct is not ootype-backend-friendly yet. +# + +# this module 'borrowed' from +# https://bitbucket.org/pypy/pypy/src/18626459a9b2/lib_pypy/_struct.py?at=py3k-listview_str +# with many bug fixes + +"""Functions to convert between Python values and C structs. +Python strings are used to hold the data representing the C struct +and also as format strings to describe the layout of data in the C struct. + +The optional first format char indicates byte order, size and alignment: + @: native order, size & alignment (default) + =: native order, std. size & alignment + <: little-endian, std. size & alignment + >: big-endian, std. size & alignment + !: same as > + +The remaining chars indicate types of args and must match exactly; +these can be preceded by a decimal repeat count: + x: pad byte (no data); + c:char; + b:signed byte; + B:unsigned byte; + h:short; + H:unsigned short; + i:int; + I:unsigned int; + l:long; + L:unsigned long; + f:float; + d:double. +Special cases (preceding decimal count indicates length): + s:string (array of char); p: pascal string (with count byte). +Special case (only available in native format): + P:an integer type that is wide enough to hold a pointer. +Special case (not in native mode unless 'long long' in platform C): + q:long long; + Q:unsigned long long +Whitespace between formats is ignored. + +The variable struct.error is an exception raised on errors.""" + +import math, sys + +# TODO: XXX Find a way to get information on native sizes and alignments +class StructError(Exception): + pass +error = StructError +def unpack_int(data,index,size,le): + bytes = [b for b in data[index:index+size]] + if le == 'little': + bytes.reverse() + number = 0 + for b in bytes: + number = number << 8 | b + return int(number) + +def unpack_signed_int(data,index,size,le): + number = unpack_int(data,index,size,le) + max = 2**(size*8) + if number > 2**(size*8 - 1) - 1: + number = int(-1*(max - number)) + return number + +INFINITY = 1e200 * 1e200 +NAN = INFINITY / INFINITY + +def unpack_char(data,index,size,le): + return data[index:index+size] + +def pack_int(number,size,le): + x=number + res=[] + for i in range(size): + res.append(x&0xff) + x >>= 8 + if le == 'big': + res.reverse() + return bytes(res) + +def pack_signed_int(number,size,le): + if not isinstance(number, int): + raise StructError("argument for i,I,l,L,q,Q,h,H must be integer") + if number > 2**(8*size-1)-1 or number < -1*2**(8*size-1): + raise OverflowError("Number:%i too large to convert" % number) + return pack_int(number,size,le) + +def pack_unsigned_int(number,size,le): + if not isinstance(number, int): + raise StructError("argument for i,I,l,L,q,Q,h,H must be integer") + if number < 0: + raise TypeError("can't convert negative long to unsigned") + if number > 2**(8*size)-1: + raise OverflowError("Number:%i too large to convert" % number) + return pack_int(number,size,le) + +def pack_char(char,size,le): + return bytes(char) + +def isinf(x): + return x != 0.0 and x / 2 == x +def isnan(v): + return v != v*1.0 or (v == 1.0 and v == 2.0) + +def pack_float(x, size, le): + unsigned = float_pack(x, size) + result = [] + for i in range(size): + result.append((unsigned >> (i * 8)) & 0xFF) + if le == "big": + result.reverse() + return bytes(result) + +def unpack_float(data, index, size, le): + binary = [data[i] for i in range(index, index + size)] + if le == "big": + binary.reverse() + unsigned = 0 + for i in range(size): + unsigned |= binary[i] << (i * 8) + return float_unpack(unsigned, size, le) + +def round_to_nearest(x): + """Python 3 style round: round a float x to the nearest int, but + unlike the builtin Python 2.x round function: + + - return an int, not a float + - do round-half-to-even, not round-half-away-from-zero. + + We assume that x is finite and nonnegative; except wrong results + if you use this for negative x. + + """ + int_part = int(x) + frac_part = x - int_part + if frac_part > 0.5 or frac_part == 0.5 and int_part & 1 == 1: + int_part += 1 + return int_part + +def float_unpack(Q, size, le): + """Convert a 32-bit or 64-bit integer created + by float_pack into a Python float.""" + + if size == 8: + MIN_EXP = -1021 # = sys.float_info.min_exp + MAX_EXP = 1024 # = sys.float_info.max_exp + MANT_DIG = 53 # = sys.float_info.mant_dig + BITS = 64 + elif size == 4: + MIN_EXP = -125 # C's FLT_MIN_EXP + MAX_EXP = 128 # FLT_MAX_EXP + MANT_DIG = 24 # FLT_MANT_DIG + BITS = 32 + else: + raise ValueError("invalid size value") + + if Q >> BITS: + raise ValueError("input out of range") + + # extract pieces + sign = Q >> BITS - 1 + exp = (Q & ((1 << BITS - 1) - (1 << MANT_DIG - 1))) >> MANT_DIG - 1 + mant = Q & ((1 << MANT_DIG - 1) - 1) + + if exp == MAX_EXP - MIN_EXP + 2: + # nan or infinity + result = float('nan') if mant else float('inf') + elif exp == 0: + # subnormal or zero + result = math.ldexp(float(mant), MIN_EXP - MANT_DIG) + else: + # normal + mant += 1 << MANT_DIG - 1 + result = math.ldexp(float(mant), exp + MIN_EXP - MANT_DIG - 1) + return -result if sign else result + + +def float_pack(x, size): + """Convert a Python float x into a 64-bit unsigned integer + with the same byte representation.""" + + if size == 8: + MIN_EXP = -1021 # = sys.float_info.min_exp + MAX_EXP = 1024 # = sys.float_info.max_exp + MANT_DIG = 53 # = sys.float_info.mant_dig + BITS = 64 + elif size == 4: + MIN_EXP = -125 # C's FLT_MIN_EXP + MAX_EXP = 128 # FLT_MAX_EXP + MANT_DIG = 24 # FLT_MANT_DIG + BITS = 32 + else: + raise ValueError("invalid size value") + + sign = math.copysign(1.0, x) < 0.0 + if math.isinf(x): + mant = 0 + exp = MAX_EXP - MIN_EXP + 2 + elif math.isnan(x): + mant = 1 << (MANT_DIG-2) # other values possible + exp = MAX_EXP - MIN_EXP + 2 + elif x == 0.0: + mant = 0 + exp = 0 + else: + m, e = math.frexp(abs(x)) # abs(x) == m * 2**e + exp = e - (MIN_EXP - 1) + if exp > 0: + # Normal case. + mant = round_to_nearest(m * (1 << MANT_DIG)) + mant -= 1 << MANT_DIG - 1 + else: + # Subnormal case. + if exp + MANT_DIG - 1 >= 0: + mant = round_to_nearest(m * (1 << exp + MANT_DIG - 1)) + else: + mant = 0 + exp = 0 + + # Special case: rounding produced a MANT_DIG-bit mantissa. + assert 0 <= mant <= 1 << MANT_DIG - 1 + if mant == 1 << MANT_DIG - 1: + mant = 0 + exp += 1 + + # Raise on overflow (in some circumstances, may want to return + # infinity instead). + if exp >= MAX_EXP - MIN_EXP + 2: + raise OverflowError("float too large to pack in this format") + + # check constraints + assert 0 <= mant < 1 << MANT_DIG - 1 + assert 0 <= exp <= MAX_EXP - MIN_EXP + 2 + assert 0 <= sign <= 1 + return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant + + +big_endian_format = { + 'x':{ 'size' : 1, 'alignment' : 0, 'pack' : None, 'unpack' : None}, + 'b':{ 'size' : 1, 'alignment' : 0, 'pack' : pack_signed_int, 'unpack' : unpack_signed_int}, + 'B':{ 'size' : 1, 'alignment' : 0, 'pack' : pack_unsigned_int, 'unpack' : unpack_int}, + 'c':{ 'size' : 1, 'alignment' : 0, 'pack' : pack_char, 'unpack' : unpack_char}, + 's':{ 'size' : 1, 'alignment' : 0, 'pack' : None, 'unpack' : None}, + 'p':{ 'size' : 1, 'alignment' : 0, 'pack' : None, 'unpack' : None}, + 'h':{ 'size' : 2, 'alignment' : 0, 'pack' : pack_signed_int, 'unpack' : unpack_signed_int}, + 'H':{ 'size' : 2, 'alignment' : 0, 'pack' : pack_unsigned_int, 'unpack' : unpack_int}, + 'i':{ 'size' : 4, 'alignment' : 0, 'pack' : pack_signed_int, 'unpack' : unpack_signed_int}, + 'I':{ 'size' : 4, 'alignment' : 0, 'pack' : pack_unsigned_int, 'unpack' : unpack_int}, + 'l':{ 'size' : 4, 'alignment' : 0, 'pack' : pack_signed_int, 'unpack' : unpack_signed_int}, + 'L':{ 'size' : 4, 'alignment' : 0, 'pack' : pack_unsigned_int, 'unpack' : unpack_int}, + 'q':{ 'size' : 8, 'alignment' : 0, 'pack' : pack_signed_int, 'unpack' : unpack_signed_int}, + 'Q':{ 'size' : 8, 'alignment' : 0, 'pack' : pack_unsigned_int, 'unpack' : unpack_int}, + 'f':{ 'size' : 4, 'alignment' : 0, 'pack' : pack_float, 'unpack' : unpack_float}, + 'd':{ 'size' : 8, 'alignment' : 0, 'pack' : pack_float, 'unpack' : unpack_float}, + } +default = big_endian_format +formatmode={ '<' : (default, 'little'), + '>' : (default, 'big'), + '!' : (default, 'big'), + '=' : (default, sys.byteorder), + '@' : (default, sys.byteorder) + } + +def getmode(fmt): + try: + formatdef,endianness = formatmode[fmt[0]] + alignment = fmt[0] not in formatmode or fmt[0]=='@' + index = 1 + except (IndexError, KeyError): + formatdef,endianness = formatmode['@'] + alignment = True + index = 0 + return formatdef,endianness,index,alignment + +def getNum(fmt,i): + num=None + cur = fmt[i] + while ('0'<= cur ) and ( cur <= '9'): + if num == None: + num = int(cur) + else: + num = 10*num + int(cur) + i += 1 + cur = fmt[i] + return num,i + +def calcsize(fmt): + """calcsize(fmt) -> int + Return size of C struct described by format string fmt. + See struct.__doc__ for more on format strings.""" + + formatdef,endianness,i,alignment = getmode(fmt) + num = 0 + result = 0 + while i string + Return string containing values v1, v2, ... packed according to fmt. + See struct.__doc__ for more on format strings.""" + formatdef,endianness,i,alignment = getmode(fmt) + args = list(args) + n_args = len(args) + result = [] + while i 0: + result += [bytes([len(args[0])]) + args[0][:num-1] + b'\0'*padding] + else: + if num<255: + result += [bytes([num-1]) + args[0][:num-1]] + else: + result += [bytes([255]) + args[0][:num-1]] + args.pop(0) + else: + raise StructError("arg for string format not a string") + + else: + if len(args) < num: + raise StructError("insufficient arguments to pack") + for var in args[:num]: + # pad with 0 until position is a multiple of size + if len(result) and alignment: + padding = format['size'] - len(result) % format['size'] + result += [bytes([0])]*padding + result += [format['pack'](var,format['size'],endianness)] + args=args[num:] + num = None + i += 1 + if len(args) != 0: + raise StructError("too many arguments for pack format") + return b''.join(result) + +def unpack(fmt,data): + """unpack(fmt, string) -> (v1, v2, ...) + Unpack the string, containing packed C structure data, according + to fmt. Requires len(string)==calcsize(fmt). + See struct.__doc__ for more on format strings.""" + formatdef,endianness,i,alignment = getmode(fmt) + j = 0 + num = 0 + result = [] + length= calcsize(fmt) + if length != len (data): + raise StructError("unpack str size does not match format") + while i= num: + n = num-1 + result.append(data[j+1:j+n+1]) + j += num + else: + # skip padding bytes until we get at a multiple of size + if j>0 and alignment: + padding = format['size'] - j % format['size'] + j += padding + for n in range(num): + result += [format['unpack'](data,j,format['size'],endianness)] + j += format['size'] + + return tuple(result) + +def pack_into(fmt, buf, offset, *args): + data = pack(fmt, *args) + buf[offset:offset+len(data)] = data + +def unpack_from(fmt, buf, offset=0): + size = calcsize(fmt) + data = buf[offset:offset+size] + if len(data) != size: + raise error("unpack_from requires a buffer of at least %d bytes" + % (size,)) + return unpack(fmt, data) + +def _clearcache(): + "Clear the internal cache." + # No cache in this implementation + +if __name__=='__main__': + t = pack('Bf',1,2) + print(t, len(t)) + print(unpack('Bf', t)) + print(calcsize('Bf')) + + \ No newline at end of file