mirror of https://github.com/python/cpython
Make PyDict_Next safe to use for loops that merely modify the values
associated with existing dict keys. This is a variant of part of Michael Hudson's patch #409864 "lazy fix for Pings bizarre scoping crash".
This commit is contained in:
parent
66b0e9c2a7
commit
6783070ebf
|
@ -92,7 +92,7 @@ The value ma_fill is the number of non-NULL keys (sum of Active and Dummy);
|
||||||
ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
|
ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
|
||||||
values == the number of Active items).
|
values == the number of Active items).
|
||||||
To avoid slowing down lookups on a near-full table, we resize the table when
|
To avoid slowing down lookups on a near-full table, we resize the table when
|
||||||
it is more than half filled.
|
it's two-thirds full.
|
||||||
*/
|
*/
|
||||||
typedef struct dictobject dictobject;
|
typedef struct dictobject dictobject;
|
||||||
struct dictobject {
|
struct dictobject {
|
||||||
|
@ -486,13 +486,15 @@ PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
|
||||||
if (hash == -1)
|
if (hash == -1)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* if fill >= 2/3 size, double in size */
|
/* If fill >= 2/3 size, adjust size. Normally, this doubles the
|
||||||
if (mp->ma_fill*3 >= mp->ma_size*2) {
|
* size, but it's also possible for the dict to shrink (if ma_fill is
|
||||||
if (dictresize(mp, mp->ma_used*2) != 0) {
|
* much larger than ma_used, meaning a lot of dict keys have been
|
||||||
if (mp->ma_fill+1 > mp->ma_size)
|
* deleted).
|
||||||
return -1;
|
* CAUTION: this resize logic must match the logic in PyDict_Next.
|
||||||
}
|
*/
|
||||||
}
|
if (mp->ma_fill*3 >= mp->ma_size*2 &&
|
||||||
|
dictresize(mp, mp->ma_used*2) != 0)
|
||||||
|
return -1;
|
||||||
Py_INCREF(value);
|
Py_INCREF(value);
|
||||||
Py_INCREF(key);
|
Py_INCREF(key);
|
||||||
insertdict(mp, key, hash, value);
|
insertdict(mp, key, hash, value);
|
||||||
|
@ -562,6 +564,11 @@ PyDict_Clear(PyObject *op)
|
||||||
PyMem_DEL(table);
|
PyMem_DEL(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CAUTION: In general, it isn't safe to use PyDict_Next in a loop that
|
||||||
|
* mutates the dict. One exception: it is safe if the loop merely changes
|
||||||
|
* the values associated with the keys (but doesn't insert new keys or
|
||||||
|
* delete keys), via PyDict_SetItem().
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
|
PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
|
||||||
{
|
{
|
||||||
|
@ -573,6 +580,23 @@ PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
|
||||||
i = *ppos;
|
i = *ppos;
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* A hack to support loops that merely change values.
|
||||||
|
* The problem: PyDict_SetItem() can either grow or shrink the dict
|
||||||
|
* even when passed a key that's already in the dict. This was a
|
||||||
|
* repeated source of subtle bugs, bad enough to justify a hack here.
|
||||||
|
* Approach: If this is the first time PyDict_Next() is being called
|
||||||
|
* (i==0), first figure out whether PyDict_SetItem() *will* change the
|
||||||
|
* size, and if so get it changed before we start passing out internal
|
||||||
|
* indices.
|
||||||
|
*/
|
||||||
|
if (i == 0) {
|
||||||
|
/* This must be a clone of PyDict_SetItem's resize logic. */
|
||||||
|
if (mp->ma_fill*3 >= mp->ma_size*2 &&
|
||||||
|
dictresize(mp, mp->ma_used*2) != 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
while (i < mp->ma_size && mp->ma_table[i].me_value == NULL)
|
while (i < mp->ma_size && mp->ma_table[i].me_value == NULL)
|
||||||
i++;
|
i++;
|
||||||
*ppos = i+1;
|
*ppos = i+1;
|
||||||
|
|
Loading…
Reference in New Issue