diff --git a/Objects/setobject.c b/Objects/setobject.c --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -48,11 +48,13 @@ /* This must be >= 1 */ #define PERTURB_SHIFT 5 +/* Use this lookkey function when there are possible dummy entries + and you would like to stop the search when you find one. */ + static setentry * -set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash) +set_lookkey_dummy_allowed(PySetObject *so, PyObject *key, Py_hash_t hash) { setentry *table = so->table; - setentry *freeslot = NULL; setentry *entry; size_t perturb = hash; size_t mask = so->mask; @@ -81,38 +83,67 @@ if (cmp < 0) /* unlikely */ return NULL; if (table != so->table || entry->key != startkey) /* unlikely */ - return set_lookkey(so, key, hash); + return set_lookkey_dummy_allowed(so, key, hash); if (cmp > 0) /* likely */ return entry; } - if (entry->key == dummy && freeslot == NULL) - freeslot = entry; + if (entry->key == dummy) + return entry; - for (j = 1 ; j <= LINEAR_PROBES ; j++) { - entry = &table[(i + j) & mask]; - if (entry->key == NULL) - goto found_null; - if (entry->hash == hash) { - PyObject *startkey = entry->key; - assert(startkey != dummy); - if (startkey == key) + if (i + LINEAR_PROBES <= mask) { + for (j = 1 ; j <= LINEAR_PROBES ; j++) { + entry++; + if (entry->key == NULL) return entry; - if (PyUnicode_CheckExact(startkey) - && PyUnicode_CheckExact(key) - && unicode_eq(startkey, key)) - return entry; - Py_INCREF(startkey); - cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); - Py_DECREF(startkey); - if (cmp < 0) - return NULL; - if (table != so->table || entry->key != startkey) - return set_lookkey(so, key, hash); - if (cmp > 0) + if (entry->hash == hash) { + PyObject *startkey = entry->key; + assert(startkey != dummy); + if (startkey == key) + return entry; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return entry; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (table != so->table || entry->key != startkey) + return set_lookkey_dummy_allowed(so, key, hash); + if (cmp > 0) + return entry; + } + if (entry->key == dummy) return entry; } - if (entry->key == dummy && freeslot == NULL) - freeslot = entry; + } else { + for (j = 1 ; j <= LINEAR_PROBES ; j++) { + entry = &table[(i + j) & mask]; + if (entry->key == NULL) + return entry; + if (entry->hash == hash) { + PyObject *startkey = entry->key; + assert(startkey != dummy); + if (startkey == key) + return entry; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return entry; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (table != so->table || entry->key != startkey) + return set_lookkey_dummy_allowed(so, key, hash); + if (cmp > 0) + return entry; + } + if (entry->key == dummy) + return entry; + } } perturb >>= PERTURB_SHIFT; @@ -120,10 +151,109 @@ entry = &table[i]; if (entry->key == NULL) - goto found_null; + return entry; } - found_null: - return freeslot == NULL ? entry : freeslot; +} + +/* Use this lookup function when there are either no dummy objects + or when you want to ignore them (treated as if they were active) */ + +static setentry * +set_lookkey_dummy_ignored(PySetObject *so, PyObject *key, Py_hash_t hash) +{ + setentry *table = so->table; + setentry *entry; + size_t perturb = hash; + size_t mask = so->mask; + size_t i = (size_t)hash & mask; /* Unsigned for defined overflow behavior */ + size_t j; + int cmp; + + entry = &table[i]; + if (entry->key == NULL) + return entry; + + while (1) { + if (entry->hash == hash) { + PyObject *startkey = entry->key; + /* startkey cannot be a dummy because the dummy hash field is -1 */ + assert(startkey != dummy); + if (startkey == key) + return entry; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return entry; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) /* unlikely */ + return NULL; + if (table != so->table || entry->key != startkey) /* unlikely */ + return set_lookkey_dummy_ignored(so, key, hash); + if (cmp > 0) /* likely */ + return entry; + } + + if (i + LINEAR_PROBES <= mask) { + for (j = 1 ; j <= LINEAR_PROBES ; j++) { + entry++; + if (entry->hash == 0 && entry->key == NULL) + return entry; + if (entry->hash == hash) { + PyObject *startkey = entry->key; + assert(startkey != dummy); + if (startkey == key) + return entry; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return entry; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (table != so->table || entry->key != startkey) + return set_lookkey_dummy_ignored(so, key, hash); + if (cmp > 0) + return entry; + } + } + } else { + for (j = 1 ; j <= LINEAR_PROBES ; j++) { + entry = &table[(i + j) & mask]; + if (entry->hash == 0 && entry->key == NULL) + return entry; + if (entry->hash == hash) { + PyObject *startkey = entry->key; + assert(startkey != dummy); + if (startkey == key) + return entry; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return entry; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (table != so->table || entry->key != startkey) + return set_lookkey_dummy_ignored(so, key, hash); + if (cmp > 0) + return entry; + } + } + } + + perturb >>= PERTURB_SHIFT; + i = (i * 5 + 1 + perturb) & mask; + + entry = &table[i]; + if (entry->key == NULL) + return entry; + } } /* @@ -185,7 +315,7 @@ { setentry *entry; - entry = set_lookkey(so, key, hash); + entry = set_lookkey_dummy_allowed(so, key, hash); if (entry == NULL) return -1; if (entry->key == NULL) { @@ -344,10 +474,10 @@ setentry *entry; PyObject *old_key; - entry = set_lookkey(so, oldentry->key, oldentry->hash); + entry = set_lookkey_dummy_ignored(so, oldentry->key, oldentry->hash); if (entry == NULL) return -1; - if (entry->key == NULL || entry->key == dummy) + if (entry->key == NULL) return DISCARD_NOTFOUND; old_key = entry->key; entry->key = dummy; @@ -588,14 +718,12 @@ static int set_contains_entry(PySetObject *so, setentry *entry) { - PyObject *key; setentry *lu_entry; - lu_entry = set_lookkey(so, entry->key, entry->hash); + lu_entry = set_lookkey_dummy_ignored(so, entry->key, entry->hash); if (lu_entry == NULL) return -1; - key = lu_entry->key; - return key != NULL && key != dummy; + return lu_entry->key != NULL; } static int