pypy /pypy/module/_rawffi/structure.py

Language Python Lines 410
MD5 Hash 83b0a64b6ace0f2037124c7308686273 Estimated Cost $8,677 (why?)
Repository https://bitbucket.org/pypy/pypy/ View Raw File View Project SPDX
  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
""" Interpreter-level implementation of structure, exposing ll-structure
to app-level with apropriate interface
"""

from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import interp_attrproperty
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.error import OperationError, oefmt
from pypy.module._rawffi.interp_rawffi import segfault_exception, _MS_WINDOWS
from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
from pypy.module._rawffi.interp_rawffi import wrap_value, unwrap_value
from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
from pypy.module._rawffi.interp_rawffi import LL_TYPEMAP
from pypy.module._rawffi.interp_rawffi import unroll_letters_for_numbers
from pypy.module._rawffi.interp_rawffi import size_alignment
from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
from rpython.rlib import clibffi, rgc
from rpython.rlib.rarithmetic import intmask, signedtype, r_uint, \
    r_ulonglong
from rpython.rtyper.lltypesystem import lltype, rffi
import sys

IS_BIG_ENDIAN = sys.byteorder == 'big'



def unpack_fields(space, w_fields):
    fields_w = space.unpackiterable(w_fields)
    fields = []
    for w_tup in fields_w:
        l_w = space.unpackiterable(w_tup)
        len_l = len(l_w)

        if len_l < 2 or len_l > 3:
            raise oefmt(space.w_ValueError,
                        "Expected list of 2- or 3-size tuples")

        try:
            name = space.str_w(l_w[0])
        except OperationError:
            raise oefmt(space.w_TypeError,
                        "structure field name must be string not %T", l_w[0])
        tp = unpack_shape_with_length(space, l_w[1])

        if len_l == 3:
            for c in unroll_letters_for_numbers:
                if c == tp.itemcode:
                    break
            else:
                raise oefmt(space.w_TypeError,
                            "bit fields not allowed for type")
            bitsize = space.int_w(l_w[2])
            if bitsize <= 0 or bitsize > tp.size * 8:
                raise oefmt(space.w_ValueError,
                            "number of bits invalid for bit field")
        else:
            bitsize = 0

        fields.append((name, tp, bitsize))
    return fields

def round_up(size, alignment):
    return (size + alignment - 1) & -alignment

NO_BITFIELD, NEW_BITFIELD, CONT_BITFIELD, EXPAND_BITFIELD = range(4)

def size_alignment_pos(fields, is_union=False, pack=0):
    size = 0
    alignment = 1
    pos = []
    bitsizes = []
    bitoffset = 0
    has_bitfield = False
    last_size = 0

    for fieldname, fieldtype, bitsize in fields:
        # fieldtype is a W_Array
        fieldsize = fieldtype.size
        fieldalignment = fieldtype.alignment
        if pack:
            fieldalignment = min(fieldalignment, pack)
        alignment = max(alignment, fieldalignment)

        if not bitsize:
            # not a bit field
            field_type = NO_BITFIELD
            last_size = 0
            bitoffset = 0
        elif (last_size and # we have a bitfield open
              (not _MS_WINDOWS or fieldsize * 8 == last_size) and
              fieldsize * 8 <= last_size and
              bitoffset + bitsize <= last_size):
            # continue bit field
            field_type = CONT_BITFIELD
        elif (not _MS_WINDOWS and
              last_size and # we have a bitfield open
              fieldsize * 8 >= last_size and
              bitoffset + bitsize <= fieldsize * 8):
            # expand bit field
            field_type = EXPAND_BITFIELD
        else:
            # start new bitfield
            field_type = NEW_BITFIELD
            has_bitfield = True
            bitoffset = 0
            last_size = fieldsize * 8

        if is_union:
            pos.append(0)
            bitsizes.append(fieldsize)
            size = max(size, fieldsize)
        else:
            if field_type == NO_BITFIELD:
                # the usual case
                size = round_up(size, fieldalignment)
                pos.append(size)
                size += intmask(fieldsize)
                bitsizes.append(fieldsize)
            elif field_type == NEW_BITFIELD:
                if IS_BIG_ENDIAN:
                    off = last_size - bitoffset - bitsize
                else:
                    off = bitoffset
                bitsizes.append((bitsize << 16) + off)
                bitoffset = bitsize
                size = round_up(size, fieldalignment)
                pos.append(size)
                size += fieldsize
            elif field_type == CONT_BITFIELD:
                if IS_BIG_ENDIAN:
                    off = last_size - bitoffset - bitsize
                else:
                    off = bitoffset
                bitsizes.append((bitsize << 16) + off)
                bitoffset += bitsize
                # offset is already updated for the NEXT field
                pos.append(size - fieldsize)
            elif field_type == EXPAND_BITFIELD:
                size += fieldsize - last_size / 8
                last_size = fieldsize * 8
                if IS_BIG_ENDIAN:
                    off = last_size - bitoffset - bitsize
                else:
                    off = bitoffset
                bitsizes.append((bitsize << 16) + off)
                bitoffset += bitsize
                # offset is already updated for the NEXT field
                pos.append(size - fieldsize)

    if not has_bitfield:
        bitsizes = None
    size = round_up(size, alignment)
    return size, alignment, pos, bitsizes


