/pypy/module/_rawffi/array.py
Python | 230 lines | 192 code | 26 blank | 12 comment | 30 complexity | a1cb234a04de779086a0e7b35f34b07b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- """ Interpreter-level implementation of array, exposing ll-structure
- to app-level with apropriate interface
- """
- from pypy.interpreter.gateway import interp2app, unwrap_spec
- from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
- from rpython.rtyper.lltypesystem import lltype, rffi
- from pypy.interpreter.error import OperationError, oefmt
- from pypy.module._rawffi.interp_rawffi import segfault_exception
- from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
- from pypy.module._rawffi.interp_rawffi import unwrap_value, wrap_value
- from pypy.module._rawffi.interp_rawffi import TYPEMAP
- from pypy.module._rawffi.interp_rawffi import size_alignment
- from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
- from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
- from rpython.rlib.rarithmetic import r_uint
- from rpython.rlib import rgc, clibffi
- class W_Array(W_DataShape):
- def __init__(self, basicffitype, size):
- # A W_Array represent the C type '*T', which can also represent
- # the type of pointers to arrays of T. So the following fields
- # are used to describe T only. It is 'basicffitype' possibly
- # repeated until reaching the length 'size'.
- self.basicffitype = basicffitype
- self.size = size
- self.alignment = size_alignment(basicffitype)[1]
- def allocate(self, space, length, autofree=False):
- if autofree:
- return W_ArrayInstanceAutoFree(space, self, length)
- return W_ArrayInstance(space, self, length)
- def get_basic_ffi_type(self):
- return self.basicffitype
- @unwrap_spec(length=int, autofree=bool)
- def descr_call(self, space, length, w_items=None, autofree=False):
- result = self.allocate(space, length, autofree)
- if not space.is_none(w_items):
- items_w = space.unpackiterable(w_items)
- iterlength = len(items_w)
- if iterlength > length:
- raise oefmt(space.w_ValueError,
- "too many items for specified array length")
- for num in range(iterlength):
- w_item = items_w[num]
- unwrap_value(space, write_ptr, result.ll_buffer, num,
- self.itemcode, w_item)
- return space.wrap(result)
- def descr_repr(self, space):
- return space.wrap("<_rawffi.Array '%s' (%d, %d)>" % (self.itemcode,
- self.size,
- self.alignment))
- @unwrap_spec(address=r_uint, length=int)
- def fromaddress(self, space, address, length):
- return space.wrap(W_ArrayInstance(space, self, length, address))
- PRIMITIVE_ARRAY_TYPES = {}
- for _code in TYPEMAP:
- PRIMITIVE_ARRAY_TYPES[_code] = W_Array(TYPEMAP[_code],
- size_alignment(TYPEMAP[_code])[0])
- PRIMITIVE_ARRAY_TYPES[_code].itemcode = _code
- ARRAY_OF_PTRS = PRIMITIVE_ARRAY_TYPES['P']
- def descr_new_array(space, w_type, w_shape):
- return unpack_shape_with_length(space, w_shape)
- W_Array.typedef = TypeDef(
- 'Array',
- __new__ = interp2app(descr_new_array),
- __call__ = interp2app(W_Array.descr_call),
- __repr__ = interp2app(W_Array.descr_repr),
- fromaddress = interp2app(W_Array.fromaddress),
- size_alignment = interp2app(W_Array.descr_size_alignment)
- )
- W_Array.typedef.acceptable_as_base_class = False
- class W_ArrayInstance(W_DataInstance):
- def __init__(self, space, shape, length, address=r_uint(0)):
- memsize = shape.size * length
- # For W_ArrayInstances that are used as the result value of a
- # function call, ffi_call() writes 8 bytes into it even if the
- # function's result type asks for less.
- memsize = clibffi.adjust_return_size(memsize)
- W_DataInstance.__init__(self, space, memsize, address)
- self.length = length
- self.shape = shape
- def descr_repr(self, space):
- addr = rffi.cast(lltype.Unsigned, self.ll_buffer)
- return space.wrap("<_rawffi array %x of length %d>" % (addr,
- self.length))
- # This only allows non-negative indexes. Arrays of shape 'c' also
- # support simple slices.
- def setitem(self, space, num, w_value):
- if not self.ll_buffer:
- raise segfault_exception(space, "setting element of freed array")
- if num >= self.length or num < 0:
- raise OperationError(space.w_IndexError, space.w_None)
- unwrap_value(space, write_ptr, self.ll_buffer, num,
- self.shape.itemcode, w_value)
- def descr_setitem(self, space, w_index, w_value):
- try:
- num = space.int_w(w_index)
- except OperationError as e:
- if not e.match(space, space.w_TypeError):
- raise
- self.setslice(space, w_index, w_value)
- else:
- self.setitem(space, num, w_value)
- def getitem(self, space, num):
- if not self.ll_buffer:
- raise segfault_exception(space, "accessing elements of freed array")
- if num >= self.length or num < 0:
- raise OperationError(space.w_IndexError, space.w_None)
- return wrap_value(space, read_ptr, self.ll_buffer, num,
- self.shape.itemcode)
- def descr_getitem(self, space, w_index):
- try:
- num = space.int_w(w_index)
- except OperationError as e:
- if not e.match(space, space.w_TypeError):
- raise
- return self.getslice(space, w_index)
- else:
- return self.getitem(space, num)
- def getlength(self, space):
- return space.wrap(self.length)
- @unwrap_spec(num=int)
- def descr_itemaddress(self, space, num):
- itemsize = self.shape.size
- ptr = rffi.ptradd(self.ll_buffer, itemsize * num)
- return space.wrap(rffi.cast(lltype.Unsigned, ptr))
- def getrawsize(self):
- itemsize = self.shape.size
- return itemsize * self.length
- def decodeslice(self, space, w_slice):
- if not space.isinstance_w(w_slice, space.w_slice):
- raise oefmt(space.w_TypeError, "index must be int or slice")
- letter = self.shape.itemcode
- if letter != 'c':
- raise oefmt(space.w_TypeError, "only 'c' arrays support slicing")
- w_start = space.getattr(w_slice, space.wrap('start'))
- w_stop = space.getattr(w_slice, space.wrap('stop'))
- w_step = space.getattr(w_slice, space.wrap('step'))
- if space.is_w(w_start, space.w_None):
- start = 0
- else:
- start = space.int_w(w_start)
- if space.is_w(w_stop, space.w_None):
- stop = self.length
- else:
- stop = space.int_w(w_stop)
- if not space.is_w(w_step, space.w_None):
- step = space.int_w(w_step)
- if step != 1:
- raise oefmt(space.w_ValueError, "no step support")
- if not (0 <= start <= stop <= self.length):
- raise oefmt(space.w_ValueError, "slice out of bounds")
- if not self.ll_buffer:
- raise segfault_exception(space, "accessing a freed array")
- return start, stop
- def getslice(self, space, w_slice):
- start, stop = self.decodeslice(space, w_slice)
- ll_buffer = self.ll_buffer
- result = [ll_buffer[i] for i in range(start, stop)]
- return space.newbytes(''.join(result))
- def setslice(self, space, w_slice, w_value):
- start, stop = self.decodeslice(space, w_slice)
- value = space.str_w(w_value)
- if start + len(value) != stop:
- raise oefmt(space.w_ValueError, "cannot resize array")
- ll_buffer = self.ll_buffer
- for i in range(len(value)):
- ll_buffer[start + i] = value[i]
- W_ArrayInstance.typedef = TypeDef(
- 'ArrayInstance',
- __repr__ = interp2app(W_ArrayInstance.descr_repr),
- __setitem__ = interp2app(W_ArrayInstance.descr_setitem),
- __getitem__ = interp2app(W_ArrayInstance.descr_getitem),
- __len__ = interp2app(W_ArrayInstance.getlength),
- buffer = GetSetProperty(W_ArrayInstance.getbuffer),
- shape = interp_attrproperty('shape', W_ArrayInstance),
- free = interp2app(W_ArrayInstance.free),
- byptr = interp2app(W_ArrayInstance.byptr),
- itemaddress = interp2app(W_ArrayInstance.descr_itemaddress),
- )
- W_ArrayInstance.typedef.acceptable_as_base_class = False
- class W_ArrayInstanceAutoFree(W_ArrayInstance):
- def __init__(self, space, shape, length):
- W_ArrayInstance.__init__(self, space, shape, length, 0)
- @rgc.must_be_light_finalizer
- def __del__(self):
- if self.ll_buffer:
- self._free()
- W_ArrayInstanceAutoFree.typedef = TypeDef(
- 'ArrayInstanceAutoFree',
- __repr__ = interp2app(W_ArrayInstance.descr_repr),
- __setitem__ = interp2app(W_ArrayInstance.descr_setitem),
- __getitem__ = interp2app(W_ArrayInstance.descr_getitem),
- __len__ = interp2app(W_ArrayInstance.getlength),
- buffer = GetSetProperty(W_ArrayInstance.getbuffer),
- shape = interp_attrproperty('shape', W_ArrayInstance),
- byptr = interp2app(W_ArrayInstance.byptr),
- itemaddress = interp2app(W_ArrayInstance.descr_itemaddress),
- )
- W_ArrayInstanceAutoFree.typedef.acceptable_as_base_class = False