mirror of
				https://github.com/python/cpython.git
				synced 2025-10-25 10:44:55 +00:00 
			
		
		
		
	 723d4d2fe8
			
		
	
	
		723d4d2fe8
		
			
		
	
	
	
	
		
			
			We already intern and immortalize most string constants. In the free-threaded build, other constants can be a source of reference count contention because they are shared by all threads running the same code objects.
		
			
				
	
	
		
			2769 lines
		
	
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2769 lines
		
	
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| /* set object implementation
 | |
| 
 | |
|    Written and maintained by Raymond D. Hettinger <python@rcn.com>
 | |
|    Derived from Objects/dictobject.c.
 | |
| 
 | |
|    The basic lookup function used by all operations.
 | |
|    This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
 | |
| 
 | |
|    The initial probe index is computed as hash mod the table size.
 | |
|    Subsequent probe indices are computed as explained in Objects/dictobject.c.
 | |
| 
 | |
|    To improve cache locality, each probe inspects a series of consecutive
 | |
|    nearby entries before moving on to probes elsewhere in memory.  This leaves
 | |
|    us with a hybrid of linear probing and randomized probing.  The linear probing
 | |
|    reduces the cost of hash collisions because consecutive memory accesses
 | |
|    tend to be much cheaper than scattered probes.  After LINEAR_PROBES steps,
 | |
|    we then use more of the upper bits from the hash value and apply a simple
 | |
|    linear congruential random number generator.  This helps break-up long
 | |
|    chains of collisions.
 | |
| 
 | |
|    All arithmetic on hash should ignore overflow.
 | |
| 
 | |
|    Unlike the dictionary implementation, the lookkey function can return
 | |
|    NULL if the rich comparison returns an error.
 | |
| 
 | |
|    Use cases for sets differ considerably from dictionaries where looked-up
 | |
|    keys are more likely to be present.  In contrast, sets are primarily
 | |
|    about membership testing where the presence of an element is not known in
 | |
|    advance.  Accordingly, the set implementation needs to optimize for both
 | |
|    the found and not-found case.
 | |
| */
 | |
| 
 | |
| #include "Python.h"
 | |
| #include "pycore_ceval.h"               // _PyEval_GetBuiltin()
 | |
| #include "pycore_critical_section.h"    // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
 | |
| #include "pycore_dict.h"                // _PyDict_Contains_KnownHash()
 | |
| #include "pycore_modsupport.h"          // _PyArg_NoKwnames()
 | |
| #include "pycore_object.h"              // _PyObject_GC_UNTRACK()
 | |
| #include "pycore_pyatomic_ft_wrappers.h"  // FT_ATOMIC_LOAD_SSIZE_RELAXED()
 | |
| #include "pycore_pyerrors.h"            // _PyErr_SetKeyError()
 | |
| #include "pycore_setobject.h"           // _PySet_NextEntry() definition
 | |
| #include <stddef.h>                     // offsetof()
 | |
| #include "clinic/setobject.c.h"
 | |
| 
 | |
| /*[clinic input]
 | |
| class set "PySetObject *" "&PySet_Type"
 | |
| class frozenset "PySetObject *" "&PyFrozenSet_Type"
 | |
| [clinic start generated code]*/
 | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=97ad1d3e9f117079]*/
 | |
| 
 | |
| /*[python input]
 | |
| class setobject_converter(self_converter):
 | |
|     type = "PySetObject *"
 | |
| [python start generated code]*/
 | |
| /*[python end generated code: output=da39a3ee5e6b4b0d input=33a44506d4d57793]*/
 | |
| 
 | |
| /* Object used as dummy key to fill deleted entries */
 | |
| static PyObject _dummy_struct;
 | |
| 
 | |
| #define dummy (&_dummy_struct)
 | |
| 
 | |
| 
 | |
| /* ======================================================================== */
 | |
| /* ======= Begin logic for probing the hash table ========================= */
 | |
| 
 | |
| /* Set this to zero to turn-off linear probing */
 | |
| #ifndef LINEAR_PROBES
 | |
| #define LINEAR_PROBES 9
 | |
| #endif
 | |
| 
 | |
| /* This must be >= 1 */
 | |
| #define PERTURB_SHIFT 5
 | |
| 
 | |
| static setentry *
 | |
| set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     setentry *table;
 | |
|     setentry *entry;
 | |
|     size_t perturb = hash;
 | |
|     size_t mask = so->mask;
 | |
|     size_t i = (size_t)hash & mask; /* Unsigned for defined overflow behavior */
 | |
|     int probes;
 | |
|     int cmp;
 | |
| 
 | |
|     while (1) {
 | |
|         entry = &so->table[i];
 | |
|         probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0;
 | |
|         do {
 | |
|             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)
 | |
|                     && _PyUnicode_EQ(startkey, key))
 | |
|                     return entry;
 | |
|                 table = so->table;
 | |
|                 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)
 | |
|                     return entry;
 | |
|                 mask = so->mask;
 | |
|             }
 | |
|             entry++;
 | |
|         } while (probes--);
 | |
|         perturb >>= PERTURB_SHIFT;
 | |
|         i = (i * 5 + 1 + perturb) & mask;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int set_table_resize(PySetObject *, Py_ssize_t);
 | |
| 
 | |
| static int
 | |
| set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     setentry *table;
 | |
|     setentry *freeslot;
 | |
|     setentry *entry;
 | |
|     size_t perturb;
 | |
|     size_t mask;
 | |
|     size_t i;                       /* Unsigned for defined overflow behavior */
 | |
|     int probes;
 | |
|     int cmp;
 | |
| 
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
| 
 | |
|     /* Pre-increment is necessary to prevent arbitrary code in the rich
 | |
|        comparison from deallocating the key just before the insertion. */
 | |
|     Py_INCREF(key);
 | |
| 
 | |
|   restart:
 | |
| 
 | |
|     mask = so->mask;
 | |
|     i = (size_t)hash & mask;
 | |
|     freeslot = NULL;
 | |
|     perturb = hash;
 | |
| 
 | |
|     while (1) {
 | |
|         entry = &so->table[i];
 | |
|         probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0;
 | |
|         do {
 | |
|             if (entry->hash == 0 && entry->key == NULL)
 | |
|                 goto found_unused_or_dummy;
 | |
|             if (entry->hash == hash) {
 | |
|                 PyObject *startkey = entry->key;
 | |
|                 assert(startkey != dummy);
 | |
|                 if (startkey == key)
 | |
|                     goto found_active;
 | |
|                 if (PyUnicode_CheckExact(startkey)
 | |
|                     && PyUnicode_CheckExact(key)
 | |
|                     && _PyUnicode_EQ(startkey, key))
 | |
|                     goto found_active;
 | |
|                 table = so->table;
 | |
|                 Py_INCREF(startkey);
 | |
|                 cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
 | |
|                 Py_DECREF(startkey);
 | |
|                 if (cmp > 0)
 | |
|                     goto found_active;
 | |
|                 if (cmp < 0)
 | |
|                     goto comparison_error;
 | |
|                 if (table != so->table || entry->key != startkey)
 | |
|                     goto restart;
 | |
|                 mask = so->mask;
 | |
|             }
 | |
|             else if (entry->hash == -1) {
 | |
|                 assert (entry->key == dummy);
 | |
|                 freeslot = entry;
 | |
|             }
 | |
|             entry++;
 | |
|         } while (probes--);
 | |
|         perturb >>= PERTURB_SHIFT;
 | |
|         i = (i * 5 + 1 + perturb) & mask;
 | |
|     }
 | |
| 
 | |
|   found_unused_or_dummy:
 | |
|     if (freeslot == NULL)
 | |
|         goto found_unused;
 | |
|     so->used++;
 | |
|     freeslot->key = key;
 | |
|     freeslot->hash = hash;
 | |
|     return 0;
 | |
| 
 | |
|   found_unused:
 | |
|     so->fill++;
 | |
|     so->used++;
 | |
|     entry->key = key;
 | |
|     entry->hash = hash;
 | |
|     if ((size_t)so->fill*5 < mask*3)
 | |
|         return 0;
 | |
|     return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
 | |
| 
 | |
|   found_active:
 | |
|     Py_DECREF(key);
 | |
|     return 0;
 | |
| 
 | |
|   comparison_error:
 | |
|     Py_DECREF(key);
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Internal routine used by set_table_resize() to insert an item which is
 | |
| known to be absent from the set.  Besides the performance benefit,
 | |
| there is also safety benefit since using set_add_entry() risks making
 | |
| a callback in the middle of a set_table_resize(), see issue 1456209.
 | |
| The caller is responsible for updating the key's reference count and
 | |
| the setobject's fill and used fields.
 | |
| */
 | |
| static void
 | |
| set_insert_clean(setentry *table, size_t mask, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     setentry *entry;
 | |
|     size_t perturb = hash;
 | |
|     size_t i = (size_t)hash & mask;
 | |
|     size_t j;
 | |
| 
 | |
|     while (1) {
 | |
|         entry = &table[i];
 | |
|         if (entry->key == NULL)
 | |
|             goto found_null;
 | |
|         if (i + LINEAR_PROBES <= mask) {
 | |
|             for (j = 0; j < LINEAR_PROBES; j++) {
 | |
|                 entry++;
 | |
|                 if (entry->key == NULL)
 | |
|                     goto found_null;
 | |
|             }
 | |
|         }
 | |
|         perturb >>= PERTURB_SHIFT;
 | |
|         i = (i * 5 + 1 + perturb) & mask;
 | |
|     }
 | |
|   found_null:
 | |
|     entry->key = key;
 | |
|     entry->hash = hash;
 | |
| }
 | |
| 
 | |
| /* ======== End logic for probing the hash table ========================== */
 | |
| /* ======================================================================== */
 | |
| 
 | |
| /*
 | |
| Restructure the table by allocating a new table and reinserting all
 | |
| keys again.  When entries have been deleted, the new table may
 | |
| actually be smaller than the old one.
 | |
| */
 | |
| static int
 | |
| set_table_resize(PySetObject *so, Py_ssize_t minused)
 | |
