summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2016-10-31 18:14:05 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2016-10-31 18:14:05 (GMT)
commit7f0514ad54dd806817ce6d1f54969b8979475d34 (patch)
tree33d292a94ea903a97a3879c80940bd16fdde265b /Objects
parentf9cb5593e382b0781471668624dab36dd5aafea9 (diff)
downloadcpython-7f0514ad54dd806817ce6d1f54969b8979475d34.zip
cpython-7f0514ad54dd806817ce6d1f54969b8979475d34.tar.gz
cpython-7f0514ad54dd806817ce6d1f54969b8979475d34.tar.bz2
Backed out changeset 6b88dfc7b25d
Diffstat (limited to 'Objects')
-rw-r--r--Objects/dictobject.c123
1 files changed, 60 insertions, 63 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 0806e47..62ca484 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -1195,21 +1195,41 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
}
/*
-Internal routine used by dictresize() to buid a hashtable of entries.
+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
*/
static void
-build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n)
+insertdict_clean(PyDictObject *mp, PyObject *key, Py_hash_t hash,
+ PyObject *value)
{
- size_t mask = (size_t)DK_SIZE(keys) - 1;
- for (Py_ssize_t ix = 0; ix != n; ix++, ep++) {
- Py_hash_t hash = ep->me_hash;
- size_t i = hash & mask;
- for (size_t perturb = hash; dk_get_index(keys, i) != DKIX_EMPTY;) {
- perturb >>= PERTURB_SHIFT;
- i = mask & ((i << 2) + i + perturb + 1);
- }
- dk_set_index(keys, i, ix);
+ 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;
+ 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;
}
/*
@@ -1225,10 +1245,10 @@ but can be resplit by make_keys_shared().
static int
dictresize(PyDictObject *mp, Py_ssize_t minused)
{
- Py_ssize_t newsize, numentries;
+ Py_ssize_t i, newsize;
PyDictKeysObject *oldkeys;
PyObject **oldvalues;
- PyDictKeyEntry *oldentries, *newentries;
+ PyDictKeyEntry *ep0;
/* Find the smallest table size > minused. */
for (newsize = PyDict_MINSIZE;
@@ -1239,14 +1259,8 @@ dictresize(PyDictObject *mp, Py_ssize_t minused)
PyErr_NoMemory();
return -1;
}
-
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 reimplement odict.
- */
-
+ oldvalues = mp->ma_values;
/* Allocate a new table. */
mp->ma_keys = new_keys_object(newsize);
if (mp->ma_keys == NULL) {
@@ -1255,59 +1269,42 @@ dictresize(PyDictObject *mp, Py_ssize_t minused)
}
if (oldkeys->dk_lookup == lookdict)
mp->ma_keys->dk_lookup = lookdict;
-
- numentries = mp->ma_used;
- oldentries = DK_ENTRIES(oldkeys);
- newentries = DK_ENTRIES(mp->ma_keys);
- 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) {
- /* 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 < numentries; i++) {
- assert(oldvalues[i] != NULL);
- PyDictKeyEntry *ep = &oldentries[i];
- PyObject *key = ep->me_key;
- Py_INCREF(key);
- newentries[i].me_key = key;
- newentries[i].me_hash = ep->me_hash;
- newentries[i].me_value = oldvalues[i];
+ for (i = 0; i < oldkeys->dk_nentries; i++) {
+ if (oldvalues[i] != NULL) {
+ Py_INCREF(ep0[i].me_key);
+ ep0[i].me_value = oldvalues[i];
+ }
}
-
+ }
+ /* 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;
DK_DECREF(oldkeys);
- mp->ma_values = NULL;
if (oldvalues != empty_values) {
free_values(oldvalues);
}
}
- else { // combined table.
- if (oldkeys->dk_nentries == numentries) {
- memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry));
- }
- else {
- PyDictKeyEntry *ep = oldentries;
- for (Py_ssize_t i = 0; i < numentries; i++) {
- while (ep->me_value == NULL)
- ep++;
- newentries[i] = *ep++;
- }
- }
-
+ else {
assert(oldkeys->dk_lookup != lookdict_split);
assert(oldkeys->dk_refcnt == 1);
- if (oldkeys->dk_size == PyDict_MINSIZE &&
- numfreekeys < PyDict_MAXFREELIST) {
- DK_DEBUG_DECREF keys_free_list[numfreekeys++] = oldkeys;
- }
- else {
- DK_DEBUG_DECREF PyObject_FREE(oldkeys);
- }
+ DK_DEBUG_DECREF PyObject_FREE(oldkeys);
}
-
- build_indices(mp->ma_keys, newentries, numentries);
- mp->ma_keys->dk_usable -= numentries;
- mp->ma_keys->dk_nentries = numentries;
return 0;
}