aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Objects/setobject.c
diff options
context:
space:
mode:
authornkozlovskiy <nmk@ydb.tech>2023-09-29 12:24:06 +0300
committernkozlovskiy <nmk@ydb.tech>2023-09-29 12:41:34 +0300
commite0e3e1717e3d33762ce61950504f9637a6e669ed (patch)
treebca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Objects/setobject.c
parent38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff)
downloadydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Objects/setobject.c')
-rw-r--r--contrib/tools/python3/src/Objects/setobject.c2561
1 files changed, 2561 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Objects/setobject.c b/contrib/tools/python3/src/Objects/setobject.c
new file mode 100644
index 0000000000..4b6a8b8dfb
--- /dev/null
+++ b/contrib/tools/python3/src/Objects/setobject.c
@@ -0,0 +1,2561 @@
+
+/* set object implementation
+
+ Written and maintained by Raymond D. Hettinger <python@rcn.com>
+ Derived from Lib/sets.py and 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_object.h" // _PyObject_GC_UNTRACK()
+#include <stddef.h> // offsetof()
+
+/* Object used as dummy key to fill deleted entries */
+static PyObject _dummy_struct;
+
+#define dummy (&_dummy_struct)
+
+
+/* ======================================================================== */
+/* ======= Begin logic for probing the hash table ========================= */
+
+/* Set this to zero to turn-off linear probing */
+#ifndef LINEAR_PROBES
+#define LINEAR_PROBES 9
+#endif
+
+/* This must be >= 1 */
+#define PERTURB_SHIFT 5
+
+static setentry *
+set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
+{
+ setentry *table;
+ setentry *entry;
+ size_t perturb = hash;
+ size_t mask = so->mask;
+ size_t i = (size_t)hash & mask; /* Unsigned for defined overflow behavior */
+ int probes;
+ int cmp;
+
+ while (1) {
+ entry = &so->table[i];
+ probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0;
+ do {
+ if (entry->hash == 0 && entry->key == NULL)
+ return entry;
+ if (entry->hash == hash) {
+ PyObject *startkey = entry->key;
+ assert(startkey != dummy);
+ if (startkey == key)
+ return entry;
+ if (PyUnicode_CheckExact(startkey)
+ && PyUnicode_CheckExact(key)
+ && _PyUnicode_EQ(startkey, key))
+ return entry;
+ table = so->table;
+ Py_INCREF(startkey);
+ cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0)
+ return NULL;
+ if (table != so->table || entry->key != startkey)
+ return set_lookkey(so, key, hash);
+ if (cmp > 0)
+ return entry;
+ mask = so->mask;
+ }
+ entry++;
+ } while (probes--);
+ perturb >>= PERTURB_SHIFT;
+ i = (i * 5 + 1 + perturb) & mask;
+ }
+}
+
+static int set_table_resize(PySetObject *, Py_ssize_t);
+
+static int
+set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
+{
+ setentry *table;
+ setentry *freeslot;
+ setentry *entry;
+ size_t perturb;
+ size_t mask;
+ size_t i; /* Unsigned for defined overflow behavior */
+ int probes;
+ int cmp;
+
+ /* Pre-increment is necessary to prevent arbitrary code in the rich
+ comparison from deallocating the key just before the insertion. */
+ Py_INCREF(key);
+
+ restart:
+
+ mask = so->mask;
+ i = (size_t)hash & mask;
+ freeslot = NULL;
+ perturb = hash;
+
+ while (1) {
+ entry = &so->table[i];
+ probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0;
+ do {
+ if (entry->hash == 0 && entry->key == NULL)
+ goto found_unused_or_dummy;
+ if (entry->hash == hash) {
+ PyObject *startkey = entry->key;
+ assert(startkey != dummy);
+ if (startkey == key)
+ goto found_active;
+ if (PyUnicode_CheckExact(startkey)
+ && PyUnicode_CheckExact(key)
+ && _PyUnicode_EQ(startkey, key))
+ goto found_active;
+ table = so->table;
+ Py_INCREF(startkey);
+ cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp > 0)
+ goto found_active;
+ if (cmp < 0)
+ goto comparison_error;
+ if (table != so->table || entry->key != startkey)
+ goto restart;
+ mask = so->mask;
+ }
+ else if (entry->hash == -1) {
+ assert (entry->key == dummy);
+ freeslot = entry;
+ }
+ entry++;
+ } while (probes--);
+ perturb >>= PERTURB_SHIFT;
+ i = (i * 5 + 1 + perturb) & mask;
+ }
+
+ found_unused_or_dummy:
+ if (freeslot == NULL)
+ goto found_unused;
+ so->used++;
+ freeslot->key = key;
+ freeslot->hash = hash;
+ return 0;
+
+ found_unused:
+ so->fill++;
+ so->used++;
+ entry->key = key;
+ entry->hash = hash;
+ if ((size_t)so->fill*5 < mask*3)
+ return 0;
+ return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
+
+ found_active:
+ Py_DECREF(key);
+ return 0;
+
+ comparison_error:
+ Py_DECREF(key);
+ return -1;
+}
+
+/*
+Internal routine used by set_table_resize() to insert an item which is
+known to be absent from the set. Besides the performance benefit,
+there is also safety benefit since using set_add_entry() risks making
+a callback in the middle of a set_table_resize(), see issue 1456209.
+The caller is responsible for updating the key's reference count and
+the setobject's fill and used fields.
+*/
+static void
+set_insert_clean(setentry *table, size_t mask, PyObject *key, Py_hash_t hash)
+{
+ setentry *entry;
+ size_t perturb = hash;
+ size_t i = (size_t)hash & mask;
+ size_t j;
+
+ while (1) {
+ entry = &table[i];
+ if (entry->key == NULL)
+ goto found_null;
+ if (i + LINEAR_PROBES <= mask) {
+ for (j = 0; j < LINEAR_PROBES; j++) {
+ entry++;
+ if (entry->key == NULL)
+ goto found_null;
+ }
+ }
+ perturb >>= PERTURB_SHIFT;
+ i = (i * 5 + 1 + perturb) & mask;
+ }
+ found_null:
+ entry->key = key;
+ entry->hash = hash;
+}
+
+/* ======== End logic for probing the hash table ========================== */
+/* ======================================================================== */
+
+/*
+Restructure the table by allocating a new table and reinserting all
+keys again. When entries have been deleted, the new table may
+actually be smaller than the old one.
+*/
+static int
+set_table_resize(PySetObject *so, Py_ssize_t minused)
+{
+ setentry *oldtable, *newtable, *entry;
+ Py_ssize_t oldmask = so->mask;
+ size_t newmask;
+ int is_oldtable_malloced;
+ setentry small_copy[PySet_MINSIZE];
+
+ assert(minused >= 0);
+
+ /* Find the smallest table size > minused. */
+ /* XXX speed-up with intrinsics */
+ size_t newsize = PySet_MINSIZE;
+ while (newsize <= (size_t)minused) {
+ newsize <<= 1; // The largest possible value is PY_SSIZE_T_MAX + 1.
+ }
+
+ /* Get space for a new table. */
+ oldtable = so->table;
+ assert(oldtable != NULL);
+ is_oldtable_malloced = oldtable != so->smalltable;
+
+ if (newsize == PySet_MINSIZE) {
+ /* A large table is shrinking, or we can't get any smaller. */
+ newtable = so->smalltable;
+ if (newtable == oldtable) {
+ if (so->fill == so->used) {
+ /* No dummies, so no point doing anything. */
+ return 0;
+ }
+ /* We're not going to resize it, but rebuild the
+ table anyway to purge old dummy entries.
+ Subtle: This is *necessary* if fill==size,
+ as set_lookkey needs at least one virgin slot to
+ terminate failing searches. If fill < size, it's
+ merely desirable, as dummies slow searches. */
+ assert(so->fill > so->used);
+ memcpy(small_copy, oldtable, sizeof(small_copy));
+ oldtable = small_copy;
+ }
+ }
+ else {
+ newtable = PyMem_NEW(setentry, newsize);
+ if (newtable == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ }
+
+ /* Make the set empty, using the new table. */
+ assert(newtable != oldtable);
+ memset(newtable, 0, sizeof(setentry) * newsize);
+ so->mask = newsize - 1;
+ so->table = newtable;
+
+ /* Copy the data over; this is refcount-neutral for active entries;
+ dummy entries aren't copied over, of course */
+ newmask = (size_t)so->mask;
+ if (so->fill == so->used) {
+ for (entry = oldtable; entry <= oldtable + oldmask; entry++) {
+ if (entry->key != NULL) {
+ set_insert_clean(newtable, newmask, entry->key, entry->hash);
+ }
+ }
+ } else {
+ so->fill = so->used;
+ for (entry = oldtable; entry <= oldtable + oldmask; entry++) {
+ if (entry->key != NULL && entry->key != dummy) {
+ set_insert_clean(newtable, newmask, entry->key, entry->hash);
+ }
+ }
+ }
+
+ if (is_oldtable_malloced)
+ PyMem_Free(oldtable);
+ return 0;
+}
+
+static int
+set_contains_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
+{
+ setentry *entry;
+
+ entry = set_lookkey(so, key, hash);
+ if (entry != NULL)
+ return entry->key != NULL;
+ return -1;
+}
+
+#define DISCARD_NOTFOUND 0
+#define DISCARD_FOUND 1
+
+static int
+set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
+{
+ setentry *entry;
+ PyObject *old_key;
+
+ entry = set_lookkey(so, key, hash);
+ if (entry == NULL)
+ return -1;
+ if (entry->key == NULL)
+ return DISCARD_NOTFOUND;
+ old_key = entry->key;
+ entry->key = dummy;
+ entry->hash = -1;
+ so->used--;
+ Py_DECREF(old_key);
+ return DISCARD_FOUND;
+}
+
+static int
+set_add_key(PySetObject *so, PyObject *key)
+{
+ Py_hash_t hash;
+
+ if (!PyUnicode_CheckExact(key) ||
+ (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
+ hash = PyObject_Hash(key);
+ if (hash == -1)
+ return -1;
+ }
+ return set_add_entry(so, key, hash);
+}
+
+static int
+set_contains_key(PySetObject *so, PyObject *key)
+{
+ Py_hash_t hash;
+
+ if (!PyUnicode_CheckExact(key) ||
+ (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
+ hash = PyObject_Hash(key);
+ if (hash == -1)
+ return -1;
+ }
+ return set_contains_entry(so, key, hash);
+}
+
+static int
+set_discard_key(PySetObject *so, PyObject *key)
+{
+ Py_hash_t hash;
+
+ if (!PyUnicode_CheckExact(key) ||
+ (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
+ hash = PyObject_Hash(key);
+ if (hash == -1)
+ return -1;
+ }
+ return set_discard_entry(so, key, hash);
+}
+
+static void
+set_empty_to_minsize(PySetObject *so)
+{
+ memset(so->smalltable, 0, sizeof(so->smalltable));
+ so->fill = 0;
+ so->used = 0;
+ so->mask = PySet_MINSIZE - 1;
+ so->table = so->smalltable;
+ so->hash = -1;
+}
+
+static int
+set_clear_internal(PySetObject *so)
+{
+ setentry *entry;
+ setentry *table = so->table;
+ Py_ssize_t fill = so->fill;
+ Py_ssize_t used = so->used;
+ int table_is_malloced = table != so->smalltable;
+ setentry small_copy[PySet_MINSIZE];
+
+ assert (PyAnySet_Check(so));
+ assert(table != NULL);
+
+ /* This is delicate. During the process of clearing the set,
+ * decrefs can cause the set to mutate. To avoid fatal confusion
+ * (voice of experience), we have to make the set empty before
+ * clearing the slots, and never refer to anything via so->ref while
+ * clearing.
+ */
+ if (table_is_malloced)
+ set_empty_to_minsize(so);
+
+ else if (fill > 0) {
+ /* It's a small table with something that needs to be cleared.
+ * Afraid the only safe way is to copy the set entries into
+ * another small table first.
+ */
+ memcpy(small_copy, table, sizeof(small_copy));
+ table = small_copy;
+ set_empty_to_minsize(so);
+ }
+ /* else it's a small table that's already empty */
+
+ /* Now we can finally clear things. If C had refcounts, we could
+ * assert that the refcount on table is 1 now, i.e. that this function
+ * has unique access to it, so decref side-effects can't alter it.
+ */
+ for (entry = table; used > 0; entry++) {
+ if (entry->key && entry->key != dummy) {
+ used--;
+ Py_DECREF(entry->key);
+ }
+ }
+
+ if (table_is_malloced)
+ PyMem_Free(table);
+ return 0;
+}
+
+/*
+ * Iterate over a set table. Use like so:
+ *
+ * Py_ssize_t pos;
+ * setentry *entry;
+ * pos = 0; # important! pos should not otherwise be changed by you
+ * while (set_next(yourset, &pos, &entry)) {
+ * Refer to borrowed reference in entry->key.
+ * }
+ *
+ * CAUTION: In general, it isn't safe to use set_next in a loop that
+ * mutates the table.
+ */
+static int
+set_next(PySetObject *so, Py_ssize_t *pos_ptr, setentry **entry_ptr)
+{
+ Py_ssize_t i;
+ Py_ssize_t mask;
+ setentry *entry;
+
+ assert (PyAnySet_Check(so));
+ i = *pos_ptr;
+ assert(i >= 0);
+ mask = so->mask;
+ entry = &so->table[i];
+ while (i <= mask && (entry->key == NULL || entry->key == dummy)) {
+ i++;
+ entry++;
+ }
+ *pos_ptr = i+1;
+ if (i > mask)
+ return 0;
+ assert(entry != NULL);
+ *entry_ptr = entry;
+ return 1;
+}
+
+static void
+set_dealloc(PySetObject *so)
+{
+ setentry *entry;
+ Py_ssize_t used = so->used;
+
+ /* bpo-31095: UnTrack is needed before calling any callbacks */
+ PyObject_GC_UnTrack(so);
+ Py_TRASHCAN_BEGIN(so, set_dealloc)
+ if (so->weakreflist != NULL)
+ PyObject_ClearWeakRefs((PyObject *) so);
+
+ for (entry = so->table; used > 0; entry++) {
+ if (entry->key && entry->key != dummy) {
+ used--;
+ Py_DECREF(entry->key);
+ }
+ }
+ if (so->table != so->smalltable)
+ PyMem_Free(so->table);
+ Py_TYPE(so)->tp_free(so);
+ Py_TRASHCAN_END
+}
+
+static PyObject *
+set_repr(PySetObject *so)
+{
+ PyObject *result=NULL, *keys, *listrepr, *tmp;
+ int status = Py_ReprEnter((PyObject*)so);
+
+ if (status != 0) {
+ if (status < 0)
+ return NULL;
+ return PyUnicode_FromFormat("%s(...)", Py_TYPE(so)->tp_name);
+ }
+
+ /* shortcut for the empty set */
+ if (!so->used) {
+ Py_ReprLeave((PyObject*)so);
+ return PyUnicode_FromFormat("%s()", Py_TYPE(so)->tp_name);
+ }
+
+ keys = PySequence_List((PyObject *)so);
+ if (keys == NULL)
+ goto done;
+
+ /* repr(keys)[1:-1] */
+ listrepr = PyObject_Repr(keys);
+ Py_DECREF(keys);
+ if (listrepr == NULL)
+ goto done;
+ tmp = PyUnicode_Substring(listrepr, 1, PyUnicode_GET_LENGTH(listrepr)-1);
+ Py_DECREF(listrepr);
+ if (tmp == NULL)
+ goto done;
+ listrepr = tmp;
+
+ if (!PySet_CheckExact(so))
+ result = PyUnicode_FromFormat("%s({%U})",
+ Py_TYPE(so)->tp_name,
+ listrepr);
+ else
+ result = PyUnicode_FromFormat("{%U}", listrepr);
+ Py_DECREF(listrepr);
+done:
+ Py_ReprLeave((PyObject*)so);
+ return result;
+}
+
+static Py_ssize_t
+set_len(PyObject *so)
+{
+ return ((PySetObject *)so)->used;
+}
+
+static int
+set_merge(PySetObject *so, PyObject *otherset)
+{
+ PySetObject *other;
+ PyObject *key;
+ Py_ssize_t i;
+ setentry *so_entry;
+ setentry *other_entry;
+
+ assert (PyAnySet_Check(so));
+ assert (PyAnySet_Check(otherset));
+
+ other = (PySetObject*)otherset;
+ if (other == so || other->used == 0)
+ /* a.update(a) or a.update(set()); nothing to do */
+ return 0;
+ /* Do one big resize at the start, rather than
+ * incrementally resizing as we insert new keys. Expect
+ * that there will be no (or few) overlapping keys.
+ */
+ if ((so->fill + other->used)*5 >= so->mask*3) {
+ if (set_table_resize(so, (so->used + other->used)*2) != 0)
+ return -1;
+ }
+ so_entry = so->table;
+ other_entry = other->table;
+
+ /* If our table is empty, and both tables have the same size, and
+ there are no dummies to eliminate, then just copy the pointers. */
+ if (so->fill == 0 && so->mask == other->mask && other->fill == other->used) {
+ for (i = 0; i <= other->mask; i++, so_entry++, other_entry++) {
+ key = other_entry->key;
+ if (key != NULL) {
+ assert(so_entry->key == NULL);
+ Py_INCREF(key);
+ so_entry->key = key;
+ so_entry->hash = other_entry->hash;
+ }
+ }
+ so->fill = other->fill;
+ so->used = other->used;
+ return 0;
+ }
+
+ /* If our table is empty, we can use set_insert_clean() */
+ if (so->fill == 0) {
+ setentry *newtable = so->table;
+ size_t newmask = (size_t)so->mask;
+ so->fill = other->used;
+ so->used = other->used;
+ for (i = other->mask + 1; i > 0 ; i--, other_entry++) {
+ key = other_entry->key;
+ if (key != NULL && key != dummy) {
+ Py_INCREF(key);
+ set_insert_clean(newtable, newmask, 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;
+}
+
+static PyObject *
+set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ /* Make sure the search finger is in bounds */
+ setentry *entry = so->table + (so->finger & so->mask);
+ setentry *limit = so->table + so->mask;
+ PyObject *key;
+
+ if (so->used == 0) {
+ PyErr_SetString(PyExc_KeyError, "pop from an empty set");
+ return NULL;
+ }
+ while (entry->key == NULL || entry->key==dummy) {
+ entry++;
+ if (entry > limit)
+ entry = so->table;
+ }
+ key = entry->key;
+ entry->key = dummy;
+ entry->hash = -1;
+ so->used--;
+ so->finger = entry - so->table + 1; /* next place to start */
+ return key;
+}
+
+PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.\n\
+Raises KeyError if the set is empty.");
+
+static int
+set_traverse(PySetObject *so, visitproc visit, void *arg)
+{
+ Py_ssize_t pos = 0;
+ setentry *entry;
+
+ while (set_next(so, &pos, &entry))
+ Py_VISIT(entry->key);
+ return 0;
+}
+
+/* Work to increase the bit dispersion for closely spaced hash values.
+ This is important because some use cases have many combinations of a
+ small number of elements with nearby hashes so that many distinct
+ combinations collapse to only a handful of distinct hash values. */
+
+static Py_uhash_t
+_shuffle_bits(Py_uhash_t h)
+{
+ return ((h ^ 89869747UL) ^ (h << 16)) * 3644798167UL;
+}
+
+/* Most of the constants in this hash algorithm are randomly chosen
+ large primes with "interesting bit patterns" and that passed tests
+ for good collision statistics on a variety of problematic datasets
+ including powersets and graph structures (such as David Eppstein's
+ graph recipes in Lib/test/test_set.py) */
+
+static Py_hash_t
+frozenset_hash(PyObject *self)
+{
+ PySetObject *so = (PySetObject *)self;
+ Py_uhash_t hash = 0;
+ setentry *entry;
+
+ if (so->hash != -1)
+ return so->hash;
+
+ /* Xor-in shuffled bits from every entry's hash field because xor is
+ commutative and a frozenset hash should be independent of order.
+
+ For speed, include null entries and dummy entries and then
+ subtract out their effect afterwards so that the final hash
+ depends only on active entries. This allows the code to be
+ vectorized by the compiler and it saves the unpredictable
+ branches that would arise when trying to exclude null and dummy
+ entries on every iteration. */
+
+ for (entry = so->table; entry <= &so->table[so->mask]; entry++)
+ hash ^= _shuffle_bits(entry->hash);
+
+ /* Remove the effect of an odd number of NULL entries */
+ if ((so->mask + 1 - so->fill) & 1)
+ hash ^= _shuffle_bits(0);
+
+ /* Remove the effect of an odd number of dummy entries */
+ if ((so->fill - so->used) & 1)
+ hash ^= _shuffle_bits(-1);
+
+ /* Factor in the number of active entries */
+ hash ^= ((Py_uhash_t)PySet_GET_SIZE(self) + 1) * 1927868237UL;
+
+ /* Disperse patterns arising in nested frozensets */
+ hash ^= (hash >> 11) ^ (hash >> 25);
+ hash = hash * 69069U + 907133923UL;
+
+ /* -1 is reserved as an error code */
+ if (hash == (Py_uhash_t)-1)
+ hash = 590923713UL;
+
+ so->hash = hash;
+ return hash;
+}
+
+/***** Set iterator type ***********************************************/
+
+typedef struct {
+ PyObject_HEAD
+ PySetObject *si_set; /* Set to NULL when iterator is exhausted */
+ Py_ssize_t si_used;
+ Py_ssize_t si_pos;
+ Py_ssize_t len;
+} setiterobject;
+
+static void
+setiter_dealloc(setiterobject *si)
+{
+ /* bpo-31095: UnTrack is needed before calling any callbacks */
+ _PyObject_GC_UNTRACK(si);
+ Py_XDECREF(si->si_set);
+ PyObject_GC_Del(si);
+}
+
+static int
+setiter_traverse(setiterobject *si, visitproc visit, void *arg)
+{
+ Py_VISIT(si->si_set);
+ return 0;
+}
+
+static PyObject *
+setiter_len(setiterobject *si, PyObject *Py_UNUSED(ignored))
+{
+ Py_ssize_t len = 0;
+ if (si->si_set != NULL && si->si_used == si->si_set->used)
+ len = si->len;
+ return PyLong_FromSsize_t(len);
+}
+
+PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
+
+static PyObject *setiter_iternext(setiterobject *si);
+
+static PyObject *
+setiter_reduce(setiterobject *si, PyObject *Py_UNUSED(ignored))
+{
+ /* copy the iterator state */
+ setiterobject tmp = *si;
+ Py_XINCREF(tmp.si_set);
+
+ /* iterate the temporary into a list */
+ PyObject *list = PySequence_List((PyObject*)&tmp);
+ Py_XDECREF(tmp.si_set);
+ if (list == NULL) {
+ return NULL;
+ }
+ return Py_BuildValue("N(N)", _PyEval_GetBuiltin(&_Py_ID(iter)), list);
+}
+
+PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
+
+static PyMethodDef setiter_methods[] = {
+ {"__length_hint__", (PyCFunction)setiter_len, METH_NOARGS, length_hint_doc},
+ {"__reduce__", (PyCFunction)setiter_reduce, METH_NOARGS, reduce_doc},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *setiter_iternext(setiterobject *si)
+{
+ PyObject *key;
+ Py_ssize_t i, mask;
+ setentry *entry;
+ PySetObject *so = si->si_set;
+
+ if (so == NULL)
+ return NULL;
+ assert (PyAnySet_Check(so));
+
+ if (si->si_used != so->used) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Set changed size during iteration");
+ si->si_used = -1; /* Make this state sticky */
+ return NULL;
+ }
+
+ 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++;
+ si->si_pos = i+1;
+ if (i > mask)
+ goto fail;
+ si->len--;
+ key = entry[i].key;
+ Py_INCREF(key);
+ return key;
+
+fail:
+ si->si_set = NULL;
+ Py_DECREF(so);
+ return NULL;
+}
+
+PyTypeObject PySetIter_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "set_iterator", /* tp_name */
+ sizeof(setiterobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)setiter_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_as_async */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)setiter_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)setiter_iternext, /* tp_iternext */
+ setiter_methods, /* tp_methods */
+ 0,
+};
+
+static PyObject *
+set_iter(PySetObject *so)
+{
+ setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type);
+ if (si == NULL)
+ return NULL;
+ Py_INCREF(so);
+ si->si_set = so;
+ si->si_used = so->used;
+ si->si_pos = 0;
+ si->len = so->used;
+ _PyObject_GC_TRACK(si);
+ return (PyObject *)si;
+}
+
+static int
+set_update_internal(PySetObject *so, PyObject *other)
+{
+ PyObject *key, *it;
+
+ if (PyAnySet_Check(other))
+ return set_merge(so, other);
+
+ if (PyDict_CheckExact(other)) {
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ Py_hash_t hash;
+ Py_ssize_t dictsize = PyDict_GET_SIZE(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.
+ */
+ if (dictsize < 0)
+ return -1;
+ if ((so->fill + dictsize)*5 >= so->mask*3) {
+ if (set_table_resize(so, (so->used + dictsize)*2) != 0)
+ return -1;
+ }
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ if (set_add_entry(so, key, hash))
+ return -1;
+ }
+ return 0;
+ }
+
+ it = PyObject_GetIter(other);
+ if (it == NULL)
+ return -1;
+
+ 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 PyObject *
+set_update(PySetObject *so, PyObject *args)
+{
+ Py_ssize_t i;
+
+ for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
+ PyObject *other = PyTuple_GET_ITEM(args, i);
+ if (set_update_internal(so, other))
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(update_doc,
+"Update a set with the union of itself and others.");
+
+/* 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_internal(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 */
+ Py_INCREF(iterable);
+ return iterable;
+ }
+ return make_new_set(type, iterable);
+}
+
+static PyObject *
+frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *iterable = NULL;
+
+ if ((type == &PyFrozenSet_Type ||
+ type->tp_init == PyFrozenSet_Type.tp_init) &&
+ !_PyArg_NoKeywords("frozenset", kwds)) {
+ return NULL;
+ }
+
+ if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable)) {
+ return NULL;
+ }
+
+ return make_new_frozenset(type, iterable);
+}
+
+static PyObject *
+frozenset_vectorcall(PyObject *type, PyObject * const*args,
+ size_t nargsf, PyObject *kwnames)
+{
+ if (!_PyArg_NoKwnames("frozenset", kwnames)) {
+ return NULL;
+ }
+
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (!_PyArg_CheckPositional("frozenset", nargs, 0, 1)) {
+ return NULL;
+ }
+
+ PyObject *iterable = (nargs ? args[0] : NULL);
+ return make_new_frozenset(_PyType_CAST(type), iterable);
+}
+
+static PyObject *
+set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ return make_new_set(type, NULL);
+}
+
+/* set_swap_bodies() switches the contents of any two sets by moving their
+ internal data pointers and, if needed, copying the internal smalltables.
+ Semantically equivalent to:
+
+ t=set(a); a.clear(); a.update(b); b.clear(); b.update(t); del t
+
+ The function always succeeds and it leaves both objects in a stable state.
+ Useful for operations that update in-place (by allowing an intermediate
+ result to be swapped into one of the original inputs).
+*/
+
+static void
+set_swap_bodies(PySetObject *a, PySetObject *b)
+{
+ Py_ssize_t t;
+ setentry *u;
+ setentry tab[PySet_MINSIZE];
+ Py_hash_t h;
+
+ t = a->fill; a->fill = b->fill; b->fill = t;
+ t = a->used; a->used = b->used; b->used = t;
+ t = a->mask; a->mask = b->mask; b->mask = t;
+
+ u = a->table;
+ if (a->table == a->smalltable)
+ u = b->smalltable;
+ a->table = b->table;
+ if (b->table == b->smalltable)
+ a->table = a->smalltable;
+ b->table = u;
+
+ if (a->table == a->smalltable || b->table == b->smalltable) {
+ memcpy(tab, a->smalltable, sizeof(tab));
+ memcpy(a->smalltable, b->smalltable, sizeof(tab));
+ memcpy(b->smalltable, tab, sizeof(tab));
+ }
+
+ if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type) &&
+ PyType_IsSubtype(Py_TYPE(b), &PyFrozenSet_Type)) {
+ h = a->hash; a->hash = b->hash; b->hash = h;
+ } else {
+ a->hash = -1;
+ b->hash = -1;
+ }
+}
+
+static PyObject *
+set_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ return make_new_set_basetype(Py_TYPE(so), (PyObject *)so);
+}
+
+static PyObject *
+frozenset_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ if (PyFrozenSet_CheckExact(so)) {
+ Py_INCREF(so);
+ return (PyObject *)so;
+ }
+ return set_copy(so, NULL);
+}
+
+PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
+
+static PyObject *
+set_clear(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ set_clear_internal(so);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(clear_doc, "Remove all elements from this set.");
+
+static PyObject *
+set_union(PySetObject *so, PyObject *args)
+{
+ PySetObject *result;
+ PyObject *other;
+ Py_ssize_t i;
+
+ result = (PySetObject *)set_copy(so, NULL);
+ if (result == NULL)
+ return NULL;
+
+ for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
+ other = PyTuple_GET_ITEM(args, i);
+ if ((PyObject *)so == other)
+ continue;
+ if (set_update_internal(result, other)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ return (PyObject *)result;
+}
+
+PyDoc_STRVAR(union_doc,
+ "Return the union of sets as a new set.\n\
+\n\
+(i.e. all elements that are in either set.)");
+
+static PyObject *
+set_or(PySetObject *so, PyObject *other)
+{
+ PySetObject *result;
+
+ if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+
+ result = (PySetObject *)set_copy(so, NULL);
+ if (result == NULL)
+ return NULL;
+ if ((PyObject *)so == other)
+ return (PyObject *)result;
+ if (set_update_internal(result, other)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return (PyObject *)result;
+}
+
+static PyObject *
+set_ior(PySetObject *so, PyObject *other)
+{
+ if (!PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+
+ if (set_update_internal(so, other))
+ return NULL;
+ Py_INCREF(so);
+ return (PyObject *)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(so, NULL);
+
+ 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;
+}
+
+static PyObject *
+set_intersection_multi(PySetObject *so, PyObject *args)
+{
+ Py_ssize_t i;
+ PyObject *result = (PyObject *)so;
+
+ if (PyTuple_GET_SIZE(args) == 0)
+ return set_copy(so, NULL);
+
+ Py_INCREF(so);
+ for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
+ PyObject *other = PyTuple_GET_ITEM(args, i);
+ PyObject *newresult = set_intersection((PySetObject *)result, other);
+ if (newresult == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ Py_DECREF(result);
+ result = newresult;
+ }
+ return result;
+}
+
+PyDoc_STRVAR(intersection_doc,
+"Return the intersection of two sets as a new set.\n\
+\n\
+(i.e. all elements that are in both sets.)");
+
+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;
+}
+
+static PyObject *
+set_intersection_update_multi(PySetObject *so, PyObject *args)
+{
+ PyObject *tmp;
+
+ tmp = set_intersection_multi(so, args);
+ if (tmp == NULL)
+ return NULL;
+ set_swap_bodies(so, (PySetObject *)tmp);
+ Py_DECREF(tmp);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(intersection_update_doc,
+"Update a set with the intersection of itself and another.");
+
+static PyObject *
+set_and(PySetObject *so, PyObject *other)
+{
+ if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ return set_intersection(so, other);
+}
+
+static PyObject *
+set_iand(PySetObject *so, PyObject *other)
+{
+ PyObject *result;
+
+ if (!PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ result = set_intersection_update(so, other);
+ if (result == NULL)
+ return NULL;
+ Py_DECREF(result);
+ Py_INCREF(so);
+ return (PyObject *)so;
+}
+
+static PyObject *
+set_isdisjoint(PySetObject *so, PyObject *other)
+{
+ 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;
+}
+
+PyDoc_STRVAR(isdisjoint_doc,
+"Return True if two sets have a null intersection.");
+
+static int
+set_difference_update_internal(PySetObject *so, PyObject *other)
+{
+ if ((PyObject *)so == other)
+ return set_clear_internal(so);
+
+ if (PyAnySet_Check(other)) {
+ setentry *entry;
+ Py_ssize_t pos = 0;
+
+ /* Optimization: When the other set is more than 8 times
+ larger than the base set, replace the other set with
+ intersection of the two sets.
+ */
+ if ((PySet_GET_SIZE(other) >> 3) > PySet_GET_SIZE(so)) {
+ other = set_intersection(so, other);
+ if (other == NULL)
+ return -1;
+ } else {
+ Py_INCREF(other);
+ }
+
+ while (set_next((PySetObject *)other, &pos, &entry)) {
+ PyObject *key = entry->key;
+ Py_INCREF(key);
+ if (set_discard_entry(so, key, entry->hash) < 0) {
+ Py_DECREF(other);
+ Py_DECREF(key);
+ return -1;
+ }
+ Py_DECREF(key);
+ }
+
+ Py_DECREF(other);
+ } else {
+ PyObject *key, *it;
+ it = PyObject_GetIter(other);
+ if (it == NULL)
+ return -1;
+
+ while ((key = PyIter_Next(it)) != NULL) {
+ if (set_discard_key(so, key) < 0) {
+ Py_DECREF(it);
+ Py_DECREF(key);
+ return -1;
+ }
+ Py_DECREF(key);
+ }
+ Py_DECREF(it);
+ if (PyErr_Occurred())
+ return -1;
+ }
+ /* If more than 1/4th are dummies, then resize them away. */
+ if ((size_t)(so->fill - so->used) <= (size_t)so->mask / 4)
+ return 0;
+ return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
+}
+
+static PyObject *
+set_difference_update(PySetObject *so, PyObject *args)
+{
+ Py_ssize_t i;
+
+ for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
+ PyObject *other = PyTuple_GET_ITEM(args, i);
+ if (set_difference_update_internal(so, other))
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(difference_update_doc,
+"Remove all elements of another set from this set.");
+
+static PyObject *
+set_copy_and_difference(PySetObject *so, PyObject *other)
+{
+ PyObject *result;
+
+ result = set_copy(so, NULL);
+ 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;
+}
+
+static PyObject *
+set_difference_multi(PySetObject *so, PyObject *args)
+{
+ Py_ssize_t i;
+ PyObject *result, *other;
+
+ if (PyTuple_GET_SIZE(args) == 0)
+ return set_copy(so, NULL);
+
+ other = PyTuple_GET_ITEM(args, 0);
+ result = set_difference(so, other);
+ if (result == NULL)
+ return NULL;
+
+ for (i=1 ; i<PyTuple_GET_SIZE(args) ; i++) {
+ other = PyTuple_GET_ITEM(args, i);
+ if (set_difference_update_internal((PySetObject *)result, other)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ return result;
+}
+
+PyDoc_STRVAR(difference_doc,
+"Return the difference of two or more sets as a new set.\n\
+\n\
+(i.e. all elements that are in this set but not the others.)");
+static PyObject *
+set_sub(PySetObject *so, PyObject *other)
+{
+ if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ return set_difference(so, other);
+}
+
+static PyObject *
+set_isub(PySetObject *so, PyObject *other)
+{
+ if (!PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ if (set_difference_update_internal(so, other))
+ return NULL;
+ Py_INCREF(so);
+ return (PyObject *)so;
+}
+
+static PyObject *
+set_symmetric_difference_update(PySetObject *so, PyObject *other)
+{
+ PySetObject *otherset;
+ PyObject *key;
+ Py_ssize_t pos = 0;
+ Py_hash_t hash;
+ setentry *entry;
+ int rv;
+
+ if ((PyObject *)so == other)
+ return set_clear(so, NULL);
+
+ if (PyDict_CheckExact(other)) {
+ PyObject *value;
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ Py_INCREF(key);
+ rv = set_discard_entry(so, key, hash);
+ if (rv < 0) {
+ Py_DECREF(key);
+ return NULL;
+ }
+ if (rv == DISCARD_NOTFOUND) {
+ if (set_add_entry(so, key, hash)) {
+ Py_DECREF(key);
+ return NULL;
+ }
+ }
+ Py_DECREF(key);
+ }
+ Py_RETURN_NONE;
+ }
+
+ if (PyAnySet_Check(other)) {
+ Py_INCREF(other);
+ otherset = (PySetObject *)other;
+ } else {
+ otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
+ if (otherset == NULL)
+ return NULL;
+ }
+
+ while (set_next(otherset, &pos, &entry)) {
+ key = entry->key;
+ hash = entry->hash;
+ Py_INCREF(key);
+ rv = set_discard_entry(so, key, hash);
+ if (rv < 0) {
+ Py_DECREF(otherset);
+ Py_DECREF(key);
+ return NULL;
+ }
+ if (rv == DISCARD_NOTFOUND) {
+ if (set_add_entry(so, key, hash)) {
+ Py_DECREF(otherset);
+ Py_DECREF(key);
+ return NULL;
+ }
+ }
+ Py_DECREF(key);
+ }
+ Py_DECREF(otherset);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(symmetric_difference_update_doc,
+"Update a set with the symmetric difference of itself and another.");
+
+static PyObject *
+set_symmetric_difference(PySetObject *so, PyObject *other)
+{
+ PyObject *rv;
+ PySetObject *otherset;
+
+ otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
+ if (otherset == NULL)
+ return NULL;
+ rv = set_symmetric_difference_update(otherset, (PyObject *)so);
+ if (rv == NULL) {
+ Py_DECREF(otherset);
+ return NULL;
+ }
+ Py_DECREF(rv);
+ return (PyObject *)otherset;
+}
+
+PyDoc_STRVAR(symmetric_difference_doc,
+"Return the symmetric difference of two sets as a new set.\n\
+\n\
+(i.e. all elements that are in exactly one of the sets.)");
+
+static PyObject *
+set_xor(PySetObject *so, PyObject *other)
+{
+ if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ return set_symmetric_difference(so, other);
+}
+
+static PyObject *
+set_ixor(PySetObject *so, PyObject *other)
+{
+ PyObject *result;
+
+ if (!PyAnySet_Check(other))
+ Py_RETURN_NOTIMPLEMENTED;
+ result = set_symmetric_difference_update(so, other);
+ if (result == NULL)
+ return NULL;
+ Py_DECREF(result);
+ Py_INCREF(so);
+ return (PyObject *)so;
+}
+
+static PyObject *
+set_issubset(PySetObject *so, PyObject *other)
+{
+ setentry *entry;
+ Py_ssize_t pos = 0;
+ int rv;
+
+ if (!PyAnySet_Check(other)) {
+ PyObject *tmp, *result;
+ tmp = make_new_set(&PySet_Type, other);
+ if (tmp == NULL)
+ return NULL;
+ result = set_issubset(so, tmp);
+ Py_DECREF(tmp);
+ return 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;
+}
+
+PyDoc_STRVAR(issubset_doc, "Report whether another set contains this set.");
+
+static PyObject *
+set_issuperset(PySetObject *so, PyObject *other)
+{
+ if (PyAnySet_Check(other)) {
+ return set_issubset((PySetObject *)other, (PyObject *)so);
+ }
+
+ PyObject *key, *it = PyObject_GetIter(other);
+ if (it == NULL) {
+ return NULL;
+ }
+ while ((key = PyIter_Next(it)) != NULL) {
+ int rv = set_contains_key(so, key);
+ Py_DECREF(key);
+ if (rv < 0) {
+ Py_DECREF(it);
+ return NULL;
+ }
+ if (!rv) {
+ Py_DECREF(it);
+ Py_RETURN_FALSE;
+ }
+ }
+ Py_DECREF(it);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ Py_RETURN_TRUE;
+}
+
+PyDoc_STRVAR(issuperset_doc, "Report whether this set contains another set.");
+
+static PyObject *
+set_richcompare(PySetObject *v, PyObject *w, int op)
+{
+ PyObject *r1;
+ int r2;
+
+ if(!PyAnySet_Check(w))
+ Py_RETURN_NOTIMPLEMENTED;
+
+ switch (op) {
+ case Py_EQ:
+ if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
+ Py_RETURN_FALSE;
+ if (v->hash != -1 &&
+ ((PySetObject *)w)->hash != -1 &&
+ v->hash != ((PySetObject *)w)->hash)
+ Py_RETURN_FALSE;
+ return set_issubset(v, w);
+ case Py_NE:
+ r1 = set_richcompare(v, w, Py_EQ);
+ if (r1 == NULL)
+ return NULL;
+ r2 = PyObject_IsTrue(r1);
+ Py_DECREF(r1);
+ if (r2 < 0)
+ return NULL;
+ return PyBool_FromLong(!r2);
+ case Py_LE:
+ return set_issubset(v, w);
+ case Py_GE:
+ return set_issuperset(v, w);
+ case Py_LT:
+ if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w))
+ Py_RETURN_FALSE;
+ return set_issubset(v, w);
+ case Py_GT:
+ if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w))
+ Py_RETURN_FALSE;
+ return set_issuperset(v, w);
+ }
+ Py_RETURN_NOTIMPLEMENTED;
+}
+
+static PyObject *
+set_add(PySetObject *so, PyObject *key)
+{
+ if (set_add_key(so, key))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(add_doc,
+"Add an element to a set.\n\
+\n\
+This has no effect if the element is already present.");
+
+static int
+set_contains(PySetObject *so, PyObject *key)
+{
+ PyObject *tmpkey;
+ int rv;
+
+ rv = set_contains_key(so, key);
+ if (rv < 0) {
+ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
+ return -1;
+ PyErr_Clear();
+ tmpkey = make_new_set(&PyFrozenSet_Type, key);
+ if (tmpkey == NULL)
+ return -1;
+ rv = set_contains_key(so, tmpkey);
+ Py_DECREF(tmpkey);
+ }
+ return rv;
+}
+
+static PyObject *
+set_direct_contains(PySetObject *so, PyObject *key)
+{
+ long result;
+
+ result = set_contains(so, key);
+ if (result < 0)
+ return NULL;
+ return PyBool_FromLong(result);
+}
+
+PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x.");
+
+static PyObject *
+set_remove(PySetObject *so, PyObject *key)
+{
+ PyObject *tmpkey;
+ int rv;
+
+ rv = set_discard_key(so, key);
+ if (rv < 0) {
+ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
+ return NULL;
+ PyErr_Clear();
+ tmpkey = make_new_set(&PyFrozenSet_Type, key);
+ if (tmpkey == NULL)
+ return NULL;
+ rv = set_discard_key(so, tmpkey);
+ Py_DECREF(tmpkey);
+ if (rv < 0)
+ return NULL;
+ }
+
+ if (rv == DISCARD_NOTFOUND) {
+ _PyErr_SetKeyError(key);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(remove_doc,
+"Remove an element from a set; it must be a member.\n\
+\n\
+If the element is not a member, raise a KeyError.");
+
+static PyObject *
+set_discard(PySetObject *so, PyObject *key)
+{
+ PyObject *tmpkey;
+ int rv;
+
+ rv = set_discard_key(so, key);
+ if (rv < 0) {
+ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
+ return NULL;
+ PyErr_Clear();
+ tmpkey = make_new_set(&PyFrozenSet_Type, key);
+ if (tmpkey == NULL)
+ return NULL;
+ rv = set_discard_key(so, tmpkey);
+ Py_DECREF(tmpkey);
+ if (rv < 0)
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(discard_doc,
+"Remove an element from a set if it is a member.\n\
+\n\
+Unlike set.remove(), the discard() method does not raise\n\
+an exception when an element is missing from the set.");
+
+static PyObject *
+set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ 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;
+}
+
+static PyObject *
+set_sizeof(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ Py_ssize_t res;
+
+ res = _PyObject_SIZE(Py_TYPE(so));
+ if (so->table != so->smalltable)
+ res = res + (so->mask + 1) * sizeof(setentry);
+ return PyLong_FromSsize_t(res);
+}
+
+PyDoc_STRVAR(sizeof_doc, "S.__sizeof__() -> size of S in memory, in bytes");
+static int
+set_init(PySetObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *iterable = NULL;
+
+ if (!_PyArg_NoKeywords("set", kwds))
+ return -1;
+ if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
+ return -1;
+ if (self->fill)
+ set_clear_internal(self);
+ self->hash = -1;
+ 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 */
+ (objobjproc)set_contains, /* sq_contains */
+};
+
+/* set object ********************************************************/
+
+#ifdef Py_DEBUG
+static PyObject *test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored));
+
+PyDoc_STRVAR(test_c_api_doc, "Exercises C API. Returns True.\n\
+All is well if assertions don't fail.");
+#endif
+
+static PyMethodDef set_methods[] = {
+ {"add", (PyCFunction)set_add, METH_O,
+ add_doc},
+ {"clear", (PyCFunction)set_clear, METH_NOARGS,
+ clear_doc},
+ {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
+ contains_doc},
+ {"copy", (PyCFunction)set_copy, METH_NOARGS,
+ copy_doc},
+ {"discard", (PyCFunction)set_discard, METH_O,
+ discard_doc},
+ {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
+ difference_doc},
+ {"difference_update", (PyCFunction)set_difference_update, METH_VARARGS,
+ difference_update_doc},
+ {"intersection",(PyCFunction)set_intersection_multi, METH_VARARGS,
+ intersection_doc},
+ {"intersection_update",(PyCFunction)set_intersection_update_multi, METH_VARARGS,
+ intersection_update_doc},
+ {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
+ isdisjoint_doc},
+ {"issubset", (PyCFunction)set_issubset, METH_O,
+ issubset_doc},
+ {"issuperset", (PyCFunction)set_issuperset, METH_O,
+ issuperset_doc},
+ {"pop", (PyCFunction)set_pop, METH_NOARGS,
+ pop_doc},
+ {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
+ reduce_doc},
+ {"remove", (PyCFunction)set_remove, METH_O,
+ remove_doc},
+ {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
+ sizeof_doc},
+ {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
+ symmetric_difference_doc},
+ {"symmetric_difference_update",(PyCFunction)set_symmetric_difference_update, METH_O,
+ symmetric_difference_update_doc},
+#ifdef Py_DEBUG
+ {"test_c_api", (PyCFunction)test_c_api, METH_NOARGS,
+ test_c_api_doc},
+#endif
+ {"union", (PyCFunction)set_union, METH_VARARGS,
+ union_doc},
+ {"update", (PyCFunction)set_update, METH_VARARGS,
+ update_doc},
+ {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyNumberMethods set_as_number = {
+ 0, /*nb_add*/
+ (binaryfunc)set_sub, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_bool*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ (binaryfunc)set_and, /*nb_and*/
+ (binaryfunc)set_xor, /*nb_xor*/
+ (binaryfunc)set_or, /*nb_or*/
+ 0, /*nb_int*/
+ 0, /*nb_reserved*/
+ 0, /*nb_float*/
+ 0, /*nb_inplace_add*/
+ (binaryfunc)set_isub, /*nb_inplace_subtract*/
+ 0, /*nb_inplace_multiply*/
+ 0, /*nb_inplace_remainder*/
+ 0, /*nb_inplace_power*/
+ 0, /*nb_inplace_lshift*/
+ 0, /*nb_inplace_rshift*/
+ (binaryfunc)set_iand, /*nb_inplace_and*/
+ (binaryfunc)set_ixor, /*nb_inplace_xor*/
+ (binaryfunc)set_ior, /*nb_inplace_or*/
+};
+
+PyDoc_STRVAR(set_doc,
+"set() -> new empty set object\n\
+set(iterable) -> new set object\n\
+\n\
+Build an unordered collection of unique elements.");
+
+PyTypeObject PySet_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "set", /* tp_name */
+ sizeof(PySetObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)set_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_as_async */
+ (reprfunc)set_repr, /* tp_repr */
+ &set_as_number, /* tp_as_number */
+ &set_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ PyObject_HashNotImplemented, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE |
+ _Py_TPFLAGS_MATCH_SELF, /* tp_flags */
+ set_doc, /* tp_doc */
+ (traverseproc)set_traverse, /* tp_traverse */
+ (inquiry)set_clear_internal, /* tp_clear */
+ (richcmpfunc)set_richcompare, /* tp_richcompare */
+ offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)set_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ set_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)set_init, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ set_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ .tp_vectorcall = set_vectorcall,
+};
+
+/* frozenset object ********************************************************/
+
+
+static PyMethodDef frozenset_methods[] = {
+ {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
+ contains_doc},
+ {"copy", (PyCFunction)frozenset_copy, METH_NOARGS,
+ copy_doc},
+ {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
+ difference_doc},
+ {"intersection", (PyCFunction)set_intersection_multi, METH_VARARGS,
+ intersection_doc},
+ {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
+ isdisjoint_doc},
+ {"issubset", (PyCFunction)set_issubset, METH_O,
+ issubset_doc},
+ {"issuperset", (PyCFunction)set_issuperset, METH_O,
+ issuperset_doc},
+ {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
+ reduce_doc},
+ {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
+ sizeof_doc},
+ {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
+ symmetric_difference_doc},
+ {"union", (PyCFunction)set_union, METH_VARARGS,
+ union_doc},
+ {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyNumberMethods frozenset_as_number = {
+ 0, /*nb_add*/
+ (binaryfunc)set_sub, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_bool*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ (binaryfunc)set_and, /*nb_and*/
+ (binaryfunc)set_xor, /*nb_xor*/
+ (binaryfunc)set_or, /*nb_or*/
+};
+
+PyDoc_STRVAR(frozenset_doc,
+"frozenset() -> empty frozenset object\n\
+frozenset(iterable) -> frozenset object\n\
+\n\
+Build an immutable unordered collection of unique elements.");
+
+PyTypeObject PyFrozenSet_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "frozenset", /* tp_name */
+ sizeof(PySetObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)set_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_as_async */
+ (reprfunc)set_repr, /* tp_repr */
+ &frozenset_as_number, /* tp_as_number */
+ &set_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ frozenset_hash, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE |
+ _Py_TPFLAGS_MATCH_SELF, /* tp_flags */
+ frozenset_doc, /* tp_doc */
+ (traverseproc)set_traverse, /* tp_traverse */
+ (inquiry)set_clear_internal, /* tp_clear */
+ (richcmpfunc)set_richcompare, /* tp_richcompare */
+ offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)set_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ frozenset_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ frozenset_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ .tp_vectorcall = frozenset_vectorcall,
+};
+
+
+/***** C API functions *************************************************/
+
+PyObject *
+PySet_New(PyObject *iterable)
+{
+ return make_new_set(&PySet_Type, iterable);
+}
+
+PyObject *
+PyFrozenSet_New(PyObject *iterable)
+{
+ return make_new_set(&PyFrozenSet_Type, iterable);
+}
+
+Py_ssize_t
+PySet_Size(PyObject *anyset)
+{
+ if (!PyAnySet_Check(anyset)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return PySet_GET_SIZE(anyset);
+}
+
+int
+PySet_Clear(PyObject *set)
+{
+ if (!PySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return set_clear_internal((PySetObject *)set);
+}
+
+int
+PySet_Contains(PyObject *anyset, PyObject *key)
+{
+ if (!PyAnySet_Check(anyset)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return set_contains_key((PySetObject *)anyset, key);
+}
+
+int
+PySet_Discard(PyObject *set, PyObject *key)
+{
+ if (!PySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return set_discard_key((PySetObject *)set, key);
+}
+
+int
+PySet_Add(PyObject *anyset, PyObject *key)
+{
+ if (!PySet_Check(anyset) &&
+ (!PyFrozenSet_Check(anyset) || Py_REFCNT(anyset) != 1)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return set_add_key((PySetObject *)anyset, key);
+}
+
+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;
+}
+
+PyObject *
+PySet_Pop(PyObject *set)
+{
+ if (!PySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ return set_pop((PySetObject *)set, NULL);
+}
+
+int
+_PySet_Update(PyObject *set, PyObject *iterable)
+{
+ if (!PySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return set_update_internal((PySetObject *)set, iterable);
+}
+
+/* Exported for the gdb plugin's benefit. */
+PyObject *_PySet_Dummy = dummy;
+
+#ifdef Py_DEBUG
+
+/* Test code to be called with any three element set.
+ Returns True and original set is restored. */
+
+#define assertRaises(call_return_value, exception) \
+ do { \
+ assert(call_return_value); \
+ assert(PyErr_ExceptionMatches(exception)); \
+ PyErr_Clear(); \
+ } while(0)
+
+static PyObject *
+test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ Py_ssize_t count;
+ const char *s;
+ Py_ssize_t i;
+ PyObject *elem=NULL, *dup=NULL, *t, *f, *dup2, *x=NULL;
+ PyObject *ob = (PyObject *)so;
+ Py_hash_t hash;
+ PyObject *str;
+
+ /* Verify preconditions */
+ assert(PyAnySet_Check(ob));
+ assert(PyAnySet_CheckExact(ob));
+ assert(!PyFrozenSet_CheckExact(ob));
+
+ /* so.clear(); so |= set("abc"); */
+ str = PyUnicode_FromString("abc");
+ if (str == NULL)
+ return NULL;
+ set_clear_internal(so);
+ if (set_update_internal(so, str)) {
+ Py_DECREF(str);
+ return NULL;
+ }
+ Py_DECREF(str);
+
+ /* Exercise type/size checks */
+ assert(PySet_Size(ob) == 3);
+ assert(PySet_GET_SIZE(ob) == 3);
+
+ /* Raise TypeError for non-iterable constructor arguments */
+ assertRaises(PySet_New(Py_None) == NULL, PyExc_TypeError);
+ assertRaises(PyFrozenSet_New(Py_None) == NULL, PyExc_TypeError);
+
+ /* Raise TypeError for unhashable key */
+ dup = PySet_New(ob);
+ assertRaises(PySet_Discard(ob, dup) == -1, PyExc_TypeError);
+ assertRaises(PySet_Contains(ob, dup) == -1, PyExc_TypeError);
+ assertRaises(PySet_Add(ob, dup) == -1, PyExc_TypeError);
+
+ /* Exercise successful pop, contains, add, and discard */
+ elem = PySet_Pop(ob);
+ assert(PySet_Contains(ob, elem) == 0);
+ assert(PySet_GET_SIZE(ob) == 2);
+ assert(PySet_Add(ob, elem) == 0);
+ assert(PySet_Contains(ob, elem) == 1);
+ assert(PySet_GET_SIZE(ob) == 3);
+ assert(PySet_Discard(ob, elem) == 1);
+ assert(PySet_GET_SIZE(ob) == 2);
+ assert(PySet_Discard(ob, elem) == 0);
+ assert(PySet_GET_SIZE(ob) == 2);
+
+ /* Exercise clear */
+ dup2 = PySet_New(dup);
+ assert(PySet_Clear(dup2) == 0);
+ assert(PySet_Size(dup2) == 0);
+ Py_DECREF(dup2);
+
+ /* Raise SystemError on clear or update of frozen set */
+ f = PyFrozenSet_New(dup);
+ assertRaises(PySet_Clear(f) == -1, PyExc_SystemError);
+ assertRaises(_PySet_Update(f, dup) == -1, PyExc_SystemError);
+ assert(PySet_Add(f, elem) == 0);
+ Py_INCREF(f);
+ assertRaises(PySet_Add(f, elem) == -1, PyExc_SystemError);
+ Py_DECREF(f);
+ Py_DECREF(f);
+
+ /* Exercise direct iteration */
+ i = 0, count = 0;
+ while (_PySet_NextEntry((PyObject *)dup, &i, &x, &hash)) {
+ s = PyUnicode_AsUTF8(x);
+ assert(s && (s[0] == 'a' || s[0] == 'b' || s[0] == 'c'));
+ count++;
+ }
+ assert(count == 3);
+
+ /* Exercise updates */
+ dup2 = PySet_New(NULL);
+ assert(_PySet_Update(dup2, dup) == 0);
+ assert(PySet_Size(dup2) == 3);
+ assert(_PySet_Update(dup2, dup) == 0);
+ assert(PySet_Size(dup2) == 3);
+ Py_DECREF(dup2);
+
+ /* Raise SystemError when self argument is not a set or frozenset. */
+ t = PyTuple_New(0);
+ assertRaises(PySet_Size(t) == -1, PyExc_SystemError);
+ assertRaises(PySet_Contains(t, elem) == -1, PyExc_SystemError);
+ Py_DECREF(t);
+
+ /* Raise SystemError when self argument is not a set. */
+ f = PyFrozenSet_New(dup);
+ assert(PySet_Size(f) == 3);
+ assert(PyFrozenSet_CheckExact(f));
+ assertRaises(PySet_Discard(f, elem) == -1, PyExc_SystemError);
+ assertRaises(PySet_Pop(f) == NULL, PyExc_SystemError);
+ Py_DECREF(f);
+
+ /* Raise KeyError when popping from an empty set */
+ assert(PyNumber_InPlaceSubtract(ob, ob) == ob);
+ Py_DECREF(ob);
+ assert(PySet_GET_SIZE(ob) == 0);
+ assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError);
+
+ /* Restore the set from the copy using the PyNumber API */
+ assert(PyNumber_InPlaceOr(ob, dup) == ob);
+ Py_DECREF(ob);
+
+ /* Verify constructors accept NULL arguments */
+ f = PySet_New(NULL);
+ assert(f != NULL);
+ assert(PySet_GET_SIZE(f) == 0);
+ Py_DECREF(f);
+ f = PyFrozenSet_New(NULL);
+ assert(f != NULL);
+ assert(PyFrozenSet_CheckExact(f));
+ assert(PySet_GET_SIZE(f) == 0);
+ Py_DECREF(f);
+
+ Py_DECREF(elem);
+ Py_DECREF(dup);
+ Py_RETURN_TRUE;
+}
+
+#undef assertRaises
+
+#endif
+
+/***** 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_EXTRA_INIT
+ 2, &_PySetDummy_Type
+};