| {
 | |
|     setentry *oldtable, *newtable, *entry;
 | |
|     Py_ssize_t oldmask = so->mask;
 | |
|     size_t newmask;
 | |
|     int is_oldtable_malloced;
 | |
|     setentry small_copy[PySet_MINSIZE];
 | |
| 
 | |
|     assert(minused >= 0);
 | |
| 
 | |
|     /* Find the smallest table size > minused. */
 | |
|     /* XXX speed-up with intrinsics */
 | |
|     size_t newsize = PySet_MINSIZE;
 | |
|     while (newsize <= (size_t)minused) {
 | |
|         newsize <<= 1; // The largest possible value is PY_SSIZE_T_MAX + 1.
 | |
|     }
 | |
| 
 | |
|     /* Get space for a new table. */
 | |
|     oldtable = so->table;
 | |
|     assert(oldtable != NULL);
 | |
|     is_oldtable_malloced = oldtable != so->smalltable;
 | |
| 
 | |
|     if (newsize == PySet_MINSIZE) {
 | |
|         /* A large table is shrinking, or we can't get any smaller. */
 | |
|         newtable = so->smalltable;
 | |
|         if (newtable == oldtable) {
 | |
|             if (so->fill == so->used) {
 | |
|                 /* No dummies, so no point doing anything. */
 | |
|                 return 0;
 | |
|             }
 | |
|             /* We're not going to resize it, but rebuild the
 | |
|                table anyway to purge old dummy entries.
 | |
|                Subtle:  This is *necessary* if fill==size,
 | |
|                as set_lookkey needs at least one virgin slot to
 | |
|                terminate failing searches.  If fill < size, it's
 | |
|                merely desirable, as dummies slow searches. */
 | |
|             assert(so->fill > so->used);
 | |
|             memcpy(small_copy, oldtable, sizeof(small_copy));
 | |
|             oldtable = small_copy;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         newtable = PyMem_NEW(setentry, newsize);
 | |
|         if (newtable == NULL) {
 | |
|             PyErr_NoMemory();
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Make the set empty, using the new table. */
 | |
|     assert(newtable != oldtable);
 | |
|     memset(newtable, 0, sizeof(setentry) * newsize);
 | |
|     so->mask = newsize - 1;
 | |
|     so->table = newtable;
 | |
| 
 | |
|     /* Copy the data over; this is refcount-neutral for active entries;
 | |
|        dummy entries aren't copied over, of course */
 | |
|     newmask = (size_t)so->mask;
 | |
|     if (so->fill == so->used) {
 | |
|         for (entry = oldtable; entry <= oldtable + oldmask; entry++) {
 | |
|             if (entry->key != NULL) {
 | |
|                 set_insert_clean(newtable, newmask, entry->key, entry->hash);
 | |
|             }
 | |
|         }
 | |
|     } else {
 | |
|         so->fill = so->used;
 | |
|         for (entry = oldtable; entry <= oldtable + oldmask; entry++) {
 | |
|             if (entry->key != NULL && entry->key != dummy) {
 | |
|                 set_insert_clean(newtable, newmask, entry->key, entry->hash);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (is_oldtable_malloced)
 | |
|         PyMem_Free(oldtable);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_contains_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     setentry *entry;
 | |
| 
 | |
|     entry = set_lookkey(so, key, hash);
 | |
|     if (entry != NULL)
 | |
|         return entry->key != NULL;
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| #define DISCARD_NOTFOUND 0
 | |
| #define DISCARD_FOUND 1
 | |
| 
 | |
| static int
 | |
| set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     setentry *entry;
 | |
|     PyObject *old_key;
 | |
| 
 | |
|     entry = set_lookkey(so, key, hash);
 | |
|     if (entry == NULL)
 | |
|         return -1;
 | |
|     if (entry->key == NULL)
 | |
|         return DISCARD_NOTFOUND;
 | |
|     old_key = entry->key;
 | |
|     entry->key = dummy;
 | |
|     entry->hash = -1;
 | |
|     so->used--;
 | |
|     Py_DECREF(old_key);
 | |
|     return DISCARD_FOUND;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_add_key(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     Py_hash_t hash;
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     return set_add_entry(so, key, hash);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_contains_key(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     Py_hash_t hash;
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     return set_contains_entry(so, key, hash);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_discard_key(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     Py_hash_t hash;
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     return set_discard_entry(so, key, hash);
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_empty_to_minsize(PySetObject *so)
 | |
| {
 | |
|     memset(so->smalltable, 0, sizeof(so->smalltable));
 | |
|     so->fill = 0;
 | |
|     so->used = 0;
 | |
|     so->mask = PySet_MINSIZE - 1;
 | |
|     so->table = so->smalltable;
 | |
|     so->hash = -1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_clear_internal(PySetObject *so)
 | |
| {
 | |
|     setentry *entry;
 | |
|     setentry *table = so->table;
 | |
|     Py_ssize_t fill = so->fill;
 | |
|     Py_ssize_t used = so->used;
 | |
|     int table_is_malloced = table != so->smalltable;
 | |
|     setentry small_copy[PySet_MINSIZE];
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     assert(table != NULL);
 | |
| 
 | |
|     /* This is delicate.  During the process of clearing the set,
 | |
|      * decrefs can cause the set to mutate.  To avoid fatal confusion
 | |
|      * (voice of experience), we have to make the set empty before
 | |
|      * clearing the slots, and never refer to anything via so->ref while
 | |
|      * clearing.
 | |
|      */
 | |
|     if (table_is_malloced)
 | |
|         set_empty_to_minsize(so);
 | |
| 
 | |
|     else if (fill > 0) {
 | |
|         /* It's a small table with something that needs to be cleared.
 | |
|          * Afraid the only safe way is to copy the set entries into
 | |
|          * another small table first.
 | |
|          */
 | |
|         memcpy(small_copy, table, sizeof(small_copy));
 | |
|         table = small_copy;
 | |
|         set_empty_to_minsize(so);
 | |
|     }
 | |
|     /* else it's a small table that's already empty */
 | |
| 
 | |
|     /* Now we can finally clear things.  If C had refcounts, we could
 | |
|      * assert that the refcount on table is 1 now, i.e. that this function
 | |
|      * has unique access to it, so decref side-effects can't alter it.
 | |
|      */
 | |
|     for (entry = table; used > 0; entry++) {
 | |
|         if (entry->key && entry->key != dummy) {
 | |
|             used--;
 | |
|             Py_DECREF(entry->key);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (table_is_malloced)
 | |
|         PyMem_Free(table);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Iterate over a set table.  Use like so:
 | |
|  *
 | |
|  *     Py_ssize_t pos;
 | |
|  *     setentry *entry;
 | |
|  *     pos = 0;   # important!  pos should not otherwise be changed by you
 | |
|  *     while (set_next(yourset, &pos, &entry)) {
 | |
|  *              Refer to borrowed reference in entry->key.
 | |
|  *     }
 | |
|  *
 | |
|  * CAUTION:  In general, it isn't safe to use set_next in a loop that
 | |
|  * mutates the table.
 | |
|  */
 | |
| static int
 | |
| set_next(PySetObject *so, Py_ssize_t *pos_ptr, setentry **entry_ptr)
 | |
| {
 | |
|     Py_ssize_t i;
 | |
|     Py_ssize_t mask;
 | |
|     setentry *entry;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     i = *pos_ptr;
 | |
|     assert(i >= 0);
 | |
|     mask = so->mask;
 | |
|     entry = &so->table[i];
 | |
|     while (i <= mask && (entry->key == NULL || entry->key == dummy)) {
 | |
|         i++;
 | |
|         entry++;
 | |
|     }
 | |
|     *pos_ptr = i+1;
 | |
|     if (i > mask)
 | |
|         return 0;
 | |
|     assert(entry != NULL);
 | |
|     *entry_ptr = entry;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_dealloc(PySetObject *so)
 | |
| {
 | |
|     setentry *entry;
 | |
|     Py_ssize_t used = so->used;
 | |
| 
 | |
|     /* bpo-31095: UnTrack is needed before calling any callbacks */
 | |
|     PyObject_GC_UnTrack(so);
 | |
|     Py_TRASHCAN_BEGIN(so, set_dealloc)
 | |
|     if (so->weakreflist != NULL)
 | |
|         PyObject_ClearWeakRefs((PyObject *) so);
 | |
| 
 | |
|     for (entry = so->table; used > 0; entry++) {
 | |
|         if (entry->key && entry->key != dummy) {
 | |
|                 used--;
 | |
|                 Py_DECREF(entry->key);
 | |
|         }
 | |
|     }
 | |
|     if (so->table != so->smalltable)
 | |
|         PyMem_Free(so->table);
 | |
|     Py_TYPE(so)->tp_free(so);
 | |
|     Py_TRASHCAN_END
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_repr_lock_held(PySetObject *so)
 | |
| {
 | |
|     PyObject *result=NULL, *keys, *listrepr, *tmp;
 | |
|     int status = Py_ReprEnter((PyObject*)so);
 | |
| 
 | |
|     if (status != 0) {
 | |
|         if (status < 0)
 | |
|             return NULL;
 | |
|         return PyUnicode_FromFormat("%s(...)", Py_TYPE(so)->tp_name);
 | |
|     }
 | |
| 
 | |
|     /* shortcut for the empty set */
 | |
|     if (!so->used) {
 | |
|         Py_ReprLeave((PyObject*)so);
 | |
|         return PyUnicode_FromFormat("%s()", Py_TYPE(so)->tp_name);
 | |
|     }
 | |
| 
 | |
|     keys = PySequence_List((PyObject *)so);
 | |
|     if (keys == NULL)
 | |
|         goto done;
 | |
| 
 | |
|     /* repr(keys)[1:-1] */
 | |
|     listrepr = PyObject_Repr(keys);
 | |
|     Py_DECREF(keys);
 | |
|     if (listrepr == NULL)
 | |
|         goto done;
 | |
|     tmp = PyUnicode_Substring(listrepr, 1, PyUnicode_GET_LENGTH(listrepr)-1);
 | |
|     Py_DECREF(listrepr);
 | |
|     if (tmp == NULL)
 | |
|         goto done;
 | |
|     listrepr = tmp;
 | |
| 
 | |
|     if (!PySet_CheckExact(so))
 | |
|         result = PyUnicode_FromFormat("%s({%U})",
 | |
|                                       Py_TYPE(so)->tp_name,
 | |
|                                       listrepr);
 | |
|     else
 | |
|         result = PyUnicode_FromFormat("{%U}", listrepr);
 | |
|     Py_DECREF(listrepr);
 | |
| done:
 | |
|     Py_ReprLeave((PyObject*)so);
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_repr(PySetObject *so)
 | |
| {
 | |
|     PyObject *result;
 | |
|     Py_BEGIN_CRITICAL_SECTION(so);
 | |
|     result = set_repr_lock_held(so);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static Py_ssize_t
 | |
| set_len(PySetObject *so)
 | |
| {
 | |
|     return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->used);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_merge_lock_held(PySetObject *so, PyObject *otherset)
 | |
| {
 | |
|     PySetObject *other;
 | |
|     PyObject *key;
 | |
|     Py_ssize_t i;
 | |
|     setentry *so_entry;
 | |
|     setentry *other_entry;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     assert (PyAnySet_Check(otherset));
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(otherset);
 | |
| 
 | |
|     other = (PySetObject*)otherset;
 | |
|     if (other == so || other->used == 0)
 | |
|         /* a.update(a) or a.update(set()); nothing to do */
 | |
|         return 0;
 | |
|     /* Do one big resize at the start, rather than
 | |
|      * incrementally resizing as we insert new keys.  Expect
 | |
|      * that there will be no (or few) overlapping keys.
 | |
|      */
 | |
|     if ((so->fill + other->used)*5 >= so->mask*3) {
 | |
|         if (set_table_resize(so, (so->used + other->used)*2) != 0)
 | |
|             return -1;
 | |
|     }
 | |
|     so_entry = so->table;
 | |
|     other_entry = other->table;
 | |
| 
 | |
|     /* If our table is empty, and both tables have the same size, and
 | |
|        there are no dummies to eliminate, then just copy the pointers. */
 | |
|     if (so->fill == 0 && so->mask == other->mask && other->fill == other->used) {
 | |
|         for (i = 0; i <= other->mask; i++, so_entry++, other_entry++) {
 | |
|             key = other_entry->key;
 | |
|             if (key != NULL) {
 | |
|                 assert(so_entry->key == NULL);
 | |
|                 so_entry->key = Py_NewRef(key);
 | |
|                 so_entry->hash = other_entry->hash;
 | |
|             }
 | |
|         }
 | |
|         so->fill = other->fill;
 | |
|         so->used = other->used;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* If our table is empty, we can use set_insert_clean() */
 | |
|     if (so->fill == 0) {
 | |
|         setentry *newtable = so->table;
 | |
|         size_t newmask = (size_t)so->mask;
 | |
|         so->fill = other->used;
 | |
|         so->used = other->used;
 | |
|         for (i = other->mask + 1; i > 0 ; i--, other_entry++) {
 | |
|             key = other_entry->key;
 | |
|             if (key != NULL && key != dummy) {
 | |
|                 set_insert_clean(newtable, newmask, Py_NewRef(key),
 | |
|                                  other_entry->hash);
 | |
|             }
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* We can't assure there are no duplicates, so do normal insertions */
 | |
|     for (i = 0; i <= other->mask; i++) {
 | |
|         other_entry = &other->table[i];
 | |
|         key = other_entry->key;
 | |
|         if (key != NULL && key != dummy) {
 | |
|             if (set_add_entry(so, key, other_entry->hash))
 | |
|                 return -1;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.pop
 | |
|     so: setobject
 | |
| 
 | |
| Remove and return an arbitrary set element.
 | |
| 
 | |
| Raises KeyError if the set is empty.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_pop_impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=4d65180f1271871b input=9296c84921125060]*/
 | |
| {
 | |
|     /* Make sure the search finger is in bounds */
 | |
|     setentry *entry = so->table + (so->finger & so->mask);
 | |
|     setentry *limit = so->table + so->mask;
 | |
|     PyObject *key;
 | |
| 
 | |
|     if (so->used == 0) {
 | |
|         PyErr_SetString(PyExc_KeyError, "pop from an empty set");
 | |
|         return NULL;
 | |
|     }
 | |
|     while (entry->key == NULL || entry->key==dummy) {
 | |
|         entry++;
 | |
|         if (entry > limit)
 | |
|             entry = so->table;
 | |
|     }
 | |
|     key = entry->key;
 | |
|     entry->key = dummy;
 | |
|     entry->hash = -1;
 | |
|     so->used--;
 | |
|     so->finger = entry - so->table + 1;   /* next place to start */
 | |
|     return key;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_traverse(PySetObject *so, visitproc visit, void *arg)
 | |
| {
 | |
|     Py_ssize_t pos = 0;
 | |
|     setentry *entry;
 | |
| 
 | |
|     while (set_next(so, &pos, &entry))
 | |
|         Py_VISIT(entry->key);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Work to increase the bit dispersion for closely spaced hash values.
 | |
|    This is important because some use cases have many combinations of a
 | |
|    small number of elements with nearby hashes so that many distinct
 | |
|    combinations collapse to only a handful of distinct hash values. */
 | |
| 
 | |
| static Py_uhash_t
 | |
| _shuffle_bits(Py_uhash_t h)
 | |
| {
 | |
|     return ((h ^ 89869747UL) ^ (h << 16)) * 3644798167UL;
 | |
| }
 | |
| 
 | |
| /* Most of the constants in this hash algorithm are randomly chosen
 | |
|    large primes with "interesting bit patterns" and that passed tests
 | |
|    for good collision statistics on a variety of problematic datasets
 | |
|    including powersets and graph structures (such as David Eppstein's
 | |
|    graph recipes in Lib/test/test_set.py) */
 | |
| 
 | |
| static Py_hash_t
 | |
| frozenset_hash(PyObject *self)
 | |
| {
 | |
|     PySetObject *so = (PySetObject *)self;
 | |
|     Py_uhash_t hash = 0;
 | |
|     setentry *entry;
 | |
| 
 | |
|     if (so->hash != -1)
 | |
|         return so->hash;
 | |
| 
 | |
|     /* Xor-in shuffled bits from every entry's hash field because xor is
 | |
|        commutative and a frozenset hash should be independent of order.
 | |
| 
 | |
|        For speed, include null entries and dummy entries and then
 | |
|        subtract out their effect afterwards so that the final hash
 | |
|        depends only on active entries.  This allows the code to be
 | |
|        vectorized by the compiler and it saves the unpredictable
 | |
|        branches that would arise when trying to exclude null and dummy
 | |
|        entries on every iteration. */
 | |
| 
 | |
|     for (entry = so->table; entry <= &so->table[so->mask]; entry++)
 | |
|         hash ^= _shuffle_bits(entry->hash);
 | |
| 
 | |
|     /* Remove the effect of an odd number of NULL entries */
 | |
|     if ((so->mask + 1 - so->fill) & 1)
 | |
|         hash ^= _shuffle_bits(0);
 | |
| 
 | |
|     /* Remove the effect of an odd number of dummy entries */
 | |
|     if ((so->fill - so->used) & 1)
 | |
|         hash ^= _shuffle_bits(-1);
 | |
| 
 | |
|     /* Factor in the number of active entries */
 | |
|     hash ^= ((Py_uhash_t)PySet_GET_SIZE(self) + 1) * 1927868237UL;
 | |
| 
 | |
|     /* Disperse patterns arising in nested frozensets */
 | |
|     hash ^= (hash >> 11) ^ (hash >> 25);
 | |
|     hash = hash * 69069U + 907133923UL;
 | |
| 
 | |
|     /* -1 is reserved as an error code */
 | |
|     if (hash == (Py_uhash_t)-1)
 | |
|         hash = 590923713UL;
 | |
| 
 | |
|     so->hash = hash;
 | |
|     return hash;
 | |
| }
 | |
| 
 | |
| /***** Set iterator type ***********************************************/
 | |
| 
 | |
| typedef struct {
 | |
|     PyObject_HEAD
 | |
|     PySetObject *si_set; /* Set to NULL when iterator is exhausted */
 | |
|     Py_ssize_t si_used;
 | |
|     Py_ssize_t si_pos;
 | |
|     Py_ssize_t len;
 | |
| } setiterobject;
 | |
| 
 | |
| static void
 | |
| setiter_dealloc(setiterobject *si)
 | |
| {
 | |
|     /* bpo-31095: UnTrack is needed before calling any callbacks */
 | |
|     _PyObject_GC_UNTRACK(si);
 | |
|     Py_XDECREF(si->si_set);
 | |
|     PyObject_GC_Del(si);
 | |
| }
 | |
| 
 | |
| static int
 | |
| setiter_traverse(setiterobject *si, visitproc visit, void *arg)
 | |
| {
 | |
|     Py_VISIT(si->si_set);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| setiter_len(setiterobject *si, PyObject *Py_UNUSED(ignored))
 | |
| {
 | |
|     Py_ssize_t len = 0;
 | |
|     if (si->si_set != NULL && si->si_used == si->si_set->used)
 | |
|         len = si->len;
 | |
|     return PyLong_FromSsize_t(len);
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
 | |
| 
 | |
| static PyObject *setiter_iternext(setiterobject *si);
 | |
| 
 | |
| static PyObject *
 | |
| setiter_reduce(setiterobject *si, PyObject *Py_UNUSED(ignored))
 | |
| {
 | |
|     /* copy the iterator state */
 | |
|     setiterobject tmp = *si;
 | |
|     Py_XINCREF(tmp.si_set);
 | |
| 
 | |
|     /* iterate the temporary into a list */
 | |
|     PyObject *list = PySequence_List((PyObject*)&tmp);
 | |
|     Py_XDECREF(tmp.si_set);
 | |
|     if (list == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return Py_BuildValue("N(N)", _PyEval_GetBuiltin(&_Py_ID(iter)), list);
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
 | |
| 
 | |
| static PyMethodDef setiter_methods[] = {
 | |
|     {"__length_hint__", (PyCFunction)setiter_len, METH_NOARGS, length_hint_doc},
 | |
|     {"__reduce__", (PyCFunction)setiter_reduce, METH_NOARGS, reduce_doc},
 | |
|     {NULL,              NULL}           /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyObject *setiter_iternext(setiterobject *si)
 | |
| {
 | |
|     PyObject *key = NULL;
 | |
|     Py_ssize_t i, mask;
 | |
|     setentry *entry;
 | |
|     PySetObject *so = si->si_set;
 | |
| 
 | |
|     if (so == NULL)
 | |
|         return NULL;
 | |
|     assert (PyAnySet_Check(so));
 | |
| 
 | |
|     Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE(so->used);
 | |
|     Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE(si->si_used);
 | |
|     if (si_used != so_used) {
 | |
|         PyErr_SetString(PyExc_RuntimeError,
 | |
|                         "Set changed size during iteration");
 | |
|         si->si_used = -1; /* Make this state sticky */
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     Py_BEGIN_CRITICAL_SECTION(so);
 | |
|     i = si->si_pos;
 | |
|     assert(i>=0);
 | |
|     entry = so->table;
 | |
|     mask = so->mask;
 | |
|     while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) {
 | |
|         i++;
 | |
|     }
 | |
|     if (i <= mask) {
 | |
|         key = Py_NewRef(entry[i].key);
 | |
|     }
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     si->si_pos = i+1;
 | |
|     if (key == NULL) {
 | |
|         si->si_set = NULL;
 | |
|         Py_DECREF(so);
 | |
|         return NULL;
 | |
|     }
 | |
|     si->len--;
 | |
|     return key;
 | |
| }
 | |
| 
 | |
| PyTypeObject PySetIter_Type = {
 | |
|     PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | |
|     "set_iterator",                             /* tp_name */
 | |
|     sizeof(setiterobject),                      /* tp_basicsize */
 | |
|     0,                                          /* tp_itemsize */
 | |
|     /* methods */
 | |
|     (destructor)setiter_dealloc,                /* tp_dealloc */
 | |
|     0,                                          /* tp_vectorcall_offset */
 | |
|     0,                                          /* tp_getattr */
 | |
|     0,                                          /* tp_setattr */
 | |
|     0,                                          /* tp_as_async */
 | |
|     0,                                          /* tp_repr */
 | |
|     0,                                          /* tp_as_number */
 | |
|     0,                                          /* tp_as_sequence */
 | |
|     0,                                          /* tp_as_mapping */
 | |
|     0,                                          /* tp_hash */
 | |
|     0,                                          /* tp_call */
 | |
|     0,                                          /* tp_str */
 | |
|     PyObject_GenericGetAttr,                    /* tp_getattro */
 | |
|     0,                                          /* tp_setattro */
 | |
|     0,                                          /* tp_as_buffer */
 | |
|     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
 | |
|     0,                                          /* tp_doc */
 | |
|     (traverseproc)setiter_traverse,             /* tp_traverse */
 | |
|     0,                                          /* tp_clear */
 | |
|     0,                                          /* tp_richcompare */
 | |
|     0,                                          /* tp_weaklistoffset */
 | |
|     PyObject_SelfIter,                          /* tp_iter */
 | |
|     (iternextfunc)setiter_iternext,             /* tp_iternext */
 | |
|     setiter_methods,                            /* tp_methods */
 | |
|     0,
 | |
| };
 | |
| 
 | |
| static PyObject *
 | |
| set_iter(PySetObject *so)
 | |
| {
 | |
|     Py_ssize_t size = set_len(so);
 | |
|     setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type);
 | |
|     if (si == NULL)
 | |
|         return NULL;
 | |
|     si->si_set = (PySetObject*)Py_NewRef(so);
 | |
|     si->si_used = size;
 | |
|     si->si_pos = 0;
 | |
|     si->len = size;
 | |
|     _PyObject_GC_TRACK(si);
 | |
|     return (PyObject *)si;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_update_dict_lock_held(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     assert(PyDict_CheckExact(other));
 | |
| 
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
 | |
| 
 | |
|     /* Do one big resize at the start, rather than
 | |
|     * incrementally resizing as we insert new keys.  Expect
 | |
|     * that there will be no (or few) overlapping keys.
 | |
|     */
 | |
|     Py_ssize_t dictsize = PyDict_GET_SIZE(other);
 | |
|     if ((so->fill + dictsize)*5 >= so->mask*3) {
 | |
|         if (set_table_resize(so, (so->used + dictsize)*2) != 0) {
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     Py_ssize_t pos = 0;
 | |
|     PyObject *key;
 | |
|     PyObject *value;
 | |
|     Py_hash_t hash;
 | |
|     while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
 | |
|         if (set_add_entry(so, key, hash)) {
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_update_iterable_lock_held(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
| 
 | |
|     PyObject *it = PyObject_GetIter(other);
 | |
|     if (it == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     PyObject *key;
 | |
|     while ((key = PyIter_Next(it)) != NULL) {
 | |
|         if (set_add_key(so, key)) {
 | |
|             Py_DECREF(it);
 | |
|             Py_DECREF(key);
 | |
|             return -1;
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     Py_DECREF(it);
 | |
|     if (PyErr_Occurred())
 | |
|         return -1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_update_lock_held(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         return set_merge_lock_held(so, other);
 | |
|     }
 | |
|     else if (PyDict_CheckExact(other)) {
 | |
|         return set_update_dict_lock_held(so, other);
 | |
|     }
 | |
|     return set_update_iterable_lock_held(so, other);
 | |
| }
 | |
| 
 | |
| // set_update for a `so` that is only visible to the current thread
 | |
| static int
 | |
| set_update_local(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     assert(Py_REFCNT(so) == 1);
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION(other);
 | |
|         rv = set_merge_lock_held(so, other);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
|         return rv;
 | |
|     }
 | |
|     else if (PyDict_CheckExact(other)) {
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION(other);
 | |
|         rv = set_update_dict_lock_held(so, other);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
|         return rv;
 | |
|     }
 | |
|     return set_update_iterable_lock_held(so, other);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_update_internal(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         if (Py_Is((PyObject *)so, other)) {
 | |
|             return 0;
 | |
|         }
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|         rv = set_merge_lock_held(so, other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|         return rv;
 | |
|     }
 | |
|     else if (PyDict_CheckExact(other)) {
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|         rv = set_update_dict_lock_held(so, other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|         return rv;
 | |
|     }
 | |
|     else {
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION(so);
 | |
|         rv = set_update_iterable_lock_held(so, other);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
|         return rv;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.update
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Update the set, adding elements from all others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_update_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=34f6371704974c8a input=eb47c4fbaeb3286e]*/
 | |
| {
 | |
|     Py_ssize_t i;
 | |
| 
 | |
|     for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
 | |
|         PyObject *other = PyTuple_GET_ITEM(args, i);
 | |
|         if (set_update_internal(so, other))
 | |
|             return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /* XXX Todo:
 | |
|    If aligned memory allocations become available, make the
 | |
|    set object 64 byte aligned so that most of the fields
 | |
|    can be retrieved or updated in a single cache line.
 | |
| */
 | |
| 
 | |
| static PyObject *
 | |
| make_new_set(PyTypeObject *type, PyObject *iterable)
 | |
| {
 | |
|     assert(PyType_Check(type));
 | |
|     PySetObject *so;
 | |
| 
 | |
|     so = (PySetObject *)type->tp_alloc(type, 0);
 | |
|     if (so == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     so->fill = 0;
 | |
|     so->used = 0;
 | |
|     so->mask = PySet_MINSIZE - 1;
 | |
|     so->table = so->smalltable;
 | |
|     so->hash = -1;
 | |
|     so->finger = 0;
 | |
|     so->weakreflist = NULL;
 | |
| 
 | |
|     if (iterable != NULL) {
 | |
|         if (set_update_local(so, iterable)) {
 | |
|             Py_DECREF(so);
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return (PyObject *)so;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| make_new_set_basetype(PyTypeObject *type, PyObject *iterable)
 | |
| {
 | |
|     if (type != &PySet_Type && type != &PyFrozenSet_Type) {
 | |
|         if (PyType_IsSubtype(type, &PySet_Type))
 | |
|             type = &PySet_Type;
 | |
|         else
 | |
|             type = &PyFrozenSet_Type;
 | |
|     }
 | |
|     return make_new_set(type, iterable);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| make_new_frozenset(PyTypeObject *type, PyObject *iterable)
 | |
| {
 | |
|     if (type != &PyFrozenSet_Type) {
 | |
|         return make_new_set(type, iterable);
 | |
|     }
 | |
| 
 | |
|     if (iterable != NULL && PyFrozenSet_CheckExact(iterable)) {
 | |
|         /* frozenset(f) is idempotent */
 | |
|         return Py_NewRef(iterable);
 | |
|     }
 | |
|     return make_new_set(type, iterable);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | |
| {
 | |
|     PyObject *iterable = NULL;
 | |
| 
 | |
|     if ((type == &PyFrozenSet_Type ||
 | |
|          type->tp_init == PyFrozenSet_Type.tp_init) &&
 | |
|         !_PyArg_NoKeywords("frozenset", kwds)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return make_new_frozenset(type, iterable);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| frozenset_vectorcall(PyObject *type, PyObject * const*args,
 | |
|                      size_t nargsf, PyObject *kwnames)
 | |
| {
 | |
|     if (!_PyArg_NoKwnames("frozenset", kwnames)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
 | |
|     if (!_PyArg_CheckPositional("frozenset", nargs, 0, 1)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     PyObject *iterable = (nargs ? args[0] : NULL);
 | |
|     return make_new_frozenset(_PyType_CAST(type), iterable);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | |
| {
 | |
|     return make_new_set(type, NULL);
 | |
| }
 | |
| 
 | |
| /* set_swap_bodies() switches the contents of any two sets by moving their
 | |
|    internal data pointers and, if needed, copying the internal smalltables.
 | |
|    Semantically equivalent to:
 | |
| 
 | |
|      t=set(a); a.clear(); a.update(b); b.clear(); b.update(t); del t
 | |
| 
 | |
|    The function always succeeds and it leaves both objects in a stable state.
 | |
|    Useful for operations that update in-place (by allowing an intermediate
 | |
|    result to be swapped into one of the original inputs).
 | |
| */
 | |
| 
 | |
| static void
 | |
| set_swap_bodies(PySetObject *a, PySetObject *b)
 | |
| {
 | |
|     Py_ssize_t t;
 | |
|     setentry *u;
 | |
|     setentry tab[PySet_MINSIZE];
 | |
|     Py_hash_t h;
 | |
| 
 | |
|     t = a->fill;     a->fill   = b->fill;        b->fill  = t;
 | |
|     t = a->used;     a->used   = b->used;        b->used  = t;
 | |
|     t = a->mask;     a->mask   = b->mask;        b->mask  = t;
 | |
| 
 | |
|     u = a->table;
 | |
|     if (a->table == a->smalltable)
 | |
|         u = b->smalltable;
 | |
|     a->table  = b->table;
 | |
|     if (b->table == b->smalltable)
 | |
|         a->table = a->smalltable;
 | |
|     b->table = u;
 | |
| 
 | |
|     if (a->table == a->smalltable || b->table == b->smalltable) {
 | |
|         memcpy(tab, a->smalltable, sizeof(tab));
 | |
|         memcpy(a->smalltable, b->smalltable, sizeof(tab));
 | |
|         memcpy(b->smalltable, tab, sizeof(tab));
 | |
|     }
 | |
| 
 | |
|     if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type)  &&
 | |
|         PyType_IsSubtype(Py_TYPE(b), &PyFrozenSet_Type)) {
 | |
|         h = a->hash;     a->hash = b->hash;  b->hash = h;
 | |
|     } else {
 | |
|         a->hash = -1;
 | |
|         b->hash = -1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.copy
 | |
|     so: setobject
 | |
| 
 | |
| Return a shallow copy of a set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_copy_impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=c9223a1e1cc6b041 input=c169a4fbb8209257]*/
 | |
| {
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     PyObject *copy = make_new_set_basetype(Py_TYPE(so), NULL);
 | |
|     if (copy == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     if (set_merge_lock_held((PySetObject *)copy, (PyObject *)so) < 0) {
 | |
|         Py_DECREF(copy);
 | |
|         return NULL;
 | |
|     }
 | |
|     return copy;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| frozenset.copy
 | |
|     so: setobject
 | |
| 
 | |
| Return a shallow copy of a set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| frozenset_copy_impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=b356263526af9e70 input=fbf5bef131268dd7]*/
 | |
| {
 | |
|     if (PyFrozenSet_CheckExact(so)) {
 | |
|         return Py_NewRef(so);
 | |
|     }
 | |
|     return set_copy_impl(so);
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.clear
 | |
|     so: setobject
 | |
| 
 | |
| Remove all elements from this set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_clear_impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=4e71d5a83904161a input=c6f831b366111950]*/
 | |
| {
 | |
|     set_clear_internal(so);
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.union
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Return a new set with elements from the set and all others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_union_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=2c83d05a446a1477 input=2e2024fa1e40ac84]*/
 | |
| {
 | |
|     PySetObject *result;
 | |
|     PyObject *other;
 | |
|     Py_ssize_t i;
 | |
| 
 | |
|     result = (PySetObject *)set_copy(so, NULL);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
 | |
|         other = PyTuple_GET_ITEM(args, i);
 | |
|         if ((PyObject *)so == other)
 | |
|             continue;
 | |
|         if (set_update_local(result, other)) {
 | |
|             Py_DECREF(result);
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_or(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PySetObject *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     result = (PySetObject *)set_copy(so, NULL);
 | |
|     if (result == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     if (Py_Is((PyObject *)so, other)) {
 | |
|         return (PyObject *)result;
 | |
|     }
 | |
|     if (set_update_local(result, other)) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_ior(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     if (set_update_internal(so, other)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return Py_NewRef(so);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_intersection(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PySetObject *result;
 | |
|     PyObject *key, *it, *tmp;
 | |
|     Py_hash_t hash;
 | |
|     int rv;
 | |
| 
 | |
|     if ((PyObject *)so == other)
 | |
|         return set_copy_impl(so);
 | |
| 
 | |
|     result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         Py_ssize_t pos = 0;
 | |
|         setentry *entry;
 | |
| 
 | |
|         if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) {
 | |
|             tmp = (PyObject *)so;
 | |
|             so = (PySetObject *)other;
 | |
|             other = tmp;
 | |
|         }
 | |
| 
 | |
|         while (set_next((PySetObject *)other, &pos, &entry)) {
 | |
|             key = entry->key;
 | |
|             hash = entry->hash;
 | |
|             Py_INCREF(key);
 | |
|             rv = set_contains_entry(so, key, hash);
 | |
|             if (rv < 0) {
 | |
|                 Py_DECREF(result);
 | |
|                 Py_DECREF(key);
 | |
|                 return NULL;
 | |
|             }
 | |
|             if (rv) {
 | |
|                 if (set_add_entry(result, key, hash)) {
 | |
|                     Py_DECREF(result);
 | |
|                     Py_DECREF(key);
 | |
|                     return NULL;
 | |
|                 }
 | |
|             }
 | |
|             Py_DECREF(key);
 | |
|         }
 | |
|         return (PyObject *)result;
 | |
|     }
 | |
| 
 | |
|     it = PyObject_GetIter(other);
 | |
|     if (it == NULL) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     while ((key = PyIter_Next(it)) != NULL) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             goto error;
 | |
|         rv = set_contains_entry(so, key, hash);
 | |
|         if (rv < 0)
 | |
|             goto error;
 | |
|         if (rv) {
 | |
|             if (set_add_entry(result, key, hash))
 | |
|                 goto error;
 | |
|             if (PySet_GET_SIZE(result) >= PySet_GET_SIZE(so)) {
 | |
|                 Py_DECREF(key);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     Py_DECREF(it);
 | |
|     if (PyErr_Occurred()) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
|   error:
 | |
|     Py_DECREF(it);
 | |
|     Py_DECREF(result);
 | |
|     Py_DECREF(key);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.intersection as set_intersection_multi
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Return a new set with elements common to the set and all others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_intersection_multi_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=2406ef3387adbe2f input=04108ea6d7f0532b]*/
 | |
| {
 | |
|     Py_ssize_t i;
 | |
| 
 | |
|     if (PyTuple_GET_SIZE(args) == 0) {
 | |
|         return set_copy(so, NULL);
 | |
|     }
 | |
| 
 | |
|     PyObject *result = Py_NewRef(so);
 | |
|     for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
 | |
|         PyObject *other = PyTuple_GET_ITEM(args, i);
 | |
|         PyObject *newresult;
 | |
|         Py_BEGIN_CRITICAL_SECTION2(result, other);
 | |
|         newresult = set_intersection((PySetObject *)result, other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|         if (newresult == NULL) {
 | |
|             Py_DECREF(result);
 | |
|             return NULL;
 | |
|         }
 | |
|         Py_SETREF(result, newresult);
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_intersection_update(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *tmp;
 | |
| 
 | |
|     tmp = set_intersection(so, other);
 | |
|     if (tmp == NULL)
 | |
|         return NULL;
 | |
|     set_swap_bodies(so, (PySetObject *)tmp);
 | |
|     Py_DECREF(tmp);
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.intersection_update as set_intersection_update_multi
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Update the set, keeping only elements found in it and all others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_intersection_update_multi_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=251c1f729063609d input=ff8f119f97458d16]*/
 | |
| {
 | |
|     PyObject *tmp;
 | |
| 
 | |
|     tmp = set_intersection_multi_impl(so, args);
 | |
|     if (tmp == NULL)
 | |
|         return NULL;
 | |
|     Py_BEGIN_CRITICAL_SECTION(so);
 | |
|     set_swap_bodies(so, (PySetObject *)tmp);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     Py_DECREF(tmp);
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_and(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     PyObject *rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|     rv = set_intersection(so, other);
 | |
|     Py_END_CRITICAL_SECTION2();
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_iand(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|     result = set_intersection_update(so, other);
 | |
|     Py_END_CRITICAL_SECTION2();
 | |
| 
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
|     Py_DECREF(result);
 | |
|     return Py_NewRef(so);
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section so other
 | |
| set.isdisjoint
 | |
|     so: setobject
 | |
|     other: object
 | |
|     /
 | |
| 
 | |
| Return True if two sets have a null intersection.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_isdisjoint_impl(PySetObject *so, PyObject *other)
 | |
| /*[clinic end generated code: output=273493f2d57c565e input=32f8dcab5e0fc7d6]*/
 | |
| {
 | |
|     PyObject *key, *it, *tmp;
 | |
|     int rv;
 | |
| 
 | |
|     if ((PyObject *)so == other) {
 | |
|         if (PySet_GET_SIZE(so) == 0)
 | |
|             Py_RETURN_TRUE;
 | |
|         else
 | |
|             Py_RETURN_FALSE;
 | |
|     }
 | |
| 
 | |
|     if (PyAnySet_CheckExact(other)) {
 | |
|         Py_ssize_t pos = 0;
 | |
|         setentry *entry;
 | |
| 
 | |
|         if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) {
 | |
|             tmp = (PyObject *)so;
 | |
|             so = (PySetObject *)other;
 | |
|             other = tmp;
 | |
|         }
 | |
|         while (set_next((PySetObject *)other, &pos, &entry)) {
 | |
|             PyObject *key = entry->key;
 | |
|             Py_INCREF(key);
 | |
|             rv = set_contains_entry(so, key, entry->hash);
 | |
|             Py_DECREF(key);
 | |
|             if (rv < 0) {
 | |
|                 return NULL;
 | |
|             }
 | |
|             if (rv) {
 | |
|                 Py_RETURN_FALSE;
 | |
|             }
 | |
|         }
 | |
|         Py_RETURN_TRUE;
 | |
|     }
 | |
| 
 | |
|     it = PyObject_GetIter(other);
 | |
|     if (it == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     while ((key = PyIter_Next(it)) != NULL) {
 | |
|         rv = set_contains_key(so, key);
 | |
|         Py_DECREF(key);
 | |
|         if (rv < 0) {
 | |
|             Py_DECREF(it);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (rv) {
 | |
|             Py_DECREF(it);
 | |
|             Py_RETURN_FALSE;
 | |
|         }
 | |
|     }
 | |
|     Py_DECREF(it);
 | |
|     if (PyErr_Occurred())
 | |
|         return NULL;
 | |
|     Py_RETURN_TRUE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_difference_update_internal(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
 | |
| 
 | |
|     if ((PyObject *)so == other)
 | |
|         return set_clear_internal(so);
 | |
| 
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         setentry *entry;
 | |
|         Py_ssize_t pos = 0;
 | |
| 
 | |
|         /* Optimization:  When the other set is more than 8 times
 | |
|            larger than the base set, replace the other set with
 | |
|            intersection of the two sets.
 | |
|         */
 | |
|         if ((PySet_GET_SIZE(other) >> 3) > PySet_GET_SIZE(so)) {
 | |
|             other = set_intersection(so, other);
 | |
|             if (other == NULL)
 | |
|                 return -1;
 | |
|         } else {
 | |
|             Py_INCREF(other);
 | |
|         }
 | |
| 
 | |
|         while (set_next((PySetObject *)other, &pos, &entry)) {
 | |
|             PyObject *key = entry->key;
 | |
|             Py_INCREF(key);
 | |
|             if (set_discard_entry(so, key, entry->hash) < 0) {
 | |
|                 Py_DECREF(other);
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|             Py_DECREF(key);
 | |
|         }
 | |
| 
 | |
|         Py_DECREF(other);
 | |
|     } else {
 | |
|         PyObject *key, *it;
 | |
|         it = PyObject_GetIter(other);
 | |
|         if (it == NULL)
 | |
|             return -1;
 | |
| 
 | |
|         while ((key = PyIter_Next(it)) != NULL) {
 | |
|             if (set_discard_key(so, key) < 0) {
 | |
|                 Py_DECREF(it);
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|             Py_DECREF(key);
 | |
|         }
 | |
|         Py_DECREF(it);
 | |
|         if (PyErr_Occurred())
 | |
|             return -1;
 | |
|     }
 | |
|     /* If more than 1/4th are dummies, then resize them away. */
 | |
|     if ((size_t)(so->fill - so->used) <= (size_t)so->mask / 4)
 | |
|         return 0;
 | |
|     return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.difference_update
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Update the set, removing elements found in others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_difference_update_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=28685b2fc63e41c4 input=e7abb43c9f2c5a73]*/
 | |
| {
 | |
|     Py_ssize_t i;
 | |
| 
 | |
|     for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
 | |
|         PyObject *other = PyTuple_GET_ITEM(args, i);
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|         rv = set_difference_update_internal(so, other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|         if (rv) {
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_copy_and_difference(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
| 
 | |
|     result = set_copy_impl(so);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
|     if (set_difference_update_internal((PySetObject *) result, other) == 0)
 | |
|         return result;
 | |
|     Py_DECREF(result);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_difference(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
|     PyObject *key;
 | |
|     Py_hash_t hash;
 | |
|     setentry *entry;
 | |
|     Py_ssize_t pos = 0, other_size;
 | |
|     int rv;
 | |
| 
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         other_size = PySet_GET_SIZE(other);
 | |
|     }
 | |
|     else if (PyDict_CheckExact(other)) {
 | |
|         other_size = PyDict_GET_SIZE(other);
 | |
|     }
 | |
|     else {
 | |
|         return set_copy_and_difference(so, other);
 | |
|     }
 | |
| 
 | |
|     /* If len(so) much more than len(other), it's more efficient to simply copy
 | |
|      * so and then iterate other looking for common elements. */
 | |
|     if ((PySet_GET_SIZE(so) >> 2) > other_size) {
 | |
|         return set_copy_and_difference(so, other);
 | |
|     }
 | |
| 
 | |
|     result = make_new_set_basetype(Py_TYPE(so), NULL);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     if (PyDict_CheckExact(other)) {
 | |
|         while (set_next(so, &pos, &entry)) {
 | |
|             key = entry->key;
 | |
|             hash = entry->hash;
 | |
|             Py_INCREF(key);
 | |
|             rv = _PyDict_Contains_KnownHash(other, key, hash);
 | |
|             if (rv < 0) {
 | |
|                 Py_DECREF(result);
 | |
|                 Py_DECREF(key);
 | |
|                 return NULL;
 | |
|             }
 | |
|             if (!rv) {
 | |
|                 if (set_add_entry((PySetObject *)result, key, hash)) {
 | |
|                     Py_DECREF(result);
 | |
|                     Py_DECREF(key);
 | |
|                     return NULL;
 | |
|                 }
 | |
|             }
 | |
|             Py_DECREF(key);
 | |
|         }
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     /* Iterate over so, checking for common elements in other. */
 | |
|     while (set_next(so, &pos, &entry)) {
 | |
|         key = entry->key;
 | |
|         hash = entry->hash;
 | |
|         Py_INCREF(key);
 | |
|         rv = set_contains_entry((PySetObject *)other, key, hash);
 | |
|         if (rv < 0) {
 | |
|             Py_DECREF(result);
 | |
|             Py_DECREF(key);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (!rv) {
 | |
|             if (set_add_entry((PySetObject *)result, key, hash)) {
 | |
|                 Py_DECREF(result);
 | |
|                 Py_DECREF(key);
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.difference as set_difference_multi
 | |
|     so: setobject
 | |
|     *others as args: object
 | |
|     /
 | |
| 
 | |
| Return a new set with elements in the set that are not in the others.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_difference_multi_impl(PySetObject *so, PyObject *args)
 | |
| /*[clinic end generated code: output=3130c3bb3cac873d input=d8ae9bb6d518ab95]*/
 | |
| {
 | |
|     Py_ssize_t i;
 | |
|     PyObject *result, *other;
 | |
| 
 | |
|     if (PyTuple_GET_SIZE(args) == 0) {
 | |
|         return set_copy(so, NULL);
 | |
|     }
 | |
| 
 | |
|     other = PyTuple_GET_ITEM(args, 0);
 | |
|     Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|     result = set_difference(so, other);
 | |
|     Py_END_CRITICAL_SECTION2();
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     for (i=1 ; i<PyTuple_GET_SIZE(args) ; i++) {
 | |
|         other = PyTuple_GET_ITEM(args, i);
 | |
|         int rv;
 | |
|         Py_BEGIN_CRITICAL_SECTION(other);
 | |
|         rv = set_difference_update_internal((PySetObject *)result, other);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
|         if (rv) {
 | |
|             Py_DECREF(result);
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_sub(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     PyObject *rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|     rv = set_difference(so, other);
 | |
|     Py_END_CRITICAL_SECTION2();
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_isub(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     int rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|     rv = set_difference_update_internal(so, other);
 | |
|     Py_END_CRITICAL_SECTION2();
 | |
|     if (rv < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return Py_NewRef(so);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_symmetric_difference_update_dict(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
 | |
| 
 | |
|     Py_ssize_t pos = 0;
 | |
|     PyObject *key, *value;
 | |
|     Py_hash_t hash;
 | |
|     while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
 | |
|         Py_INCREF(key);
 | |
|         int rv = set_discard_entry(so, key, hash);
 | |
|         if (rv < 0) {
 | |
|             Py_DECREF(key);
 | |
|             return -1;
 | |
|         }
 | |
|         if (rv == DISCARD_NOTFOUND) {
 | |
|             if (set_add_entry(so, key, hash)) {
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_symmetric_difference_update_set(PySetObject *so, PySetObject *other)
 | |
| {
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
 | |
| 
 | |
|     Py_ssize_t pos = 0;
 | |
|     setentry *entry;
 | |
|     while (set_next(other, &pos, &entry)) {
 | |
|         PyObject *key = Py_NewRef(entry->key);
 | |
|         Py_hash_t hash = entry->hash;
 | |
|         int rv = set_discard_entry(so, key, hash);
 | |
|         if (rv < 0) {
 | |
|             Py_DECREF(key);
 | |
|             return -1;
 | |
|         }
 | |
|         if (rv == DISCARD_NOTFOUND) {
 | |
|             if (set_add_entry(so, key, hash)) {
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| set.symmetric_difference_update
 | |
|     so: setobject
 | |
|     other: object
 | |
|     /
 | |
| 
 | |
| Update the set, keeping only elements found in either set, but not in both.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_symmetric_difference_update(PySetObject *so, PyObject *other)
 | |
| /*[clinic end generated code: output=fbb049c0806028de input=a50acf0365e1f0a5]*/
 | |
| {
 | |
|     if (Py_Is((PyObject *)so, other)) {
 | |
|         return set_clear(so, NULL);
 | |
|     }
 | |
| 
 | |
|     int rv;
 | |
|     if (PyDict_CheckExact(other)) {
 | |
|         Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|         rv = set_symmetric_difference_update_dict(so, other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|     }
 | |
|     else if (PyAnySet_Check(other)) {
 | |
|         Py_BEGIN_CRITICAL_SECTION2(so, other);
 | |
|         rv = set_symmetric_difference_update_set(so, (PySetObject *)other);
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|     }
 | |
|     else {
 | |
|         PySetObject *otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
 | |
|         if (otherset == NULL) {
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         Py_BEGIN_CRITICAL_SECTION(so);
 | |
|         rv = set_symmetric_difference_update_set(so, otherset);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
| 
 | |
|         Py_DECREF(otherset);
 | |
|     }
 | |
|     if (rv < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section so other
 | |
| set.symmetric_difference
 | |
|     so: setobject
 | |
|     other: object
 | |
|     /
 | |
| 
 | |
| Return a new set with elements in either the set or other but not both.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_symmetric_difference_impl(PySetObject *so, PyObject *other)
 | |
| /*[clinic end generated code: output=270ee0b5d42b0797 input=624f6e7bbdf70db1]*/
 | |
| {
 | |
|     PySetObject *result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
 | |
|     if (result == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     if (set_update_lock_held(result, other) < 0) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     if (set_symmetric_difference_update_set(result, so) < 0) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_xor(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
|     return set_symmetric_difference(so, other);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_ixor(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(other))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
|     result = set_symmetric_difference_update(so, other);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
|     Py_DECREF(result);
 | |
|     return Py_NewRef(so);
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section so other
 | |
| set.issubset
 | |
|     so: setobject
 | |
|     other: object
 | |
|     /
 | |
| 
 | |
| Report whether another set contains this set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_issubset_impl(PySetObject *so, PyObject *other)
 | |
| /*[clinic end generated code: output=b2b59d5f314555ce input=f2a4fd0f2537758b]*/
 | |
| {
 | |
|     setentry *entry;
 | |
|     Py_ssize_t pos = 0;
 | |
|     int rv;
 | |
| 
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         PyObject *tmp = set_intersection(so, other);
 | |
|         if (tmp == NULL) {
 | |
|             return NULL;
 | |
|         }
 | |
|         int result = (PySet_GET_SIZE(tmp) == PySet_GET_SIZE(so));
 | |
|         Py_DECREF(tmp);
 | |
|         return PyBool_FromLong(result);
 | |
|     }
 | |
|     if (PySet_GET_SIZE(so) > PySet_GET_SIZE(other))
 | |
|         Py_RETURN_FALSE;
 | |
| 
 | |
|     while (set_next(so, &pos, &entry)) {
 | |
|         PyObject *key = entry->key;
 | |
|         Py_INCREF(key);
 | |
|         rv = set_contains_entry((PySetObject *)other, key, entry->hash);
 | |
|         Py_DECREF(key);
 | |
|         if (rv < 0) {
 | |
|             return NULL;
 | |
|         }
 | |
|         if (!rv) {
 | |
|             Py_RETURN_FALSE;
 | |
|         }
 | |
|     }
 | |
|     Py_RETURN_TRUE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section so other
 | |
| set.issuperset
 | |
|     so: setobject
 | |
|     other: object
 | |
|     /
 | |
| 
 | |
| Report whether this set contains another set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_issuperset_impl(PySetObject *so, PyObject *other)
 | |
| /*[clinic end generated code: output=ecf00ce552c09461 input=5f2e1f262e6e4ccc]*/
 | |
| {
 | |
|     if (PyAnySet_Check(other)) {
 | |
|         return set_issubset((PySetObject *)other, (PyObject *)so);
 | |
|     }
 | |
| 
 | |
|     PyObject *key, *it = PyObject_GetIter(other);
 | |
|     if (it == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     while ((key = PyIter_Next(it)) != NULL) {
 | |
|         int rv = set_contains_key(so, key);
 | |
|         Py_DECREF(key);
 | |
|         if (rv < 0) {
 | |
|             Py_DECREF(it);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (!rv) {
 | |
|             Py_DECREF(it);
 | |
|             Py_RETURN_FALSE;
 | |
|         }
 | |
|     }
 | |
|     Py_DECREF(it);
 | |
|     if (PyErr_Occurred()) {
 | |
|         return NULL;
 | |
|     }
 | |
|     Py_RETURN_TRUE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_richcompare(PySetObject *v, PyObject *w, int op)
 | |
| {
 | |
|     PyObject *r1;
 | |
|     int r2;
 | |
| 
 | |
|     if(!PyAnySet_Check(w))
 | |
|         Py_RETURN_NOTIMPLEMENTED;
 | |
| 
 | |
|     switch (op) {
 | |
|     case Py_EQ:
 | |
|         if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
 | |
|             Py_RETURN_FALSE;
 | |
|         if (v->hash != -1  &&
 | |
|             ((PySetObject *)w)->hash != -1 &&
 | |
|             v->hash != ((PySetObject *)w)->hash)
 | |
|             Py_RETURN_FALSE;
 | |
|         return set_issubset(v, w);
 | |
|     case Py_NE:
 | |
|         r1 = set_richcompare(v, w, Py_EQ);
 | |
|         if (r1 == NULL)
 | |
|             return NULL;
 | |
|         r2 = PyObject_IsTrue(r1);
 | |
|         Py_DECREF(r1);
 | |
|         if (r2 < 0)
 | |
|             return NULL;
 | |
|         return PyBool_FromLong(!r2);
 | |
|     case Py_LE:
 | |
|         return set_issubset(v, w);
 | |
|     case Py_GE:
 | |
|         return set_issuperset(v, w);
 | |
|     case Py_LT:
 | |
|         if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w))
 | |
|             Py_RETURN_FALSE;
 | |
|         return set_issubset(v, w);
 | |
|     case Py_GT:
 | |
|         if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w))
 | |
|             Py_RETURN_FALSE;
 | |
|         return set_issuperset(v, w);
 | |
|     }
 | |
|     Py_RETURN_NOTIMPLEMENTED;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.add
 | |
|     so: setobject
 | |
|     object as key: object
 | |
|     /
 | |
| 
 | |
| Add an element to a set.
 | |
| 
 | |
| This has no effect if the element is already present.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_add_impl(PySetObject *so, PyObject *key)
 | |
| /*[clinic end generated code: output=4cc4a937f1425c96 input=03baf62cb0e66514]*/
 | |
| {
 | |
|     if (set_add_key(so, key))
 | |
|         return NULL;
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_contains_lock_held(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     PyObject *tmpkey;
 | |
|     int rv;
 | |
| 
 | |
|     rv = set_contains_key(so, key);
 | |
|     if (rv < 0) {
 | |
|         if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | |
|             return -1;
 | |
|         PyErr_Clear();
 | |
|         tmpkey = make_new_set(&PyFrozenSet_Type, key);
 | |
|         if (tmpkey == NULL)
 | |
|             return -1;
 | |
|         rv = set_contains_key(so, tmpkey);
 | |
|         Py_DECREF(tmpkey);
 | |
|     }
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| int
 | |
| _PySet_Contains(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     int rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION(so);
 | |
|     rv = set_contains_lock_held(so, key);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| @coexist
 | |
| set.__contains__
 | |
|     so: setobject
 | |
|     object as key: object
 | |
|     /
 | |
| 
 | |
| x.__contains__(y) <==> y in x.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set___contains___impl(PySetObject *so, PyObject *key)
 | |
| /*[clinic end generated code: output=b44863d034b3c70e input=4a7d568459617f24]*/
 | |
| {
 | |
|     long result;
 | |
| 
 | |
|     result = set_contains_lock_held(so, key);
 | |
|     if (result < 0)
 | |
|         return NULL;
 | |
|     return PyBool_FromLong(result);
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.remove
 | |
|     so: setobject
 | |
|     object as key: object
 | |
|     /
 | |
| 
 | |
| Remove an element from a set; it must be a member.
 | |
| 
 | |
| If the element is not a member, raise a KeyError.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_remove_impl(PySetObject *so, PyObject *key)
 | |
| /*[clinic end generated code: output=0b9134a2a2200363 input=893e1cb1df98227a]*/
 | |
| {
 | |
|     PyObject *tmpkey;
 | |
|     int rv;
 | |
| 
 | |
|     rv = set_discard_key(so, key);
 | |
|     if (rv < 0) {
 | |
|         if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | |
|             return NULL;
 | |
|         PyErr_Clear();
 | |
|         tmpkey = make_new_set(&PyFrozenSet_Type, key);
 | |
|         if (tmpkey == NULL)
 | |
|             return NULL;
 | |
|         rv = set_discard_key(so, tmpkey);
 | |
|         Py_DECREF(tmpkey);
 | |
|         if (rv < 0)
 | |
|             return NULL;
 | |
|     }
 | |
| 
 | |
|     if (rv == DISCARD_NOTFOUND) {
 | |
|         _PyErr_SetKeyError(key);
 | |
|         return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.discard
 | |
|     so: setobject
 | |
|     object as key: object
 | |
|     /
 | |
| 
 | |
| Remove an element from a set if it is a member.
 | |
| 
 | |
| Unlike set.remove(), the discard() method does not raise
 | |
| an exception when an element is missing from the set.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set_discard_impl(PySetObject *so, PyObject *key)
 | |
| /*[clinic end generated code: output=eec3b687bf32759e input=861cb7fb69b4def0]*/
 | |
| {
 | |
|     PyObject *tmpkey;
 | |
|     int rv;
 | |
| 
 | |
|     rv = set_discard_key(so, key);
 | |
|     if (rv < 0) {
 | |
|         if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | |
|             return NULL;
 | |
|         PyErr_Clear();
 | |
|         tmpkey = make_new_set(&PyFrozenSet_Type, key);
 | |
|         if (tmpkey == NULL)
 | |
|             return NULL;
 | |
|         rv = set_discard_key(so, tmpkey);
 | |
|         Py_DECREF(tmpkey);
 | |
|         if (rv < 0)
 | |
|             return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.__reduce__
 | |
|     so: setobject
 | |
| 
 | |
| Return state information for pickling.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set___reduce___impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=9af7d0e029df87ee input=59405a4249e82f71]*/
 | |
| {
 | |
|     PyObject *keys=NULL, *args=NULL, *result=NULL, *state=NULL;
 | |
| 
 | |
|     keys = PySequence_List((PyObject *)so);
 | |
|     if (keys == NULL)
 | |
|         goto done;
 | |
|     args = PyTuple_Pack(1, keys);
 | |
|     if (args == NULL)
 | |
|         goto done;
 | |
|     state = _PyObject_GetState((PyObject *)so);
 | |
|     if (state == NULL)
 | |
|         goto done;
 | |
|     result = PyTuple_Pack(3, Py_TYPE(so), args, state);
 | |
| done:
 | |
|     Py_XDECREF(args);
 | |
|     Py_XDECREF(keys);
 | |
|     Py_XDECREF(state);
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| @critical_section
 | |
| set.__sizeof__
 | |
|     so: setobject
 | |
| 
 | |
| S.__sizeof__() -> size of S in memory, in bytes.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| set___sizeof___impl(PySetObject *so)
 | |
| /*[clinic end generated code: output=4bfa3df7bd38ed88 input=09e1a09f168eaa23]*/
 | |
| {
 | |
|     size_t res = _PyObject_SIZE(Py_TYPE(so));
 | |
|     if (so->table != so->smalltable) {
 | |
|         res += ((size_t)so->mask + 1) * sizeof(setentry);
 | |
|     }
 | |
|     return PyLong_FromSize_t(res);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_init(PySetObject *self, PyObject *args, PyObject *kwds)
 | |
| {
 | |
|     PyObject *iterable = NULL;
 | |
| 
 | |
|     if (!_PyArg_NoKeywords("set", kwds))
 | |
|         return -1;
 | |
|     if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
 | |
|         return -1;
 | |
| 
 | |
|     if (Py_REFCNT(self) == 1 && self->fill == 0) {
 | |
|         self->hash = -1;
 | |
|         if (iterable == NULL) {
 | |
|             return 0;
 | |
|         }
 | |
|         return set_update_local(self, iterable);
 | |
|     }
 | |
|     Py_BEGIN_CRITICAL_SECTION(self);
 | |
|     if (self->fill)
 | |
|         set_clear_internal(self);
 | |
|     self->hash = -1;
 | |
|     Py_END_CRITICAL_SECTION();
 | |
| 
 | |
|     if (iterable == NULL)
 | |
|         return 0;
 | |
|     return set_update_internal(self, iterable);
 | |
| }
 | |
| 
 | |
| static PyObject*
 | |
| set_vectorcall(PyObject *type, PyObject * const*args,
 | |
|                size_t nargsf, PyObject *kwnames)
 | |
| {
 | |
|     assert(PyType_Check(type));
 | |
| 
 | |
|     if (!_PyArg_NoKwnames("set", kwnames)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
 | |
|     if (!_PyArg_CheckPositional("set", nargs, 0, 1)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (nargs) {
 | |
|         return make_new_set(_PyType_CAST(type), args[0]);
 | |
|     }
 | |
| 
 | |
|     return make_new_set(_PyType_CAST(type), NULL);
 | |
| }
 | |
| 
 | |
| static PySequenceMethods set_as_sequence = {
 | |
|     (lenfunc)set_len,                   /* sq_length */
 | |
|     0,                                  /* sq_concat */
 | |
|     0,                                  /* sq_repeat */
 | |
|     0,                                  /* sq_item */
 | |
|     0,                                  /* sq_slice */
 | |
|     0,                                  /* sq_ass_item */
 | |
|     0,                                  /* sq_ass_slice */
 | |
|     (objobjproc)_PySet_Contains,        /* sq_contains */
 | |
| };
 | |
| 
 | |
| /* set object ********************************************************/
 | |
| 
 | |
| static PyMethodDef set_methods[] = {
 | |
|     SET_ADD_METHODDEF
 | |
|     SET_CLEAR_METHODDEF
 | |
|     SET___CONTAINS___METHODDEF
 | |
|     SET_COPY_METHODDEF
 | |
|     SET_DISCARD_METHODDEF
 | |
|     SET_DIFFERENCE_MULTI_METHODDEF
 | |
|     SET_DIFFERENCE_UPDATE_METHODDEF
 | |
|     SET_INTERSECTION_MULTI_METHODDEF
 | |
|     SET_INTERSECTION_UPDATE_MULTI_METHODDEF
 | |
|     SET_ISDISJOINT_METHODDEF
 | |
|     SET_ISSUBSET_METHODDEF
 | |
|     SET_ISSUPERSET_METHODDEF
 | |
|     SET_POP_METHODDEF
 | |
|     SET___REDUCE___METHODDEF
 | |
|     SET_REMOVE_METHODDEF
 | |
|     SET___SIZEOF___METHODDEF
 | |
|     SET_SYMMETRIC_DIFFERENCE_METHODDEF
 | |
|     SET_SYMMETRIC_DIFFERENCE_UPDATE_METHODDEF
 | |
|     SET_UNION_METHODDEF
 | |
|     SET_UPDATE_METHODDEF
 | |
|     {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
 | |
|     {NULL,              NULL}   /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyNumberMethods set_as_number = {
 | |
|     0,                                  /*nb_add*/
 | |
|     (binaryfunc)set_sub,                /*nb_subtract*/
 | |
|     0,                                  /*nb_multiply*/
 | |
|     0,                                  /*nb_remainder*/
 | |
|     0,                                  /*nb_divmod*/
 | |
|     0,                                  /*nb_power*/
 | |
|     0,                                  /*nb_negative*/
 | |
|     0,                                  /*nb_positive*/
 | |
|     0,                                  /*nb_absolute*/
 | |
|     0,                                  /*nb_bool*/
 | |
|     0,                                  /*nb_invert*/
 | |
|     0,                                  /*nb_lshift*/
 | |
|     0,                                  /*nb_rshift*/
 | |
|     (binaryfunc)set_and,                /*nb_and*/
 | |
|     (binaryfunc)set_xor,                /*nb_xor*/
 | |
|     (binaryfunc)set_or,                 /*nb_or*/
 | |
|     0,                                  /*nb_int*/
 | |
|     0,                                  /*nb_reserved*/
 | |
|     0,                                  /*nb_float*/
 | |
|     0,                                  /*nb_inplace_add*/
 | |
|     (binaryfunc)set_isub,               /*nb_inplace_subtract*/
 | |
|     0,                                  /*nb_inplace_multiply*/
 | |
|     0,                                  /*nb_inplace_remainder*/
 | |
|     0,                                  /*nb_inplace_power*/
 | |
|     0,                                  /*nb_inplace_lshift*/
 | |
|     0,                                  /*nb_inplace_rshift*/
 | |
|     (binaryfunc)set_iand,               /*nb_inplace_and*/
 | |
|     (binaryfunc)set_ixor,               /*nb_inplace_xor*/
 | |
|     (binaryfunc)set_ior,                /*nb_inplace_or*/
 | |
| };
 | |
| 
 | |
| PyDoc_STRVAR(set_doc,
 | |
| "set(iterable=(), /)\n\
 | |
| --\n\
 | |
| \n\
 | |
| Build an unordered collection of unique elements.");
 | |
| 
 | |
| PyTypeObject PySet_Type = {
 | |
|     PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | |
|     "set",                              /* tp_name */
 | |
|     sizeof(PySetObject),                /* tp_basicsize */
 | |
|     0,                                  /* tp_itemsize */
 | |
|     /* methods */
 | |
|     (destructor)set_dealloc,            /* tp_dealloc */
 | |
|     0,                                  /* tp_vectorcall_offset */
 | |
|     0,                                  /* tp_getattr */
 | |
|     0,                                  /* tp_setattr */
 | |
|     0,                                  /* tp_as_async */
 | |
|     (reprfunc)set_repr,                 /* tp_repr */
 | |
|     &set_as_number,                     /* tp_as_number */
 | |
|     &set_as_sequence,                   /* tp_as_sequence */
 | |
|     0,                                  /* tp_as_mapping */
 | |
|     PyObject_HashNotImplemented,        /* tp_hash */
 | |
|     0,                                  /* tp_call */
 | |
|     0,                                  /* tp_str */
 | |
|     PyObject_GenericGetAttr,            /* tp_getattro */
 | |
|     0,                                  /* tp_setattro */
 | |
|     0,                                  /* tp_as_buffer */
 | |
|     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
 | |
|         Py_TPFLAGS_BASETYPE |
 | |
|         _Py_TPFLAGS_MATCH_SELF,       /* tp_flags */
 | |
|     set_doc,                            /* tp_doc */
 | |
|     (traverseproc)set_traverse,         /* tp_traverse */
 | |
|     (inquiry)set_clear_internal,        /* tp_clear */
 | |
|     (richcmpfunc)set_richcompare,       /* tp_richcompare */
 | |
|     offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
 | |
|     (getiterfunc)set_iter,              /* tp_iter */
 | |
|     0,                                  /* tp_iternext */
 | |
|     set_methods,                        /* tp_methods */
 | |
|     0,                                  /* tp_members */
 | |
|     0,                                  /* tp_getset */
 | |
|     0,                                  /* tp_base */
 | |
|     0,                                  /* tp_dict */
 | |
|     0,                                  /* tp_descr_get */
 | |
|     0,                                  /* tp_descr_set */
 | |
|     0,                                  /* tp_dictoffset */
 | |
|     (initproc)set_init,                 /* tp_init */
 | |
|     PyType_GenericAlloc,                /* tp_alloc */
 | |
|     set_new,                            /* tp_new */
 | |
|     PyObject_GC_Del,                    /* tp_free */
 | |
|     .tp_vectorcall = set_vectorcall,
 | |
| };
 | |
| 
 | |
| /* frozenset object ********************************************************/
 | |
| 
 | |
| 
 | |
| static PyMethodDef frozenset_methods[] = {
 | |
|     SET___CONTAINS___METHODDEF
 | |
|     FROZENSET_COPY_METHODDEF
 | |
|     SET_DIFFERENCE_MULTI_METHODDEF
 | |
|     SET_INTERSECTION_MULTI_METHODDEF
 | |
|     SET_ISDISJOINT_METHODDEF
 | |
|     SET_ISSUBSET_METHODDEF
 | |
|     SET_ISSUPERSET_METHODDEF
 | |
|     SET___REDUCE___METHODDEF
 | |
|     SET___SIZEOF___METHODDEF
 | |
|     SET_SYMMETRIC_DIFFERENCE_METHODDEF
 | |
|     SET_UNION_METHODDEF
 | |
|     {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
 | |
|     {NULL,              NULL}   /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyNumberMethods frozenset_as_number = {
 | |
|     0,                                  /*nb_add*/
 | |
|     (binaryfunc)set_sub,                /*nb_subtract*/
 | |
|     0,                                  /*nb_multiply*/
 | |
|     0,                                  /*nb_remainder*/
 | |
|     0,                                  /*nb_divmod*/
 | |
|     0,                                  /*nb_power*/
 | |
|     0,                                  /*nb_negative*/
 | |
|     0,                                  /*nb_positive*/
 | |
|     0,                                  /*nb_absolute*/
 | |
|     0,                                  /*nb_bool*/
 | |
|     0,                                  /*nb_invert*/
 | |
|     0,                                  /*nb_lshift*/
 | |
|     0,                                  /*nb_rshift*/
 | |
|     (binaryfunc)set_and,                /*nb_and*/
 | |
|     (binaryfunc)set_xor,                /*nb_xor*/
 | |
|     (binaryfunc)set_or,                 /*nb_or*/
 | |
| };
 | |
| 
 | |
| PyDoc_STRVAR(frozenset_doc,
 | |
| "frozenset(iterable=(), /)\n\
 | |
| --\n\
 | |
| \n\
 | |
| Build an immutable unordered collection of unique elements.");
 | |
| 
 | |
| PyTypeObject PyFrozenSet_Type = {
 | |
|     PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | |
|     "frozenset",                        /* tp_name */
 | |
|     sizeof(PySetObject),                /* tp_basicsize */
 | |
|     0,                                  /* tp_itemsize */
 | |
|     /* methods */
 | |
|     (destructor)set_dealloc,            /* tp_dealloc */
 | |
|     0,                                  /* tp_vectorcall_offset */
 | |
|     0,                                  /* tp_getattr */
 | |
|     0,                                  /* tp_setattr */
 | |
|     0,                                  /* tp_as_async */
 | |
|     (reprfunc)set_repr,                 /* tp_repr */
 | |
|     &frozenset_as_number,               /* tp_as_number */
 | |
|     &set_as_sequence,                   /* tp_as_sequence */
 | |
|     0,                                  /* tp_as_mapping */
 | |
|     frozenset_hash,                     /* tp_hash */
 | |
|     0,                                  /* tp_call */
 | |
|     0,                                  /* tp_str */
 | |
|     PyObject_GenericGetAttr,            /* tp_getattro */
 | |
|     0,                                  /* tp_setattro */
 | |
|     0,                                  /* tp_as_buffer */
 | |
|     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
 | |
|         Py_TPFLAGS_BASETYPE |
 | |
|         _Py_TPFLAGS_MATCH_SELF,       /* tp_flags */
 | |
|     frozenset_doc,                      /* tp_doc */
 | |
|     (traverseproc)set_traverse,         /* tp_traverse */
 | |
|     (inquiry)set_clear_internal,        /* tp_clear */
 | |
|     (richcmpfunc)set_richcompare,       /* tp_richcompare */
 | |
|     offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
 | |
|     (getiterfunc)set_iter,              /* tp_iter */
 | |
|     0,                                  /* tp_iternext */
 | |
|     frozenset_methods,                  /* tp_methods */
 | |
|     0,                                  /* tp_members */
 | |
|     0,                                  /* tp_getset */
 | |
|     0,                                  /* tp_base */
 | |
|     0,                                  /* tp_dict */
 | |
|     0,                                  /* tp_descr_get */
 | |
|     0,                                  /* tp_descr_set */
 | |
|     0,                                  /* tp_dictoffset */
 | |
|     0,                                  /* tp_init */
 | |
|     PyType_GenericAlloc,                /* tp_alloc */
 | |
|     frozenset_new,                      /* tp_new */
 | |
|     PyObject_GC_Del,                    /* tp_free */
 | |
|     .tp_vectorcall = frozenset_vectorcall,
 | |
| };
 | |
| 
 | |
| 
 | |
| /***** C API functions *************************************************/
 | |
| 
 | |
| PyObject *
 | |
| PySet_New(PyObject *iterable)
 | |
| {
 | |
|     return make_new_set(&PySet_Type, iterable);
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyFrozenSet_New(PyObject *iterable)
 | |
| {
 | |
|     return make_new_set(&PyFrozenSet_Type, iterable);
 | |
| }
 | |
| 
 | |
| Py_ssize_t
 | |
| PySet_Size(PyObject *anyset)
 | |
| {
 | |
|     if (!PyAnySet_Check(anyset)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     return set_len((PySetObject *)anyset);
 | |
| }
 | |
| 
 | |
| int
 | |
| PySet_Clear(PyObject *set)
 | |
| {
 | |
|     if (!PySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     (void)set_clear((PySetObject *)set, NULL);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| _PySet_ClearInternal(PySetObject *so)
 | |
| {
 | |
|     (void)set_clear_internal(so);
 | |
| }
 | |
| 
 | |
| int
 | |
| PySet_Contains(PyObject *anyset, PyObject *key)
 | |
| {
 | |
|     if (!PyAnySet_Check(anyset)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     int rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION(anyset);
 | |
|     rv = set_contains_key((PySetObject *)anyset, key);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| int
 | |
| PySet_Discard(PyObject *set, PyObject *key)
 | |
| {
 | |
|     if (!PySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     int rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION(set);
 | |
|     rv = set_discard_key((PySetObject *)set, key);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| int
 | |
| PySet_Add(PyObject *anyset, PyObject *key)
 | |
| {
 | |
|     if (!PySet_Check(anyset) &&
 | |
|         (!PyFrozenSet_Check(anyset) || Py_REFCNT(anyset) != 1)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     int rv;
 | |
|     Py_BEGIN_CRITICAL_SECTION(anyset);
 | |
|     rv = set_add_key((PySetObject *)anyset, key);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| int
 | |
| _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
 | |
| {
 | |
|     setentry *entry;
 | |
| 
 | |
|     if (!PyAnySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     if (set_next((PySetObject *)set, pos, &entry) == 0)
 | |
|         return 0;
 | |
|     *key = entry->key;
 | |
|     *hash = entry->hash;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| int
 | |
| _PySet_NextEntryRef(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
 | |
| {
 | |
|     setentry *entry;
 | |
| 
 | |
|     if (!PyAnySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(set);
 | |
|     if (set_next((PySetObject *)set, pos, &entry) == 0)
 | |
|         return 0;
 | |
|     *key = Py_NewRef(entry->key);
 | |
|     *hash = entry->hash;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PySet_Pop(PyObject *set)
 | |
| {
 | |
|     if (!PySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return NULL;
 | |
|     }
 | |
|     return set_pop((PySetObject *)set, NULL);
 | |
| }
 | |
| 
 | |
| int
 | |
| _PySet_Update(PyObject *set, PyObject *iterable)
 | |
| {
 | |
|     if (!PySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     return set_update_internal((PySetObject *)set, iterable);
 | |
| }
 | |
| 
 | |
| /* Exported for the gdb plugin's benefit. */
 | |
| PyObject *_PySet_Dummy = dummy;
 | |
| 
 | |
| /***** Dummy Struct  *************************************************/
 | |
| 
 | |
| static PyObject *
 | |
| dummy_repr(PyObject *op)
 | |
| {
 | |
|     return PyUnicode_FromString("<dummy key>");
 | |
| }
 | |
| 
 | |
| static void _Py_NO_RETURN
 | |
| dummy_dealloc(PyObject* ignore)
 | |
| {
 | |
|     Py_FatalError("deallocating <dummy key>");
 | |
| }
 | |
| 
 | |
| static PyTypeObject _PySetDummy_Type = {
 | |
|     PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | |
|     "<dummy key> type",
 | |
|     0,
 | |
|     0,
 | |
|     dummy_dealloc,      /*tp_dealloc*/ /*never called*/
 | |
|     0,                  /*tp_vectorcall_offset*/
 | |
|     0,                  /*tp_getattr*/
 | |
|     0,                  /*tp_setattr*/
 | |
|     0,                  /*tp_as_async*/
 | |
|     dummy_repr,         /*tp_repr*/
 | |
|     0,                  /*tp_as_number*/
 | |
|     0,                  /*tp_as_sequence*/
 | |
|     0,                  /*tp_as_mapping*/
 | |
|     0,                  /*tp_hash */
 | |
|     0,                  /*tp_call */
 | |
|     0,                  /*tp_str */
 | |
|     0,                  /*tp_getattro */
 | |
|     0,                  /*tp_setattro */
 | |
|     0,                  /*tp_as_buffer */
 | |
|     Py_TPFLAGS_DEFAULT, /*tp_flags */
 | |
| };
 | |
| 
 | |
| static PyObject _dummy_struct = _PyObject_HEAD_INIT(&_PySetDummy_Type);
 |