class W_Structure(W_DataShape):
    def __init__(self, space, fields, size, alignment, is_union=False, pack=0):
        name_to_index = {}
        if fields is not None:
            for i in range(len(fields)):
                name, tp, bitsize = fields[i]
                if name in name_to_index:
                    raise oefmt(space.w_ValueError,
                                "duplicate field name %s", name)
                name_to_index[name] = i
            size, alignment, pos, bitsizes = size_alignment_pos(
                fields, is_union, pack)
        else: # opaque case
            fields = []
            pos = []
            bitsizes = None
        self.fields = fields
        self.size = size
        self.alignment = alignment
        self.ll_positions = pos
        self.ll_bitsizes = bitsizes
        self.name_to_index = name_to_index

    def allocate(self, space, length, autofree=False):
        # length is ignored!
        if autofree:
            return W_StructureInstanceAutoFree(space, self)
        return W_StructureInstance(space, self, 0)

    def getindex(self, space, attr):
        try:
            return self.name_to_index[attr]
        except KeyError:
            raise oefmt(space.w_AttributeError,
                        "C Structure has no attribute %s", attr)

    @unwrap_spec(autofree=bool)
    def descr_call(self, space, autofree=False):
        return space.wrap(self.allocate(space, 1, autofree))

    def descr_repr(self, space):
        fieldnames = ' '.join(["'%s'" % name for name, _, _ in self.fields])
        return space.wrap("<_rawffi.Structure %s (%d, %d)>" % (fieldnames,
                                                               self.size,
                                                               self.alignment))

    @unwrap_spec(address=r_uint)
    def fromaddress(self, space, address):
        return space.wrap(W_StructureInstance(space, self, address))

    @unwrap_spec(attr=str)
    def descr_fieldoffset(self, space, attr):
        index = self.getindex(space, attr)
        return space.wrap(self.ll_positions[index])

    @unwrap_spec(attr=str)
    def descr_fieldsize(self, space, attr):
        index = self.getindex(space, attr)
        if self.ll_bitsizes and index < len(self.ll_bitsizes):
            return space.wrap(self.ll_bitsizes[index])
        else:
            return space.wrap(self.fields[index][1].size)

    # get the corresponding ffi_type
    ffi_struct = lltype.nullptr(clibffi.FFI_STRUCT_P.TO)

    def get_basic_ffi_type(self):
        if not self.ffi_struct:
            # Repeated fields are delicate.  Consider for example
            #     struct { int a[5]; }
            # or  struct { struct {int x;} a[5]; }
            # Seeing no corresponding doc in clibffi, let's just repeat
            # the field 5 times...
            fieldtypes = []
            for name, tp, bitsize in self.fields:
                basic_ffi_type = tp.get_basic_ffi_type()
                basic_size, _ = size_alignment(basic_ffi_type)
                total_size = tp.size
                count = 0
                while count + basic_size <= total_size:
                    fieldtypes.append(basic_ffi_type)
                    count += basic_size
                    if basic_size == 0: # corner case. get out of this infinite
                        break           # loop after 1 iteration ("why not")
            self.ffi_struct = clibffi.make_struct_ffitype_e(self.size,
                                                           self.alignment,
                                                           fieldtypes)
        return self.ffi_struct.ffistruct

    @rgc.must_be_light_finalizer
    def __del__(self):
        if self.ffi_struct:
            lltype.free(self.ffi_struct, flavor='raw')


@unwrap_spec(union=bool, pack=int)
def descr_new_structure(space, w_type, w_shapeinfo, union=False, pack=0):
    if pack < 0:
        raise oefmt(space.w_ValueError,
                    "_pack_ must be a non-negative integer")

    if space.isinstance_w(w_shapeinfo, space.w_tuple):
        w_size, w_alignment = space.fixedview(w_shapeinfo, expected_length=2)
        S = W_Structure(space, None, space.int_w(w_size),
                                     space.int_w(w_alignment), union)
    else:
        fields = unpack_fields(space, w_shapeinfo)
        S = W_Structure(space, fields, 0, 0, union, pack)
    return space.wrap(S)

W_Structure.typedef = TypeDef(
    'Structure',
    __new__     = interp2app(descr_new_structure),
    __call__    = interp2app(W_Structure.descr_call),
    __repr__    = interp2app(W_Structure.descr_repr),
    fromaddress = interp2app(W_Structure.fromaddress),
    size        = interp_attrproperty('size', W_Structure),
    alignment   = interp_attrproperty('alignment', W_Structure),
    fieldoffset = interp2app(W_Structure.descr_fieldoffset),
    fieldsize   = interp2app(W_Structure.descr_fieldsize),
    size_alignment = interp2app(W_Structure.descr_size_alignment),
    get_ffi_type   = interp2app(W_Structure.descr_get_ffi_type),
)
W_Structure.typedef.acceptable_as_base_class = False

def LOW_BIT(x):
    return x & 0xFFFF

def NUM_BITS(x):
    return x >> 16

