File: misc.py

package info (click to toggle)
pypy3 7.3.19%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212,236 kB
  • sloc: python: 2,098,316; ansic: 540,565; sh: 21,462; asm: 14,419; cpp: 4,451; makefile: 4,209; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 12; awk: 4
file content (446 lines) | stat: -rw-r--r-- 16,993 bytes parent folder | download | duplicates (2)
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
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
from __future__ import with_statement
import sys

from pypy.interpreter.error import OperationError, oefmt
from pypy.module._rawffi.interp_rawffi import wrap_dlopenerror

from rpython.rlib import jit
from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rlib.rarithmetic import r_uint, r_ulonglong
from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib.rdynload import dlopen, DLOpenError, DLLHANDLE
from rpython.rlib.nonconst import NonConstant
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
from rpython.translator.tool.cbuild import ExternalCompilationInfo

_WIN32 = sys.platform == 'win32'


# ____________________________________________________________

_prim_signed_types = unrolling_iterable([
    (rffi.SIGNEDCHAR, rffi.SIGNEDCHARP),
    (rffi.SHORT, rffi.SHORTP),
    (rffi.INT, rffi.INTP),
    (rffi.LONG, rffi.LONGP),
    (rffi.LONGLONG, rffi.LONGLONGP)])

_prim_unsigned_types = unrolling_iterable([
    (rffi.UCHAR, rffi.UCHARP),
    (rffi.USHORT, rffi.USHORTP),
    (rffi.UINT, rffi.UINTP),
    (rffi.ULONG, rffi.ULONGP),
    (rffi.ULONGLONG, rffi.ULONGLONGP)])

_prim_float_types = unrolling_iterable([
    (rffi.FLOAT, rffi.FLOATP),
    (rffi.DOUBLE, rffi.DOUBLEP)])

def read_raw_signed_data(target, size):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            return rffi.cast(lltype.SignedLongLong, rffi.cast(TPP, target)[0])
    raise NotImplementedError("bad integer size")

def read_raw_long_data(target, size):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            assert rffi.sizeof(TP) <= rffi.sizeof(lltype.Signed)
            return rffi.cast(lltype.Signed, rffi.cast(TPP, target)[0])
    raise NotImplementedError("bad integer size")

def read_raw_unsigned_data(target, size):
    for TP, TPP in _prim_unsigned_types:
        if size == rffi.sizeof(TP):
            return rffi.cast(lltype.UnsignedLongLong, rffi.cast(TPP, target)[0])
    raise NotImplementedError("bad integer size")

def read_raw_ulong_data(target, size):
    for TP, TPP in _prim_unsigned_types:
        if size == rffi.sizeof(TP):
            assert rffi.sizeof(TP) <= rffi.sizeof(lltype.Unsigned)
            return rffi.cast(lltype.Unsigned, rffi.cast(TPP, target)[0])
    raise NotImplementedError("bad integer size")

@specialize.arg(0)
def _read_raw_float_data_tp(TPP, target):
    # in its own function: FLOAT may make the whole function jit-opaque
    return rffi.cast(lltype.Float, rffi.cast(TPP, target)[0])

def read_raw_float_data(target, size):
    for TP, TPP in _prim_float_types:
        if size == rffi.sizeof(TP):
            return _read_raw_float_data_tp(TPP, target)
    raise NotImplementedError("bad float size")

def read_raw_longdouble_data(target):
    return rffi.cast(rffi.LONGDOUBLEP, target)[0]

@specialize.argtype(1)
def write_raw_unsigned_data(target, source, size):
    for TP, TPP in _prim_unsigned_types:
        if size == rffi.sizeof(TP):
            rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
            return
    raise NotImplementedError("bad integer size")

@specialize.argtype(1)
def write_raw_signed_data(target, source, size):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
            return
    raise NotImplementedError("bad integer size")


@specialize.arg(0, 1)
def _write_raw_float_data_tp(TP, TPP, target, source):
    # in its own function: FLOAT may make the whole function jit-opaque
    rffi.cast(TPP, target)[0] = rffi.cast(TP, source)

def write_raw_float_data(target, source, size):
    for TP, TPP in _prim_float_types:
        if size == rffi.sizeof(TP):
            _write_raw_float_data_tp(TP, TPP, target, source)
            return
    raise NotImplementedError("bad float size")

def write_raw_longdouble_data(target, source):
    rffi.cast(rffi.LONGDOUBLEP, target)[0] = source

