PageRenderTime 72ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/rtyper/lltypesystem/rordereddict.py

https://bitbucket.org/pypy/pypy/
Python | 1317 lines | 985 code | 147 blank | 185 comment | 199 complexity | 8b455325648955e6eaa49c2f447f2365 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. import sys
  2. from rpython.tool.pairtype import pairtype
  3. from rpython.flowspace.model import Constant
  4. from rpython.rtyper.rdict import AbstractDictRepr, AbstractDictIteratorRepr
  5. from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
  6. from rpython.rlib import objectmodel, jit, rgc, types
  7. from rpython.rlib.signature import signature
  8. from rpython.rlib.objectmodel import specialize, likely
  9. from rpython.rtyper.debug import ll_assert
  10. from rpython.rlib.rarithmetic import r_uint, intmask
  11. from rpython.rtyper import rmodel
  12. from rpython.rtyper.error import TyperError
  13. from rpython.rtyper.annlowlevel import llhelper
  14. # ____________________________________________________________
  15. #
  16. # generic implementation of RPython dictionary, with parametric DICTKEY and
  17. # DICTVALUE types. The basic implementation is a sparse array of indexes
  18. # plus a dense array of structs that contain keys and values. struct looks
  19. # like that:
  20. #
  21. #
  22. # struct dictentry {
  23. # DICTKEY key;
  24. # DICTVALUE value;
  25. # long f_hash; # (optional) key hash, if hard to recompute
  26. # bool f_valid; # (optional) the entry is filled
  27. # }
  28. #
  29. # struct dicttable {
  30. # int num_live_items;
  31. # int num_ever_used_items;
  32. # int resize_counter;
  33. # {byte, short, int, long} *indexes;
  34. # dictentry *entries;
  35. # lookup_function_no; # one of the four possible functions for different
  36. # # size dicts; the rest of the word is a counter for how
  37. # # many 'entries' at the start are known to be deleted
  38. # (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
  39. # (Function DICTKEY -> int) *fnkeyhash;
  40. # }
  41. #
  42. #
  43. @jit.look_inside_iff(lambda d, key, hash, flag: jit.isvirtual(d))
  44. @jit.oopspec('ordereddict.lookup(d, key, hash, flag)')
  45. def ll_call_lookup_function(d, key, hash, flag):
  46. fun = d.lookup_function_no & FUNC_MASK
  47. # This likely() here forces gcc to compile the check for fun == FUNC_BYTE
  48. # first. Otherwise, this is a regular switch and gcc (at least 4.7)
  49. # compiles this as a series of checks, with the FUNC_BYTE case last.
  50. # It sounds minor, but it is worth 6-7% on a PyPy microbenchmark.
  51. if likely(fun == FUNC_BYTE):
  52. return ll_dict_lookup(d, key, hash, flag, TYPE_BYTE)
  53. elif fun == FUNC_SHORT:
  54. return ll_dict_lookup(d, key, hash, flag, TYPE_SHORT)
  55. elif IS_64BIT and fun == FUNC_INT:
  56. return ll_dict_lookup(d, key, hash, flag, TYPE_INT)
  57. elif fun == FUNC_LONG:
  58. return ll_dict_lookup(d, key, hash, flag, TYPE_LONG)
  59. assert False
  60. def get_ll_dict(DICTKEY, DICTVALUE, get_custom_eq_hash=None, DICT=None,
  61. ll_fasthash_function=None, ll_hash_function=None,
  62. ll_eq_function=None, method_cache={},
  63. dummykeyobj=None, dummyvalueobj=None, rtyper=None):
  64. # get the actual DICT type. if DICT is None, it's created, otherwise
  65. # forward reference is becoming DICT
  66. if DICT is None:
  67. DICT = lltype.GcForwardReference()
  68. # compute the shape of the DICTENTRY structure
  69. entryfields = []
  70. entrymeths = {
  71. 'allocate': lltype.typeMethod(_ll_malloc_entries),
  72. 'delete': _ll_free_entries,
  73. 'must_clear_key': (isinstance(DICTKEY, lltype.Ptr)
  74. and DICTKEY._needsgc()),
  75. 'must_clear_value': (isinstance(DICTVALUE, lltype.Ptr)
  76. and DICTVALUE._needsgc()),
  77. }
  78. if getattr(ll_eq_function, 'no_direct_compare', False):
  79. entrymeths['no_direct_compare'] = True
  80. # * the key
  81. entryfields.append(("key", DICTKEY))
  82. # * the state of the entry - trying to encode it as dummy objects
  83. if dummykeyobj:
  84. # all the state can be encoded in the key
  85. entrymeths['dummy_obj'] = dummykeyobj
  86. entrymeths['valid'] = ll_valid_from_key
  87. entrymeths['mark_deleted'] = ll_mark_deleted_in_key
  88. # the key is overwritten by 'dummy' when the entry is deleted
  89. entrymeths['must_clear_key'] = False
  90. elif dummyvalueobj:
  91. # all the state can be encoded in the value
  92. entrymeths['dummy_obj'] = dummyvalueobj
  93. entrymeths['valid'] = ll_valid_from_value
  94. entrymeths['mark_deleted'] = ll_mark_deleted_in_value
  95. # value is overwritten by 'dummy' when entry is deleted
  96. entrymeths['must_clear_value'] = False
  97. else:
  98. # we need a flag to know if the entry was ever used
  99. entryfields.append(("f_valid", lltype.Bool))
  100. entrymeths['valid'] = ll_valid_from_flag
  101. entrymeths['mark_deleted'] = ll_mark_deleted_in_flag
  102. # * the value
  103. entryfields.append(("value", DICTVALUE))
  104. if ll_fasthash_function is None:
  105. entryfields.append(("f_hash", lltype.Signed))
  106. entrymeths['hash'] = ll_hash_from_cache
  107. else:
  108. entrymeths['hash'] = ll_hash_recomputed
  109. entrymeths['fasthashfn'] = ll_fasthash_function
  110. # Build the lltype data structures
  111. DICTENTRY = lltype.Struct("odictentry", *entryfields)
  112. DICTENTRYARRAY = lltype.GcArray(DICTENTRY,
  113. adtmeths=entrymeths)
  114. fields = [ ("num_live_items", lltype.Signed),
  115. ("num_ever_used_items", lltype.Signed),
  116. ("resize_counter", lltype.Signed),
  117. ("indexes", llmemory.GCREF),
  118. ("lookup_function_no", lltype.Signed),
  119. ("entries", lltype.Ptr(DICTENTRYARRAY)) ]
  120. if get_custom_eq_hash is not None:
  121. r_rdict_eqfn, r_rdict_hashfn = get_custom_eq_hash()
  122. fields.extend([ ("fnkeyeq", r_rdict_eqfn.lowleveltype),
  123. ("fnkeyhash", r_rdict_hashfn.lowleveltype) ])
  124. adtmeths = {
  125. 'keyhash': ll_keyhash_custom,
  126. 'keyeq': ll_keyeq_custom,
  127. 'r_rdict_eqfn': r_rdict_eqfn,
  128. 'r_rdict_hashfn': r_rdict_hashfn,
  129. 'paranoia': True,
  130. }
  131. else:
  132. # figure out which functions must be used to hash and compare
  133. ll_keyhash = ll_hash_function
  134. ll_keyeq = ll_eq_function
  135. ll_keyhash = lltype.staticAdtMethod(ll_keyhash)
  136. if ll_keyeq is not None:
  137. ll_keyeq = lltype.staticAdtMethod(ll_keyeq)
  138. adtmeths = {
  139. 'keyhash': ll_keyhash,
  140. 'keyeq': ll_keyeq,
  141. 'paranoia': False,
  142. }
  143. adtmeths['KEY'] = DICTKEY
  144. adtmeths['VALUE'] = DICTVALUE
  145. adtmeths['lookup_function'] = lltype.staticAdtMethod(ll_call_lookup_function)
  146. adtmeths['allocate'] = lltype.typeMethod(_ll_malloc_dict)
  147. DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths,
  148. *fields))
  149. return DICT
  150. class OrderedDictRepr(AbstractDictRepr):
  151. def __init__(self, rtyper, key_repr, value_repr, dictkey, dictvalue,
  152. custom_eq_hash=None, force_non_null=False):
  153. #assert not force_non_null
  154. self.rtyper = rtyper
  155. self.finalized = False
  156. self.DICT = lltype.GcForwardReference()
  157. self.lowleveltype = lltype.Ptr(self.DICT)
  158. self.custom_eq_hash = custom_eq_hash is not None
  159. if not isinstance(key_repr, rmodel.Repr): # not computed yet, done by setup()
  160. assert callable(key_repr)
  161. self._key_repr_computer = key_repr
  162. else:
  163. self.external_key_repr, self.key_repr = self.pickkeyrepr(key_repr)
  164. if not isinstance(value_repr, rmodel.Repr): # not computed yet, done by setup()
  165. assert callable(value_repr)
  166. self._value_repr_computer = value_repr
  167. else:
  168. self.external_value_repr, self.value_repr = self.pickrepr(value_repr)
  169. self.dictkey = dictkey
  170. self.dictvalue = dictvalue
  171. self.dict_cache = {}
  172. self._custom_eq_hash_repr = custom_eq_hash
  173. # setup() needs to be called to finish this initialization
  174. def _externalvsinternal(self, rtyper, item_repr):
  175. return rmodel.externalvsinternal(self.rtyper, item_repr)
  176. def _setup_repr(self):
  177. if 'key_repr' not in self.__dict__:
  178. key_repr = self._key_repr_computer()
  179. self.external_key_repr, self.key_repr = self.pickkeyrepr(key_repr)
  180. if 'value_repr' not in self.__dict__:
  181. self.external_value_repr, self.value_repr = self.pickrepr(self._value_repr_computer())
  182. if isinstance(self.DICT, lltype.GcForwardReference):
  183. DICTKEY = self.key_repr.lowleveltype
  184. DICTVALUE = self.value_repr.lowleveltype
  185. # * we need an explicit flag if the key and the value is not
  186. # able to store dummy values
  187. s_key = self.dictkey.s_value
  188. s_value = self.dictvalue.s_value
  189. kwd = {}
  190. if self.custom_eq_hash:
  191. self.r_rdict_eqfn, self.r_rdict_hashfn = (
  192. self._custom_eq_hash_repr())
  193. kwd['get_custom_eq_hash'] = self._custom_eq_hash_repr
  194. else:
  195. kwd['ll_hash_function'] = self.key_repr.get_ll_hash_function()
  196. kwd['ll_eq_function'] = self.key_repr.get_ll_eq_function()
  197. kwd['ll_fasthash_function'] = self.key_repr.get_ll_fasthash_function()
  198. kwd['dummykeyobj'] = self.key_repr.get_ll_dummyval_obj(self.rtyper,
  199. s_key)
  200. kwd['dummyvalueobj'] = self.value_repr.get_ll_dummyval_obj(
  201. self.rtyper, s_value)
  202. get_ll_dict(DICTKEY, DICTVALUE, DICT=self.DICT,
  203. rtyper=self.rtyper, **kwd)
  204. def convert_const(self, dictobj):
  205. from rpython.rtyper.lltypesystem import llmemory
  206. # get object from bound dict methods
  207. #dictobj = getattr(dictobj, '__self__', dictobj)
  208. if dictobj is None:
  209. return lltype.nullptr(self.DICT)
  210. if not isinstance(dictobj, (dict, objectmodel.r_dict)):
  211. raise TypeError("expected a dict: %r" % (dictobj,))
  212. try:
  213. key = Constant(dictobj)
  214. return self.dict_cache[key]
  215. except KeyError:
  216. self.setup()
  217. self.setup_final()
  218. l_dict = ll_newdict_size(self.DICT, len(dictobj))
  219. self.dict_cache[key] = l_dict
  220. r_key = self.key_repr
  221. if r_key.lowleveltype == llmemory.Address:
  222. raise TypeError("No prebuilt dicts of address keys")
  223. r_value = self.value_repr
  224. if isinstance(dictobj, objectmodel.r_dict):
  225. if self.r_rdict_eqfn.lowleveltype != lltype.Void:
  226. l_fn = self.r_rdict_eqfn.convert_const(dictobj.key_eq)
  227. l_dict.fnkeyeq = l_fn
  228. if self.r_rdict_hashfn.lowleveltype != lltype.Void:
  229. l_fn = self.r_rdict_hashfn.convert_const(dictobj.key_hash)
  230. l_dict.fnkeyhash = l_fn
  231. for dictkeycontainer, dictvalue in dictobj._dict.items():
  232. llkey = r_key.convert_const(dictkeycontainer.key)
  233. llvalue = r_value.convert_const(dictvalue)
  234. _ll_dict_insertclean(l_dict, llkey, llvalue,
  235. dictkeycontainer.hash)
  236. return l_dict
  237. else:
  238. for dictkey, dictvalue in dictobj.items():
  239. llkey = r_key.convert_const(dictkey)
  240. llvalue = r_value.convert_const(dictvalue)
  241. _ll_dict_insertclean(l_dict, llkey, llvalue,
  242. l_dict.keyhash(llkey))
  243. return l_dict
  244. def rtype_len(self, hop):
  245. v_dict, = hop.inputargs(self)
  246. return hop.gendirectcall(ll_dict_len, v_dict)
  247. def rtype_bool(self, hop):
  248. v_dict, = hop.inputargs(self)
  249. return hop.gendirectcall(ll_dict_bool, v_dict)
  250. def make_iterator_repr(self, *variant):
  251. return DictIteratorRepr(self, *variant)
  252. def rtype_method_get(self, hop):
  253. v_dict, v_key, v_default = hop.inputargs(self, self.key_repr,
  254. self.value_repr)
  255. hop.exception_cannot_occur()
  256. v_res = hop.gendirectcall(ll_dict_get, v_dict, v_key, v_default)
  257. return self.recast_value(hop.llops, v_res)
  258. def rtype_method_setdefault(self, hop):
  259. v_dict, v_key, v_default = hop.inputargs(self, self.key_repr,
  260. self.value_repr)
  261. hop.exception_cannot_occur()
  262. v_res = hop.gendirectcall(ll_dict_setdefault, v_dict, v_key, v_default)
  263. return self.recast_value(hop.llops, v_res)
  264. def rtype_method_copy(self, hop):
  265. v_dict, = hop.inputargs(self)
  266. hop.exception_cannot_occur()
  267. return hop.gendirectcall(ll_dict_copy, v_dict)
  268. def rtype_method_update(self, hop):
  269. v_dic1, v_dic2 = hop.inputargs(self, self)
  270. hop.exception_cannot_occur()
  271. return hop.gendirectcall(ll_dict_update, v_dic1, v_dic2)
  272. def rtype_method__prepare_dict_update(self, hop):
  273. v_dict, v_num = hop.inputargs(self, lltype.Signed)
  274. hop.exception_cannot_occur()
  275. hop.gendirectcall(ll_prepare_dict_update, v_dict, v_num)
  276. def _rtype_method_kvi(self, hop, ll_func):
  277. v_dic, = hop.inputargs(self)
  278. r_list = hop.r_result
  279. cLIST = hop.inputconst(lltype.Void, r_list.lowleveltype.TO)
  280. hop.exception_cannot_occur()
  281. return hop.gendirectcall(ll_func, cLIST, v_dic)
  282. def rtype_method_keys(self, hop):
  283. return self._rtype_method_kvi(hop, ll_dict_keys)
  284. def rtype_method_values(self, hop):
  285. return self._rtype_method_kvi(hop, ll_dict_values)
  286. def rtype_method_items(self, hop):
  287. return self._rtype_method_kvi(hop, ll_dict_items)
  288. def rtype_bltn_list(self, hop):
  289. return self._rtype_method_kvi(hop, ll_dict_keys)
  290. def rtype_method_iterkeys(self, hop):
  291. hop.exception_cannot_occur()
  292. return DictIteratorRepr(self, "keys").newiter(hop)
  293. def rtype_method_itervalues(self, hop):
  294. hop.exception_cannot_occur()
  295. return DictIteratorRepr(self, "values").newiter(hop)
  296. def rtype_method_iteritems(self, hop):
  297. hop.exception_cannot_occur()
  298. return DictIteratorRepr(self, "items").newiter(hop)
  299. def rtype_method_iterkeys_with_hash(self, hop):
  300. hop.exception_cannot_occur()
  301. return DictIteratorRepr(self, "keys_with_hash").newiter(hop)
  302. def rtype_method_iteritems_with_hash(self, hop):
  303. hop.exception_cannot_occur()
  304. return DictIteratorRepr(self, "items_with_hash").newiter(hop)
  305. def rtype_method_clear(self, hop):
  306. v_dict, = hop.inputargs(self)
  307. hop.exception_cannot_occur()
  308. return hop.gendirectcall(ll_dict_clear, v_dict)
  309. def rtype_method_popitem(self, hop):
  310. v_dict, = hop.inputargs(self)
  311. r_tuple = hop.r_result
  312. cTUPLE = hop.inputconst(lltype.Void, r_tuple.lowleveltype)
  313. hop.exception_is_here()
  314. return hop.gendirectcall(ll_dict_popitem, cTUPLE, v_dict)
  315. def rtype_method_pop(self, hop):
  316. if hop.nb_args == 2:
  317. v_args = hop.inputargs(self, self.key_repr)
  318. target = ll_dict_pop
  319. elif hop.nb_args == 3:
  320. v_args = hop.inputargs(self, self.key_repr, self.value_repr)
  321. target = ll_dict_pop_default
  322. hop.exception_is_here()
  323. v_res = hop.gendirectcall(target, *v_args)
  324. return self.recast_value(hop.llops, v_res)
  325. def rtype_method_contains_with_hash(self, hop):
  326. v_dict, v_key, v_hash = hop.inputargs(self, self.key_repr,
  327. lltype.Signed)
  328. hop.exception_is_here()
  329. return hop.gendirectcall(ll_dict_contains_with_hash,
  330. v_dict, v_key, v_hash)
  331. def rtype_method_setitem_with_hash(self, hop):
  332. v_dict, v_key, v_hash, v_value = hop.inputargs(
  333. self, self.key_repr, lltype.Signed, self.value_repr)
  334. if self.custom_eq_hash:
  335. hop.exception_is_here()
  336. else:
  337. hop.exception_cannot_occur()
  338. hop.gendirectcall(ll_dict_setitem_with_hash,
  339. v_dict, v_key, v_hash, v_value)
  340. def rtype_method_getitem_with_hash(self, hop):
  341. v_dict, v_key, v_hash = hop.inputargs(
  342. self, self.key_repr, lltype.Signed)
  343. if not self.custom_eq_hash:
  344. hop.has_implicit_exception(KeyError) # record that we know about it
  345. hop.exception_is_here()
  346. v_res = hop.gendirectcall(ll_dict_getitem_with_hash,
  347. v_dict, v_key, v_hash)
  348. return self.recast_value(hop.llops, v_res)
  349. def rtype_method_delitem_with_hash(self, hop):
  350. v_dict, v_key, v_hash = hop.inputargs(
  351. self, self.key_repr, lltype.Signed)
  352. if not self.custom_eq_hash:
  353. hop.has_implicit_exception(KeyError) # record that we know about it
  354. hop.exception_is_here()
  355. hop.gendirectcall(ll_dict_delitem_with_hash, v_dict, v_key, v_hash)
  356. class __extend__(pairtype(OrderedDictRepr, rmodel.Repr)):
  357. def rtype_getitem((r_dict, r_key), hop):
  358. v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
  359. if not r_dict.custom_eq_hash:
  360. hop.has_implicit_exception(KeyError) # record that we know about it
  361. hop.exception_is_here()
  362. v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key)
  363. return r_dict.recast_value(hop.llops, v_res)
  364. def rtype_delitem((r_dict, r_key), hop):
  365. v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
  366. if not r_dict.custom_eq_hash:
  367. hop.has_implicit_exception(KeyError) # record that we know about it
  368. hop.exception_is_here()
  369. hop.gendirectcall(ll_dict_delitem, v_dict, v_key)
  370. def rtype_setitem((r_dict, r_key), hop):
  371. v_dict, v_key, v_value = hop.inputargs(r_dict, r_dict.key_repr, r_dict.value_repr)
  372. if r_dict.custom_eq_hash:
  373. hop.exception_is_here()
  374. else:
  375. hop.exception_cannot_occur()
  376. hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value)
  377. def rtype_contains((r_dict, r_key), hop):
  378. v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
  379. hop.exception_is_here()
  380. return hop.gendirectcall(ll_dict_contains, v_dict, v_key)
  381. class __extend__(pairtype(OrderedDictRepr, OrderedDictRepr)):
  382. def convert_from_to((r_dict1, r_dict2), v, llops):
  383. # check that we don't convert from Dicts with
  384. # different key/value types
  385. if r_dict1.dictkey is None or r_dict2.dictkey is None:
  386. return NotImplemented
  387. if r_dict1.dictkey is not r_dict2.dictkey:
  388. return NotImplemented
  389. if r_dict1.dictvalue is None or r_dict2.dictvalue is None:
  390. return NotImplemented
  391. if r_dict1.dictvalue is not r_dict2.dictvalue:
  392. return NotImplemented
  393. return v
  394. # ____________________________________________________________
  395. #
  396. # Low-level methods. These can be run for testing, but are meant to
  397. # be direct_call'ed from rtyped flow graphs, which means that they will
  398. # get flowed and annotated, mostly with SomePtr.
  399. DICTINDEX_LONG = lltype.Ptr(lltype.GcArray(lltype.Unsigned))
  400. DICTINDEX_INT = lltype.Ptr(lltype.GcArray(rffi.UINT))
  401. DICTINDEX_SHORT = lltype.Ptr(lltype.GcArray(rffi.USHORT))
  402. DICTINDEX_BYTE = lltype.Ptr(lltype.GcArray(rffi.UCHAR))
  403. IS_64BIT = sys.maxint != 2 ** 31 - 1
  404. FUNC_SHIFT = 2
  405. FUNC_MASK = 0x03 # two bits
  406. if IS_64BIT:
  407. FUNC_BYTE, FUNC_SHORT, FUNC_INT, FUNC_LONG = range(4)
  408. else:
  409. FUNC_BYTE, FUNC_SHORT, FUNC_LONG = range(3)
  410. TYPE_BYTE = rffi.UCHAR
  411. TYPE_SHORT = rffi.USHORT
  412. TYPE_INT = rffi.UINT
  413. TYPE_LONG = lltype.Unsigned
  414. def ll_malloc_indexes_and_choose_lookup(d, n):
  415. # keep in sync with ll_clear_indexes() below
  416. if n <= 256:
  417. d.indexes = lltype.cast_opaque_ptr(llmemory.GCREF,
  418. lltype.malloc(DICTINDEX_BYTE.TO, n,
  419. zero=True))
  420. d.lookup_function_no = FUNC_BYTE
  421. elif n <= 65536:
  422. d.indexes = lltype.cast_opaque_ptr(llmemory.GCREF,
  423. lltype.malloc(DICTINDEX_SHORT.TO, n,
  424. zero=True))
  425. d.lookup_function_no = FUNC_SHORT
  426. elif IS_64BIT and n <= 2 ** 32:
  427. d.indexes = lltype.cast_opaque_ptr(llmemory.GCREF,
  428. lltype.malloc(DICTINDEX_INT.TO, n,
  429. zero=True))
  430. d.lookup_function_no = FUNC_INT
  431. else:
  432. d.indexes = lltype.cast_opaque_ptr(llmemory.GCREF,
  433. lltype.malloc(DICTINDEX_LONG.TO, n,
  434. zero=True))
  435. d.lookup_function_no = FUNC_LONG
  436. def ll_clear_indexes(d, n):
  437. fun = d.lookup_function_no & FUNC_MASK
  438. d.lookup_function_no = fun
  439. if fun == FUNC_BYTE:
  440. rgc.ll_arrayclear(lltype.cast_opaque_ptr(DICTINDEX_BYTE, d.indexes))
  441. elif fun == FUNC_SHORT:
  442. rgc.ll_arrayclear(lltype.cast_opaque_ptr(DICTINDEX_SHORT, d.indexes))
  443. elif IS_64BIT and fun == FUNC_INT:
  444. rgc.ll_arrayclear(lltype.cast_opaque_ptr(DICTINDEX_INT, d.indexes))
  445. elif fun == FUNC_LONG:
  446. rgc.ll_arrayclear(lltype.cast_opaque_ptr(DICTINDEX_LONG, d.indexes))
  447. else:
  448. assert False
  449. @jit.dont_look_inside
  450. def ll_call_insert_clean_function(d, hash, i):
  451. fun = d.lookup_function_no & FUNC_MASK
  452. if fun == FUNC_BYTE:
  453. ll_dict_store_clean(d, hash, i, TYPE_BYTE)
  454. elif fun == FUNC_SHORT:
  455. ll_dict_store_clean(d, hash, i, TYPE_SHORT)
  456. elif IS_64BIT and fun == FUNC_INT:
  457. ll_dict_store_clean(d, hash, i, TYPE_INT)
  458. elif fun == FUNC_LONG:
  459. ll_dict_store_clean(d, hash, i, TYPE_LONG)
  460. else:
  461. assert False
  462. def ll_call_delete_by_entry_index(d, hash, i):
  463. fun = d.lookup_function_no & FUNC_MASK
  464. if fun == FUNC_BYTE:
  465. ll_dict_delete_by_entry_index(d, hash, i, TYPE_BYTE)
  466. elif fun == FUNC_SHORT:
  467. ll_dict_delete_by_entry_index(d, hash, i, TYPE_SHORT)
  468. elif IS_64BIT and fun == FUNC_INT:
  469. ll_dict_delete_by_entry_index(d, hash, i, TYPE_INT)
  470. elif fun == FUNC_LONG:
  471. ll_dict_delete_by_entry_index(d, hash, i, TYPE_LONG)
  472. else:
  473. assert False
  474. def ll_valid_from_flag(entries, i):
  475. return entries[i].f_valid
  476. def ll_valid_from_key(entries, i):
  477. ENTRIES = lltype.typeOf(entries).TO
  478. dummy = ENTRIES.dummy_obj.ll_dummy_value
  479. return entries[i].key != dummy
  480. def ll_valid_from_value(entries, i):
  481. ENTRIES = lltype.typeOf(entries).TO
  482. dummy = ENTRIES.dummy_obj.ll_dummy_value
  483. return entries[i].value != dummy
  484. def ll_mark_deleted_in_flag(entries, i):
  485. entries[i].f_valid = False
  486. def ll_mark_deleted_in_key(entries, i):
  487. ENTRIES = lltype.typeOf(entries).TO
  488. dummy = ENTRIES.dummy_obj.ll_dummy_value
  489. entries[i].key = dummy
  490. def ll_mark_deleted_in_value(entries, i):
  491. ENTRIES = lltype.typeOf(entries).TO
  492. dummy = ENTRIES.dummy_obj.ll_dummy_value
  493. entries[i].value = dummy
  494. @signature(types.any(), types.int(), returns=types.any())
  495. def ll_hash_from_cache(entries, i):
  496. return entries[i].f_hash
  497. @signature(types.any(), types.int(), returns=types.any())
  498. def ll_hash_recomputed(entries, i):
  499. ENTRIES = lltype.typeOf(entries).TO
  500. return ENTRIES.fasthashfn(entries[i].key)
  501. def ll_keyhash_custom(d, key):
  502. DICT = lltype.typeOf(d).TO
  503. return objectmodel.hlinvoke(DICT.r_rdict_hashfn, d.fnkeyhash, key)
  504. def ll_keyeq_custom(d, key1, key2):
  505. DICT = lltype.typeOf(d).TO
  506. return objectmodel.hlinvoke(DICT.r_rdict_eqfn, d.fnkeyeq, key1, key2)
  507. def ll_dict_len(d):
  508. return d.num_live_items
  509. def ll_dict_bool(d):
  510. # check if a dict is True, allowing for None
  511. return bool(d) and d.num_live_items != 0
  512. def ll_dict_getitem(d, key):
  513. return ll_dict_getitem_with_hash(d, key, d.keyhash(key))
  514. def ll_dict_getitem_with_hash(d, key, hash):
  515. index = d.lookup_function(d, key, hash, FLAG_LOOKUP)
  516. if index >= 0:
  517. return d.entries[index].value
  518. else:
  519. raise KeyError
  520. def ll_dict_setitem(d, key, value):
  521. ll_dict_setitem_with_hash(d, key, d.keyhash(key), value)
  522. def ll_dict_setitem_with_hash(d, key, hash, value):
  523. index = d.lookup_function(d, key, hash, FLAG_STORE)
  524. _ll_dict_setitem_lookup_done(d, key, value, hash, index)
  525. # It may be safe to look inside always, it has a few branches though, and their
  526. # frequencies needs to be investigated.
  527. @jit.look_inside_iff(lambda d, key, value, hash, i: jit.isvirtual(d) and jit.isconstant(key))
  528. def _ll_dict_setitem_lookup_done(d, key, value, hash, i):
  529. ENTRY = lltype.typeOf(d.entries).TO.OF
  530. if i >= 0:
  531. entry = d.entries[i]
  532. entry.value = value
  533. else:
  534. reindexed = False
  535. if len(d.entries) == d.num_ever_used_items:
  536. try:
  537. reindexed = ll_dict_grow(d)
  538. except:
  539. _ll_dict_rescue(d)
  540. raise
  541. rc = d.resize_counter - 3
  542. if rc <= 0:
  543. try:
  544. ll_dict_resize(d)
  545. reindexed = True
  546. except:
  547. _ll_dict_rescue(d)
  548. raise
  549. rc = d.resize_counter - 3
  550. ll_assert(rc > 0, "ll_dict_resize failed?")
  551. if reindexed:
  552. ll_call_insert_clean_function(d, hash, d.num_ever_used_items)
  553. #
  554. d.resize_counter = rc
  555. entry = d.entries[d.num_ever_used_items]
  556. entry.key = key
  557. entry.value = value
  558. if hasattr(ENTRY, 'f_hash'):
  559. entry.f_hash = hash
  560. if hasattr(ENTRY, 'f_valid'):
  561. entry.f_valid = True
  562. d.num_ever_used_items += 1
  563. d.num_live_items += 1
  564. @jit.dont_look_inside
  565. def _ll_dict_rescue(d):
  566. # MemoryError situation! The 'indexes' contains an invalid entry
  567. # at this point. But we can call ll_dict_reindex() with the
  568. # following arguments, ensuring no further malloc occurs.
  569. ll_dict_reindex(d, _ll_len_of_d_indexes(d))
  570. _ll_dict_rescue._dont_inline_ = True
  571. def _ll_dict_insertclean(d, key, value, hash):
  572. # never translated
  573. ENTRY = lltype.typeOf(d.entries).TO.OF
  574. ll_call_insert_clean_function(d, hash, d.num_ever_used_items)
  575. entry = d.entries[d.num_ever_used_items]
  576. entry.key = key
  577. entry.value = value
  578. if hasattr(ENTRY, 'f_hash'):
  579. entry.f_hash = hash
  580. if hasattr(ENTRY, 'f_valid'):
  581. entry.f_valid = True
  582. d.num_ever_used_items += 1
  583. d.num_live_items += 1
  584. rc = d.resize_counter - 3
  585. d.resize_counter = rc
  586. def _ll_len_of_d_indexes(d):
  587. # xxx Haaaack: returns len(d.indexes). Works independently of
  588. # the exact type pointed to by d, using a forced cast...
  589. # Must only be called by @jit.dont_look_inside functions.
  590. return lltype.length_of_simple_gcarray_from_opaque(d.indexes)
  591. def _overallocate_entries_len(baselen):
  592. # This over-allocates proportional to the list size, making room
  593. # for additional growth. This over-allocates slightly more eagerly
  594. # than with regular lists. The idea is that there are many more
  595. # lists than dicts around in PyPy, and dicts of 5 to 8 items are
  596. # not that rare (so a single jump from 0 to 8 is a good idea).
  597. # The growth pattern is: 0, 8, 17, 27, 38, 50, 64, 80, 98, ...
  598. newsize = baselen + (baselen >> 3)
  599. return newsize + 8
  600. @jit.look_inside_iff(lambda d: jit.isvirtual(d))
  601. def ll_dict_grow(d):
  602. # note: this @jit.look_inside_iff is here to inline the three lines
  603. # at the end of this function. It's important because dicts start
  604. # with a length-zero 'd.entries' which must be grown as soon as we
  605. # insert an element.
  606. if d.num_live_items < d.num_ever_used_items // 2:
  607. # At least 50% of the allocated entries are dead, so perform a
  608. # compaction. If ll_dict_remove_deleted_items detects that over
  609. # 75% of allocated entries are dead, then it will also shrink the
  610. # memory allocated at the same time as doing a compaction.
  611. ll_dict_remove_deleted_items(d)
  612. return True
  613. new_allocated = _overallocate_entries_len(len(d.entries))
  614. # Detect a relatively rare case where the indexes numeric type is too
  615. # small to store all the entry indexes: there would be 'new_allocated'
  616. # entries, which may in corner cases be larger than 253 even though we
  617. # have single bytes in 'd.indexes' (and the same for the larger
  618. # boundaries). The 'd.indexes' hashtable is never more than 2/3rd
  619. # full, so we know that 'd.num_live_items' should be at most 2/3 * 256
  620. # (or 65536 or etc.) so after the ll_dict_remove_deleted_items() below
  621. # at least 1/3rd items in 'd.entries' are free.
  622. fun = d.lookup_function_no & FUNC_MASK
  623. toobig = False
  624. if fun == FUNC_BYTE:
  625. assert d.num_live_items < ((1 << 8) - MIN_INDEXES_MINUS_ENTRIES)
  626. toobig = new_allocated > ((1 << 8) - MIN_INDEXES_MINUS_ENTRIES)
  627. elif fun == FUNC_SHORT:
  628. assert d.num_live_items < ((1 << 16) - MIN_INDEXES_MINUS_ENTRIES)
  629. toobig = new_allocated > ((1 << 16) - MIN_INDEXES_MINUS_ENTRIES)
  630. elif IS_64BIT and fun == FUNC_INT:
  631. assert d.num_live_items < ((1 << 32) - MIN_INDEXES_MINUS_ENTRIES)
  632. toobig = new_allocated > ((1 << 32) - MIN_INDEXES_MINUS_ENTRIES)
  633. #
  634. if toobig:
  635. ll_dict_remove_deleted_items(d)
  636. assert d.num_live_items == d.num_ever_used_items
  637. return True
  638. newitems = lltype.malloc(lltype.typeOf(d).TO.entries.TO, new_allocated)
  639. rgc.ll_arraycopy(d.entries, newitems, 0, 0, len(d.entries))
  640. d.entries = newitems
  641. return False
  642. @jit.dont_look_inside
  643. def ll_dict_remove_deleted_items(d):
  644. if d.num_live_items < len(d.entries) // 4:
  645. # At least 75% of the allocated entries are dead, so shrink the memory
  646. # allocated as well as doing a compaction.
  647. new_allocated = _overallocate_entries_len(d.num_live_items)
  648. newitems = lltype.malloc(lltype.typeOf(d).TO.entries.TO, new_allocated)
  649. else:
  650. newitems = d.entries
  651. # The loop below does a lot of writes into 'newitems'. It's a better
  652. # idea to do a single gc_writebarrier rather than activating the
  653. # card-by-card logic (worth 11% in microbenchmarks).
  654. from rpython.rtyper.lltypesystem.lloperation import llop
  655. llop.gc_writebarrier(lltype.Void, newitems)
  656. #
  657. ENTRIES = lltype.typeOf(d).TO.entries.TO
  658. ENTRY = ENTRIES.OF
  659. isrc = 0
  660. idst = 0
  661. isrclimit = d.num_ever_used_items
  662. while isrc < isrclimit:
  663. if d.entries.valid(isrc):
  664. src = d.entries[isrc]
  665. dst = newitems[idst]
  666. dst.key = src.key
  667. dst.value = src.value
  668. if hasattr(ENTRY, 'f_hash'):
  669. dst.f_hash = src.f_hash
  670. if hasattr(ENTRY, 'f_valid'):
  671. assert src.f_valid
  672. dst.f_valid = True
  673. idst += 1
  674. isrc += 1
  675. assert d.num_live_items == idst
  676. d.num_ever_used_items = idst
  677. if ((ENTRIES.must_clear_key or ENTRIES.must_clear_value) and
  678. d.entries == newitems):
  679. # must clear the extra entries: they may contain valid pointers
  680. # which would create a temporary memory leak
  681. while idst < isrclimit:
  682. entry = newitems[idst]
  683. if ENTRIES.must_clear_key:
  684. entry.key = lltype.nullptr(ENTRY.key.TO)
  685. if ENTRIES.must_clear_value:
  686. entry.value = lltype.nullptr(ENTRY.value.TO)
  687. idst += 1
  688. else:
  689. d.entries = newitems
  690. ll_dict_reindex(d, _ll_len_of_d_indexes(d))
  691. def ll_dict_delitem(d, key):
  692. ll_dict_delitem_with_hash(d, key, d.keyhash(key))
  693. def ll_dict_delitem_with_hash(d, key, hash):
  694. index = d.lookup_function(d, key, hash, FLAG_DELETE)
  695. if index < 0:
  696. raise KeyError
  697. _ll_dict_del(d, index)
  698. @jit.look_inside_iff(lambda d, i: jit.isvirtual(d) and jit.isconstant(i))
  699. def _ll_dict_del(d, index):
  700. d.entries.mark_deleted(index)
  701. d.num_live_items -= 1
  702. # clear the key and the value if they are GC pointers
  703. ENTRIES = lltype.typeOf(d.entries).TO
  704. ENTRY = ENTRIES.OF
  705. entry = d.entries[index]
  706. if ENTRIES.must_clear_key:
  707. entry.key = lltype.nullptr(ENTRY.key.TO)
  708. if ENTRIES.must_clear_value:
  709. entry.value = lltype.nullptr(ENTRY.value.TO)
  710. if d.num_live_items == 0:
  711. # Dict is now empty. Reset these fields.
  712. d.num_ever_used_items = 0
  713. d.lookup_function_no &= FUNC_MASK
  714. elif index == d.num_ever_used_items - 1:
  715. # The last element of the ordereddict has been deleted. Instead of
  716. # simply marking the item as dead, we can safely reuse it. Since it's
  717. # also possible that there are more dead items immediately behind the
  718. # last one, we reclaim all the dead items at the end of the ordereditem
  719. # at the same point.
  720. i = d.num_ever_used_items - 2
  721. while i >= 0 and not d.entries.valid(i):
  722. i -= 1
  723. j = i + 1
  724. assert j >= 0
  725. d.num_ever_used_items = j
  726. # If the dictionary is at least 87.5% dead items, then consider shrinking
  727. # it.
  728. if d.num_live_items + DICT_INITSIZE <= len(d.entries) / 8:
  729. ll_dict_resize(d)
  730. def ll_dict_resize(d):
  731. # make a 'new_size' estimate and shrink it if there are many
  732. # deleted entry markers. See CPython for why it is a good idea to
  733. # quadruple the dictionary size as long as it's not too big.
  734. # (Quadrupling comes from '(d.num_live_items + d.num_live_items + 1) * 2'
  735. # as long as num_live_items is not too large.)
  736. num_extra = min(d.num_live_items + 1, 30000)
  737. _ll_dict_resize_to(d, num_extra)
  738. ll_dict_resize.oopspec = 'odict.resize(d)'
  739. def _ll_dict_resize_to(d, num_extra):
  740. new_estimate = (d.num_live_items + num_extra) * 2
  741. new_size = DICT_INITSIZE
  742. while new_size <= new_estimate:
  743. new_size *= 2
  744. if new_size < _ll_len_of_d_indexes(d):
  745. ll_dict_remove_deleted_items(d)
  746. else:
  747. ll_dict_reindex(d, new_size)
  748. def ll_dict_reindex(d, new_size):
  749. if bool(d.indexes) and _ll_len_of_d_indexes(d) == new_size:
  750. ll_clear_indexes(d, new_size) # hack: we can reuse the same array
  751. else:
  752. ll_malloc_indexes_and_choose_lookup(d, new_size)
  753. d.resize_counter = new_size * 2 - d.num_live_items * 3
  754. ll_assert(d.resize_counter > 0, "reindex: resize_counter <= 0")
  755. ll_assert((d.lookup_function_no >> FUNC_SHIFT) == 0,
  756. "reindex: lookup_fun >> SHIFT")
  757. #
  758. entries = d.entries
  759. i = 0
  760. ibound = d.num_ever_used_items
  761. while i < ibound:
  762. if entries.valid(i):
  763. hash = entries.hash(i)
  764. ll_call_insert_clean_function(d, hash, i)
  765. i += 1
  766. #old_entries.delete() XXXX!
  767. # ------- a port of CPython's dictobject.c's lookdict implementation -------
  768. PERTURB_SHIFT = 5
  769. FREE = 0
  770. DELETED = 1
  771. VALID_OFFSET = 2
  772. MIN_INDEXES_MINUS_ENTRIES = VALID_OFFSET + 1
  773. FLAG_LOOKUP = 0
  774. FLAG_STORE = 1
  775. FLAG_DELETE = 2
  776. @specialize.memo()
  777. def _ll_ptr_to_array_of(T):
  778. return lltype.Ptr(lltype.GcArray(T))
  779. @jit.look_inside_iff(lambda d, key, hash, store_flag, T:
  780. jit.isvirtual(d) and jit.isconstant(key))
  781. @jit.oopspec('ordereddict.lookup(d, key, hash, store_flag, T)')
  782. def ll_dict_lookup(d, key, hash, store_flag, T):
  783. INDEXES = _ll_ptr_to_array_of(T)
  784. entries = d.entries
  785. indexes = lltype.cast_opaque_ptr(INDEXES, d.indexes)
  786. mask = len(indexes) - 1
  787. i = r_uint(hash & mask)
  788. # do the first try before any looping
  789. ENTRIES = lltype.typeOf(entries).TO
  790. direct_compare = not hasattr(ENTRIES, 'no_direct_compare')
  791. index = rffi.cast(lltype.Signed, indexes[intmask(i)])
  792. if index >= VALID_OFFSET:
  793. checkingkey = entries[index - VALID_OFFSET].key
  794. if direct_compare and checkingkey == key:
  795. if store_flag == FLAG_DELETE:
  796. indexes[i] = rffi.cast(T, DELETED)
  797. return index - VALID_OFFSET # found the entry
  798. if d.keyeq is not None and entries.hash(index - VALID_OFFSET) == hash:
  799. # correct hash, maybe the key is e.g. a different pointer to
  800. # an equal object
  801. found = d.keyeq(checkingkey, key)
  802. #llop.debug_print(lltype.Void, "comparing keys", ll_debugrepr(checkingkey), ll_debugrepr(key), found)
  803. if d.paranoia:
  804. if (entries != d.entries or lltype.cast_opaque_ptr(llmemory.GCREF, indexes) != d.indexes or
  805. not entries.valid(index - VALID_OFFSET) or
  806. entries[index - VALID_OFFSET].key != checkingkey):
  807. # the compare did major nasty stuff to the dict: start over
  808. return ll_dict_lookup(d, key, hash, store_flag, T)
  809. if found:
  810. if store_flag == FLAG_DELETE:
  811. indexes[i] = rffi.cast(T, DELETED)
  812. return index - VALID_OFFSET
  813. deletedslot = -1
  814. elif index == DELETED:
  815. deletedslot = intmask(i)
  816. else:
  817. # pristine entry -- lookup failed
  818. if store_flag == FLAG_STORE:
  819. indexes[i] = rffi.cast(T, d.num_ever_used_items + VALID_OFFSET)
  820. return -1
  821. # In the loop, a deleted entry (everused and not valid) is by far
  822. # (factor of 100s) the least likely outcome, so test for that last.
  823. perturb = r_uint(hash)
  824. while 1:
  825. # compute the next index using unsigned arithmetic
  826. i = (i << 2) + i + perturb + 1
  827. i = i & mask
  828. index = rffi.cast(lltype.Signed, indexes[intmask(i)])
  829. if index == FREE:
  830. if store_flag == FLAG_STORE:
  831. if deletedslot == -1:
  832. deletedslot = intmask(i)
  833. indexes[deletedslot] = rffi.cast(T, d.num_ever_used_items +
  834. VALID_OFFSET)
  835. return -1
  836. elif index >= VALID_OFFSET:
  837. checkingkey = entries[index - VALID_OFFSET].key
  838. if direct_compare and checkingkey == key:
  839. if store_flag == FLAG_DELETE:
  840. indexes[i] = rffi.cast(T, DELETED)
  841. return index - VALID_OFFSET # found the entry
  842. if d.keyeq is not None and entries.hash(index - VALID_OFFSET) == hash:
  843. # correct hash, maybe the key is e.g. a different pointer to
  844. # an equal object
  845. found = d.keyeq(checkingkey, key)
  846. if d.paranoia:
  847. if (entries != d.entries or lltype.cast_opaque_ptr(llmemory.GCREF, indexes) != d.indexes or
  848. not entries.valid(index - VALID_OFFSET) or
  849. entries[index - VALID_OFFSET].key != checkingkey):
  850. # the compare did major nasty stuff to the dict: start over
  851. return ll_dict_lookup(d, key, hash, store_flag, T)
  852. if found:
  853. if store_flag == FLAG_DELETE:
  854. indexes[i] = rffi.cast(T, DELETED)
  855. return index - VALID_OFFSET
  856. elif deletedslot == -1:
  857. deletedslot = intmask(i)
  858. perturb >>= PERTURB_SHIFT
  859. def ll_dict_store_clean(d, hash, index, T):
  860. # a simplified version of ll_dict_lookup() which assumes that the
  861. # key is new, and the dictionary doesn't contain deleted entries.
  862. # It only finds the next free slot for the given hash.
  863. INDEXES = _ll_ptr_to_array_of(T)
  864. indexes = lltype.cast_opaque_ptr(INDEXES, d.indexes)
  865. mask = len(indexes) - 1
  866. i = r_uint(hash & mask)
  867. perturb = r_uint(hash)
  868. while rffi.cast(lltype.Signed, indexes[i]) != FREE:
  869. i = (i << 2) + i + perturb + 1
  870. i = i & mask
  871. perturb >>= PERTURB_SHIFT
  872. indexes[i] = rffi.cast(T, index + VALID_OFFSET)
  873. def ll_dict_delete_by_entry_index(d, hash, locate_index, T):
  874. # Another simplified version of ll_dict_lookup() which locates a
  875. # hashtable entry with the given 'index' stored in it, and deletes it.
  876. # This *should* be safe against evil user-level __eq__/__hash__
  877. # functions because the 'hash' argument here should be the one stored
  878. # into the directory, which is correct.
  879. INDEXES = _ll_ptr_to_array_of(T)
  880. indexes = lltype.cast_opaque_ptr(INDEXES, d.indexes)
  881. mask = len(indexes) - 1
  882. i = r_uint(hash & mask)
  883. perturb = r_uint(hash)
  884. locate_value = locate_index + VALID_OFFSET
  885. while rffi.cast(lltype.Signed, indexes[i]) != locate_value:
  886. assert rffi.cast(lltype.Signed, indexes[i]) != FREE
  887. i = (i << 2) + i + perturb + 1
  888. i = i & mask
  889. perturb >>= PERTURB_SHIFT
  890. indexes[i] = rffi.cast(T, DELETED)
  891. # ____________________________________________________________
  892. #
  893. # Irregular operations.
  894. # Start the hashtable size at 16 rather than 8, as with rdict.py, because
  895. # it is only an array of bytes
  896. DICT_INITSIZE = 16
  897. @specialize.memo()
  898. def _ll_empty_array(DICT):
  899. """Memo function: cache a single prebuilt allocated empty array."""
  900. return DICT.entries.TO.allocate(0)
  901. def ll_newdict(DICT):
  902. d = DICT.allocate()
  903. d.entries = _ll_empty_array(DICT)
  904. ll_malloc_indexes_and_choose_lookup(d, DICT_INITSIZE)
  905. d.num_live_items = 0
  906. d.num_ever_used_items = 0
  907. d.resize_counter = DICT_INITSIZE * 2
  908. return d
  909. OrderedDictRepr.ll_newdict = staticmethod(ll_newdict)
  910. def ll_newdict_size(DICT, orig_length_estimate):
  911. length_estimate = (orig_length_estimate // 2) * 3
  912. n = DICT_INITSIZE
  913. while n < length_estimate:
  914. n *= 2
  915. d = DICT.allocate()
  916. d.entries = DICT.entries.TO.allocate(orig_length_estimate)
  917. ll_malloc_indexes_and_choose_lookup(d, n)
  918. d.num_live_items = 0
  919. d.num_ever_used_items = 0
  920. d.resize_counter = n * 2
  921. return d
  922. # rpython.memory.lldict uses a dict based on Struct and Array
  923. # instead of GcStruct and GcArray, which is done by using different
  924. # 'allocate' and 'delete' adtmethod implementations than the ones below
  925. def _ll_malloc_dict(DICT):
  926. return lltype.malloc(DICT)
  927. def _ll_malloc_entries(ENTRIES, n):
  928. return lltype.malloc(ENTRIES, n, zero=True)
  929. def _ll_free_entries(entries):
  930. pass
  931. # ____________________________________________________________
  932. #
  933. # Iteration.
  934. def get_ll_dictiter(DICTPTR):
  935. return lltype.Ptr(lltype.GcStruct('dictiter',
  936. ('dict', DICTPTR),
  937. ('index', lltype.Signed)))
  938. class DictIteratorRepr(AbstractDictIteratorRepr):
  939. def __init__(self, r_dict, variant="keys"):
  940. self.r_dict = r_dict
  941. self.variant = variant
  942. self.lowleveltype = get_ll_dictiter(r_dict.lowleveltype)
  943. if variant == 'reversed':
  944. self.ll_dictiter = ll_dictiter_reversed
  945. self._ll_dictnext = _ll_dictnext_reversed
  946. else:
  947. self.ll_dictiter = ll_dictiter
  948. self._ll_dictnext = _ll_dictnext
  949. def ll_dictiter(ITERPTR, d):
  950. iter = lltype.malloc(ITERPTR.TO)
  951. iter.dict = d
  952. # initialize the index with usually 0, but occasionally a larger value
  953. iter.index = d.lookup_function_no >> FUNC_SHIFT
  954. return iter
  955. @jit.look_inside_iff(lambda iter: jit.isvirtual(iter)
  956. and (iter.dict is None or
  957. jit.isvirtual(iter.dict)))
  958. @jit.oopspec("odictiter.next(iter)")
  959. def _ll_dictnext(iter):
  960. dict = iter.dict
  961. if dict:
  962. entries = dict.entries
  963. index = iter.index
  964. assert index >= 0
  965. entries_len = dict.num_ever_used_items
  966. while index < entries_len:
  967. nextindex = index + 1
  968. if entries.valid(index):
  969. iter.index = nextindex
  970. return index
  971. else:
  972. # In case of repeated iteration over the start of
  973. # a dict where the items get removed, like
  974. # collections.OrderedDict.popitem(last=False),
  975. # the hack below will increase the value stored in
  976. # the high bits of lookup_function_no and so the
  977. # next iteration will start at a higher value.
  978. # We should carefully reset these high bits to zero
  979. # as soon as we do something like ll_dict_reindex().
  980. if index == (dict.lookup_function_no >> FUNC_SHIFT):
  981. dict.lookup_function_no += (1 << FUNC_SHIFT)
  982. index = nextindex
  983. # clear the reference to the dict and prevent restarts
  984. iter.dict = lltype.nullptr(lltype.typeOf(iter).TO.dict.TO)
  985. raise StopIteration
  986. def ll_dictiter_reversed(ITERPTR, d):
  987. iter = lltype.malloc(ITERPTR.TO)
  988. iter.dict = d
  989. iter.index = d.num_ever_used_items
  990. return iter
  991. def _ll_dictnext_reversed(iter):
  992. dict = iter.dict
  993. if dict:
  994. entries = dict.entries
  995. index = iter.index - 1
  996. while index >= 0:
  997. if entries.valid(index):
  998. iter.index = index
  999. return index
  1000. index = index - 1
  1001. # clear the reference to the dict and prevent restarts
  1002. iter.dict = lltype.nullptr(lltype.typeOf(iter).TO.dict.TO)
  1003. raise StopIteration
  1004. # _____________________________________________________________
  1005. # methods
  1006. def ll_dict_get(dict, key, default):
  1007. index = dict.lookup_function(dict, key, dict.keyhash(key), FLAG_LOOKUP)
  1008. if index < 0:
  1009. return default
  1010. else:
  1011. return dict.entries[index].value
  1012. def ll_dict_setdefault(dict, key, default):
  1013. hash = dict.keyhash(key)
  1014. index = dict.lookup_function(dict, key, hash, FLAG_STORE)
  1015. if index < 0:
  1016. _ll_dict_setitem_lookup_done(dict, key, default, hash, -1)
  1017. return default
  1018. else:
  1019. return dict.entries[index].value
  1020. def ll_dict_copy(dict):
  1021. DICT = lltype.typeOf(dict).TO
  1022. newdict = DICT.allocate()
  1023. newdict.entries = DICT.entries.TO.allocate(len(dict.entries))
  1024. newdict.num_live_items = dict.num_live_items
  1025. newdict.num_ever_used_items = dict.num_ever_used_items
  1026. if hasattr(DICT, 'fnkeyeq'):
  1027. newdict.fnkeyeq = dict.fnkeyeq
  1028. if hasattr(DICT, 'fnkeyhash'):
  1029. newdict.fnkeyhash = dict.fnkeyhash
  1030. i = 0
  1031. while i < newdict.num_ever_used_items:
  1032. d_entry = newdict.entries[i]
  1033. entry = dict.entries[i]
  1034. ENTRY = lltype.typeOf(newdict.entries).TO.OF
  1035. d_entry.key = entry.key
  1036. if hasattr(ENTRY, 'f_valid'):
  1037. d_entry.f_valid = entry.f_valid
  1038. d_entry.value = entry.value
  1039. if hasattr(ENTRY, 'f_hash'):
  1040. d_entry.f_hash = entry.f_hash
  1041. i += 1
  1042. ll_dict_reindex(newdict, _ll_len_of_d_indexes(dict))
  1043. return newdict
  1044. ll_dict_copy.oopspec = 'odict.copy(dict)'
  1045. def ll_dict_clear(d):
  1046. if d.num_ever_used_items == 0:
  1047. return
  1048. DICT = lltype.typeOf(d).TO
  1049. old_entries = d.entries
  1050. d.entries = _ll_empty_array(DICT)
  1051. ll_malloc_indexes_and_choose_lookup(d, DICT_INITSIZE)
  1052. d.num_live_items = 0
  1053. d.num_ever_used_items = 0
  1054. d.resize_counter = DICT_INITSIZE * 2
  1055. # old_entries.delete() XXX
  1056. ll_dict_clear.oopspec = 'odict.clear(d)'
  1057. def ll_dict_update(dic1, dic2):
  1058. if dic1 == dic2:
  1059. return
  1060. ll_prepare_dict_update(dic1, dic2.num_live_items)
  1061. i = 0
  1062. while i < dic2.num_ever_used_items:
  1063. entries = dic2.entries
  1064. if entries.valid(i):
  1065. entry = entries[i]
  1066. hash = entries.hash(i)
  1067. key = entry.key
  1068. value = entry.value
  1069. index = dic1.lookup_function(dic1, key, hash, FLAG_STORE)
  1070. _ll_dict_setitem_lookup_done(dic1, key, value, hash, index)
  1071. i += 1
  1072. ll_dict_update.oopspec = 'odict.update(dic1, dic2)'
  1073. def ll_prepare_dict_update(d, num_extra):
  1074. # Prescale 'd' for 'num_extra' items, assuming that most items don't
  1075. # collide. If this assumption is false, 'd' becomes too large by at
  1076. # most 'num_extra'. The logic is based on:
  1077. # (d.resize_counter - 1) // 3 = room left in d
  1078. # so, if num_extra == 1, we need d.resize_counter > 3
  1079. # if num_extra == 2, we need d.resize_counter > 6 etc.
  1080. # Note however a further hack: if num_extra <= d.num_live_items,
  1081. # we avoid calling _ll_dict_resize_to here. This is to handle
  1082. # the case where dict.update() actually has a lot of collisions.
  1083. # If num_extra is much greater than d.num_live_items the conditional_call
  1084. # will trigger anyway, which is really the goal.
  1085. x = num_extra - d.num_live_items
  1086. jit.conditional_call(d.resize_counter <= x * 3,
  1087. _ll_dict_resize_to, d, num_extra)
  1088. # this is an implementation of keys(), values() and items()
  1089. # in a single function.
  1090. # note that by specialization on func, three different
  1091. # and very efficient functions are created.
  1092. def recast(P, v):
  1093. if isinstance(P, lltype.Ptr):
  1094. return lltype.cast_pointer(P, v)
  1095. else:
  1096. return v
  1097. def _make_ll_keys_values_items(kind):
  1098. def ll_kvi(LIST, dic):
  1099. res = LIST.ll_newlist(dic.num_live_items)
  1100. entries = dic.entries
  1101. dlen = dic.num_ever_used_items
  1102. items = res.ll_items()
  1103. i = 0
  1104. p = 0
  1105. while i < dlen:
  1106. if entries.valid(i):
  1107. ELEM = lltype.typeOf(items).TO.OF
  1108. if ELEM is not lltype.Void:
  1109. entry = entries[i]
  1110. if kind == 'items':
  1111. r = lltype.malloc(ELEM.TO)
  1112. r.item0 = recast(ELEM.TO.item0, entry.key)
  1113. r.item1 = recast(ELEM.TO.item1, entry.value)
  1114. items[p] = r
  1115. elif kind == 'keys':
  1116. items[p] = recast(ELEM, entry.key)
  1117. elif kind == 'values':
  1118. items[p] = recast(ELEM, entry.value)
  1119. p += 1
  1120. i += 1
  1121. assert p == res.ll_length()
  1122. return res
  1123. ll_kvi.o

Large files files are truncated, but you can click here to view the full file