[Python-checkins] CVS: python/dist/src/Objects dictobject.c,2.73,2.74

Tim Peters tim_one@users.sourceforge.net
Wed, 21 Mar 2001 11:23:58 -0800


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv30665/python/dist/src/Objects

Modified Files:
	dictobject.c 
Log Message:
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".


Index: dictobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/dictobject.c,v
retrieving revision 2.73
retrieving revision 2.74
diff -C2 -r2.73 -r2.74
*** dictobject.c	2001/01/18 00:39:02	2.73
--- dictobject.c	2001/03/21 19:23:56	2.74
***************
*** 93,97 ****
  values == the number of Active items).
  To avoid slowing down lookups on a near-full table, we resize the table when
! it is more than half filled.
  */
  typedef struct dictobject dictobject;
--- 93,97 ----
  values == the number of Active items).
  To avoid slowing down lookups on a near-full table, we resize the table when
! it's two-thirds full.
  */
  typedef struct dictobject dictobject;
***************
*** 487,497 ****
  			return -1;
  	}
! 	/* if fill >= 2/3 size, double in size */
! 	if (mp->ma_fill*3 >= mp->ma_size*2) {
! 		if (dictresize(mp, mp->ma_used*2) != 0) {
! 			if (mp->ma_fill+1 > mp->ma_size)
! 				return -1;
! 		}
! 	}
  	Py_INCREF(value);
  	Py_INCREF(key);
--- 487,499 ----
  			return -1;
  	}
! 	/* If fill >= 2/3 size, adjust size.  Normally, this doubles the
! 	 * size, but it's also possible for the dict to shrink (if ma_fill is
! 	 * much larger than ma_used, meaning a lot of dict keys have been
! 	 * deleted).
! 	 * 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(key);
***************
*** 563,566 ****
--- 565,573 ----
  }
  
+ /* 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
  PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
***************
*** 574,577 ****
--- 581,601 ----
  	if (i < 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)
  		i++;