PageRenderTime 127ms CodeModel.GetById 3ms app.highlight 47ms RepoModel.GetById 72ms app.codeStats 0ms

/pypy/module/_rawffi/structure.py

https://bitbucket.org/pypy/pypy/
Python | 409 lines | 339 code | 48 blank | 22 comment | 65 complexity | 83b0a64b6ace0f2037124c7308686273 MD5 | raw file
  1""" Interpreter-level implementation of structure, exposing ll-structure
  2to app-level with apropriate interface
  3"""
  4
  5from pypy.interpreter.gateway import interp2app, unwrap_spec
  6from pypy.interpreter.typedef import interp_attrproperty
  7from pypy.interpreter.typedef import TypeDef, GetSetProperty
  8from pypy.interpreter.error import OperationError, oefmt
  9from pypy.module._rawffi.interp_rawffi import segfault_exception, _MS_WINDOWS
 10from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
 11from pypy.module._rawffi.interp_rawffi import wrap_value, unwrap_value
 12from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
 13from pypy.module._rawffi.interp_rawffi import LL_TYPEMAP
 14from pypy.module._rawffi.interp_rawffi import unroll_letters_for_numbers
 15from pypy.module._rawffi.interp_rawffi import size_alignment
 16from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
 17from rpython.rlib import clibffi, rgc
 18from rpython.rlib.rarithmetic import intmask, signedtype, r_uint, \
 19    r_ulonglong
 20from rpython.rtyper.lltypesystem import lltype, rffi
 21import sys
 22
 23IS_BIG_ENDIAN = sys.byteorder == 'big'
 24
 25
 26
 27def unpack_fields(space, w_fields):
 28    fields_w = space.unpackiterable(w_fields)
 29    fields = []
 30    for w_tup in fields_w:
 31        l_w = space.unpackiterable(w_tup)
 32        len_l = len(l_w)
 33
 34        if len_l < 2 or len_l > 3:
 35            raise oefmt(space.w_ValueError,
 36                        "Expected list of 2- or 3-size tuples")
 37
 38        try:
 39            name = space.str_w(l_w[0])
 40        except OperationError:
 41            raise oefmt(space.w_TypeError,
 42                        "structure field name must be string not %T", l_w[0])
 43        tp = unpack_shape_with_length(space, l_w[1])
 44
 45        if len_l == 3:
 46            for c in unroll_letters_for_numbers:
 47                if c == tp.itemcode:
 48                    break
 49            else:
 50                raise oefmt(space.w_TypeError,
 51                            "bit fields not allowed for type")
 52            bitsize = space.int_w(l_w[2])
 53            if bitsize <= 0 or bitsize > tp.size * 8:
 54                raise oefmt(space.w_ValueError,
 55                            "number of bits invalid for bit field")
 56        else:
 57            bitsize = 0
 58
 59        fields.append((name, tp, bitsize))
 60    return fields
 61
 62def round_up(size, alignment):
 63    return (size + alignment - 1) & -alignment
 64
 65NO_BITFIELD, NEW_BITFIELD, CONT_BITFIELD, EXPAND_BITFIELD = range(4)
 66
 67def size_alignment_pos(fields, is_union=False, pack=0):
 68    size = 0
 69    alignment = 1
 70    pos = []
 71    bitsizes = []
 72    bitoffset = 0
 73    has_bitfield = False
 74    last_size = 0
 75
 76    for fieldname, fieldtype, bitsize in fields:
 77        # fieldtype is a W_Array
 78        fieldsize = fieldtype.size
 79        fieldalignment = fieldtype.alignment
 80        if pack:
 81            fieldalignment = min(fieldalignment, pack)
 82        alignment = max(alignment, fieldalignment)
 83
 84        if not bitsize:
 85            # not a bit field
 86            field_type = NO_BITFIELD
 87            last_size = 0
 88            bitoffset = 0
 89        elif (last_size and # we have a bitfield open
 90              (not _MS_WINDOWS or fieldsize * 8 == last_size) and
 91              fieldsize * 8 <= last_size and
 92              bitoffset + bitsize <= last_size):
 93            # continue bit field
 94            field_type = CONT_BITFIELD
 95        elif (not _MS_WINDOWS and
 96              last_size and # we have a bitfield open
 97              fieldsize * 8 >= last_size and
 98              bitoffset + bitsize <= fieldsize * 8):
 99            # expand bit field
