/pypy/module/_rawffi/array.py

https://bitbucket.org/pypy/pypy/ · Python · 230 lines · 186 code · 32 blank · 12 comment · 33 complexity · a1cb234a04de779086a0e7b35f34b07b MD5 · raw file

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