mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2824 lines
		
	
	
	
		
			78 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2824 lines
		
	
	
	
		
			78 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 "stringlib/eq.h"               // unicode_eq()
 | 
						|
#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)
 | 
						|
                    && unicode_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_takeref(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;
 | 
						|
 | 
						|
  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)
 | 
						|
                    && unicode_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;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
 | 
						|
    freeslot->key = key;
 | 
						|
    freeslot->hash = hash;
 | 
						|
    return 0;
 | 
						|
 | 
						|
  found_unused:
 | 
						|
    so->fill++;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
 | 
						|
{
 | 
						|
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | 
						|
 | 
						|
    return set_add_entry_takeref(so, Py_NewRef(key), hash);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_PySet_AddTakeRef(PySetObject *so, PyObject *key)
 | 
						|
{
 | 
						|
    Py_hash_t hash = _PyObject_HashFast(key);
 | 
						|
    if (hash == -1) {
 | 
						|
        Py_DECREF(key);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    // We don't pre-increment here, the caller holds a strong
 | 
						|
    // reference to the object which we are stealing.
 | 
						|
    return set_add_entry_takeref(so, key, hash);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
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;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
 | 
						|
    Py_DECREF(old_key);
 | 
						|
    return DISCARD_FOUND;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_add_key(PySetObject *so, PyObject *key)
 | 
						|
{
 | 
						|
    Py_hash_t hash = _PyObject_HashFast(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 = _PyObject_HashFast(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 = _PyObject_HashFast(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;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, 0);
 | 
						|
    so->mask = PySet_MINSIZE - 1;
 | 
						|
    so->table = so->smalltable;
 | 
						|
    so->hash = -1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_clear_internal(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    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(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    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);
 | 
						|
    }
 | 
						|
 | 
						|
    // gh-129967: avoid PySequence_List because it might re-lock the object
 | 
						|
    // lock or the GIL and allow something to clear the set from underneath us.
 | 
						|
    keys = PyList_New(so->used);
 | 
						|
    if (keys == NULL) {
 | 
						|
        goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    Py_ssize_t pos = 0, idx = 0;
 | 
						|
    setentry *entry;
 | 
						|
    while (set_next(so, &pos, &entry)) {
 | 
						|
        PyList_SET_ITEM(keys, idx++, Py_NewRef(entry->key));
 | 
						|
    }
 | 
						|
 | 
						|
    /* 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(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    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(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    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));
 | 
						|
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
 | 
						|
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(otherset);
 | 
						|
 | 
						|
    other = _PySet_CAST(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;
 | 
						|
        FT_ATOMIC_STORE_SSIZE_RELAXED(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;
 | 
						|
        FT_ATOMIC_STORE_SSIZE_RELAXED(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;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
 | 
						|
    so->finger = entry - so->table + 1;   /* next place to start */
 | 
						|
    return key;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_traverse(PyObject *self, visitproc visit, void *arg)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    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).
 | 
						|
 | 
						|
   This hash algorithm can be used on either a frozenset or a set.
 | 
						|
   When it is used on a set, it computes the hash value of the equivalent
 | 
						|
   frozenset without creating a new frozenset object. */
 | 
						|
 | 
						|
static Py_hash_t
 | 
						|
frozenset_hash_impl(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    Py_uhash_t hash = 0;
 | 
						|
    setentry *entry;
 | 
						|
 | 
						|
    /* 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;
 | 
						|
 | 
						|
    return (Py_hash_t)hash;
 | 
						|
}
 | 
						|
 | 
						|
static Py_hash_t
 | 
						|
frozenset_hash(PyObject *self)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    Py_uhash_t hash;
 | 
						|
 | 
						|
    if (so->hash != -1) {
 | 
						|
        return so->hash;
 | 
						|
    }
 | 
						|
 | 
						|
    hash = frozenset_hash_impl(self);
 | 
						|
    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(PyObject *self)
 | 
						|
{
 | 
						|
    setiterobject *si = (setiterobject*)self;
 | 
						|
    /* 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(PyObject *self, visitproc visit, void *arg)
 | 
						|
{
 | 
						|
    setiterobject *si = (setiterobject*)self;
 | 
						|
    Py_VISIT(si->si_set);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
setiter_len(PyObject *op, PyObject *Py_UNUSED(ignored))
 | 
						|
{
 | 
						|
    setiterobject *si = (setiterobject*)op;
 | 
						|
    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_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
 | 
						|
{
 | 
						|
    setiterobject *si = (setiterobject*)op;
 | 
						|
 | 
						|
    /* 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__", setiter_len, METH_NOARGS, length_hint_doc},
 | 
						|
    {"__reduce__", setiter_reduce, METH_NOARGS, reduce_doc},
 | 
						|
    {NULL,              NULL}           /* sentinel */
 | 
						|
};
 | 
						|
 | 
						|
static PyObject *setiter_iternext(PyObject *self)
 | 
						|
{
 | 
						|
    setiterobject *si = (setiterobject*)self;
 | 
						|
    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 */
 | 
						|
    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 */
 | 
						|
    setiter_traverse,                           /* tp_traverse */
 | 
						|
    0,                                          /* tp_clear */
 | 
						|
    0,                                          /* tp_richcompare */
 | 
						|
    0,                                          /* tp_weaklistoffset */
 | 
						|
    PyObject_SelfIter,                          /* tp_iter */
 | 
						|
    setiter_iternext,                           /* tp_iternext */
 | 
						|
    setiter_methods,                            /* tp_methods */
 | 
						|
    0,
 | 
						|
};
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_iter(PyObject *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: array
 | 
						|
 | 
						|
Update the set, adding elements from all others.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_update_impl(PySetObject *so, PyObject * const *others,
 | 
						|
                Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=017c781c992d5c23 input=ed5d78885b076636]*/
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    for (i = 0; i < others_length; i++) {
 | 
						|
        PyObject *other = others[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;
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used);
 | 
						|
    FT_ATOMIC_STORE_SSIZE_RELAXED(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((PyObject*)so);
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
set.union
 | 
						|
    so: setobject
 | 
						|
    *others: array
 | 
						|
 | 
						|
Return a new set with elements from the set and all others.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_union_impl(PySetObject *so, PyObject * const *others,
 | 
						|
               Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=b1bfa3d74065f27e input=55a2e81db6347a4f]*/
 | 
						|
{
 | 
						|
    PySetObject *result;
 | 
						|
    PyObject *other;
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    result = (PySetObject *)set_copy((PyObject *)so, NULL);
 | 
						|
    if (result == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    for (i = 0; i < others_length; i++) {
 | 
						|
        other = others[i];
 | 
						|
        if ((PyObject *)so == other)
 | 
						|
            continue;
 | 
						|
        if (set_update_local(result, other)) {
 | 
						|
            Py_DECREF(result);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return (PyObject *)result;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_or(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    PySetObject *result;
 | 
						|
 | 
						|
    if (!PyAnySet_Check(self) || !PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
 | 
						|
    result = (PySetObject *)set_copy(self, NULL);
 | 
						|
    if (result == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (Py_Is(self, other)) {
 | 
						|
        return (PyObject *)result;
 | 
						|
    }
 | 
						|
    if (set_update_local(result, other)) {
 | 
						|
        Py_DECREF(result);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return (PyObject *)result;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_ior(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    if (!PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    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: array
 | 
						|
 | 
						|
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 * const *others,
 | 
						|
                            Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=db9ff9f875132b6b input=36c7b615694cadae]*/
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    if (others_length == 0) {
 | 
						|
        return set_copy((PyObject *)so, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    PyObject *result = Py_NewRef(so);
 | 
						|
    for (i = 0; i < others_length; i++) {
 | 
						|
        PyObject *other = others[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: array
 | 
						|
 | 
						|
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 * const *others,
 | 
						|
                                   Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=d768b5584675b48d input=782e422fc370e4fc]*/
 | 
						|
{
 | 
						|
    PyObject *tmp;
 | 
						|
 | 
						|
    tmp = set_intersection_multi_impl(so, others, others_length);
 | 
						|
    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(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    if (!PyAnySet_Check(self) || !PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    PyObject *rv;
 | 
						|
    Py_BEGIN_CRITICAL_SECTION2(so, other);
 | 
						|
    rv = set_intersection(so, other);
 | 
						|
    Py_END_CRITICAL_SECTION2();
 | 
						|
 | 
						|
    return rv;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_iand(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    PyObject *result;
 | 
						|
 | 
						|
    if (!PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    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((PyObject*)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: array
 | 
						|
 | 
						|
Update the set, removing elements found in others.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_difference_update_impl(PySetObject *so, PyObject * const *others,
 | 
						|
                           Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=04a22179b322cfe6 input=93ac28ba5b233696]*/
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    for (i = 0; i < others_length; i++) {
 | 
						|
        PyObject *other = others[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: array
 | 
						|
 | 
						|
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 * const *others,
 | 
						|
                          Py_ssize_t others_length)
 | 
						|
/*[clinic end generated code: output=b0d33fb05d5477a7 input=c1eb448d483416ad]*/
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
    PyObject *result, *other;
 | 
						|
 | 
						|
    if (others_length == 0) {
 | 
						|
        return set_copy((PyObject *)so, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    other = others[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 < others_length; i++) {
 | 
						|
        other = others[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(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    if (!PyAnySet_Check(self) || !PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    PyObject *rv;
 | 
						|
    Py_BEGIN_CRITICAL_SECTION2(so, other);
 | 
						|
    rv = set_difference(so, other);
 | 
						|
    Py_END_CRITICAL_SECTION2();
 | 
						|
    return rv;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_isub(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    if (!PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    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_impl(PySetObject *so, PyObject *other)
 | 
						|
/*[clinic end generated code: output=79f80b4ee5da66c1 input=a50acf0365e1f0a5]*/
 | 
						|
{
 | 
						|
    if (Py_Is((PyObject *)so, other)) {
 | 
						|
        return set_clear((PyObject *)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(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    if (!PyAnySet_Check(self) || !PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    return set_symmetric_difference((PyObject*)so, other);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
set_ixor(PyObject *self, PyObject *other)
 | 
						|
{
 | 
						|
    PyObject *result;
 | 
						|
 | 
						|
    if (!PyAnySet_Check(other))
 | 
						|
        Py_RETURN_NOTIMPLEMENTED;
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
 | 
						|
    result = set_symmetric_difference_update((PyObject*)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(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(PyObject *self, PyObject *w, int op)
 | 
						|
{
 | 
						|
    PySetObject *v = _PySet_CAST(self);
 | 
						|
    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((PyObject*)v, w);
 | 
						|
    case Py_NE:
 | 
						|
        r1 = set_richcompare((PyObject*)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((PyObject*)v, w);
 | 
						|
    case Py_GE:
 | 
						|
        return set_issuperset((PyObject*)v, w);
 | 
						|
    case Py_LT:
 | 
						|
        if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w))
 | 
						|
            Py_RETURN_FALSE;
 | 
						|
        return set_issubset((PyObject*)v, w);
 | 
						|
    case Py_GT:
 | 
						|
        if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w))
 | 
						|
            Py_RETURN_FALSE;
 | 
						|
        return set_issuperset((PyObject*)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)
 | 
						|
{
 | 
						|
    int rv;
 | 
						|
 | 
						|
    rv = set_contains_key(so, key);
 | 
						|
    if (rv < 0) {
 | 
						|
        if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | 
						|
            return -1;
 | 
						|
        PyErr_Clear();
 | 
						|
        Py_hash_t hash;
 | 
						|
        Py_BEGIN_CRITICAL_SECTION(key);
 | 
						|
        hash = frozenset_hash_impl(key);
 | 
						|
        Py_END_CRITICAL_SECTION();
 | 
						|
        rv = set_contains_entry(so, key, hash);
 | 
						|
    }
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_contains(PyObject *self, PyObject *key)
 | 
						|
{
 | 
						|
    PySetObject *so = _PySet_CAST(self);
 | 
						|
    return _PySet_Contains(so, key);
 | 
						|
}
 | 
						|
 | 
						|
/*[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]*/
 | 
						|
{
 | 
						|
    int rv;
 | 
						|
 | 
						|
    rv = set_discard_key(so, key);
 | 
						|
    if (rv < 0) {
 | 
						|
        if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | 
						|
            return NULL;
 | 
						|
        PyErr_Clear();
 | 
						|
        Py_hash_t hash;
 | 
						|
        Py_BEGIN_CRITICAL_SECTION(key);
 | 
						|
        hash = frozenset_hash_impl(key);
 | 
						|
        Py_END_CRITICAL_SECTION();
 | 
						|
        rv = set_discard_entry(so, key, hash);
 | 
						|
        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]*/
 | 
						|
{
 | 
						|
    int rv;
 | 
						|
 | 
						|
    rv = set_discard_key(so, key);
 | 
						|
    if (rv < 0) {
 | 
						|
        if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | 
						|
            return NULL;
 | 
						|
        PyErr_Clear();
 | 
						|
        Py_hash_t hash;
 | 
						|
        Py_BEGIN_CRITICAL_SECTION(key);
 | 
						|
        hash = frozenset_hash_impl(key);
 | 
						|
        Py_END_CRITICAL_SECTION();
 | 
						|
        rv = set_discard_entry(so, key, hash);
 | 
						|
        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(PyObject *so, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    PySetObject *self = _PySet_CAST(so);
 | 
						|
    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((PyObject*)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 = {
 | 
						|
    set_len,                            /* sq_length */
 | 
						|
    0,                                  /* sq_concat */
 | 
						|
    0,                                  /* sq_repeat */
 | 
						|
    0,                                  /* sq_item */
 | 
						|
    0,                                  /* sq_slice */
 | 
						|
    0,                                  /* sq_ass_item */
 | 
						|
    0,                                  /* sq_ass_slice */
 | 
						|
    set_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*/
 | 
						|
    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*/
 | 
						|
    set_and,                            /*nb_and*/
 | 
						|
    set_xor,                            /*nb_xor*/
 | 
						|
    set_or,                             /*nb_or*/
 | 
						|
    0,                                  /*nb_int*/
 | 
						|
    0,                                  /*nb_reserved*/
 | 
						|
    0,                                  /*nb_float*/
 | 
						|
    0,                                  /*nb_inplace_add*/
 | 
						|
    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*/
 | 
						|
    set_iand,                           /*nb_inplace_and*/
 | 
						|
    set_ixor,                           /*nb_inplace_xor*/
 | 
						|
    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 */
 | 
						|
    set_dealloc,                        /* tp_dealloc */
 | 
						|
    0,                                  /* tp_vectorcall_offset */
 | 
						|
    0,                                  /* tp_getattr */
 | 
						|
    0,                                  /* tp_setattr */
 | 
						|
    0,                                  /* tp_as_async */
 | 
						|
    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 */
 | 
						|
    set_traverse,                       /* tp_traverse */
 | 
						|
    set_clear_internal,                 /* tp_clear */
 | 
						|
    set_richcompare,                    /* tp_richcompare */
 | 
						|
    offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
 | 
						|
    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 */
 | 
						|
    set_init,                           /* tp_init */
 | 
						|
    PyType_GenericAlloc,                /* tp_alloc */
 | 
						|
    set_new,                            /* tp_new */
 | 
						|
    PyObject_GC_Del,                    /* tp_free */
 | 
						|
    .tp_vectorcall = set_vectorcall,
 | 
						|
    .tp_version_tag = _Py_TYPE_VERSION_SET,
 | 
						|
};
 | 
						|
 | 
						|
/* 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*/
 | 
						|
    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*/
 | 
						|
    set_and,                            /*nb_and*/
 | 
						|
    set_xor,                            /*nb_xor*/
 | 
						|
    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 */
 | 
						|
    set_dealloc,                        /* tp_dealloc */
 | 
						|
    0,                                  /* tp_vectorcall_offset */
 | 
						|
    0,                                  /* tp_getattr */
 | 
						|
    0,                                  /* tp_setattr */
 | 
						|
    0,                                  /* tp_as_async */
 | 
						|
    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 */
 | 
						|
    set_traverse,                       /* tp_traverse */
 | 
						|
    set_clear_internal,                 /* tp_clear */
 | 
						|
    set_richcompare,                    /* tp_richcompare */
 | 
						|
    offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
 | 
						|
    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,
 | 
						|
    .tp_version_tag = _Py_TYPE_VERSION_FROZEN_SET,
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/***** 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(anyset);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
PySet_Clear(PyObject *set)
 | 
						|
{
 | 
						|
    if (!PySet_Check(set)) {
 | 
						|
        PyErr_BadInternalCall();
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    (void)set_clear(set, NULL);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_PySet_ClearInternal(PySetObject *so)
 | 
						|
{
 | 
						|
    (void)set_clear_internal((PyObject*)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(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);
 |