Armin committed his patch while I was reviewing it (I'm sure

he didn't know this), so merged in some changes I made during
review.  Nothing material apart from changing a new `mask` local
from int to Py_ssize_t.  Mostly this is repairing comments that
were made incorrect, and adding new comments.  Also a few
minor code rewrites for clarity or helpful succinctness.
This commit is contained in:
Tim Peters 2006-06-01 15:50:44 +00:00
parent 80a18f0f9c
commit d770ebd286
1 changed files with 40 additions and 28 deletions

View File

@ -227,24 +227,28 @@ All arithmetic on hash should ignore overflow.
contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and
Christian Tismer). Christian Tismer).
This function must never return NULL; failures are indicated by returning lookdict() is general-purpose, and may return NULL if (and only if) a
a dictentry* for which the me_value field is NULL. Exceptions are never comparison raises an exception (this was new in Python 2.5).
reported by this function, and outstanding exceptions are maintained. lookdict_string() below is specialized to string keys, comparison of which can
never raise an exception; that function can never return NULL. For both, when
the key isn't found a dictentry* is returned for which the me_value field is
NULL; this is the slot in the dict at which the key would have been found, and
the caller can (if it wishes) add the <key, value> pair to the returned
dictentry*.
*/ */
static dictentry * static dictentry *
lookdict(dictobject *mp, PyObject *key, register long hash) lookdict(dictobject *mp, PyObject *key, register long hash)
{ {
register Py_ssize_t i; register size_t i;
register size_t perturb; register size_t perturb;
register dictentry *freeslot; register dictentry *freeslot;
register Py_ssize_t mask = mp->ma_mask; register size_t mask = (size_t)mp->ma_mask;
dictentry *ep0 = mp->ma_table; dictentry *ep0 = mp->ma_table;
register dictentry *ep; register dictentry *ep;
register int cmp; register int cmp;
PyObject *startkey; PyObject *startkey;
i = hash & mask; i = (size_t)hash & mask;
ep = &ep0[i]; ep = &ep0[i];
if (ep->me_key == NULL || ep->me_key == key) if (ep->me_key == NULL || ep->me_key == key)
return ep; return ep;
@ -307,21 +311,20 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
/* /*
* Hacked up version of lookdict which can assume keys are always strings; * Hacked up version of lookdict which can assume keys are always strings;
* this assumption allows testing for errors during PyObject_Compare() to * this assumption allows testing for errors during PyObject_RichCompareBool()
* be dropped; string-string comparisons never raise exceptions. This also * to be dropped; string-string comparisons never raise exceptions. This also
* means we don't need to go through PyObject_Compare(); we can always use * means we don't need to go through PyObject_RichCompareBool(); we can always
* _PyString_Eq directly. * use _PyString_Eq() directly.
* *
* This is valuable because the general-case error handling in lookdict() is * This is valuable because dicts with only string keys are very common.
* expensive, and dicts with pure-string keys are very common.
*/ */
static dictentry * static dictentry *
lookdict_string(dictobject *mp, PyObject *key, register long hash) lookdict_string(dictobject *mp, PyObject *key, register long hash)
{ {
register Py_ssize_t i; register size_t i;
register size_t perturb; register size_t perturb;
register dictentry *freeslot; register dictentry *freeslot;
register Py_ssize_t mask = mp->ma_mask; register size_t mask = (size_t)mp->ma_mask;
dictentry *ep0 = mp->ma_table; dictentry *ep0 = mp->ma_table;
register dictentry *ep; register dictentry *ep;
@ -343,10 +346,8 @@ lookdict_string(dictobject *mp, PyObject *key, register long hash)
if (ep->me_key == dummy) if (ep->me_key == dummy)
freeslot = ep; freeslot = ep;
else { else {
if (ep->me_hash == hash if (ep->me_hash == hash && _PyString_Eq(ep->me_key, key))
&& _PyString_Eq(ep->me_key, key)) {
return ep; return ep;
}
freeslot = NULL; freeslot = NULL;
} }
@ -371,6 +372,7 @@ lookdict_string(dictobject *mp, PyObject *key, register long hash)
Internal routine to insert a new item into the table. Internal routine to insert a new item into the table.
Used both by the internal resize routine and by the public insert routine. Used both by the internal resize routine and by the public insert routine.
Eats a reference to key and one to value. Eats a reference to key and one to value.
Returns -1 if an error occurred, or 0 on success.
*/ */
static int static int
insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value) insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value)
@ -412,14 +414,16 @@ Internal routine used by dictresize() to insert an item which is
known to be absent from the dict. This routine also assumes that known to be absent from the dict. This routine also assumes that
the dict contains no deleted entries. Besides the performance benefit, the dict contains no deleted entries. Besides the performance benefit,
using insertdict() in dictresize() is dangerous (SF bug #1456209). 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`.
*/ */
static void static void
insertdict_clean(register dictobject *mp, PyObject *key, long hash, insertdict_clean(register dictobject *mp, PyObject *key, long hash,
PyObject *value) PyObject *value)
{ {
register Py_ssize_t i; register size_t i;
register size_t perturb; register size_t perturb;
register unsigned int mask = mp->ma_mask; register size_t mask = (size_t)mp->ma_mask;
dictentry *ep0 = mp->ma_table; dictentry *ep0 = mp->ma_table;
register dictentry *ep; register dictentry *ep;
@ -429,6 +433,7 @@ insertdict_clean(register dictobject *mp, PyObject *key, long hash,
i = (i << 2) + i + perturb + 1; i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask]; ep = &ep0[i & mask];
} }
assert(ep->me_value == NULL);
mp->ma_fill++; mp->ma_fill++;
ep->me_key = key; ep->me_key = key;
ep->me_hash = (Py_ssize_t)hash; ep->me_hash = (Py_ssize_t)hash;
@ -524,6 +529,16 @@ dictresize(dictobject *mp, Py_ssize_t minused)
return 0; return 0;
} }
/* Note that, for historical reasons, PyDict_GetItem() suppresses all errors
* that may occur (originally dicts supported only string keys, and exceptions
* weren't possible). So, while the original intent was that a NULL return
* meant the key wasn't present, it reality it can mean that, or that an error
* (suppressed) occurred while computing the key's hash, or that some error
* (suppressed) occurred when comparing keys in the dict's internal probe
* sequence. A nasty example of the latter is when a Python-coded comparison
* function hits a stack-depth error, which can cause this to return NULL
* even if the key is present.
*/
PyObject * PyObject *
PyDict_GetItem(PyObject *op, PyObject *key) PyDict_GetItem(PyObject *op, PyObject *key)
{ {
@ -531,9 +546,8 @@ PyDict_GetItem(PyObject *op, PyObject *key)
dictobject *mp = (dictobject *)op; dictobject *mp = (dictobject *)op;
dictentry *ep; dictentry *ep;
PyThreadState *tstate; PyThreadState *tstate;
if (!PyDict_Check(op)) { if (!PyDict_Check(op))
return NULL; return NULL;
}
if (!PyString_CheckExact(key) || if (!PyString_CheckExact(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1) (hash = ((PyStringObject *) key)->ob_shash) == -1)
{ {
@ -1607,8 +1621,8 @@ static PyObject *
dict_has_key(register dictobject *mp, PyObject *key) dict_has_key(register dictobject *mp, PyObject *key)
{ {
long hash; long hash;
register long ok;
dictentry *ep; dictentry *ep;
if (!PyString_CheckExact(key) || if (!PyString_CheckExact(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1) { (hash = ((PyStringObject *) key)->ob_shash) == -1) {
hash = PyObject_Hash(key); hash = PyObject_Hash(key);
@ -1618,8 +1632,7 @@ dict_has_key(register dictobject *mp, PyObject *key)
ep = (mp->ma_lookup)(mp, key, hash); ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL) if (ep == NULL)
return NULL; return NULL;
ok = ep->me_value != NULL; return PyBool_FromLong(ep->me_value != NULL);
return PyBool_FromLong(ok);
} }
static PyObject * static PyObject *
@ -1932,6 +1945,7 @@ static PyMethodDef mapp_methods[] = {
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
int int
PyDict_Contains(PyObject *op, PyObject *key) PyDict_Contains(PyObject *op, PyObject *key)
{ {
@ -1946,9 +1960,7 @@ PyDict_Contains(PyObject *op, PyObject *key)
return -1; return -1;
} }
ep = (mp->ma_lookup)(mp, key, hash); ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL) return ep == NULL ? -1 : (ep->me_value != NULL);
return -1;
return ep->me_value != NULL;
} }
/* Hack to implement "key in dict" */ /* Hack to implement "key in dict" */