@jit.dont_look_inside    # lets get_nonmovingbuffer_ll_final_null be inlined
def write_string_as_charp(target, string):
    from pypy.module._cffi_backend.ctypefunc import set_mustfree_flag
    buf, llobj, buf_flag = rffi.get_nonmovingbuffer_ll_final_null(string)
    set_mustfree_flag(target, ord(buf_flag))   # 4, 5 or 6
    rffi.cast(rffi.CCHARPP, target)[0] = buf
    return llobj

# ____________________________________________________________

sprintf_longdouble = rffi.llexternal(
    "sprintf", [rffi.CCHARP, rffi.CCHARP, rffi.LONGDOUBLE], lltype.Void,
    _nowrapper=True, sandboxsafe=True)

FORMAT_LONGDOUBLE = rffi.str2charp("%LE")

def longdouble2str(lvalue):
    with lltype.scoped_alloc(rffi.CCHARP.TO, 128) as p:    # big enough
        sprintf_longdouble(p, FORMAT_LONGDOUBLE, lvalue)
        return rffi.charp2str(p)

# ____________________________________________________________

def _is_a_float(space, w_ob):
    from pypy.module._cffi_backend.cdataobj import W_CData
    from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveFloat
    if isinstance(w_ob, W_CData):
        return isinstance(w_ob.ctype, W_CTypePrimitiveFloat)
    return space.isinstance_w(w_ob, space.w_float)

def as_long_long(space, w_ob):
    # (possibly) convert and cast a Python object to a long long.
    # This version accepts a Python int too, and does convertions from
    # other types of objects.  It refuses floats.
    try:
        return space.int_w(w_ob, allow_conversion=False)
    except OperationError as e:
        if not (e.match(space, space.w_OverflowError) or
                e.match(space, space.w_TypeError)):
            raise
        if _is_a_float(space, w_ob):
            raise
    bigint = space.bigint_w(w_ob, allow_conversion=True)
    try:
        return bigint.tolonglong()
    except OverflowError:
        raise OperationError(space.w_OverflowError, space.newtext(ovf_msg))

def as_long(space, w_ob):
    # Same as as_long_long(), but returning an int instead.
    try:
        return space.int_w(w_ob, allow_conversion=False)
    except OperationError as e:
        if not (e.match(space, space.w_OverflowError) or
                e.match(space, space.w_TypeError)):
            raise
        if _is_a_float(space, w_ob):
            raise
    return space.int_w(w_ob, allow_conversion=True)

def as_unsigned_long_long(space, w_ob, strict):
    # (possibly) convert and cast a Python object to an unsigned long long.
    # This accepts a Python int too, and does convertions from other types of
    # objects.  If 'strict', complains with OverflowError; if 'not strict',
    # mask the result and round floats.
    try:
        value = space.int_w(w_ob, allow_conversion=False)
    except OperationError as e:
        if not (e.match(space, space.w_OverflowError) or
                e.match(space, space.w_TypeError)):
            raise
        if strict and _is_a_float(space, w_ob):
            raise
    else:
        if strict and value < 0:
            raise OperationError(space.w_OverflowError, space.newtext(neg_msg))
        return r_ulonglong(value)
    # note that if not 'strict', then space.int() will round down floats
    bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
    if strict:
        try:
            return bigint.toulonglong()
        except ValueError:
            raise OperationError(space.w_OverflowError, space.newtext(neg_msg))
        except OverflowError:
            raise OperationError(space.w_OverflowError, space.newtext(ovf_msg))
    else:
        return bigint.ulonglongmask()

def as_unsigned_long(space, w_ob, strict):
    # same as as_unsigned_long_long(), but returning just an Unsigned
    try:
        value = space.int_w(w_ob, allow_conversion=False)
    except OperationError as e:
        if not (e.match(space, space.w_OverflowError) or
                e.match(space, space.w_TypeError)):
            raise
        if strict and _is_a_float(space, w_ob):
            raise
    else:
        if strict and value < 0:
            raise OperationError(space.w_OverflowError, space.newtext(neg_msg))
        if not we_are_translated():
            if isinstance(value, NonConstant):   # hack for test_ztranslation
                return r_uint(0)
        return r_uint(value)
    # note that if not 'strict', then space.int() will round down floats
    bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
    if strict:
        try:
            return bigint.touint()
        except ValueError:
            raise OperationError(space.w_OverflowError, space.newtext(neg_msg))
        except OverflowError:
            raise OperationError(space.w_OverflowError, space.newtext(ovf_msg))
    else:
        return bigint.uintmask()