def BIT_MASK(x, ll_t):
    if ll_t is lltype.SignedLongLong or ll_t is lltype.UnsignedLongLong:
        one = r_ulonglong(1)
    else:
        one = r_uint(1)
    # to avoid left shift by x == sizeof(ll_t)
    return (((one << (x - 1)) - 1) << 1) + 1
BIT_MASK._annspecialcase_ = 'specialize:arg(1)'

def push_field(self, num, value):
    ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[num])
    TP = lltype.typeOf(value)
    # Handle bitfields
    for c in unroll_letters_for_numbers:
        if LL_TYPEMAP[c] is TP and self.shape.ll_bitsizes:
            # Modify the current value with the bitfield changed
            bitsize = self.shape.ll_bitsizes[num]
            numbits = NUM_BITS(bitsize)
            if numbits:
                lowbit = LOW_BIT(bitsize)
                bitmask = BIT_MASK(numbits, TP)
                masktype = lltype.typeOf(bitmask)
                value = rffi.cast(masktype, value)
                current = rffi.cast(masktype, read_ptr(ptr, 0, TP))
                current &= ~(bitmask << lowbit)
                current |= (value & bitmask) << lowbit
                value = rffi.cast(TP, current)
            break
    write_ptr(ptr, 0, value)
push_field._annspecialcase_ = 'specialize:argtype(2)'

def cast_pos(self, i, ll_t):
    pos = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
    value = read_ptr(pos, 0, ll_t)
    # Handle bitfields
    for c in unroll_letters_for_numbers:
        if LL_TYPEMAP[c] is ll_t and self.shape.ll_bitsizes:
            bitsize = self.shape.ll_bitsizes[i]
            numbits = NUM_BITS(bitsize)
            if numbits:
                lowbit = LOW_BIT(bitsize)
                bitmask = BIT_MASK(numbits, ll_t)
                masktype = lltype.typeOf(bitmask)
                value = rffi.cast(masktype, value)
                value >>= lowbit
                value &= bitmask
                if ll_t is lltype.Bool or signedtype(ll_t._type):
                    sign = (value >> (numbits - 1)) & 1
                    if sign:
                        value -= bitmask + 1
                value = rffi.cast(ll_t, value)
            break
    return value
cast_pos._annspecialcase_ = 'specialize:arg(2)'

class W_StructureInstance(W_DataInstance):
    def __init__(self, space, shape, address):
        W_DataInstance.__init__(self, space, shape.size, address)
        self.shape = shape

    def descr_repr(self, space):
        addr = rffi.cast(lltype.Unsigned, self.ll_buffer)
        return space.wrap("<_rawffi struct %x>" % (addr,))

    @unwrap_spec(attr=str)
    def getattr(self, space, attr):
        if not self.ll_buffer:
            raise segfault_exception(space, "accessing NULL pointer")
        i = self.shape.getindex(space, attr)
        _, tp, _ = self.shape.fields[i]
        return wrap_value(space, cast_pos, self, i, tp.itemcode)

    @unwrap_spec(attr=str)
    def setattr(self, space, attr, w_value):
        if not self.ll_buffer:
            raise segfault_exception(space, "accessing NULL pointer")
        i = self.shape.getindex(space, attr)
        _, tp, _ = self.shape.fields[i]
        unwrap_value(space, push_field, self, i, tp.itemcode, w_value)

    @unwrap_spec(attr=str)
    def descr_fieldaddress(self, space, attr):
        i = self.shape.getindex(space, attr)
        ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
        return space.wrap(rffi.cast(lltype.Unsigned, ptr))

    def getrawsize(self):
        return self.shape.size


W_StructureInstance.typedef = TypeDef(
    'StructureInstance',
    __repr__    = interp2app(W_StructureInstance.descr_repr),
    __getattr__ = interp2app(W_StructureInstance.getattr),
    __setattr__ = interp2app(W_StructureInstance.setattr),
    buffer      = GetSetProperty(W_StructureInstance.getbuffer),
    free        = interp2app(W_StructureInstance.free),
    shape       = interp_attrproperty('shape', W_StructureInstance),
    byptr       = interp2app(W_StructureInstance.byptr),
    fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
)
W_StructureInstance.typedef.acceptable_as_base_class = False

class W_StructureInstanceAutoFree(W_StructureInstance):
    def __init__(self, space, shape):
        W_StructureInstance.__init__(self, space, shape, 0)

    @rgc.must_be_light_finalizer
    def __del__(self):
        if self.ll_buffer:
            self._free()

W_StructureInstanceAutoFree.typedef = TypeDef(
    'StructureInstanceAutoFree',
    __repr__    = interp2app(W_StructureInstance.descr_repr),
    __getattr__ = interp2app(W_StructureInstance.getattr),
    __setattr__ = interp2app(W_StructureInstance.setattr),
    buffer      = GetSetProperty(W_StructureInstance.getbuffer),
    shape       = interp_attrproperty('shape', W_StructureInstance),
    byptr       = interp2app(W_StructureInstance.byptr),
    fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
)
W_StructureInstanceAutoFree.typedef.acceptable_as_base_class = False
Back to Top