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:
parent
80a18f0f9c
commit
d770ebd286
|
@ -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" */
|
||||||
|
|
Loading…
Reference in New Issue