neg_msg = "can't convert negative number to unsigned"
ovf_msg = "long too big to convert"

def signext(value, size):
    # 'value' is sign-extended from 'size' bytes to a full integer.
    # 'size' should be smaller than a full integer size.
    if size == rffi.sizeof(rffi.SIGNEDCHAR):
        return rffi.cast(lltype.Signed, rffi.cast(rffi.SIGNEDCHAR, value))
    elif size == rffi.sizeof(rffi.SHORT):
        return rffi.cast(lltype.Signed, rffi.cast(rffi.SHORT, value))
    elif size == rffi.sizeof(rffi.INT):
        return rffi.cast(lltype.Signed, rffi.cast(rffi.INT, value))
    else:
        raise AssertionError("unsupported size")

# ____________________________________________________________

class _NotStandardObject(Exception):
    pass

def _standard_object_as_bool(space, w_ob):
    if space.isinstance_w(w_ob, space.w_int):
        try:
            return space.int_w(w_ob) != 0
        except OperationError as e:
            if not e.match(space, space.w_OverflowError):
                raise
            return space.bigint_w(w_ob).tobool()
    if space.isinstance_w(w_ob, space.w_float):
        return space.float_w(w_ob) != 0.0
    raise _NotStandardObject

# hackish, but the most straightforward way to know if a LONGDOUBLE object
# contains the value 0 or not.
eci = ExternalCompilationInfo(post_include_bits=["""
#define pypy__is_nonnull_longdouble(x)  ((x) != 0.0)
"""])
_is_nonnull_longdouble = rffi.llexternal(
    "pypy__is_nonnull_longdouble", [rffi.LONGDOUBLE], lltype.Bool,
    compilation_info=eci, _nowrapper=True, elidable_function=True,
    sandboxsafe=True)

# split here for JIT backends that don't support floats/longlongs/etc.
@jit.dont_look_inside
def is_nonnull_longdouble(cdata):
    return _is_nonnull_longdouble(read_raw_longdouble_data(cdata))
def is_nonnull_float(cdata, size):
    return read_raw_float_data(cdata, size) != 0.0    # note: True if a NaN

def object_as_bool(space, w_ob):
    # convert and cast a Python object to a boolean.  Accept an integer
    # or a float object, up to a CData 'long double'.
    try:
        return _standard_object_as_bool(space, w_ob)
    except _NotStandardObject:
        pass
    #
    from pypy.module._cffi_backend.cdataobj import W_CData
    from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveFloat
    from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveLongDouble
    is_cdata = isinstance(w_ob, W_CData)
    if is_cdata and isinstance(w_ob.ctype, W_CTypePrimitiveFloat):
        with w_ob as ptr:
            if isinstance(w_ob.ctype, W_CTypePrimitiveLongDouble):
                result = is_nonnull_longdouble(ptr)
            else:
                result = is_nonnull_float(ptr, w_ob.ctype.size)
        return result
    #
    if not is_cdata and space.lookup(w_ob, '__float__') is not None:
        w_io = space.float(w_ob)
    else:
        w_io = space.int(w_ob)
    try:
        return _standard_object_as_bool(space, w_io)
    except _NotStandardObject:
        raise oefmt(space.w_TypeError, "integer/float expected")

# ____________________________________________________________

@specialize.arg(0)
def _raw_memcopy_tp(TPP, source, dest):
    # in its own function: LONGLONG may make the whole function jit-opaque
    rffi.cast(TPP, dest)[0] = rffi.cast(TPP, source)[0]

def _raw_memcopy(source, dest, size):
    if jit.isconstant(size):
        # for the JIT: first handle the case where 'size' is known to be
        # a constant equal to 1, 2, 4, 8
        for TP, TPP in _prim_unsigned_types:
            if size == rffi.sizeof(TP):
                _raw_memcopy_tp(TPP, source, dest)
                return
    _raw_memcopy_opaque(source, dest, size)

@jit.dont_look_inside
def _raw_memcopy_opaque(source, dest, size):
    # push push push at the llmemory interface (with hacks that are all
    # removed after translation)
    zero = llmemory.itemoffsetof(rffi.CCHARP.TO, 0)
    llmemory.raw_memcopy(
        llmemory.cast_ptr_to_adr(source) + zero,
        llmemory.cast_ptr_to_adr(dest) + zero,
        size * llmemory.sizeof(lltype.Char))

