PageRenderTime 41ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/memory/support.py

https://bitbucket.org/pypy/pypy/
Python | 401 lines | 358 code | 30 blank | 13 comment | 15 complexity | 8eaddc93049b681327b9b2c4ac2fb8e5 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.rtyper.lltypesystem import lltype, llmemory
  2. from rpython.rlib.objectmodel import free_non_gc_object, we_are_translated
  3. from rpython.rlib.debug import ll_assert
  4. from rpython.tool.identity_dict import identity_dict
  5. from rpython.rtyper.rclass import NONGCOBJECTPTR
  6. from rpython.rtyper.annlowlevel import cast_nongc_instance_to_base_ptr
  7. from rpython.rtyper.annlowlevel import cast_base_ptr_to_nongc_instance
  8. def mangle_hash(i):
  9. # To hash pointers in dictionaries. Assumes that i shows some
  10. # alignment (to 4, 8, maybe 16 bytes), so we use the following
  11. # formula to avoid the trailing bits being always 0.
  12. return i ^ (i >> 4)
  13. # ____________________________________________________________
  14. DEFAULT_CHUNK_SIZE = 1019
  15. def get_chunk_manager(chunk_size=DEFAULT_CHUNK_SIZE, cache={}):
  16. try:
  17. return cache[chunk_size]
  18. except KeyError:
  19. pass
  20. CHUNK = lltype.ForwardReference()
  21. CHUNK.become(lltype.Struct('AddressChunk',
  22. ('next', lltype.Ptr(CHUNK)),
  23. ('items', lltype.FixedSizeArray(
  24. llmemory.Address, chunk_size))))
  25. null_chunk = lltype.nullptr(CHUNK)
  26. class FreeList(object):
  27. _alloc_flavor_ = "raw"
  28. def __init__(self):
  29. self.free_list = null_chunk
  30. def get(self):
  31. if not self.free_list:
  32. # we zero-initialize the chunks to make the translation
  33. # backends happy, but we don't need to do it at run-time.
  34. zero = not we_are_translated()
  35. return lltype.malloc(CHUNK, flavor="raw", zero=zero,
  36. track_allocation=False)
  37. result = self.free_list
  38. self.free_list = result.next
  39. return result
  40. def put(self, chunk):
  41. if we_are_translated():
  42. chunk.next = self.free_list
  43. self.free_list = chunk
  44. else:
  45. # Don't cache the old chunks but free them immediately.
  46. # Helps debugging, and avoids that old chunks full of
  47. # addresses left behind by a test end up in genc...
  48. lltype.free(chunk, flavor="raw", track_allocation=False)
  49. unused_chunks = FreeList()
  50. cache[chunk_size] = unused_chunks, null_chunk
  51. def partition(array, left, right):
  52. last_item = array[right]
  53. pivot = last_item
  54. storeindex = left
  55. for i in range(left, right):
  56. if array[i] >= pivot:
  57. array[i], array[storeindex] = array[storeindex], array[i]
  58. storeindex += 1
  59. # Move pivot to its final place
  60. array[storeindex], array[right] = last_item, array[storeindex]
  61. return storeindex
  62. def quicksort(array, left, right):
  63. # sort array[left:right+1] (i.e. bounds included)
  64. if right > left:
  65. pivotnewindex = partition(array, left, right)
  66. quicksort(array, left, pivotnewindex - 1)
  67. quicksort(array, pivotnewindex + 1, right)
  68. def sort_chunk(chunk, size):
  69. quicksort(chunk.items, 0, size - 1)
  70. return unused_chunks, null_chunk, sort_chunk
  71. def get_address_stack(chunk_size=DEFAULT_CHUNK_SIZE, cache={}):
  72. try:
  73. return cache[chunk_size]
  74. except KeyError:
  75. pass
  76. unused_chunks, null_chunk, sort_chunk = get_chunk_manager(chunk_size)
  77. class AddressStack(object):
  78. _alloc_flavor_ = "raw"
  79. def __init__(self):
  80. self.chunk = unused_chunks.get()
  81. self.chunk.next = null_chunk
  82. self.used_in_last_chunk = 0
  83. # invariant: self.used_in_last_chunk == 0 if and only if
  84. # the AddressStack is empty
  85. def enlarge(self):
  86. new = unused_chunks.get()
  87. new.next = self.chunk
  88. self.chunk = new
  89. self.used_in_last_chunk = 0
  90. enlarge._dont_inline_ = True
  91. def shrink(self):
  92. old = self.chunk
  93. self.chunk = old.next
  94. unused_chunks.put(old)
  95. self.used_in_last_chunk = chunk_size
  96. shrink._dont_inline_ = True
  97. def append(self, addr):
  98. used = self.used_in_last_chunk
  99. if used == chunk_size:
  100. self.enlarge()
  101. used = 0
  102. self.chunk.items[used] = addr
  103. self.used_in_last_chunk = used + 1 # always > 0 here
  104. def non_empty(self):
  105. return self.used_in_last_chunk != 0
  106. def pop(self):
  107. used = self.used_in_last_chunk - 1
  108. ll_assert(used >= 0, "pop on empty AddressStack")
  109. result = self.chunk.items[used]
  110. self.used_in_last_chunk = used
  111. if used == 0 and self.chunk.next:
  112. self.shrink()
  113. return result
  114. def delete(self):
  115. cur = self.chunk
  116. while cur:
  117. next = cur.next
  118. unused_chunks.put(cur)
  119. cur = next
  120. free_non_gc_object(self)
  121. def length(self):
  122. chunk = self.chunk
  123. result = 0
  124. count = self.used_in_last_chunk
  125. while chunk:
  126. result += count
  127. chunk = chunk.next
  128. count = chunk_size
  129. return result
  130. def foreach(self, callback, arg):
  131. """Invoke 'callback(address, arg)' for all addresses in the stack.
  132. Typically, 'callback' is a bound method and 'arg' can be None.
  133. """
  134. chunk = self.chunk
  135. count = self.used_in_last_chunk
  136. while chunk:
  137. while count > 0:
  138. count -= 1
  139. callback(chunk.items[count], arg)
  140. chunk = chunk.next
  141. count = chunk_size
  142. foreach._annspecialcase_ = 'specialize:arg(1)'
  143. def stack2dict(self):
  144. result = AddressDict(self.length())
  145. self.foreach(_add_in_dict, result)
  146. return result
  147. def tolist(self):
  148. """NOT_RPYTHON. Returns the content as a list."""
  149. lst = []
  150. def _add(obj, lst):
  151. lst.append(obj)
  152. self.foreach(_add, lst)
  153. return lst
  154. def remove(self, addr):
  155. """Remove 'addr' from the stack. The addr *must* be in the list,
  156. and preferrably near the top.
  157. """
  158. got = self.pop()
  159. chunk = self.chunk
  160. count = self.used_in_last_chunk
  161. while got != addr:
  162. count -= 1
  163. if count < 0:
  164. chunk = chunk.next
  165. count = chunk_size - 1
  166. next = chunk.items[count]
  167. chunk.items[count] = got
  168. got = next
  169. def sort(self):
  170. """Sorts the items in the AddressStack. They must not be more
  171. than one chunk of them. This results in a **reverse** order,
  172. so that the first pop()ped items are the smallest ones."""
  173. ll_assert(self.chunk.next == null_chunk, "too big for sorting")
  174. sort_chunk(self.chunk, self.used_in_last_chunk)
  175. cache[chunk_size] = AddressStack
  176. return AddressStack
  177. def _add_in_dict(item, d):
  178. d.add(item)
  179. def get_address_deque(chunk_size=DEFAULT_CHUNK_SIZE, cache={}):
  180. try:
  181. return cache[chunk_size]
  182. except KeyError:
  183. pass
  184. unused_chunks, null_chunk = get_chunk_manager(chunk_size)
  185. class AddressDeque(object):
  186. _alloc_flavor_ = "raw"
  187. def __init__(self):
  188. chunk = unused_chunks.get()
  189. chunk.next = null_chunk
  190. self.oldest_chunk = self.newest_chunk = chunk
  191. self.index_in_oldest = 0
  192. self.index_in_newest = 0
  193. def enlarge(self):
  194. new = unused_chunks.get()
  195. new.next = null_chunk
  196. self.newest_chunk.next = new
  197. self.newest_chunk = new
  198. self.index_in_newest = 0
  199. enlarge._dont_inline_ = True
  200. def shrink(self):
  201. old = self.oldest_chunk
  202. self.oldest_chunk = old.next
  203. unused_chunks.put(old)
  204. self.index_in_oldest = 0
  205. shrink._dont_inline_ = True
  206. def append(self, addr):
  207. index = self.index_in_newest
  208. if index == chunk_size:
  209. self.enlarge()
  210. index = 0
  211. self.newest_chunk.items[index] = addr
  212. self.index_in_newest = index + 1
  213. def non_empty(self):
  214. return (self.oldest_chunk != self.newest_chunk
  215. or self.index_in_oldest < self.index_in_newest)
  216. def popleft(self):
  217. ll_assert(self.non_empty(), "pop on empty AddressDeque")
  218. index = self.index_in_oldest
  219. if index == chunk_size:
  220. self.shrink()
  221. index = 0
  222. result = self.oldest_chunk.items[index]
  223. self.index_in_oldest = index + 1
  224. return result
  225. def foreach(self, callback, arg):
  226. """Invoke 'callback(address, arg)' for all addresses in the deque.
  227. Typically, 'callback' is a bound method and 'arg' can be None.
  228. """
  229. chunk = self.oldest_chunk
  230. index = self.index_in_oldest
  231. while chunk is not self.newest_chunk:
  232. while index < chunk_size:
  233. callback(chunk.items[index], arg)
  234. index += 1
  235. chunk = chunk.next
  236. index = 0
  237. limit = self.index_in_newest
  238. while index < limit:
  239. callback(chunk.items[index], arg)
  240. index += 1
  241. foreach._annspecialcase_ = 'specialize:arg(1)'
  242. def delete(self):
  243. cur = self.oldest_chunk
  244. while cur:
  245. next = cur.next
  246. unused_chunks.put(cur)
  247. cur = next
  248. free_non_gc_object(self)
  249. def _was_freed(self):
  250. return False # otherwise, the __class__ changes
  251. cache[chunk_size] = AddressDeque
  252. return AddressDeque
  253. # ____________________________________________________________
  254. def AddressDict(length_estimate=0):
  255. if we_are_translated():
  256. from rpython.memory import lldict
  257. return lldict.newdict(length_estimate)
  258. else:
  259. return BasicAddressDict()
  260. def null_address_dict():
  261. from rpython.memory import lldict
  262. return lltype.nullptr(lldict.DICT)
  263. class BasicAddressDict(object):
  264. def __init__(self):
  265. self.data = identity_dict() # {_key(addr): value}
  266. def _key(self, addr):
  267. "NOT_RPYTHON: prebuilt AddressDicts are not supported"
  268. return addr._fixup().ptr._obj
  269. def _wrapkey(self, obj):
  270. return llmemory.cast_ptr_to_adr(obj._as_ptr())
  271. def delete(self):
  272. pass
  273. def length(self):
  274. return len(self.data)
  275. def contains(self, keyaddr):
  276. return self._key(keyaddr) in self.data
  277. def get(self, keyaddr, default=llmemory.NULL):
  278. return self.data.get(self._key(keyaddr), default)
  279. def setitem(self, keyaddr, valueaddr):
  280. assert keyaddr
  281. self.data[self._key(keyaddr)] = valueaddr
  282. def insertclean(self, keyaddr, valueaddr):
  283. assert keyaddr
  284. key = self._key(keyaddr)
  285. assert key not in self.data
  286. self.data[key] = valueaddr
  287. def add(self, keyaddr):
  288. self.setitem(keyaddr, llmemory.NULL)
  289. def clear(self):
  290. self.data.clear()
  291. def foreach(self, callback, arg):
  292. """Invoke 'callback(key, value, arg)' for all items in the dict.
  293. Typically, 'callback' is a bound method and 'arg' can be None."""
  294. for key, value in self.data.iteritems():
  295. callback(self._wrapkey(key), value, arg)
  296. def copy_and_update(dict, surviving, updated_address):
  297. """Make a copy of 'dict' in which the keys are updated as follows:
  298. * if surviving(key) returns False, the item is removed
  299. * otherwise, updated_address(key) is inserted in the copy.
  300. """
  301. newdict = AddressDict
  302. if not we_are_translated():
  303. # when not translated, return a dict of the same kind as 'dict'
  304. if not isinstance(dict, BasicAddressDict):
  305. from rpython.memory.lldict import newdict
  306. result = newdict(dict.length())
  307. dict.foreach(_get_updater(surviving, updated_address), result)
  308. return result
  309. copy_and_update._annspecialcase_ = 'specialize:arg(1,2)'
  310. def _get_updater(surviving, updated_address):
  311. def callback(key, value, arg):
  312. if surviving(key):
  313. newkey = updated_address(key)
  314. arg.setitem(newkey, value)
  315. return callback
  316. _get_updater._annspecialcase_ = 'specialize:memo'
  317. def copy_without_null_values(dict):
  318. """Make a copy of 'dict' without the key/value pairs where value==NULL."""
  319. newdict = AddressDict
  320. if not we_are_translated():
  321. # when not translated, return a dict of the same kind as 'dict'
  322. if not isinstance(dict, BasicAddressDict):
  323. from rpython.memory.lldict import newdict
  324. result = newdict()
  325. dict.foreach(_null_value_checker, result)
  326. return result
  327. def _null_value_checker(key, value, arg):
  328. if value:
  329. arg.setitem(key, value)