100            field_type = EXPAND_BITFIELD
101        else:
102            # start new bitfield
103            field_type = NEW_BITFIELD
104            has_bitfield = True
105            bitoffset = 0
106            last_size = fieldsize * 8
107
108        if is_union:
109            pos.append(0)
110            bitsizes.append(fieldsize)
111            size = max(size, fieldsize)
112        else:
113            if field_type == NO_BITFIELD:
114                # the usual case
115                size = round_up(size, fieldalignment)
116                pos.append(size)
117                size += intmask(fieldsize)
118                bitsizes.append(fieldsize)
119            elif field_type == NEW_BITFIELD:
120                if IS_BIG_ENDIAN:
121                    off = last_size - bitoffset - bitsize
122                else:
123                    off = bitoffset
124                bitsizes.append((bitsize << 16) + off)
125                bitoffset = bitsize
126                size = round_up(size, fieldalignment)
127                pos.append(size)
128                size += fieldsize
129            elif field_type == CONT_BITFIELD:
130                if IS_BIG_ENDIAN:
131                    off = last_size - bitoffset - bitsize
132                else:
133                    off = bitoffset
134                bitsizes.append((bitsize << 16) + off)
135                bitoffset += bitsize
136                # offset is already updated for the NEXT field
137                pos.append(size - fieldsize)
138            elif field_type == EXPAND_BITFIELD:
139                size += fieldsize - last_size / 8
140                last_size = fieldsize * 8
141                if IS_BIG_ENDIAN:
142                    off = last_size - bitoffset - bitsize
143                else:
144                    off = bitoffset
145                bitsizes.append((bitsize << 16) + off)
146                bitoffset += bitsize
147                # offset is already updated for the NEXT field
148                pos.append(size - fieldsize)
149
150    if not has_bitfield:
151        bitsizes = None
152    size = round_up(size, alignment)
153    return size, alignment, pos, bitsizes
154
155
156class W_Structure(W_DataShape):
157    def __init__(self, space, fields, size, alignment, is_union=False, pack=0):
158        name_to_index = {}
159        if fields is not None:
160            for i in range(len(fields)):
161                name, tp, bitsize = fields[i]
162                if name in name_to_index:
163                    raise oefmt(space.w_ValueError,
164                                "duplicate field name %s", name)
165                name_to_index[name] = i
166            size, alignment, pos, bitsizes = size_alignment_pos(
167                fields, is_union, pack)
168        else: # opaque case
169            fields = []
170            pos = []
171            bitsizes = None
172        self.fields = fields
173        self.size = size
174        self.alignment = alignment
175        self.ll_positions = pos
176        self.ll_bitsizes = bitsizes
177        self.name_to_index = name_to_index
178
179    def allocate(self, space, length, autofree=False):
180        # length is ignored!
181        if autofree:
182            return W_StructureInstanceAutoFree(space, self)
183        return W_StructureInstance(space, self, 0)
184
185    def getindex(self, space, attr):
186        try:
187            return self.name_to_index[attr]
188        except KeyError:
189            raise oefmt(space.w_AttributeError,
190                        "C Structure has no attribute %s", attr)
191
192    @unwrap_spec(autofree=bool)
193    def descr_call(self, space, autofree=False):
194        return space.wrap(self.allocate(space, 1, autofree))
195
196    def descr_repr(self, space):
197        fieldnames = ' '.join(["'%s'" % name for name, _, _ in self.fields])
198        return space.wrap("<_rawffi.Structure %s (%d, %d)>" % (fieldnames,
199                                                               self.size,
200                                                               self.alignment))
201
202    @unwrap_spec(address=r_uint)
203    def fromaddress(self, space, address):
204        return space.wrap(W_StructureInstance(space, self, address))
205
206    @unwrap_spec(attr=str)
207    def descr_fieldoffset(self, space, attr):
208        index = self.getindex(space, attr)
209        return space.wrap(self.ll_positions[index])
210
211    @unwrap_spec(attr=str)
212    def descr_fieldsize(self, space, attr):
213        index = self.getindex(space, attr)
214        if self.ll_bitsizes and index < len(self.ll_bitsizes):
215            return space.wrap(self.ll_bitsizes[index])
216        else:
217            return space.wrap(self.fields[index][1].size)
218
219    # get the corresponding ffi_type
220    ffi_struct = lltype.nullptr(clibffi.FFI_STRUCT_P.TO)
221
222    def get_basic_ffi_type(self):
223        if not self.ffi_struct:
224            # Repeated fields are delicate.  Consider for example
225            #     struct { int a[5]; }
226            # or  struct { struct {int x;} a[5]; }
227            # Seeing no corresponding doc in clibffi, let's just repeat
228            # the field 5 times...
229            fieldtypes = []
230            for name, tp, bitsize in self.fields:
231                basic_ffi_type = tp.get_basic_ffi_type()
232                basic_size, _ = size_alignment(basic_ffi_type)
233                total_size = tp.size
234                count = 0
235                while count + basic_size <= total_size:
236                    fieldtypes.append(basic_ffi_type)
237                    count += basic_size
238                    if basic_size == 0: # corner case. get out of this infinite
239                        break           # loop after 1 iteration ("why not")
240            self.ffi_struct = clibffi.make_struct_ffitype_e(self.size,
241                                                           self.alignment,
242                                                           fieldtypes)
243        return self.ffi_struct.ffistruct
244
245    @rgc.must_be_light_finalizer
246    def __del__(self):
247        if self.ffi_struct:
248            lltype.free(self.ffi_struct, flavor='raw')
249
250
251@unwrap_spec(union=bool, pack=int)
252def descr_new_structure(space, w_type, w_shapeinfo, union=False, pack=0):
253    if pack < 0:
254        raise oefmt(space.w_ValueError,
255                    "_pack_ must be a non-negative integer")
256
257    if space.isinstance_w(w_shapeinfo, space.w_tuple):
258        w_size, w_alignment = space.fixedview(w_shapeinfo, expected_length=2)
259        S = W_Structure(space, None, space.int_w(w_size),
260                                     space.int_w(w_alignment), union)
261    else:
262        fields = unpack_fields(space, w_shapeinfo)
263        S = W_Structure(space, fields, 0, 0, union, pack)
264    return space.wrap(S)
265
266W_Structure.typedef = TypeDef(
267    'Structure',
268    __new__     = interp2app(descr_new_structure),
269    __call__    = interp2app(W_Structure.descr_call),
270    __repr__    = interp2app(W_Structure.descr_repr),
271    fromaddress = interp2app(W_Structure.fromaddress),
272    size        = interp_attrproperty('size', W_Structure),
273    alignment   = interp_attrproperty('alignment', W_Structure),
274    fieldoffset = interp2app(W_Structure.descr_fieldoffset),
275    fieldsize   = interp2app(W_Structure.descr_fieldsize),
276    size_alignment = interp2app(W_Structure.descr_size_alignment),
277    get_ffi_type   = interp2app(W_Structure.descr_get_ffi_type),
278)
279W_Structure.typedef.acceptable_as_base_class = False
280
281def LOW_BIT(x):
282    return x & 0xFFFF
283
284def NUM_BITS(x):
285    return x >> 16
286
287def BIT_MASK(x, ll_t):
288    if ll_t is lltype.SignedLongLong or ll_t is lltype.UnsignedLongLong:
289        one = r_ulonglong(1)
290    else:
291        one = r_uint(1)
292    # to avoid left shift by x == sizeof(ll_t)
293    return (((one << (x - 1)) - 1) << 1) + 1
294BIT_MASK._annspecialcase_ = 'specialize:arg(1)'
295
296def push_field(self, num, value):
297    ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[num])
298    TP = lltype.typeOf(value)
299    # Handle bitfields
300    for c in unroll_letters_for_numbers:
301        if LL_TYPEMAP[c] is TP and self.shape.ll_bitsizes:
302            # Modify the current value with the bitfield changed
303            bitsize = self.shape.ll_bitsizes[num]
304            numbits = NUM_BITS(bitsize)
305            if numbits:
306                lowbit = LOW_BIT(bitsize)
307                bitmask = BIT_MASK(numbits, TP)
308                masktype = lltype.typeOf(bitmask)
309                value = rffi.cast(masktype, value)
310                current = rffi.cast(masktype, read_ptr(ptr, 0, TP))
311                current &= ~(bitmask << lowbit)
312                current |= (value & bitmask) << lowbit
313                value = rffi.cast(TP, current)
314            break
315    write_ptr(ptr, 0, value)
316push_field._annspecialcase_ = 'specialize:argtype(2)'
317
318def cast_pos(self, i, ll_t):
319    pos = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
320    value = read_ptr(pos, 0, ll_t)
321    # Handle bitfields
322    for c in unroll_letters_for_numbers:
323        if LL_TYPEMAP[c] is ll_t and self.shape.ll_bitsizes:
324            bitsize = self.shape.ll_bitsizes[i]
325            numbits = NUM_BITS(bitsize)
326            if numbits:
327                lowbit = LOW_BIT(bitsize)
328                bitmask = BIT_MASK(numbits, ll_t)
329                masktype = lltype.typeOf(bitmask)
330                value = rffi.cast(masktype, value)
331                value >>= lowbit
332                value &= bitmask
333                if ll_t is lltype.Bool or signedtype(ll_t._type):
334                    sign = (value >> (numbits - 1)) & 1
335                    if sign:
336                        value -= bitmask + 1
337                value = rffi.cast(ll_t, value)
338            break
339    return value
340cast_pos._annspecialcase_ = 'specialize:arg(2)'
341
342class W_StructureInstance(W_DataInstance):
343    def __init__(self, space, shape, address):
344        W_DataInstance.__init__(self, space, shape.size, address)
345        self.shape = shape
346
347    def descr_repr(self, space):
348        addr = rffi.cast(lltype.Unsigned, self.ll_buffer)
349        return space.wrap("<_rawffi struct %x>" % (addr,))
350
351    @unwrap_spec(attr=str)
352    def getattr(self, space, attr):
353        if not self.ll_buffer:
354            raise segfault_exception(space, "accessing NULL pointer")
355        i = self.shape.getindex(space, attr)
356        _, tp, _ = self.shape.fields[i]
357        return wrap_value(space, cast_pos, self, i, tp.itemcode)
358
359    @unwrap_spec(attr=str)
360    def setattr(self, space, attr, w_value):
361        if not self.ll_buffer:
362            raise segfault_exception(space, "accessing NULL pointer")
363        i = self.shape.getindex(space, attr)
364        _, tp, _ = self.shape.fields[i]
365        unwrap_value(space, push_field, self, i, tp.itemcode, w_value)
366
367    @unwrap_spec(attr=str)
368    def descr_fieldaddress(self, space, attr):
369        i = self.shape.getindex(space, attr)
370        ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
371        return space.wrap(rffi.cast(lltype.Unsigned, ptr))
372
373    def getrawsize(self):
374        return self.shape.size
375
376
377W_StructureInstance.typedef = TypeDef(
378    'StructureInstance',
379    __repr__    = interp2app(W_StructureInstance.descr_repr),
380    __getattr__ = interp2app(W_StructureInstance.getattr),
381    __setattr__ = interp2app(W_StructureInstance.setattr),
382    buffer      = GetSetProperty(W_StructureInstance.getbuffer),
383    free        = interp2app(W_StructureInstance.free),
384    shape       = interp_attrproperty('shape', W_StructureInstance),
385    byptr       = interp2app(W_StructureInstance.byptr),
386    fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
387)
388W_StructureInstance.typedef.acceptable_as_base_class = False
389
390class W_StructureInstanceAutoFree(W_StructureInstance):
391    def __init__(self, space, shape):
392        W_StructureInstance.__init__(self, space, shape, 0)
393
394    @rgc.must_be_light_finalizer
395    def __del__(self):
396        if self.ll_buffer:
397            self._free()
398
399W_StructureInstanceAutoFree.typedef = TypeDef(
400    'StructureInstanceAutoFree',
401    __repr__    = interp2app(W_StructureInstance.descr_repr),
402    __getattr__ = interp2app(W_StructureInstance.getattr),
403    __setattr__ = interp2app(W_StructureInstance.setattr),
404    buffer      = GetSetProperty(W_StructureInstance.getbuffer),
405    shape       = interp_attrproperty('shape', W_StructureInstance),
406    byptr       = interp2app(W_StructureInstance.byptr),
407    fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
408)
409W_StructureInstanceAutoFree.typedef.acceptable_as_base_class = False