Author: Armin Rigo <ar...@tunes.org> Branch: rpython-hash Changeset: r89794:eaa33d328249 Date: 2017-01-26 22:05 +0100 http://bitbucket.org/pypy/pypy/changeset/eaa33d328249/
Log: Tweaks, possibly fix a bug diff --git a/rpython/rtyper/lltypesystem/rordereddict.py b/rpython/rtyper/lltypesystem/rordereddict.py --- a/rpython/rtyper/lltypesystem/rordereddict.py +++ b/rpython/rtyper/lltypesystem/rordereddict.py @@ -642,19 +642,17 @@ except: ll_remove_index(d) raise - rc = d.resize_counter - 3 - if rc <= 0: + if d.resize_counter <= 3: try: ll_dict_resize(d) reindexed = True except: ll_remove_index(d) raise - rc = d.resize_counter - 3 - ll_assert(rc > 0, "ll_dict_resize failed?") if reindexed: ll_call_insert_clean_function(d, hash, d.num_ever_used_items) - # + rc = d.resize_counter - 3 + ll_assert(rc > 0, "ll_dict_resize failed?") d.resize_counter = rc entry = d.entries[d.num_ever_used_items] entry.key = key @@ -799,7 +797,7 @@ # then maybe it is a better idea to keep it instead of reallocating # an identical index very soon. In this case we update the index now. if bool(d.indexes): - new_estimate = d.num_live_items * 2 + new_estimate = d.num_live_items * 2 new_size = DICT_INITSIZE while new_size <= new_estimate: new_size *= 2 @@ -842,12 +840,13 @@ # also possible that there are more dead items immediately behind the # last one, we reclaim all the dead items at the end of the ordereditem # at the same point. - i = d.num_ever_used_items - 2 - while i >= 0 and not d.entries.valid(i): + i = index + while True: i -= 1 - j = i + 1 - assert j >= 0 - d.num_ever_used_items = j + assert i >= 0 + if d.entries.valid(i): # must be at least one + break + d.num_ever_used_items = i + 1 # If the dictionary is more than 75% dead items, then clean up the # deleted items and remove the index. @@ -1065,7 +1064,6 @@ ll_remove_index(d) d.num_live_items = 0 d.num_ever_used_items = 0 - d.resize_counter = DICT_INITSIZE * 2 return d OrderedDictRepr.ll_newdict = staticmethod(ll_newdict) @@ -1232,7 +1230,6 @@ ll_remove_index(d) d.num_live_items = 0 d.num_ever_used_items = 0 - d.resize_counter = DICT_INITSIZE * 2 # old_entries.delete() XXX ll_dict_clear.oopspec = 'odict.clear(d)' diff --git a/rpython/rtyper/test/test_rdict.py b/rpython/rtyper/test/test_rdict.py --- a/rpython/rtyper/test/test_rdict.py +++ b/rpython/rtyper/test/test_rdict.py @@ -1170,6 +1170,7 @@ self.reference = self.new_reference() self.ll_key = r_key.convert_const self.ll_value = r_value.convert_const + self.removed_keys = [] r_tuple = TupleRepr(rtyper, [r_key, r_value]) self.TUPLE = r_tuple.lowleveltype @@ -1185,6 +1186,7 @@ self.ll_delitem(self.l_dict, ll_key) del self.reference[key] assert not self.ll_contains(self.l_dict, ll_key) + self.removed_keys.append(key) def copydict(self): self.l_dict = self.ll_copy(self.l_dict) @@ -1192,6 +1194,8 @@ def cleardict(self): self.ll_clear(self.l_dict) + for key in self.reference: + self.removed_keys.append(key) self.reference.clear() assert self.ll_len(self.l_dict) == 0 @@ -1207,15 +1211,27 @@ if self.ll_key(key) == ll_key: assert self.ll_value(value) == ll_value del self.reference[key] + self.removed_keys.append(key) break else: raise AssertionError("popitem() returned unexpected key") + def removeindex(self): + pass # overridden in test_rordereddict + def fullcheck(self): assert self.ll_len(self.l_dict) == len(self.reference) for key, value in self.reference.iteritems(): assert (self.ll_getitem(self.l_dict, self.ll_key(key)) == self.ll_value(value)) + for key in self.removed_keys: + if key not in self.reference: + try: + self.ll_getitem(self.l_dict, self.ll_key(key)) + except KeyError: + pass + else: + raise AssertionError("removed key still shows up") class MappingSM(GenericStateMachine): def __init__(self): @@ -1238,7 +1254,7 @@ if not self.space: return builds(Action, just('setup'), tuples(st_keys, st_values)) global_actions = [Action('copydict', ()), Action('cleardict', ()), - Action('popitem', ())] + Action('popitem', ()), Action('removeindex', ())] if self.space.reference: return ( self.st_setitem() | sampled_from(global_actions) | diff --git a/rpython/rtyper/test/test_rordereddict.py b/rpython/rtyper/test/test_rordereddict.py --- a/rpython/rtyper/test/test_rordereddict.py +++ b/rpython/rtyper/test/test_rordereddict.py @@ -202,14 +202,17 @@ assert ll_d.num_ever_used_items == 0 assert ll_d.lookup_function_no == rordereddict.FUNC_BYTE # reset - def test_direct_enter_and_del(self): + def _get_int_dict(self): def eq(a, b): return a == b - DICT = rordereddict.get_ll_dict(lltype.Signed, lltype.Signed, + return rordereddict.get_ll_dict(lltype.Signed, lltype.Signed, ll_fasthash_function=intmask, ll_hash_function=intmask, ll_eq_function=eq) + + def test_direct_enter_and_del(self): + DICT = self._get_int_dict() ll_d = rordereddict.ll_newdict(DICT) numbers = [i * rordereddict.DICT_INITSIZE + 1 for i in range(8)] for num in numbers: @@ -303,6 +306,38 @@ rordereddict.ll_prepare_dict_update(ll_d, 7) # used to get UninitializedMemoryAccess + def test_bug_resize_counter(self): + DICT = self._get_int_dict() + ll_d = rordereddict.ll_newdict(DICT) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 1, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_setitem(ll_d, 2, 0) + rordereddict.ll_dict_delitem(ll_d, 1) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_delitem(ll_d, 2) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_delitem(ll_d, 0) + rordereddict.ll_dict_setitem(ll_d, 0, 0) + rordereddict.ll_dict_setitem(ll_d, 1, 0) + d = ll_d + idx = d.indexes._obj.container + num_nonfrees = 0 + for i in range(idx.getlength()): + got = idx.getitem(i) # 0: unused; 1: deleted + num_nonfrees += (got > 0) + assert d.resize_counter <= idx.getlength() * 2 - num_nonfrees * 3 + + class TestRDictDirectDummyKey(TestRDictDirect): class dummykeyobj: ll_dummy_value = llstr("dupa") @@ -377,6 +412,12 @@ key, value = self.reference.popitem() assert self.ll_key(key) == ll_key assert self.ll_value(value) == ll_value + self.removed_keys.append(key) + + def removeindex(self): + # remove the index, as done e.g. during translation for prebuilt + # dicts + rodct.ll_remove_index(self.l_dict) def fullcheck(self): # overridden to also check key order @@ -387,6 +428,35 @@ assert self.ll_key(key) == ll_key assert (self.ll_getitem(self.l_dict, self.ll_key(key)) == self.ll_value(self.reference[key])) + for key in self.removed_keys: + if key not in self.reference: + try: + self.ll_getitem(self.l_dict, self.ll_key(key)) + except KeyError: + pass + else: + raise AssertionError("removed key still shows up") + # check some internal invariants + d = self.l_dict + num_lives = 0 + for i in range(d.num_ever_used_items): + if d.entries.valid(i): + num_lives += 1 + assert num_lives == d.num_live_items + fun = d.lookup_function_no & rordereddict.FUNC_MASK + if fun == rordereddict.FUNC_NO_INDEX: + assert not d.indexes + else: + assert d.indexes + idx = d.indexes._obj.container + num_lives = 0 + num_nonfrees = 0 + for i in range(idx.getlength()): + got = idx.getitem(i) # 0: unused; 1: deleted + num_nonfrees += (got > 0) + num_lives += (got > 1) + assert num_lives == d.num_live_items + assert 0 < d.resize_counter <= idx.getlength()*2 - num_nonfrees*3 class ODictSM(MappingSM): @@ -394,4 +464,4 @@ def test_hypothesis(): run_state_machine_as_test( - ODictSM, settings(max_examples=50000, stateful_step_count=100)) + ODictSM, settings(max_examples=500, stateful_step_count=100)) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit