@@ -92,7 +92,7 @@ The value ma_fill is the number of non-NULL keys (sum of Active and Dummy);
9292ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
9393values == the number of Active items).
9494To avoid slowing down lookups on a near-full table, we resize the table when
95- it is more than half filled .
95+ it's two-thirds full .
9696*/
9797typedef struct dictobject dictobject ;
9898struct dictobject {
@@ -486,13 +486,15 @@ PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
486486 if (hash == -1 )
487487 return -1 ;
488488 }
489- /* if fill >= 2/3 size, double in size */
490- if (mp -> ma_fill * 3 >= mp -> ma_size * 2 ) {
491- if (dictresize (mp , mp -> ma_used * 2 ) != 0 ) {
492- if (mp -> ma_fill + 1 > mp -> ma_size )
493- return -1 ;
494- }
495- }
489+ /* If fill >= 2/3 size, adjust size. Normally, this doubles the
490+ * size, but it's also possible for the dict to shrink (if ma_fill is
491+ * much larger than ma_used, meaning a lot of dict keys have been
492+ * deleted).
493+ * CAUTION: this resize logic must match the logic in PyDict_Next.
494+ */
495+ if (mp -> ma_fill * 3 >= mp -> ma_size * 2 &&
496+ dictresize (mp , mp -> ma_used * 2 ) != 0 )
497+ return -1 ;
496498 Py_INCREF (value );
497499 Py_INCREF (key );
498500 insertdict (mp , key , hash , value );
@@ -562,6 +564,11 @@ PyDict_Clear(PyObject *op)
562564 PyMem_DEL (table );
563565}
564566
567+ /* CAUTION: In general, it isn't safe to use PyDict_Next in a loop that
568+ * mutates the dict. One exception: it is safe if the loop merely changes
569+ * the values associated with the keys (but doesn't insert new keys or
570+ * delete keys), via PyDict_SetItem().
571+ */
565572int
566573PyDict_Next (PyObject * op , int * ppos , PyObject * * pkey , PyObject * * pvalue )
567574{
@@ -573,6 +580,23 @@ PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
573580 i = * ppos ;
574581 if (i < 0 )
575582 return 0 ;
583+
584+ /* A hack to support loops that merely change values.
585+ * The problem: PyDict_SetItem() can either grow or shrink the dict
586+ * even when passed a key that's already in the dict. This was a
587+ * repeated source of subtle bugs, bad enough to justify a hack here.
588+ * Approach: If this is the first time PyDict_Next() is being called
589+ * (i==0), first figure out whether PyDict_SetItem() *will* change the
590+ * size, and if so get it changed before we start passing out internal
591+ * indices.
592+ */
593+ if (i == 0 ) {
594+ /* This must be a clone of PyDict_SetItem's resize logic. */
595+ if (mp -> ma_fill * 3 >= mp -> ma_size * 2 &&
596+ dictresize (mp , mp -> ma_used * 2 ) != 0 )
597+ return -1 ;
598+ }
599+
576600 while (i < mp -> ma_size && mp -> ma_table [i ].me_value == NULL )
577601 i ++ ;
578602 * ppos = i + 1 ;
0 commit comments