Skip to content

Commit 0990d55

Browse files
authored
gh-112075: refactor dictionary lookup functions for better re-usability (#114629)
Refactor dict lookup functions to use force inline helpers
1 parent 39d102c commit 0990d55

File tree

1 file changed

+95
-97
lines changed

1 file changed

+95
-97
lines changed

Objects/dictobject.c

+95-97
Original file line numberDiff line numberDiff line change
@@ -874,85 +874,38 @@ lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index)
874874
Py_UNREACHABLE();
875875
}
876876

877-
// Search non-Unicode key from Unicode table
878-
static Py_ssize_t
879-
unicodekeys_lookup_generic(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
877+
static inline Py_ALWAYS_INLINE Py_ssize_t
878+
do_lookup(PyDictObject *mp, PyDictKeysObject *dk, PyObject *key, Py_hash_t hash,
879+
Py_ssize_t (*check_lookup)(PyDictObject *, PyDictKeysObject *, void *, Py_ssize_t ix, PyObject *key, Py_hash_t))
880880
{
881-
PyDictUnicodeEntry *ep0 = DK_UNICODE_ENTRIES(dk);
881+
void *ep0 = _DK_ENTRIES(dk);
882882
size_t mask = DK_MASK(dk);
883883
size_t perturb = hash;
884884
size_t i = (size_t)hash & mask;
885885
Py_ssize_t ix;
886886
for (;;) {
887887
ix = dictkeys_get_index(dk, i);
888888
if (ix >= 0) {
889-
PyDictUnicodeEntry *ep = &ep0[ix];
890-
assert(ep->me_key != NULL);
891-
assert(PyUnicode_CheckExact(ep->me_key));
892-
if (ep->me_key == key) {
889+
Py_ssize_t cmp = check_lookup(mp, dk, ep0, ix, key, hash);
890+
if (cmp < 0) {
891+
return cmp;
892+
} else if (cmp) {
893893
return ix;
894894
}
895-
if (unicode_get_hash(ep->me_key) == hash) {
896-
PyObject *startkey = ep->me_key;
897-
Py_INCREF(startkey);
898-
int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
899-
Py_DECREF(startkey);
900-
if (cmp < 0) {
901-
return DKIX_ERROR;
902-
}
903-
if (dk == mp->ma_keys && ep->me_key == startkey) {
904-
if (cmp > 0) {
905-
return ix;
906-
}
907-
}
908-
else {
909-
/* The dict was mutated, restart */
910-
return DKIX_KEY_CHANGED;
911-
}
912-
}
913895
}
914896
else if (ix == DKIX_EMPTY) {
915897
return DKIX_EMPTY;
916898
}
917899
perturb >>= PERTURB_SHIFT;
918900
i = mask & (i*5 + perturb + 1);
919-
}
920-
Py_UNREACHABLE();
921-
}
922901

923-
// Search Unicode key from Unicode table.
924-
static Py_ssize_t _Py_HOT_FUNCTION
925-
unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
926-
{
927-
PyDictUnicodeEntry *ep0 = DK_UNICODE_ENTRIES(dk);
928-
size_t mask = DK_MASK(dk);
929-
size_t perturb = hash;
930-
size_t i = (size_t)hash & mask;
931-
Py_ssize_t ix;
932-
for (;;) {
933-
ix = dictkeys_get_index(dk, i);
934-
if (ix >= 0) {
935-
PyDictUnicodeEntry *ep = &ep0[ix];
936-
assert(ep->me_key != NULL);
937-
assert(PyUnicode_CheckExact(ep->me_key));
938-
if (ep->me_key == key ||
939-
(unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) {
940-
return ix;
941-
}
942-
}
943-
else if (ix == DKIX_EMPTY) {
944-
return DKIX_EMPTY;
945-
}
946-
perturb >>= PERTURB_SHIFT;
947-
i = mask & (i*5 + perturb + 1);
948902
// Manual loop unrolling
949903
ix = dictkeys_get_index(dk, i);
950904
if (ix >= 0) {
951-
PyDictUnicodeEntry *ep = &ep0[ix];
952-
assert(ep->me_key != NULL);
953-
assert(PyUnicode_CheckExact(ep->me_key));
954-
if (ep->me_key == key ||
955-
(unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) {
905+
Py_ssize_t cmp = check_lookup(mp, dk, ep0, ix, key, hash);
906+
if (cmp < 0) {
907+
return cmp;
908+
} else if (cmp) {
956909
return ix;
957910
}
958911
}
@@ -965,49 +918,94 @@ unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
965918
Py_UNREACHABLE();
966919
}
967920

968-
// Search key from Generic table.
921+
static inline Py_ALWAYS_INLINE Py_ssize_t
922+
compare_unicode_generic(PyDictObject *mp, PyDictKeysObject *dk,
923+
void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
924+
{
925+
PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
926+
assert(ep->me_key != NULL);
927+
assert(PyUnicode_CheckExact(ep->me_key));
928+
assert(!PyUnicode_CheckExact(key));
929+
// TODO: Thread safety
930+
931+
if (unicode_get_hash(ep->me_key) == hash) {
932+
PyObject *startkey = ep->me_key;
933+
Py_INCREF(startkey);
934+
int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
935+
Py_DECREF(startkey);
936+
if (cmp < 0) {
937+
return DKIX_ERROR;
938+
}
939+
if (dk == mp->ma_keys && ep->me_key == startkey) {
940+
return cmp;
941+
}
942+
else {
943+
/* The dict was mutated, restart */
944+
return DKIX_KEY_CHANGED;
945+
}
946+
}
947+
return 0;
948+
}
949+
950+
// Search non-Unicode key from Unicode table
969951
static Py_ssize_t
970-
dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
952+
unicodekeys_lookup_generic(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
971953
{
972-
PyDictKeyEntry *ep0 = DK_ENTRIES(dk);
973-
size_t mask = DK_MASK(dk);
974-
size_t perturb = hash;
975-
size_t i = (size_t)hash & mask;
976-
Py_ssize_t ix;
977-
for (;;) {
978-
ix = dictkeys_get_index(dk, i);
979-
if (ix >= 0) {
980-
PyDictKeyEntry *ep = &ep0[ix];
981-
assert(ep->me_key != NULL);
982-
if (ep->me_key == key) {
983-
return ix;
984-
}
985-
if (ep->me_hash == hash) {
986-
PyObject *startkey = ep->me_key;
987-
Py_INCREF(startkey);
988-
int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
989-
Py_DECREF(startkey);
990-
if (cmp < 0) {
991-
return DKIX_ERROR;
992-
}
993-
if (dk == mp->ma_keys && ep->me_key == startkey) {
994-
if (cmp > 0) {
995-
return ix;
996-
}
997-
}
998-
else {
999-
/* The dict was mutated, restart */
1000-
return DKIX_KEY_CHANGED;
1001-
}
1002-
}
954+
return do_lookup(mp, dk, key, hash, compare_unicode_generic);
955+
}
956+
957+
static inline Py_ALWAYS_INLINE Py_ssize_t
958+
compare_unicode_unicode(PyDictObject *mp, PyDictKeysObject *dk,
959+
void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
960+
{
961+
PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
962+
assert(ep->me_key != NULL);
963+
assert(PyUnicode_CheckExact(ep->me_key));
964+
if (ep->me_key == key ||
965+
(unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) {
966+
return 1;
967+
}
968+
return 0;
969+
}
970+
971+
static Py_ssize_t _Py_HOT_FUNCTION
972+
unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
973+
{
974+
return do_lookup(NULL, dk, key, hash, compare_unicode_unicode);
975+
}
976+
977+
static inline Py_ALWAYS_INLINE Py_ssize_t
978+
compare_generic(PyDictObject *mp, PyDictKeysObject *dk,
979+
void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
980+
{
981+
PyDictKeyEntry *ep = &((PyDictKeyEntry *)ep0)[ix];
982+
assert(ep->me_key != NULL);
983+
if (ep->me_key == key) {
984+
return 1;
985+
}
986+
if (ep->me_hash == hash) {
987+
PyObject *startkey = ep->me_key;
988+
Py_INCREF(startkey);
989+
int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
990+
Py_DECREF(startkey);
991+
if (cmp < 0) {
992+
return DKIX_ERROR;
1003993
}
1004-
else if (ix == DKIX_EMPTY) {
1005-
return DKIX_EMPTY;
994+
if (dk == mp->ma_keys && ep->me_key == startkey) {
995+
return cmp;
996+
}
997+
else {
998+
/* The dict was mutated, restart */
999+
return DKIX_KEY_CHANGED;
10061000
}
1007-
perturb >>= PERTURB_SHIFT;
1008-
i = mask & (i*5 + perturb + 1);
10091001
}
1010-
Py_UNREACHABLE();
1002+
return 0;
1003+
}
1004+
1005+
static Py_ssize_t
1006+
dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
1007+
{
1008+
return do_lookup(mp, dk, key, hash, compare_generic);
10111009
}
10121010

10131011
/* Lookup a string in a (all unicode) dict keys.

0 commit comments

Comments
 (0)