pypy / lib_pypy / struct.py

  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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
#
# 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.
#

"""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 = [ord(b) for b in data[index:index+size]]
    if le == 'little':
        bytes.reverse()
    number = 0L
    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(chr(x&0xff))
        x >>= 8
    if le == 'big':
        res.reverse()
    return ''.join(res)

def pack_signed_int(number,size,le):
    if not isinstance(number, (int,long)):
        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,long)):
        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 str(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(8):
        result.append(chr((unsigned >> (i * 8)) & 0xFF))
    if le == "big":
        result.reverse()
    return ''.join(result)

def unpack_float(data, index, size, le):
    binary = [data[i] for i in range(index, index + 8)]
    if le == "big":
        binary.reverse()
    unsigned = 0
    for i in range(8):
        unsigned |= ord(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]]
        index = 1
    except KeyError:
        formatdef,endianness = formatmode['@']
        index = 0
    return formatdef,endianness,index
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 = getmode(fmt)
    num = 0
    result = 0
    while i<len(fmt):
        num,i = getNum(fmt,i)
        cur = fmt[i]
        try:
            format = formatdef[cur]
        except KeyError:
            raise StructError,"%s is not a valid format"%cur
        if num != None :
            result += num*format['size']
        else:
            result += format['size']
        num = 0
        i += 1
    return result

def pack(fmt,*args):
    """pack(fmt, v1, v2, ...) -> string
       Return string containing values v1, v2, ... packed according to fmt.
       See struct.__doc__ for more on format strings."""
    formatdef,endianness,i = getmode(fmt)
    args = list(args)
    n_args = len(args)
    result = []
    while i<len(fmt):
        num,i = getNum(fmt,i)
        cur = fmt[i]
        try:
            format = formatdef[cur]
        except KeyError:
            raise StructError,"%s is not a valid format"%cur
        if num == None :
            num_s = 0
            num = 1
        else:
            num_s = num

        if cur == 'x':
            result += ['\0'*num]
        elif cur == 's':
            if isinstance(args[0], str):
                padding = num - len(args[0])
                result += [args[0][:num] + '\0'*padding]
                args.pop(0)
            else:
                raise StructError,"arg for string format not a string"
        elif cur == 'p':
            if isinstance(args[0], str):
                padding = num - len(args[0]) - 1

                if padding > 0:
                    result += [chr(len(args[0])) + args[0][:num-1] + '\0'*padding]
                else:
                    if num<255:
                        result += [chr(num-1) + args[0][:num-1]]
                    else:
                        result += [chr(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]:
                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 ''.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 = 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<len(fmt):
        num,i=getNum(fmt,i)
        cur = fmt[i]
        i += 1
        try:
            format = formatdef[cur]
        except KeyError:
            raise StructError,"%s is not a valid format"%cur

        if not num :
            num = 1

        if cur == 'x':
            j += num
        elif cur == 's':
            result.append(data[j:j+num])
            j += num
        elif cur == 'p':
            n=ord(data[j])
            if n >= num:
                n = num-1
            result.append(data[j+1:j+n+1])
            j += num
        else:
            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)
    buffer(buf)[offset:offset+len(data)] = data

def unpack_from(fmt, buf, offset=0):
    size = calcsize(fmt)
    data = buffer(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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.