PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rpython/lltypesystem/rlist.py

https://bitbucket.org/pypy/pypy/
Python | 413 lines | 292 code | 63 blank | 58 comment | 43 complexity | 6bca2778ed635b159b8a6b8f6294f7f7 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.rlib import rgc, jit
  2. from pypy.rlib.debug import ll_assert
  3. from pypy.rlib.objectmodel import enforceargs
  4. from pypy.rpython.lltypesystem import rstr
  5. from pypy.rpython.lltypesystem.lltype import (GcForwardReference, Ptr, GcArray,
  6. GcStruct, Void, Signed, malloc, typeOf, nullptr, typeMethod)
  7. from pypy.rpython.rlist import (AbstractBaseListRepr, AbstractListRepr,
  8. AbstractFixedSizeListRepr, AbstractListIteratorRepr, ll_setitem_nonneg,
  9. ADTIList, ADTIFixedList, dum_nocheck)
  10. from pypy.rpython.rmodel import Repr, inputconst, externalvsinternal
  11. from pypy.tool.pairtype import pairtype, pair
  12. # ____________________________________________________________
  13. #
  14. # Concrete implementation of RPython lists:
  15. #
  16. # struct list {
  17. # int length;
  18. # items_array *items;
  19. # }
  20. #
  21. # 'items' points to a C-like array in memory preceded by a 'length' header,
  22. # where each item contains a primitive value or pointer to the actual list
  23. # item.
  24. #
  25. # or for fixed-size lists an array is directly used:
  26. #
  27. # item_t list_items[]
  28. #
  29. class BaseListRepr(AbstractBaseListRepr):
  30. rstr_ll = rstr.LLHelpers
  31. # known_maxlength is ignored by lltype but used by ootype
  32. def __init__(self, rtyper, item_repr, listitem=None, known_maxlength=False):
  33. self.rtyper = rtyper
  34. self.LIST = GcForwardReference()
  35. self.lowleveltype = Ptr(self.LIST)
  36. if not isinstance(item_repr, Repr): # not computed yet, done by setup()
  37. assert callable(item_repr)
  38. self._item_repr_computer = item_repr
  39. else:
  40. self.external_item_repr, self.item_repr = externalvsinternal(rtyper, item_repr)
  41. self.listitem = listitem
  42. self.list_cache = {}
  43. # setup() needs to be called to finish this initialization
  44. def null_const(self):
  45. return nullptr(self.LIST)
  46. def get_eqfunc(self):
  47. return inputconst(Void, self.item_repr.get_ll_eq_function())
  48. def make_iterator_repr(self):
  49. return ListIteratorRepr(self)
  50. def get_itemarray_lowleveltype(self):
  51. ITEM = self.item_repr.lowleveltype
  52. ITEMARRAY = GcArray(ITEM,
  53. adtmeths = ADTIFixedList({
  54. "ll_newlist": ll_fixed_newlist,
  55. "ll_newlist_hint": ll_fixed_newlist,
  56. "ll_newemptylist": ll_fixed_newemptylist,
  57. "ll_length": ll_fixed_length,
  58. "ll_items": ll_fixed_items,
  59. "ITEM": ITEM,
  60. "ll_getitem_fast": ll_fixed_getitem_fast,
  61. "ll_setitem_fast": ll_fixed_setitem_fast,
  62. }))
  63. return ITEMARRAY
  64. class __extend__(pairtype(BaseListRepr, BaseListRepr)):
  65. def rtype_is_((r_lst1, r_lst2), hop):
  66. if r_lst1.lowleveltype != r_lst2.lowleveltype:
  67. # obscure logic, the is can be true only if both are None
  68. v_lst1, v_lst2 = hop.inputargs(r_lst1, r_lst2)
  69. return hop.gendirectcall(ll_both_none, v_lst1, v_lst2)
  70. return pairtype(Repr, Repr).rtype_is_(pair(r_lst1, r_lst2), hop)
  71. class ListRepr(AbstractListRepr, BaseListRepr):
  72. def _setup_repr(self):
  73. if 'item_repr' not in self.__dict__:
  74. self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer())
  75. if isinstance(self.LIST, GcForwardReference):
  76. ITEM = self.item_repr.lowleveltype
  77. ITEMARRAY = self.get_itemarray_lowleveltype()
  78. # XXX we might think of turning length stuff into Unsigned
  79. self.LIST.become(GcStruct("list", ("length", Signed),
  80. ("items", Ptr(ITEMARRAY)),
  81. adtmeths = ADTIList({
  82. "ll_newlist": ll_newlist,
  83. "ll_newlist_hint": ll_newlist_hint,
  84. "ll_newemptylist": ll_newemptylist,
  85. "ll_length": ll_length,
  86. "ll_items": ll_items,
  87. "ITEM": ITEM,
  88. "ll_getitem_fast": ll_getitem_fast,
  89. "ll_setitem_fast": ll_setitem_fast,
  90. "_ll_resize_ge": _ll_list_resize_ge,
  91. "_ll_resize_le": _ll_list_resize_le,
  92. "_ll_resize": _ll_list_resize,
  93. }),
  94. hints = {'list': True})
  95. )
  96. def compact_repr(self):
  97. return 'ListR %s' % (self.item_repr.compact_repr(),)
  98. def prepare_const(self, n):
  99. result = malloc(self.LIST, immortal=True)
  100. result.length = n
  101. result.items = malloc(self.LIST.items.TO, n)
  102. return result
  103. def rtype_method_append(self, hop):
  104. if getattr(self.listitem, 'hint_maxlength', False):
  105. v_lst, v_value = hop.inputargs(self, self.item_repr)
  106. hop.exception_cannot_occur()
  107. hop.gendirectcall(ll_append_noresize, v_lst, v_value)
  108. else:
  109. AbstractListRepr.rtype_method_append(self, hop)
  110. def rtype_hint(self, hop):
  111. optimized = getattr(self.listitem, 'hint_maxlength', False)
  112. hints = hop.args_s[-1].const
  113. if 'maxlength' in hints:
  114. if optimized:
  115. v_list = hop.inputarg(self, arg=0)
  116. v_maxlength = self._get_v_maxlength(hop)
  117. hop.llops.gendirectcall(ll_set_maxlength, v_list, v_maxlength)
  118. return v_list
  119. if 'fence' in hints:
  120. v_list = hop.inputarg(self, arg=0)
  121. if isinstance(hop.r_result, FixedSizeListRepr):
  122. if optimized and 'exactlength' in hints:
  123. llfn = ll_list2fixed_exact
  124. else:
  125. llfn = ll_list2fixed
  126. v_list = hop.llops.gendirectcall(llfn, v_list)
  127. return v_list
  128. return AbstractListRepr.rtype_hint(self, hop)
  129. class FixedSizeListRepr(AbstractFixedSizeListRepr, BaseListRepr):
  130. def _setup_repr(self):
  131. if 'item_repr' not in self.__dict__:
  132. self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer())
  133. if isinstance(self.LIST, GcForwardReference):
  134. ITEMARRAY = self.get_itemarray_lowleveltype()
  135. self.LIST.become(ITEMARRAY)
  136. def compact_repr(self):
  137. return 'FixedSizeListR %s' % (self.item_repr.compact_repr(),)
  138. def prepare_const(self, n):
  139. result = malloc(self.LIST, n, immortal=True)
  140. return result
  141. # ____________________________________________________________
  142. #
  143. # Low-level methods. These can be run for testing, but are meant to
  144. # be direct_call'ed from rtyped flow graphs, which means that they will
  145. # get flowed and annotated, mostly with SomePtr.
  146. # adapted C code
  147. @enforceargs(None, int)
  148. def _ll_list_resize_really(l, newsize):
  149. """
  150. Ensure l.items has room for at least newsize elements, and set
  151. l.length to newsize. Note that l.items may change, and even if
  152. newsize is less than l.length on entry.
  153. """
  154. # This over-allocates proportional to the list size, making room
  155. # for additional growth. The over-allocation is mild, but is
  156. # enough to give linear-time amortized behavior over a long
  157. # sequence of appends() in the presence of a poorly-performing
  158. # system malloc().
  159. # The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
  160. if newsize <= 0:
  161. ll_assert(newsize == 0, "negative list length")
  162. l.length = 0
  163. l.items = _ll_new_empty_item_array(typeOf(l).TO)
  164. return
  165. else:
  166. if newsize < 9:
  167. some = 3
  168. else:
  169. some = 6
  170. some += newsize >> 3
  171. new_allocated = newsize + some
  172. # new_allocated is a bit more than newsize, enough to ensure an amortized
  173. # linear complexity for e.g. repeated usage of l.append(). In case
  174. # it overflows sys.maxint, it is guaranteed negative, and the following
  175. # malloc() will fail.
  176. items = l.items
  177. newitems = malloc(typeOf(l).TO.items.TO, new_allocated)
  178. before_len = l.length
  179. if before_len: # avoids copying GC flags from the prebuilt_empty_array
  180. if before_len < newsize:
  181. p = before_len
  182. else:
  183. p = newsize
  184. rgc.ll_arraycopy(items, newitems, 0, 0, p)
  185. l.length = newsize
  186. l.items = newitems
  187. # this common case was factored out of _ll_list_resize
  188. # to see if inlining it gives some speed-up.
  189. def _ll_list_resize(l, newsize):
  190. # Bypass realloc() when a previous overallocation is large enough
  191. # to accommodate the newsize. If the newsize falls lower than half
  192. # the allocated size, then proceed with the realloc() to shrink the list.
  193. allocated = len(l.items)
  194. if allocated >= newsize and newsize >= ((allocated >> 1) - 5):
  195. l.length = newsize
  196. else:
  197. _ll_list_resize_really(l, newsize)
  198. @jit.look_inside_iff(lambda l, newsize: jit.isconstant(len(l.items)) and jit.isconstant(newsize))
  199. @jit.oopspec("list._resize_ge(l, newsize)")
  200. def _ll_list_resize_ge(l, newsize):
  201. if len(l.items) >= newsize:
  202. l.length = newsize
  203. else:
  204. _ll_list_resize_really(l, newsize)
  205. @jit.look_inside_iff(lambda l, newsize: jit.isconstant(len(l.items)) and jit.isconstant(newsize))
  206. @jit.oopspec("list._resize_le(l, newsize)")
  207. def _ll_list_resize_le(l, newsize):
  208. if newsize >= (len(l.items) >> 1) - 5:
  209. l.length = newsize
  210. else:
  211. _ll_list_resize_really(l, newsize)
  212. def ll_append_noresize(l, newitem):
  213. length = l.length
  214. l.length = length + 1
  215. l.ll_setitem_fast(length, newitem)
  216. def ll_both_none(lst1, lst2):
  217. return not lst1 and not lst2
  218. # ____________________________________________________________
  219. #
  220. # Accessor methods
  221. def ll_newlist(LIST, length):
  222. ll_assert(length >= 0, "negative list length")
  223. l = malloc(LIST)
  224. l.length = length
  225. l.items = malloc(LIST.items.TO, length)
  226. return l
  227. ll_newlist = typeMethod(ll_newlist)
  228. ll_newlist.oopspec = 'newlist(length)'
  229. def ll_newlist_hint(LIST, lengthhint):
  230. ll_assert(lengthhint >= 0, "negative list length")
  231. l = malloc(LIST)
  232. l.length = 0
  233. l.items = malloc(LIST.items.TO, lengthhint)
  234. return l
  235. ll_newlist_hint = typeMethod(ll_newlist_hint)
  236. ll_newlist_hint.oopspec = 'newlist(lengthhint)'
  237. # should empty lists start with no allocated memory, or with a preallocated
  238. # minimal number of entries? XXX compare memory usage versus speed, and
  239. # check how many always-empty lists there are in a typical pypy-c run...
  240. INITIAL_EMPTY_LIST_ALLOCATION = 0
  241. def _ll_prebuilt_empty_array(LISTITEM):
  242. return malloc(LISTITEM, 0)
  243. _ll_prebuilt_empty_array._annspecialcase_ = 'specialize:memo'
  244. def _ll_new_empty_item_array(LIST):
  245. if INITIAL_EMPTY_LIST_ALLOCATION > 0:
  246. return malloc(LIST.items.TO, INITIAL_EMPTY_LIST_ALLOCATION)
  247. else:
  248. return _ll_prebuilt_empty_array(LIST.items.TO)
  249. def ll_newemptylist(LIST):
  250. l = malloc(LIST)
  251. l.length = 0
  252. l.items = _ll_new_empty_item_array(LIST)
  253. return l
  254. ll_newemptylist = typeMethod(ll_newemptylist)
  255. ll_newemptylist.oopspec = 'newlist(0)'
  256. def ll_length(l):
  257. return l.length
  258. ll_length.oopspec = 'list.len(l)'
  259. def ll_items(l):
  260. return l.items
  261. def ll_getitem_fast(l, index):
  262. ll_assert(index < l.length, "getitem out of bounds")
  263. return l.ll_items()[index]
  264. ll_getitem_fast.oopspec = 'list.getitem(l, index)'
  265. def ll_setitem_fast(l, index, item):
  266. ll_assert(index < l.length, "setitem out of bounds")
  267. l.ll_items()[index] = item
  268. ll_setitem_fast.oopspec = 'list.setitem(l, index, item)'
  269. # fixed size versions
  270. def ll_fixed_newlist(LIST, length):
  271. ll_assert(length >= 0, "negative fixed list length")
  272. l = malloc(LIST, length)
  273. return l
  274. ll_fixed_newlist = typeMethod(ll_fixed_newlist)
  275. ll_fixed_newlist.oopspec = 'newlist(length)'
  276. def ll_fixed_newemptylist(LIST):
  277. return ll_fixed_newlist(LIST, 0)
  278. ll_fixed_newemptylist = typeMethod(ll_fixed_newemptylist)
  279. def ll_fixed_length(l):
  280. return len(l)
  281. ll_fixed_length.oopspec = 'list.len(l)'
  282. def ll_fixed_items(l):
  283. return l
  284. def ll_fixed_getitem_fast(l, index):
  285. ll_assert(index < len(l), "fixed getitem out of bounds")
  286. return l[index]
  287. ll_fixed_getitem_fast.oopspec = 'list.getitem(l, index)'
  288. def ll_fixed_setitem_fast(l, index, item):
  289. ll_assert(index < len(l), "fixed setitem out of bounds")
  290. l[index] = item
  291. ll_fixed_setitem_fast.oopspec = 'list.setitem(l, index, item)'
  292. def newlist(llops, r_list, items_v, v_sizehint=None):
  293. LIST = r_list.LIST
  294. if len(items_v) == 0:
  295. if v_sizehint is None:
  296. v_result = llops.gendirectcall(LIST.ll_newemptylist)
  297. else:
  298. v_result = llops.gendirectcall(LIST.ll_newlist_hint, v_sizehint)
  299. else:
  300. assert v_sizehint is None
  301. cno = inputconst(Signed, len(items_v))
  302. v_result = llops.gendirectcall(LIST.ll_newlist, cno)
  303. v_func = inputconst(Void, dum_nocheck)
  304. for i, v_item in enumerate(items_v):
  305. ci = inputconst(Signed, i)
  306. llops.gendirectcall(ll_setitem_nonneg, v_func, v_result, ci, v_item)
  307. return v_result
  308. # special operations for list comprehension optimization
  309. def ll_set_maxlength(l, n):
  310. LIST = typeOf(l).TO
  311. l.items = malloc(LIST.items.TO, n)
  312. def ll_list2fixed(l):
  313. n = l.length
  314. olditems = l.items
  315. if n == len(olditems):
  316. return olditems
  317. else:
  318. LIST = typeOf(l).TO
  319. newitems = malloc(LIST.items.TO, n)
  320. rgc.ll_arraycopy(olditems, newitems, 0, 0, n)
  321. return newitems
  322. def ll_list2fixed_exact(l):
  323. ll_assert(l.length == len(l.items), "ll_list2fixed_exact: bad length")
  324. return l.items
  325. # ____________________________________________________________
  326. #
  327. # Iteration.
  328. class ListIteratorRepr(AbstractListIteratorRepr):
  329. def __init__(self, r_list):
  330. self.r_list = r_list
  331. self.lowleveltype = Ptr(GcStruct('listiter',
  332. ('list', r_list.lowleveltype),
  333. ('index', Signed)))
  334. self.ll_listiter = ll_listiter
  335. self.ll_listnext = ll_listnext
  336. self.ll_getnextindex = ll_getnextindex
  337. def ll_listiter(ITERPTR, lst):
  338. iter = malloc(ITERPTR.TO)
  339. iter.list = lst
  340. iter.index = 0
  341. return iter
  342. def ll_listnext(iter):
  343. l = iter.list
  344. index = iter.index
  345. if index >= l.ll_length():
  346. raise StopIteration
  347. iter.index = index + 1 # cannot overflow because index < l.length
  348. return l.ll_getitem_fast(index)
  349. def ll_getnextindex(iter):
  350. return iter.index