diff -r 20c637a0bff7 Objects/dictobject.c --- a/Objects/dictobject.c Wed Oct 05 23:32:15 2016 -0700 +++ b/Objects/dictobject.c Thu Oct 06 18:20:34 2016 +0900 @@ -1195,41 +1195,19 @@ insertdict(PyDictObject *mp, PyObject *k } /* -Internal routine used by dictresize() to insert an item which is -known to be absent from the dict. This routine also assumes that -the dict contains no deleted entries. Besides the performance benefit, -using insertdict() in dictresize() is dangerous (SF bug #1456209). -Note that no refcounts are changed by this routine; if needed, the caller -is responsible for incref'ing `key` and `value`. -Neither mp->ma_used nor k->dk_usable are modified by this routine; the caller -must set them correctly +Internal routine used by dictresize() to insert an entry index into dk_indices. +This routine only touch to dk_indices; The caller must update other members. */ static void -insertdict_clean(PyDictObject *mp, PyObject *key, Py_hash_t hash, - PyObject *value) +insert_index(PyDictKeysObject *k, Py_ssize_t ix, Py_hash_t hash) { - size_t i; - PyDictKeysObject *k = mp->ma_keys; - size_t mask = (size_t)DK_SIZE(k)-1; - PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); - PyDictKeyEntry *ep; - - assert(k->dk_lookup != NULL); - assert(value != NULL); - assert(key != NULL); - assert(PyUnicode_CheckExact(key) || k->dk_lookup == lookdict); - i = hash & mask; + size_t mask = (size_t)DK_SIZE(k) - 1; + size_t i = hash & mask; for (size_t perturb = hash; dk_get_index(k, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); } - ep = &ep0[k->dk_nentries]; - assert(ep->me_value == NULL); - dk_set_index(k, i, k->dk_nentries); - k->dk_nentries++; - ep->me_key = key; - ep->me_hash = hash; - ep->me_value = value; + dk_set_index(k, i, ix); } /* @@ -1245,11 +1223,7 @@ but can be resplit by make_keys_shared() static int dictresize(PyDictObject *mp, Py_ssize_t minused) { - Py_ssize_t i, newsize; - PyDictKeysObject *oldkeys; - PyObject **oldvalues; - PyDictKeyEntry *ep0; - + Py_ssize_t newsize; /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; newsize <= minused && newsize > 0; @@ -1259,52 +1233,78 @@ dictresize(PyDictObject *mp, Py_ssize_t PyErr_NoMemory(); return -1; } - oldkeys = mp->ma_keys; - oldvalues = mp->ma_values; - /* Allocate a new table. */ + + PyDictKeysObject *oldkeys = mp->ma_keys; + + /* NOTE: Current odict checks mp->ma_keys to detect resize happen. + * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. + * TODO: Try reusing oldkeys when odict is reimplemented. + */ + + // Allocate a new table. mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } - if (oldkeys->dk_lookup == lookdict) + if (oldkeys->dk_lookup == lookdict) { mp->ma_keys->dk_lookup = lookdict; + } + + Py_ssize_t num_oldentries = oldkeys->dk_nentries, num_newentries = 0; + PyDictKeyEntry *oldentries = DK_ENTRIES(oldkeys); + PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys); + + PyObject **oldvalues = mp->ma_values; mp->ma_values = NULL; - ep0 = DK_ENTRIES(oldkeys); - /* Main loop below assumes we can transfer refcount to new keys - * and that value is stored in me_value. - * Increment ref-counts and copy values here to compensate - * This (resizing a split table) should be relatively rare */ + if (oldvalues != NULL) { - for (i = 0; i < oldkeys->dk_nentries; i++) { - if (oldvalues[i] != NULL) { - Py_INCREF(ep0[i].me_key); - ep0[i].me_value = oldvalues[i]; - } + /* Convert split table into new combined table. + * We must incref keys; we can transfer values. + * Note that values of split table is always dense. + */ + for (Py_ssize_t i = 0; i < mp->ma_used; i++) { + assert(oldvalues[i] != NULL); + PyDictKeyEntry *ep = &oldentries[i]; + + Py_INCREF(ep->me_key); + newentries[i].me_key = ep->me_key; + newentries[i].me_hash = ep->me_hash; + newentries[i].me_value = oldvalues[i]; + + insert_index(mp->ma_keys, i, ep->me_hash); } - } - /* Main loop */ - for (i = 0; i < oldkeys->dk_nentries; i++) { - PyDictKeyEntry *ep = &ep0[i]; - if (ep->me_value != NULL) { - insertdict_clean(mp, ep->me_key, ep->me_hash, ep->me_value); - } - } - mp->ma_keys->dk_usable -= mp->ma_used; - if (oldvalues != NULL) { - /* NULL out me_value slot in oldkeys, in case it was shared */ - for (i = 0; i < oldkeys->dk_nentries; i++) - ep0[i].me_value = NULL; + num_newentries = mp->ma_used; + DK_DECREF(oldkeys); if (oldvalues != empty_values) { free_values(oldvalues); } } - else { + else { // combined table. + for (Py_ssize_t i = 0; i < num_oldentries; i++) { + PyDictKeyEntry *ep = &oldentries[i]; + if (ep->me_value != NULL) { + newentries[num_newentries] = *ep; + insert_index(mp->ma_keys, num_newentries, ep->me_hash); + num_newentries++; + } + } + assert(num_newentries == mp->ma_used); assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); - DK_DEBUG_DECREF PyObject_FREE(oldkeys); + + if (oldkeys->dk_size == PyDict_MINSIZE && + numfreekeys < PyDict_MAXFREELIST) { + DK_DEBUG_DECREF keys_free_list[numfreekeys++] = oldkeys; + } + else { + DK_DEBUG_DECREF PyObject_FREE(oldkeys); + } } + + mp->ma_keys->dk_usable -= mp->ma_used; + mp->ma_keys->dk_nentries = num_newentries; return 0; }