PageRenderTime 62ms CodeModel.GetById 32ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 1ms

/pypy/module/_rawffi/array.py

https://bitbucket.org/pypy/pypy/
Python | 230 lines | 192 code | 26 blank | 12 comment | 34 complexity | a1cb234a04de779086a0e7b35f34b07b MD5 | raw file
  1
  2""" Interpreter-level implementation of array, exposing ll-structure
  3to app-level with apropriate interface
  4"""
  5
  6from pypy.interpreter.gateway import interp2app, unwrap_spec
  7from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
  8from rpython.rtyper.lltypesystem import lltype, rffi
  9from pypy.interpreter.error import OperationError, oefmt
 10from pypy.module._rawffi.interp_rawffi import segfault_exception
 11from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
 12from pypy.module._rawffi.interp_rawffi import unwrap_value, wrap_value
 13from pypy.module._rawffi.interp_rawffi import TYPEMAP
 14from pypy.module._rawffi.interp_rawffi import size_alignment
 15from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
 16from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
 17from rpython.rlib.rarithmetic import r_uint
 18from rpython.rlib import rgc, clibffi
 19
 20
 21class W_Array(W_DataShape):
 22    def __init__(self, basicffitype, size):
 23        # A W_Array represent the C type '*T', which can also represent
 24        # the type of pointers to arrays of T.  So the following fields
 25        # are used to describe T only.  It is 'basicffitype' possibly
 26        # repeated until reaching the length 'size'.
 27        self.basicffitype = basicffitype
 28        self.size = size
 29        self.alignment = size_alignment(basicffitype)[1]
 30
 31    def allocate(self, space, length, autofree=False):
 32        if autofree:
 33            return W_ArrayInstanceAutoFree(space, self, length)
 34        return W_ArrayInstance(space, self, length)
 35
 36    def get_basic_ffi_type(self):
 37        return self.basicffitype
 38
 39    @unwrap_spec(length=int, autofree=bool)
 40    def descr_call(self, space, length, w_items=None, autofree=False):
 41        result = self.allocate(space, length, autofree)
 42        if not space.is_none(w_items):
 43            items_w = space.unpackiterable(w_items)
 44            iterlength = len(items_w)
 45            if iterlength > length:
 46                raise oefmt(space.w_ValueError,
 47                            "too many items for specified array length")
 48            for num in range(iterlength):
 49                w_item = items_w[num]
 50                unwrap_value(space, write_ptr, result.ll_buffer, num,
 51                             self.itemcode, w_item)
 52        return space.wrap(result)
 53
 54    def descr_repr(self, space):
 55        return space.wrap("<_rawffi.Array '%s' (%d, %d)>" % (self.itemcode,
 56                                                             self.size,
 57                                                             self.alignment))
 58
 59    @unwrap_spec(address=r_uint, length=int)
 60    def fromaddress(self, space, address, length):
 61        return space.wrap(W_ArrayInstance(space, self, length, address))
 62
 63PRIMITIVE_ARRAY_TYPES = {}
 64for _code in TYPEMAP:
 65    PRIMITIVE_ARRAY_TYPES[_code] = W_Array(TYPEMAP[_code],
 66                                           size_alignment(TYPEMAP[_code])[0])
 67    PRIMITIVE_ARRAY_TYPES[_code].itemcode = _code
 68ARRAY_OF_PTRS = PRIMITIVE_ARRAY_TYPES['P']
 69
 70def descr_new_array(space, w_type, w_shape):
 71    return unpack_shape_with_length(space, w_shape)
 72
 73W_Array.typedef = TypeDef(
 74    'Array',
 75    __new__  = interp2app(descr_new_array),
 76    __call__ = interp2app(W_Array.descr_call),
 77    __repr__ = interp2app(W_Array.descr_repr),
 78    fromaddress = interp2app(W_Array.fromaddress),
 79    size_alignment = interp2app(W_Array.descr_size_alignment)
 80)
 81W_Array.typedef.acceptable_as_base_class = False
 82
 83
 84class W_ArrayInstance(W_DataInstance):
 85    def __init__(self, space, shape, length, address=r_uint(0)):
 86        memsize = shape.size * length
 87        # For W_ArrayInstances that are used as the result value of a
 88        # function call, ffi_call() writes 8 bytes into it even if the
 89        # function's result type asks for less.
 90        memsize = clibffi.adjust_return_size(memsize)
 91        W_DataInstance.__init__(self, space, memsize, address)
 92        self.length = length
 93        self.shape = shape
 94
 95    def descr_repr(self, space):
 96        addr = rffi.cast(lltype.Unsigned, self.ll_buffer)
 97        return space.wrap("<_rawffi array %x of length %d>" % (addr,
 98                                                               self.length))
 99
100    # This only allows non-negative indexes.  Arrays of shape 'c' also
101    # support simple slices.
102
103    def setitem(self, space, num, w_value):
104        if not self.ll_buffer:
105            raise segfault_exception(space, "setting element of freed array")
106        if num >= self.length or num < 0:
107            raise OperationError(space.w_IndexError, space.w_None)
108        unwrap_value(space, write_ptr, self.ll_buffer, num,
109                     self.shape.itemcode, w_value)
110
111    def descr_setitem(self, space, w_index, w_value):
112        try:
113            num = space.int_w(w_index)
114        except OperationError as e:
115            if not e.match(space, space.w_TypeError):
116                raise
117            self.setslice(space, w_index, w_value)
118        else:
119            self.setitem(space, num, w_value)
120
121    def getitem(self, space, num):
122        if not self.ll_buffer:
123            raise segfault_exception(space, "accessing elements of freed array")
124        if num >= self.length or num < 0:
125            raise OperationError(space.w_IndexError, space.w_None)
126        return wrap_value(space, read_ptr, self.ll_buffer, num,
127                          self.shape.itemcode)
128
129    def descr_getitem(self, space, w_index):
130        try:
131            num = space.int_w(w_index)
132        except OperationError as e:
133            if not e.match(space, space.w_TypeError):
134                raise
135            return self.getslice(space, w_index)
136        else:
137            return self.getitem(space, num)
138
139    def getlength(self, space):
140        return space.wrap(self.length)
141
142    @unwrap_spec(num=int)
143    def descr_itemaddress(self, space, num):
144        itemsize = self.shape.size
145        ptr = rffi.ptradd(self.ll_buffer, itemsize * num)
146        return space.wrap(rffi.cast(lltype.Unsigned, ptr))
147
148    def getrawsize(self):
149        itemsize = self.shape.size
150        return itemsize * self.length
151
152    def decodeslice(self, space, w_slice):
153        if not space.isinstance_w(w_slice, space.w_slice):
154            raise oefmt(space.w_TypeError, "index must be int or slice")
155        letter = self.shape.itemcode
156        if letter != 'c':
157            raise oefmt(space.w_TypeError, "only 'c' arrays support slicing")
158        w_start = space.getattr(w_slice, space.wrap('start'))
159        w_stop = space.getattr(w_slice, space.wrap('stop'))
160        w_step = space.getattr(w_slice, space.wrap('step'))
161
162        if space.is_w(w_start, space.w_None):
163            start = 0
164        else:
165            start = space.int_w(w_start)
166        if space.is_w(w_stop, space.w_None):
167            stop = self.length
168        else:
169            stop = space.int_w(w_stop)
170        if not space.is_w(w_step, space.w_None):
171            step = space.int_w(w_step)
172            if step != 1:
173                raise oefmt(space.w_ValueError, "no step support")
174        if not (0 <= start <= stop <= self.length):
175            raise oefmt(space.w_ValueError, "slice out of bounds")
176        if not self.ll_buffer:
177            raise segfault_exception(space, "accessing a freed array")
178        return start, stop
179
180    def getslice(self, space, w_slice):
181        start, stop = self.decodeslice(space, w_slice)
182        ll_buffer = self.ll_buffer
183        result = [ll_buffer[i] for i in range(start, stop)]
184        return space.newbytes(''.join(result))
185
186    def setslice(self, space, w_slice, w_value):
187        start, stop = self.decodeslice(space, w_slice)
188        value = space.str_w(w_value)
189        if start + len(value) != stop:
190            raise oefmt(space.w_ValueError, "cannot resize array")
191        ll_buffer = self.ll_buffer
192        for i in range(len(value)):
193            ll_buffer[start + i] = value[i]
194
195W_ArrayInstance.typedef = TypeDef(
196    'ArrayInstance',
197    __repr__    = interp2app(W_ArrayInstance.descr_repr),
198    __setitem__ = interp2app(W_ArrayInstance.descr_setitem),
199    __getitem__ = interp2app(W_ArrayInstance.descr_getitem),
200    __len__     = interp2app(W_ArrayInstance.getlength),
201    buffer      = GetSetProperty(W_ArrayInstance.getbuffer),
202    shape       = interp_attrproperty('shape', W_ArrayInstance),
203    free        = interp2app(W_ArrayInstance.free),
204    byptr       = interp2app(W_ArrayInstance.byptr),
205    itemaddress = interp2app(W_ArrayInstance.descr_itemaddress),
206)
207W_ArrayInstance.typedef.acceptable_as_base_class = False
208
209
210class W_ArrayInstanceAutoFree(W_ArrayInstance):
211    def __init__(self, space, shape, length):
212        W_ArrayInstance.__init__(self, space, shape, length, 0)
213
214    @rgc.must_be_light_finalizer
215    def __del__(self):
216        if self.ll_buffer:
217            self._free()
218
219W_ArrayInstanceAutoFree.typedef = TypeDef(
220    'ArrayInstanceAutoFree',
221    __repr__    = interp2app(W_ArrayInstance.descr_repr),
222    __setitem__ = interp2app(W_ArrayInstance.descr_setitem),
223    __getitem__ = interp2app(W_ArrayInstance.descr_getitem),
224    __len__     = interp2app(W_ArrayInstance.getlength),
225    buffer      = GetSetProperty(W_ArrayInstance.getbuffer),
226    shape       = interp_attrproperty('shape', W_ArrayInstance),
227    byptr       = interp2app(W_ArrayInstance.byptr),
228    itemaddress = interp2app(W_ArrayInstance.descr_itemaddress),
229)
230W_ArrayInstanceAutoFree.typedef.acceptable_as_base_class = False