@specialize.arg(0, 1)
def _raw_memclear_tp(TP, TPP, dest):
    # in its own function: LONGLONG may make the whole function jit-opaque
    rffi.cast(TPP, dest)[0] = rffi.cast(TP, 0)

def _raw_memclear(dest, size):
    # for now, only supports the cases of size = 1, 2, 4, 8
    for TP, TPP in _prim_unsigned_types:
        if size == rffi.sizeof(TP):
            _raw_memclear_tp(TP, TPP, dest)
            return
    raise NotImplementedError("bad clear size")

# ____________________________________________________________

def pack_list_to_raw_array_bounds_signed(int_list, target, size):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            ptr = rffi.cast(TPP, target)
            for i in range(len(int_list)):
                x = int_list[i]
                y = rffi.cast(TP, x)
                if x != rffi.cast(lltype.Signed, y):
                    return x      # overflow
                ptr[i] = y
            return 0
    raise NotImplementedError("bad integer size")

def pack_list_to_raw_array_bounds_unsigned(int_list, target, size, vrangemax):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            ptr = rffi.cast(TPP, target)
            for i in range(len(int_list)):
                x = int_list[i]
                if r_uint(x) > vrangemax:
                    return x      # overflow
                ptr[i] = rffi.cast(TP, x)
            return 0
    raise NotImplementedError("bad integer size")

@specialize.arg(2)
def pack_float_list_to_raw_array(float_list, target, TP, TPP):
    target = rffi.cast(TPP, target)
    for i in range(len(float_list)):
        x = float_list[i]
        target[i] = rffi.cast(TP, x)

def unpack_list_from_raw_array(int_list, source, size):
    for TP, TPP in _prim_signed_types:
        if size == rffi.sizeof(TP):
            ptr = rffi.cast(TPP, source)
            for i in range(len(int_list)):
                int_list[i] = rffi.cast(lltype.Signed, ptr[i])
            return
    raise NotImplementedError("bad integer size")

def unpack_unsigned_list_from_raw_array(int_list, source, size):
    for TP, TPP in _prim_unsigned_types:
        if size == rffi.sizeof(TP):
            ptr = rffi.cast(TPP, source)
            for i in range(len(int_list)):
                int_list[i] = rffi.cast(lltype.Signed, ptr[i])
            return
    raise NotImplementedError("bad integer size")

def unpack_cfloat_list_from_raw_array(float_list, source):
    ptr = rffi.cast(rffi.FLOATP, source)
    for i in range(len(float_list)):
        float_list[i] = rffi.cast(lltype.Float, ptr[i])

# ____________________________________________________________

def dlopen_w(space, w_filename, flags):
    from pypy.module._cffi_backend.cdataobj import W_CData
    from pypy.module._cffi_backend import ctypeptr

    autoclose = True
    if isinstance(w_filename, W_CData):
        # 'flags' ignored in this case
        w_ctype = w_filename.ctype
        if (not isinstance(w_ctype, ctypeptr.W_CTypePointer) or
            not w_ctype.is_void_ptr):
            raise oefmt(space.w_TypeError,
                    "dlopen() takes a file name or 'void *' handle, not '%s'",
                    w_ctype.name)
        handle = w_filename.unsafe_escaping_ptr()
        if not handle:
            raise oefmt(space.w_RuntimeError, "cannot call dlopen(NULL)")
        fname = w_ctype.extra_repr(handle)
        handle = rffi.cast(DLLHANDLE, handle)
        autoclose = False
        #
    elif _WIN32 and space.isinstance_w(w_filename, space.w_unicode):
        fname = space.text_w(w_filename)
        utf8_name = space.utf8_w(w_filename)
        try:
            handle = dlopen(utf8_name, flags)
        except DLOpenError as e:
            raise wrap_dlopenerror(space, e, fname)
    else:
        if space.is_none(w_filename):
            _fname = None
        else:
            _fname = space.fsencode_w(w_filename)
        if _fname is None:
            fname = "<None>"
        else:
            fname = _fname
        try:
            handle = dlopen(_fname, flags)
        except DLOpenError as e:
            raise wrap_dlopenerror(space, e, fname)
    return fname, handle, autoclose