summaryrefslogtreecommitdiffstats
path: root/Objects/dictobject.c
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-05-23 23:33:57 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-05-23 23:33:57 (GMT)
commit0c6010be75cf537e74dfa1584a19dae2247f903b (patch)
treea57165e9c8aa18b9a11c185f3bd018c9dacd65e8 /Objects/dictobject.c
parenta5ca7dd71ad0b3d05e884c43aacbc37619af9779 (diff)
downloadcpython-0c6010be75cf537e74dfa1584a19dae2247f903b.zip
cpython-0c6010be75cf537e74dfa1584a19dae2247f903b.tar.gz
cpython-0c6010be75cf537e74dfa1584a19dae2247f903b.tar.bz2
Jack Jansen hit a bug in the new dict code, reported on python-dev.
dictresize() was too aggressive about never ever resizing small dicts. If a small dict is entirely full, it needs to rebuild it despite that it won't actually resize it, in order to purge old dummy entries thus creating at least one virgin slot (lookdict assumes at least one such exists). Also took the opportunity to add some high-level comments to dictresize.
Diffstat (limited to 'Objects/dictobject.c')
-rw-r--r--Objects/dictobject.c37
1 files changed, 28 insertions, 9 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 15709cf..11963ec 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -395,14 +395,15 @@ actually be smaller than the old one.
static int
dictresize(dictobject *mp, int minused)
{
- register int newsize, newpoly;
- register dictentry *oldtable = mp->ma_table;
- register dictentry *newtable;
- register dictentry *ep;
- register int i;
+ int newsize, newpoly;
+ dictentry *oldtable, *newtable, *ep;
+ int i;
+ int is_oldtable_malloced;
+ dictentry small_copy[MINSIZE];
assert(minused >= 0);
- assert(oldtable != NULL);
+
+ /* Find the smallest table size > minused, and its poly[] entry. */
newpoly = 0;
newsize = MINSIZE;
for (i = 0; i < sizeof(polys)/sizeof(polys[0]); ++i) {
@@ -419,10 +420,26 @@ dictresize(dictobject *mp, int minused)
PyErr_NoMemory();
return -1;
}
+
+ /* Get space for a new table. */
+ oldtable = mp->ma_table;
+ assert(oldtable != NULL);
+ is_oldtable_malloced = oldtable != mp->ma_smalltable;
+
if (newsize == MINSIZE) {
+ /* Either a large table is shrinking, or we can't get any
+ smaller. */
newtable = mp->ma_smalltable;
- if (newtable == oldtable)
- return 0;
+ if (newtable == oldtable) {
+ if (mp->ma_fill < mp->ma_size)
+ return 0;
+ /* The small table is entirely full. We're not
+ going to resise it, but need to rebuild it
+ anyway to purge old dummy entries. */
+ assert(mp->ma_fill > mp->ma_used); /* a dummy exists */
+ memcpy(small_copy, oldtable, sizeof(small_copy));
+ oldtable = small_copy;
+ }
}
else {
newtable = PyMem_NEW(dictentry, newsize);
@@ -431,6 +448,8 @@ dictresize(dictobject *mp, int minused)
return -1;
}
}
+
+ /* Make the dict empty, using the new table. */
assert(newtable != oldtable);
mp->ma_table = newtable;
mp->ma_size = newsize;
@@ -455,7 +474,7 @@ dictresize(dictobject *mp, int minused)
/* else key == value == NULL: nothing to do */
}
- if (oldtable != mp->ma_smalltable)
+ if (is_oldtable_malloced)
PyMem_DEL(oldtable);
return 0;
}