@@ -68,6 +68,9 @@ static PyObject _dummy_struct;
6868#define SET_LOOKKEY_CHANGED (-2)
6969#define SET_LOOKKEY_EMPTY (-3)
7070
71+ typedef int (* compare_func )(PySetObject * so , setentry * table , setentry * ep ,
72+ PyObject * key , Py_hash_t hash );
73+
7174#ifdef Py_GIL_DISABLED
7275
7376#define SET_IS_SHARED (so ) _PyObject_GC_IS_SHARED(so)
@@ -159,6 +162,36 @@ set_compare_entry(PySetObject *so, setentry *table, setentry *entry,
159162 return SET_LOOKKEY_NO_MATCH ;
160163}
161164
165+ // This is similar to set_compare_entry() but we don't need to incref startkey
166+ // before comparing and we don't need to check if the set has changed.
167+ static inline Py_ALWAYS_INLINE int
168+ set_compare_frozenset (PySetObject * so , setentry * table , setentry * ep ,
169+ PyObject * key , Py_hash_t hash )
170+ {
171+ assert (PyFrozenSet_CheckExact (so ));
172+ PyObject * startkey = ep -> key ;
173+ if (startkey == NULL ) {
174+ return SET_LOOKKEY_EMPTY ;
175+ }
176+ if (startkey == key ) {
177+ return SET_LOOKKEY_FOUND ;
178+ }
179+ Py_ssize_t ep_hash = ep -> hash ;
180+ if (ep_hash == hash ) {
181+ if (PyUnicode_CheckExact (startkey )
182+ && PyUnicode_CheckExact (key )
183+ && unicode_eq (startkey , key ))
184+ return SET_LOOKKEY_FOUND ;
185+ int cmp = PyObject_RichCompareBool (startkey , key , Py_EQ );
186+ if (cmp < 0 ) {
187+ return SET_LOOKKEY_ERROR ;
188+ }
189+ assert (cmp == SET_LOOKKEY_FOUND || cmp == SET_LOOKKEY_NO_MATCH );
190+ return cmp ;
191+ }
192+ return SET_LOOKKEY_NO_MATCH ;
193+ }
194+
162195static void
163196set_zero_table (setentry * table , size_t size )
164197{
@@ -186,9 +219,7 @@ set_zero_table(setentry *table, size_t size)
186219
187220static int
188221set_do_lookup (PySetObject * so , setentry * table , size_t mask , PyObject * key ,
189- Py_hash_t hash , setentry * * epp ,
190- int (* compare_entry )(PySetObject * so , setentry * table , setentry * ep ,
191- PyObject * key , Py_hash_t hash ))
222+ Py_hash_t hash , setentry * * epp , compare_func compare_entry )
192223{
193224 setentry * entry ;
194225 size_t perturb = hash ;
@@ -363,11 +394,18 @@ static int
363394set_lookkey (PySetObject * so , PyObject * key , Py_hash_t hash , setentry * * epp )
364395{
365396 int status ;
366- Py_BEGIN_CRITICAL_SECTION (so );
367- do {
368- status = set_do_lookup (so , so -> table , so -> mask , key , hash , epp , set_compare_entry );
369- } while (status == SET_LOOKKEY_CHANGED );
370- Py_END_CRITICAL_SECTION ();
397+ if (PyFrozenSet_CheckExact (so )) {
398+ status = set_do_lookup (so , so -> table , so -> mask , key , hash , epp ,
399+ set_compare_frozenset );
400+ }
401+ else {
402+ Py_BEGIN_CRITICAL_SECTION (so );
403+ do {
404+ status = set_do_lookup (so , so -> table , so -> mask , key , hash , epp ,
405+ set_compare_entry );
406+ } while (status == SET_LOOKKEY_CHANGED );
407+ Py_END_CRITICAL_SECTION ();
408+ }
371409 assert (status == SET_LOOKKEY_FOUND ||
372410 status == SET_LOOKKEY_NO_MATCH ||
373411 status == SET_LOOKKEY_ERROR );
@@ -380,13 +418,22 @@ set_lookkey_threadsafe(PySetObject *so, PyObject *key, Py_hash_t hash)
380418{
381419 int status ;
382420 setentry * entry ;
421+ if (PyFrozenSet_CheckExact (so )) {
422+ status = set_do_lookup (so , so -> table , so -> mask , key , hash , & entry ,
423+ set_compare_frozenset );
424+ assert (status == SET_LOOKKEY_FOUND ||
425+ status == SET_LOOKKEY_NO_MATCH ||
426+ status == SET_LOOKKEY_ERROR );
427+ return status ;
428+ }
383429 ensure_shared_on_read (so );
384430 setentry * table = FT_ATOMIC_LOAD_PTR_ACQUIRE (so -> table );
385431 size_t mask = FT_ATOMIC_LOAD_SSIZE_RELAXED (so -> mask );
386432 if (table == NULL || table != FT_ATOMIC_LOAD_PTR_ACQUIRE (so -> table )) {
387433 return set_lookkey (so , key , hash , & entry );
388434 }
389- status = set_do_lookup (so , table , mask , key , hash , & entry , set_compare_threadsafe );
435+ status = set_do_lookup (so , table , mask , key , hash , & entry ,
436+ set_compare_threadsafe );
390437 if (status == SET_LOOKKEY_CHANGED ) {
391438 return set_lookkey (so , key , hash , & entry );
392439 }
0 commit comments