PageRenderTime 44ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rpython/lltypesystem/rlist.py

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