mirror of
				https://github.com/python/cpython.git
				synced 2025-10-25 18:54:53 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2486 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2486 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| /* set object implementation
 | |
|    Written and maintained by Raymond D. Hettinger <python@rcn.com>
 | |
|    Derived from Lib/sets.py and Objects/dictobject.c.
 | |
| 
 | |
|    Copyright (c) 2003-2008 Python Software Foundation.
 | |
|    All rights reserved.
 | |
| */
 | |
| 
 | |
| #include "Python.h"
 | |
| #include "structmember.h"
 | |
| #include "stringlib/eq.h"
 | |
| 
 | |
| /* Set a key error with the specified argument, wrapping it in a
 | |
|  * tuple automatically so that tuple keys are not unpacked as the
 | |
|  * exception arguments. */
 | |
| static void
 | |
| set_key_error(PyObject *arg)
 | |
| {
 | |
|     PyObject *tup;
 | |
|     tup = PyTuple_Pack(1, arg);
 | |
|     if (!tup)
 | |
|         return; /* caller will expect error to be set anyway */
 | |
|     PyErr_SetObject(PyExc_KeyError, tup);
 | |
|     Py_DECREF(tup);
 | |
| }
 | |
| 
 | |
| /* This must be >= 1. */
 | |
| #define PERTURB_SHIFT 5
 | |
| 
 | |
| /* Object used as dummy key to fill deleted entries */
 | |
| static PyObject *dummy = NULL; /* Initialized by first call to make_new_set() */
 | |
| 
 | |
| #ifdef Py_REF_DEBUG
 | |
| PyObject *
 | |
| _PySet_Dummy(void)
 | |
| {
 | |
|     return dummy;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #define INIT_NONZERO_SET_SLOTS(so) do {                         \
 | |
|     (so)->table = (so)->smalltable;                             \
 | |
|     (so)->mask = PySet_MINSIZE - 1;                             \
 | |
|     (so)->hash = -1;                                            \
 | |
|     } while(0)
 | |
| 
 | |
| #define EMPTY_TO_MINSIZE(so) do {                               \
 | |
|     memset((so)->smalltable, 0, sizeof((so)->smalltable));      \
 | |
|     (so)->used = (so)->fill = 0;                                \
 | |
|     INIT_NONZERO_SET_SLOTS(so);                                 \
 | |
|     } while(0)
 | |
| 
 | |
| /* Reuse scheme to save calls to malloc, free, and memset */
 | |
| #ifndef PySet_MAXFREELIST
 | |
| #define PySet_MAXFREELIST 80
 | |
| #endif
 | |
| static PySetObject *free_list[PySet_MAXFREELIST];
 | |
| static int numfree = 0;
 | |
| 
 | |
| 
 | |
| /*
 | |
| The basic lookup function used by all operations.
 | |
| This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
 | |
| Open addressing is preferred over chaining since the link overhead for
 | |
| chaining would be substantial (100% with typical malloc overhead).
 | |
| 
 | |
| The initial probe index is computed as hash mod the table size. Subsequent
 | |
| probe indices are computed as explained in Objects/dictobject.c.
 | |
| 
 | |
| All arithmetic on hash should ignore overflow.
 | |
| 
 | |
| Unlike the dictionary implementation, the lookkey functions can return
 | |
| NULL if the rich comparison returns an error.
 | |
| */
 | |
| 
 | |
| static setentry *
 | |
| set_lookkey(PySetObject *so, PyObject *key, register Py_hash_t hash)
 | |
| {
 | |
|     register Py_ssize_t i;
 | |
|     register size_t perturb;
 | |
|     register setentry *freeslot;
 | |
|     register size_t mask = so->mask;
 | |
|     setentry *table = so->table;
 | |
|     register setentry *entry;
 | |
|     register int cmp;
 | |
|     PyObject *startkey;
 | |
| 
 | |
|     i = hash & mask;
 | |
|     entry = &table[i];
 | |
|     if (entry->key == NULL || entry->key == key)
 | |
|         return entry;
 | |
| 
 | |
|     if (entry->key == dummy)
 | |
|         freeslot = entry;
 | |
|     else {
 | |
|         if (entry->hash == hash) {
 | |
|             startkey = entry->key;
 | |
|             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) {
 | |
|                 if (cmp > 0)
 | |
|                     return entry;
 | |
|             }
 | |
|             else {
 | |
|                 /* The compare did major nasty stuff to the
 | |
|                  * set:  start over.
 | |
|                  */
 | |
|                 return set_lookkey(so, key, hash);
 | |
|             }
 | |
|         }
 | |
|         freeslot = NULL;
 | |
|     }
 | |
| 
 | |
|     /* In the loop, key == dummy is by far (factor of 100s) the
 | |
|        least likely outcome, so test for that last. */
 | |
|     for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
 | |
|         i = (i << 2) + i + perturb + 1;
 | |
|         entry = &table[i & mask];
 | |
|         if (entry->key == NULL) {
 | |
|             if (freeslot != NULL)
 | |
|                 entry = freeslot;
 | |
|             break;
 | |
|         }
 | |
|         if (entry->key == key)
 | |
|             break;
 | |
|         if (entry->hash == hash && entry->key != dummy) {
 | |
|             startkey = entry->key;
 | |
|             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) {
 | |
|                 if (cmp > 0)
 | |
|                     break;
 | |
|             }
 | |
|             else {
 | |
|                 /* The compare did major nasty stuff to the
 | |
|                  * set:  start over.
 | |
|                  */
 | |
|                 return set_lookkey(so, key, hash);
 | |
|             }
 | |
|         }
 | |
|         else if (entry->key == dummy && freeslot == NULL)
 | |
|             freeslot = entry;
 | |
|     }
 | |
|     return entry;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Hacked up version of set_lookkey which can assume keys are always unicode;
 | |
|  * This means we can always use unicode_eq directly and not have to check to
 | |
|  * see if the comparison altered the table.
 | |
|  */
 | |
| static setentry *
 | |
| set_lookkey_unicode(PySetObject *so, PyObject *key, register Py_hash_t hash)
 | |
| {
 | |
|     register Py_ssize_t i;
 | |
|     register size_t perturb;
 | |
|     register setentry *freeslot;
 | |
|     register size_t mask = so->mask;
 | |
|     setentry *table = so->table;
 | |
|     register setentry *entry;
 | |
| 
 | |
|     /* Make sure this function doesn't have to handle non-unicode keys,
 | |
|        including subclasses of str; e.g., one reason to subclass
 | |
|        strings is to override __eq__, and for speed we don't cater to
 | |
|        that here. */
 | |
|     if (!PyUnicode_CheckExact(key)) {
 | |
|         so->lookup = set_lookkey;
 | |
|         return set_lookkey(so, key, hash);
 | |
|     }
 | |
|     i = hash & mask;
 | |
|     entry = &table[i];
 | |
|     if (entry->key == NULL || entry->key == key)
 | |
|         return entry;
 | |
|     if (entry->key == dummy)
 | |
|         freeslot = entry;
 | |
|     else {
 | |
|         if (entry->hash == hash && unicode_eq(entry->key, key))
 | |
|             return entry;
 | |
|         freeslot = NULL;
 | |
|     }
 | |
| 
 | |
|     /* In the loop, key == dummy is by far (factor of 100s) the
 | |
|        least likely outcome, so test for that last. */
 | |
|     for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
 | |
|         i = (i << 2) + i + perturb + 1;
 | |
|         entry = &table[i & mask];
 | |
|         if (entry->key == NULL)
 | |
|             return freeslot == NULL ? entry : freeslot;
 | |
|         if (entry->key == key
 | |
|             || (entry->hash == hash
 | |
|             && entry->key != dummy
 | |
|             && unicode_eq(entry->key, key)))
 | |
|             return entry;
 | |
|         if (entry->key == dummy && freeslot == NULL)
 | |
|             freeslot = entry;
 | |
|     }
 | |
|     assert(0);          /* NOT REACHED */
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Internal routine to insert a new key into the table.
 | |
| Used by the public insert routine.
 | |
| Eats a reference to key.
 | |
| */
 | |
| static int
 | |
| set_insert_key(register PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     register setentry *entry;
 | |
|     typedef setentry *(*lookupfunc)(PySetObject *, PyObject *, Py_hash_t);
 | |
| 
 | |
|     assert(so->lookup != NULL);
 | |
|     entry = so->lookup(so, key, hash);
 | |
|     if (entry == NULL)
 | |
|         return -1;
 | |
|     if (entry->key == NULL) {
 | |
|         /* UNUSED */
 | |
|         so->fill++;
 | |
|         entry->key = key;
 | |
|         entry->hash = hash;
 | |
|         so->used++;
 | |
|     } else if (entry->key == dummy) {
 | |
|         /* DUMMY */
 | |
|         entry->key = key;
 | |
|         entry->hash = hash;
 | |
|         so->used++;
 | |
|         Py_DECREF(dummy);
 | |
|     } else {
 | |
|         /* ACTIVE */
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Internal routine used by set_table_resize() to insert an item which is
 | |
| known to be absent from the set.  This routine also assumes that
 | |
| the set contains no deleted entries.  Besides the performance benefit,
 | |
| using set_insert_clean() in set_table_resize() is dangerous (SF bug #1456209).
 | |
| Note that no refcounts are changed by this routine; if needed, the caller
 | |
| is responsible for incref'ing `key`.
 | |
| */
 | |
| static void
 | |
| set_insert_clean(register PySetObject *so, PyObject *key, Py_hash_t hash)
 | |
| {
 | |
|     register size_t i;
 | |
|     register size_t perturb;
 | |
|     register size_t mask = (size_t)so->mask;
 | |
|     setentry *table = so->table;
 | |
|     register setentry *entry;
 | |
| 
 | |
|     i = hash & mask;
 | |
|     entry = &table[i];
 | |
|     for (perturb = hash; entry->key != NULL; perturb >>= PERTURB_SHIFT) {
 | |
|         i = (i << 2) + i + perturb + 1;
 | |
|         entry = &table[i & mask];
 | |
|     }
 | |
|     so->fill++;
 | |
|     entry->key = key;
 | |
|     entry->hash = hash;
 | |
|     so->used++;
 | |
| }
 | |
| 
 | |
| /*
 | |
| 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)
 | |
| {
 | |
|     Py_ssize_t newsize;
 | |
|     setentry *oldtable, *newtable, *entry;
 | |
|     Py_ssize_t i;
 | |
|     int is_oldtable_malloced;
 | |
|     setentry small_copy[PySet_MINSIZE];
 | |
| 
 | |
|     assert(minused >= 0);
 | |
| 
 | |
|     /* Find the smallest table size > minused. */
 | |
|     for (newsize = PySet_MINSIZE;
 | |
|          newsize <= minused && newsize > 0;
 | |
|          newsize <<= 1)
 | |
|         ;
 | |
|     if (newsize <= 0) {
 | |
|         PyErr_NoMemory();
 | |
|         return -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);
 | |
|     so->table = newtable;
 | |
|     so->mask = newsize - 1;
 | |
|     memset(newtable, 0, sizeof(setentry) * newsize);
 | |
|     so->used = 0;
 | |
|     i = so->fill;
 | |
|     so->fill = 0;
 | |
| 
 | |
|     /* Copy the data over; this is refcount-neutral for active entries;
 | |
|        dummy entries aren't copied over, of course */
 | |
|     for (entry = oldtable; i > 0; entry++) {
 | |
|         if (entry->key == NULL) {
 | |
|             /* UNUSED */
 | |
|             ;
 | |
|         } else if (entry->key == dummy) {
 | |
|             /* DUMMY */
 | |
|             --i;
 | |
|             assert(entry->key == dummy);
 | |
|             Py_DECREF(entry->key);
 | |
|         } else {
 | |
|             /* ACTIVE */
 | |
|             --i;
 | |
|             set_insert_clean(so, entry->key, entry->hash);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (is_oldtable_malloced)
 | |
|         PyMem_DEL(oldtable);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* CAUTION: set_add_key/entry() must guarantee it won't resize the table */
 | |
| 
 | |
| static int
 | |
| set_add_entry(register PySetObject *so, setentry *entry)
 | |
| {
 | |
|     register Py_ssize_t n_used;
 | |
|     PyObject *key = entry->key;
 | |
| 
 | |
|     assert(so->fill <= so->mask);  /* at least one empty slot */
 | |
|     n_used = so->used;
 | |
|     Py_INCREF(key);
 | |
|     if (set_insert_key(so, key, entry->hash) == -1) {
 | |
|         Py_DECREF(key);
 | |
|         return -1;
 | |
|     }
 | |
|     if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2))
 | |
|         return 0;
 | |
|     return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_add_key(register PySetObject *so, PyObject *key)
 | |
| {
 | |
|     register Py_hash_t hash;
 | |
|     register Py_ssize_t n_used;
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = ((PyUnicodeObject *) key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     assert(so->fill <= so->mask);  /* at least one empty slot */
 | |
|     n_used = so->used;
 | |
|     Py_INCREF(key);
 | |
|     if (set_insert_key(so, key, hash) == -1) {
 | |
|         Py_DECREF(key);
 | |
|         return -1;
 | |
|     }
 | |
|     if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2))
 | |
|         return 0;
 | |
|     return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
 | |
| }
 | |
| 
 | |
| #define DISCARD_NOTFOUND 0
 | |
| #define DISCARD_FOUND 1
 | |
| 
 | |
| static int
 | |
| set_discard_entry(PySetObject *so, setentry *oldentry)
 | |
| {       register setentry *entry;
 | |
|     PyObject *old_key;
 | |
| 
 | |
|     entry = (so->lookup)(so, oldentry->key, oldentry->hash);
 | |
|     if (entry == NULL)
 | |
|         return -1;
 | |
|     if (entry->key == NULL  ||  entry->key == dummy)
 | |
|         return DISCARD_NOTFOUND;
 | |
|     old_key = entry->key;
 | |
|     Py_INCREF(dummy);
 | |
|     entry->key = dummy;
 | |
|     so->used--;
 | |
|     Py_DECREF(old_key);
 | |
|     return DISCARD_FOUND;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_discard_key(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     register Py_hash_t hash;
 | |
|     register setentry *entry;
 | |
|     PyObject *old_key;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = ((PyUnicodeObject *) key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     entry = (so->lookup)(so, key, hash);
 | |
|     if (entry == NULL)
 | |
|         return -1;
 | |
|     if (entry->key == NULL  ||  entry->key == dummy)
 | |
|         return DISCARD_NOTFOUND;
 | |
|     old_key = entry->key;
 | |
|     Py_INCREF(dummy);
 | |
|     entry->key = dummy;
 | |
|     so->used--;
 | |
|     Py_DECREF(old_key);
 | |
|     return DISCARD_FOUND;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_clear_internal(PySetObject *so)
 | |
| {
 | |
|     setentry *entry, *table;
 | |
|     int table_is_malloced;
 | |
|     Py_ssize_t fill;
 | |
|     setentry small_copy[PySet_MINSIZE];
 | |
| #ifdef Py_DEBUG
 | |
|     Py_ssize_t i, n;
 | |
|     assert (PyAnySet_Check(so));
 | |
| 
 | |
|     n = so->mask + 1;
 | |
|     i = 0;
 | |
| #endif
 | |
| 
 | |
|     table = so->table;
 | |
|     assert(table != NULL);
 | |
|     table_is_malloced = table != so->smalltable;
 | |
| 
 | |
|     /* 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.
 | |
|      */
 | |
|     fill = so->fill;
 | |
|     if (table_is_malloced)
 | |
|         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;
 | |
|         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; fill > 0; ++entry) {
 | |
| #ifdef Py_DEBUG
 | |
|         assert(i < n);
 | |
|         ++i;
 | |
| #endif
 | |
|         if (entry->key) {
 | |
|             --fill;
 | |
|             Py_DECREF(entry->key);
 | |
|         }
 | |
| #ifdef Py_DEBUG
 | |
|         else
 | |
|             assert(entry->key == NULL);
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     if (table_is_malloced)
 | |
|         PyMem_DEL(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;
 | |
|     register setentry *table;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     i = *pos_ptr;
 | |
|     assert(i >= 0);
 | |
|     table = so->table;
 | |
|     mask = so->mask;
 | |
|     while (i <= mask && (table[i].key == NULL || table[i].key == dummy))
 | |
|         i++;
 | |
|     *pos_ptr = i+1;
 | |
|     if (i > mask)
 | |
|         return 0;
 | |
|     assert(table[i].key != NULL);
 | |
|     *entry_ptr = &table[i];
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_dealloc(PySetObject *so)
 | |
| {
 | |
|     register setentry *entry;
 | |
|     Py_ssize_t fill = so->fill;
 | |
|     PyObject_GC_UnTrack(so);
 | |
|     Py_TRASHCAN_SAFE_BEGIN(so)
 | |
|     if (so->weakreflist != NULL)
 | |
|         PyObject_ClearWeakRefs((PyObject *) so);
 | |
| 
 | |
|     for (entry = so->table; fill > 0; entry++) {
 | |
|         if (entry->key) {
 | |
|             --fill;
 | |
|             Py_DECREF(entry->key);
 | |
|         }
 | |
|     }
 | |
|     if (so->table != so->smalltable)
 | |
|         PyMem_DEL(so->table);
 | |
|     if (numfree < PySet_MAXFREELIST && PyAnySet_CheckExact(so))
 | |
|         free_list[numfree++] = so;
 | |
|     else
 | |
|         Py_TYPE(so)->tp_free(so);
 | |
|     Py_TRASHCAN_SAFE_END(so)
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_repr(PySetObject *so)
 | |
| {
 | |
|     PyObject *keys, *result=NULL;
 | |
|     Py_UNICODE *u;
 | |
|     int status = Py_ReprEnter((PyObject*)so);
 | |
|     PyObject *listrepr;
 | |
|     Py_ssize_t newsize;
 | |
| 
 | |
|     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;
 | |
| 
 | |
|     listrepr = PyObject_Repr(keys);
 | |
|     Py_DECREF(keys);
 | |
|     if (listrepr == NULL)
 | |
|         goto done;
 | |
|     newsize = PyUnicode_GET_SIZE(listrepr);
 | |
|     result = PyUnicode_FromUnicode(NULL, newsize);
 | |
|     if (result) {
 | |
|         u = PyUnicode_AS_UNICODE(result);
 | |
|         *u++ = '{';
 | |
|         /* Omit the brackets from the listrepr */
 | |
|         Py_UNICODE_COPY(u, PyUnicode_AS_UNICODE(listrepr)+1,
 | |
|                            PyUnicode_GET_SIZE(listrepr)-2);
 | |
|         u += newsize-2;
 | |
|         *u++ = '}';
 | |
|     }
 | |
|     Py_DECREF(listrepr);
 | |
|     if (Py_TYPE(so) != &PySet_Type) {
 | |
|         PyObject *tmp = PyUnicode_FromFormat("%s(%U)",
 | |
|                                              Py_TYPE(so)->tp_name,
 | |
|                                              result);
 | |
|         Py_DECREF(result);
 | |
|         result = tmp;
 | |
|     }
 | |
| 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;
 | |
|     register Py_ssize_t i;
 | |
|     register setentry *entry;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     assert (PyAnySet_Check(otherset));
 | |
| 
 | |
|     other = (PySetObject*)otherset;
 | |
|     if (other == so || other->used == 0)
 | |
|         /* a.update(a) or a.update({}); 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)*3 >= (so->mask+1)*2) {
 | |
|        if (set_table_resize(so, (so->used + other->used)*2) != 0)
 | |
|            return -1;
 | |
|     }
 | |
|     for (i = 0; i <= other->mask; i++) {
 | |
|         entry = &other->table[i];
 | |
|         key = entry->key;
 | |
|         if (key != NULL &&
 | |
|             key != dummy) {
 | |
|             Py_INCREF(key);
 | |
|             if (set_insert_key(so, key, entry->hash) == -1) {
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_contains_key(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     Py_hash_t hash;
 | |
|     setentry *entry;
 | |
| 
 | |
|     if (!PyUnicode_CheckExact(key) ||
 | |
|         (hash = ((PyUnicodeObject *) key)->hash) == -1) {
 | |
|         hash = PyObject_Hash(key);
 | |
|         if (hash == -1)
 | |
|             return -1;
 | |
|     }
 | |
|     entry = (so->lookup)(so, key, hash);
 | |
|     if (entry == NULL)
 | |
|         return -1;
 | |
|     key = entry->key;
 | |
|     return key != NULL && key != dummy;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_contains_entry(PySetObject *so, setentry *entry)
 | |
| {
 | |
|     PyObject *key;
 | |
|     setentry *lu_entry;
 | |
| 
 | |
|     lu_entry = (so->lookup)(so, entry->key, entry->hash);
 | |
|     if (lu_entry == NULL)
 | |
|         return -1;
 | |
|     key = lu_entry->key;
 | |
|     return key != NULL && key != dummy;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_pop(PySetObject *so)
 | |
| {
 | |
|     register Py_ssize_t i = 0;
 | |
|     register setentry *entry;
 | |
|     PyObject *key;
 | |
| 
 | |
|     assert (PyAnySet_Check(so));
 | |
|     if (so->used == 0) {
 | |
|         PyErr_SetString(PyExc_KeyError, "pop from an empty set");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Set entry to "the first" unused or dummy set entry.  We abuse
 | |
|      * the hash field of slot 0 to hold a search finger:
 | |
|      * If slot 0 has a value, use slot 0.
 | |
|      * Else slot 0 is being used to hold a search finger,
 | |
|      * and we use its hash value as the first index to look.
 | |
|      */
 | |
|     entry = &so->table[0];
 | |
|     if (entry->key == NULL || entry->key == dummy) {
 | |
|         i = entry->hash;
 | |
|         /* The hash field may be a real hash value, or it may be a
 | |
|          * legit search finger, or it may be a once-legit search
 | |
|          * finger that's out of bounds now because it wrapped around
 | |
|          * or the table shrunk -- simply make sure it's in bounds now.
 | |
|          */
 | |
|         if (i > so->mask || i < 1)
 | |
|             i = 1;              /* skip slot 0 */
 | |
|         while ((entry = &so->table[i])->key == NULL || entry->key==dummy) {
 | |
|             i++;
 | |
|             if (i > so->mask)
 | |
|                 i = 1;
 | |
|         }
 | |
|     }
 | |
|     key = entry->key;
 | |
|     Py_INCREF(dummy);
 | |
|     entry->key = dummy;
 | |
|     so->used--;
 | |
|     so->table[0].hash = i + 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;
 | |
| }
 | |
| 
 | |
| static Py_hash_t
 | |
| frozenset_hash(PyObject *self)
 | |
| {
 | |
|     PySetObject *so = (PySetObject *)self;
 | |
|     Py_hash_t h, hash = 1927868237L;
 | |
|     setentry *entry;
 | |
|     Py_ssize_t pos = 0;
 | |
| 
 | |
|     if (so->hash != -1)
 | |
|         return so->hash;
 | |
| 
 | |
|     hash *= PySet_GET_SIZE(self) + 1;
 | |
|     while (set_next(so, &pos, &entry)) {
 | |
|         /* Work to increase the bit dispersion for closely spaced hash
 | |
|            values.  The 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. */
 | |
|         h = entry->hash;
 | |
|         hash ^= (h ^ (h << 16) ^ 89869747L)  * 3644798167u;
 | |
|     }
 | |
|     hash = hash * 69069L + 907133923L;
 | |
|     if (hash == -1)
 | |
|         hash = 590923713L;
 | |
|     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)
 | |
| {
 | |
|     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)
 | |
| {
 | |
|     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 PyMethodDef setiter_methods[] = {
 | |
|     {"__length_hint__", (PyCFunction)setiter_len, METH_NOARGS, length_hint_doc},
 | |
|     {NULL,              NULL}           /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyObject *setiter_iternext(setiterobject *si)
 | |
| {
 | |
|     PyObject *key;
 | |
|     register Py_ssize_t i, mask;
 | |
|     register 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:
 | |
|     Py_DECREF(so);
 | |
|     si->si_set = NULL;
 | |
|     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_print */
 | |
|     0,                                          /* tp_getattr */
 | |
|     0,                                          /* tp_setattr */
 | |
|     0,                                          /* tp_reserved */
 | |
|     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_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 == -1)
 | |
|             return -1;
 | |
|         if ((so->fill + dictsize)*3 >= (so->mask+1)*2) {
 | |
|             if (set_table_resize(so, (so->used + dictsize)*2) != 0)
 | |
|                 return -1;
 | |
|         }
 | |
|         while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
 | |
|             setentry an_entry;
 | |
| 
 | |
|             an_entry.hash = hash;
 | |
|             an_entry.key = key;
 | |
|             if (set_add_entry(so, &an_entry) == -1)
 | |
|                 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) == -1) {
 | |
|             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) == -1)
 | |
|             return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(update_doc,
 | |
| "Update a set with the union of itself and others.");
 | |
| 
 | |
| static PyObject *
 | |
| make_new_set(PyTypeObject *type, PyObject *iterable)
 | |
| {
 | |
|     register PySetObject *so = NULL;
 | |
| 
 | |
|     if (dummy == NULL) { /* Auto-initialize dummy */
 | |
|         dummy = PyUnicode_FromString("<dummy key>");
 | |
|         if (dummy == NULL)
 | |
|             return NULL;
 | |
|     }
 | |
| 
 | |
|     /* create PySetObject structure */
 | |
|     if (numfree &&
 | |
|         (type == &PySet_Type  ||  type == &PyFrozenSet_Type)) {
 | |
|         so = free_list[--numfree];
 | |
|         assert (so != NULL && PyAnySet_CheckExact(so));
 | |
|         Py_TYPE(so) = type;
 | |
|         _Py_NewReference((PyObject *)so);
 | |
|         EMPTY_TO_MINSIZE(so);
 | |
|         PyObject_GC_Track(so);
 | |
|     } else {
 | |
|         so = (PySetObject *)type->tp_alloc(type, 0);
 | |
|         if (so == NULL)
 | |
|             return NULL;
 | |
|         /* tp_alloc has already zeroed the structure */
 | |
|         assert(so->table == NULL && so->fill == 0 && so->used == 0);
 | |
|         INIT_NONZERO_SET_SLOTS(so);
 | |
|     }
 | |
| 
 | |
|     so->lookup = set_lookkey_unicode;
 | |
|     so->weakreflist = NULL;
 | |
| 
 | |
|     if (iterable != NULL) {
 | |
|         if (set_update_internal(so, iterable) == -1) {
 | |
|             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);
 | |
| }
 | |
| 
 | |
| /* The empty frozenset is a singleton */
 | |
| static PyObject *emptyfrozenset = NULL;
 | |
| 
 | |
| static PyObject *
 | |
| frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | |
| {
 | |
|     PyObject *iterable = NULL, *result;
 | |
| 
 | |
|     if (type == &PyFrozenSet_Type && !_PyArg_NoKeywords("frozenset()", kwds))
 | |
|         return NULL;
 | |
| 
 | |
|     if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
 | |
|         return NULL;
 | |
| 
 | |
|     if (type != &PyFrozenSet_Type)
 | |
|         return make_new_set(type, iterable);
 | |
| 
 | |
|     if (iterable != NULL) {
 | |
|         /* frozenset(f) is idempotent */
 | |
|         if (PyFrozenSet_CheckExact(iterable)) {
 | |
|             Py_INCREF(iterable);
 | |
|             return iterable;
 | |
|         }
 | |
|         result = make_new_set(type, iterable);
 | |
|         if (result == NULL || PySet_GET_SIZE(result))
 | |
|             return result;
 | |
|         Py_DECREF(result);
 | |
|     }
 | |
|     /* The empty frozenset is a singleton */
 | |
|     if (emptyfrozenset == NULL)
 | |
|         emptyfrozenset = make_new_set(type, NULL);
 | |
|     Py_XINCREF(emptyfrozenset);
 | |
|     return emptyfrozenset;
 | |
| }
 | |
| 
 | |
| void
 | |
| PySet_Fini(void)
 | |
| {
 | |
|     PySetObject *so;
 | |
| 
 | |
|     while (numfree) {
 | |
|         numfree--;
 | |
|         so = free_list[numfree];
 | |
|         PyObject_GC_Del(so);
 | |
|     }
 | |
|     Py_CLEAR(dummy);
 | |
|     Py_CLEAR(emptyfrozenset);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | |
| {
 | |
|     if (type == &PySet_Type && !_PyArg_NoKeywords("set()", kwds))
 | |
|         return NULL;
 | |
| 
 | |
|     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 creating temporary frozensets from sets for membership testing
 | |
|    in __contains__(), discard(), and remove().  Also 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 *(*f)(PySetObject *so, PyObject *key, Py_ssize_t hash);
 | |
|     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;
 | |
| 
 | |
|     f = a->lookup;   a->lookup = b->lookup;      b->lookup = f;
 | |
| 
 | |
|     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)
 | |
| {
 | |
|     return make_new_set_basetype(Py_TYPE(so), (PyObject *)so);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| frozenset_copy(PySetObject *so)
 | |
| {
 | |
|     if (PyFrozenSet_CheckExact(so)) {
 | |
|         Py_INCREF(so);
 | |
|         return (PyObject *)so;
 | |
|     }
 | |
|     return set_copy(so);
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
 | |
| 
 | |
| static PyObject *
 | |
| set_clear(PySetObject *so)
 | |
| {
 | |
|     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);
 | |
|     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) == -1) {
 | |
|             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_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
| 
 | |
|     result = (PySetObject *)set_copy(so);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
|     if ((PyObject *)so == other)
 | |
|         return (PyObject *)result;
 | |
|     if (set_update_internal(result, other) == -1) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_ior(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         Py_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
|     if (set_update_internal(so, other) == -1)
 | |
|         return NULL;
 | |
|     Py_INCREF(so);
 | |
|     return (PyObject *)so;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_intersection(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PySetObject *result;
 | |
|     PyObject *key, *it, *tmp;
 | |
| 
 | |
|     if ((PyObject *)so == other)
 | |
|         return set_copy(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)) {
 | |
|             int rv = set_contains_entry(so, entry);
 | |
|             if (rv == -1) {
 | |
|                 Py_DECREF(result);
 | |
|                 return NULL;
 | |
|             }
 | |
|             if (rv) {
 | |
|                 if (set_add_entry(result, entry) == -1) {
 | |
|                     Py_DECREF(result);
 | |
|                     return NULL;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return (PyObject *)result;
 | |
|     }
 | |
| 
 | |
|     it = PyObject_GetIter(other);
 | |
|     if (it == NULL) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     while ((key = PyIter_Next(it)) != NULL) {
 | |
|         int rv;
 | |
|         setentry entry;
 | |
|         Py_hash_t hash = PyObject_Hash(key);
 | |
| 
 | |
|         if (hash == -1) {
 | |
|             Py_DECREF(it);
 | |
|             Py_DECREF(result);
 | |
|             Py_DECREF(key);
 | |
|             return NULL;
 | |
|         }
 | |
|         entry.hash = hash;
 | |
|         entry.key = key;
 | |
|         rv = set_contains_entry(so, &entry);
 | |
|         if (rv == -1) {
 | |
|             Py_DECREF(it);
 | |
|             Py_DECREF(result);
 | |
|             Py_DECREF(key);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (rv) {
 | |
|             if (set_add_entry(result, &entry) == -1) {
 | |
|                 Py_DECREF(it);
 | |
|                 Py_DECREF(result);
 | |
|                 Py_DECREF(key);
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|         Py_DECREF(key);
 | |
|     }
 | |
|     Py_DECREF(it);
 | |
|     if (PyErr_Occurred()) {
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
|     return (PyObject *)result;
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
|     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_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
|     return set_intersection(so, other);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_iand(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         Py_INCREF(Py_NotImplemented);
 | |
|         return Py_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;
 | |
| 
 | |
|     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)) {
 | |
|             int rv = set_contains_entry(so, entry);
 | |
|             if (rv == -1)
 | |
|                 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) {
 | |
|         int rv;
 | |
|         setentry entry;
 | |
|         Py_hash_t hash = PyObject_Hash(key);
 | |
| 
 | |
|         if (hash == -1) {
 | |
|             Py_DECREF(key);
 | |
|             Py_DECREF(it);
 | |
|             return NULL;
 | |
|         }
 | |
|         entry.hash = hash;
 | |
|         entry.key = key;
 | |
|         rv = set_contains_entry(so, &entry);
 | |
|         Py_DECREF(key);
 | |
|         if (rv == -1) {
 | |
|             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;
 | |
| 
 | |
|         while (set_next((PySetObject *)other, &pos, &entry))
 | |
|             if (set_discard_entry(so, entry) == -1)
 | |
|                 return -1;
 | |
|     } 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) == -1) {
 | |
|                 Py_DECREF(it);
 | |
|                 Py_DECREF(key);
 | |
|                 return -1;
 | |
|             }
 | |
|             Py_DECREF(key);
 | |
|         }
 | |
|         Py_DECREF(it);
 | |
|         if (PyErr_Occurred())
 | |
|             return -1;
 | |
|     }
 | |
|     /* If more than 1/5 are dummies, then resize them away. */
 | |
|     if ((so->fill - so->used) * 5 < so->mask)
 | |
|         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) == -1)
 | |
|             return NULL;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(difference_update_doc,
 | |
| "Remove all elements of another set from this set.");
 | |
| 
 | |
| static PyObject *
 | |
| set_difference(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
|     setentry *entry;
 | |
|     Py_ssize_t pos = 0;
 | |
| 
 | |
|     if (!PyAnySet_Check(other)  && !PyDict_CheckExact(other)) {
 | |
|         result = set_copy(so);
 | |
|         if (result == NULL)
 | |
|             return NULL;
 | |
|         if (set_difference_update_internal((PySetObject *)result, other) != -1)
 | |
|             return result;
 | |
|         Py_DECREF(result);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     result = make_new_set_basetype(Py_TYPE(so), NULL);
 | |
|     if (result == NULL)
 | |
|         return NULL;
 | |
| 
 | |
|     if (PyDict_CheckExact(other)) {
 | |
|         while (set_next(so, &pos, &entry)) {
 | |
|             setentry entrycopy;
 | |
|             entrycopy.hash = entry->hash;
 | |
|             entrycopy.key = entry->key;
 | |
|             if (!_PyDict_Contains(other, entry->key, entry->hash)) {
 | |
|                 if (set_add_entry((PySetObject *)result, &entrycopy) == -1) {
 | |
|                     Py_DECREF(result);
 | |
|                     return NULL;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     while (set_next(so, &pos, &entry)) {
 | |
|         int rv = set_contains_entry((PySetObject *)other, entry);
 | |
|         if (rv == -1) {
 | |
|             Py_DECREF(result);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (!rv) {
 | |
|             if (set_add_entry((PySetObject *)result, entry) == -1) {
 | |
|                 Py_DECREF(result);
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     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);
 | |
| 
 | |
|     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) == -1) {
 | |
|             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_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
|     return set_difference(so, other);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_isub(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         Py_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
|     if (set_difference_update_internal(so, other) == -1)
 | |
|         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;
 | |
|     setentry *entry;
 | |
| 
 | |
|     if ((PyObject *)so == other)
 | |
|         return set_clear(so);
 | |
| 
 | |
|     if (PyDict_CheckExact(other)) {
 | |
|         PyObject *value;
 | |
|         int rv;
 | |
|         Py_hash_t hash;
 | |
|         while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
 | |
|             setentry an_entry;
 | |
| 
 | |
|             Py_INCREF(key);
 | |
|             an_entry.hash = hash;
 | |
|             an_entry.key = key;
 | |
| 
 | |
|             rv = set_discard_entry(so, &an_entry);
 | |
|             if (rv == -1) {
 | |
|                 Py_DECREF(key);
 | |
|                 return NULL;
 | |
|             }
 | |
|             if (rv == DISCARD_NOTFOUND) {
 | |
|                 if (set_add_entry(so, &an_entry) == -1) {
 | |
|                     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)) {
 | |
|         int rv = set_discard_entry(so, entry);
 | |
|         if (rv == -1) {
 | |
|             Py_DECREF(otherset);
 | |
|             return NULL;
 | |
|         }
 | |
|         if (rv == DISCARD_NOTFOUND) {
 | |
|             if (set_add_entry(so, entry) == -1) {
 | |
|                 Py_DECREF(otherset);
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     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)
 | |
|         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_INCREF(Py_NotImplemented);
 | |
|         return Py_NotImplemented;
 | |
|     }
 | |
|     return set_symmetric_difference(so, other);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_ixor(PySetObject *so, PyObject *other)
 | |
| {
 | |
|     PyObject *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         Py_INCREF(Py_NotImplemented);
 | |
|         return Py_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;
 | |
| 
 | |
|     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)) {
 | |
|         int rv = set_contains_entry((PySetObject *)other, entry);
 | |
|         if (rv == -1)
 | |
|             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)
 | |
| {
 | |
|     PyObject *tmp, *result;
 | |
| 
 | |
|     if (!PyAnySet_Check(other)) {
 | |
|         tmp = make_new_set(&PySet_Type, other);
 | |
|         if (tmp == NULL)
 | |
|             return NULL;
 | |
|         result = set_issuperset(so, tmp);
 | |
|         Py_DECREF(tmp);
 | |
|         return result;
 | |
|     }
 | |
|     return set_issubset((PySetObject *)other, (PyObject *)so);
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(issuperset_doc, "Report whether this set contains another set.");
 | |
| 
 | |
| static PyObject *
 | |
| set_richcompare(PySetObject *v, PyObject *w, int op)
 | |
| {
 | |
|     PyObject *r1, *r2;
 | |
| 
 | |
|     if(!PyAnySet_Check(w)) {
 | |
|         Py_INCREF(Py_NotImplemented);
 | |
|         return Py_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 = PyBool_FromLong(PyObject_Not(r1));
 | |
|         Py_DECREF(r1);
 | |
|         return 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_INCREF(Py_NotImplemented);
 | |
|     return Py_NotImplemented;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| set_add(PySetObject *so, PyObject *key)
 | |
| {
 | |
|     if (set_add_key(so, key) == -1)
 | |
|         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 == -1) {
 | |
|         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(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 == -1)
 | |
|         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 == -1) {
 | |
|         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 == -1)
 | |
|             return NULL;
 | |
|     }
 | |
| 
 | |
|     if (rv == DISCARD_NOTFOUND) {
 | |
|         set_key_error(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, *result;
 | |
|     int rv;
 | |
| 
 | |
|     rv = set_discard_key(so, key);
 | |
|     if (rv == -1) {
 | |
|         if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
 | |
|             return NULL;
 | |
|         PyErr_Clear();
 | |
|         tmpkey = make_new_set(&PyFrozenSet_Type, key);
 | |
|         if (tmpkey == NULL)
 | |
|             return NULL;
 | |
|         result = set_discard(so, tmpkey);
 | |
|         Py_DECREF(tmpkey);
 | |
|         return result;
 | |
|     }
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(discard_doc,
 | |
| "Remove an element from a set if it is a member.\n\
 | |
| \n\
 | |
| If the element is not a member, do nothing.");
 | |
| 
 | |
| static PyObject *
 | |
| set_reduce(PySetObject *so)
 | |
| {
 | |
|     PyObject *keys=NULL, *args=NULL, *result=NULL, *dict=NULL;
 | |
| 
 | |
|     keys = PySequence_List((PyObject *)so);
 | |
|     if (keys == NULL)
 | |
|         goto done;
 | |
|     args = PyTuple_Pack(1, keys);
 | |
|     if (args == NULL)
 | |
|         goto done;
 | |
|     dict = PyObject_GetAttrString((PyObject *)so, "__dict__");
 | |
|     if (dict == NULL) {
 | |
|         PyErr_Clear();
 | |
|         dict = Py_None;
 | |
|         Py_INCREF(dict);
 | |
|     }
 | |
|     result = PyTuple_Pack(3, Py_TYPE(so), args, dict);
 | |
| done:
 | |
|     Py_XDECREF(args);
 | |
|     Py_XDECREF(keys);
 | |
|     Py_XDECREF(dict);
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
 | |
| 
 | |
| static PyObject *
 | |
| set_sizeof(PySetObject *so)
 | |
| {
 | |
|     Py_ssize_t res;
 | |
| 
 | |
|     res = sizeof(PySetObject);
 | |
|     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 (!PyAnySet_Check(self))
 | |
|         return -1;
 | |
|     if (PySet_Check(self) && !_PyArg_NoKeywords("set()", kwds))
 | |
|         return -1;
 | |
|     if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
 | |
|         return -1;
 | |
|     set_clear_internal(self);
 | |
|     self->hash = -1;
 | |
|     if (iterable == NULL)
 | |
|         return 0;
 | |
|     return set_update_internal(self, iterable);
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
| 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},
 | |
|     {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_print */
 | |
|     0,                                  /* tp_getattr */
 | |
|     0,                                  /* tp_setattr */
 | |
|     0,                                  /* tp_reserved */
 | |
|     (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,            /* 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 */
 | |
| };
 | |
| 
 | |
| /* 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},
 | |
|     {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_print */
 | |
|     0,                                  /* tp_getattr */
 | |
|     0,                                  /* tp_setattr */
 | |
|     0,                                  /* tp_reserved */
 | |
|     (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,            /* 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 */
 | |
| };
 | |
| 
 | |
| 
 | |
| /***** 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);
 | |
| }
 | |
| 
 | |
| int
 | |
| _PySet_Update(PyObject *set, PyObject *iterable)
 | |
| {
 | |
|     if (!PySet_Check(set)) {
 | |
|         PyErr_BadInternalCall();
 | |
|         return -1;
 | |
|     }
 | |
|     return set_update_internal((PySetObject *)set, iterable);
 | |
| }
 | |
| 
 | |
| #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)
 | |
| {
 | |
|     Py_ssize_t count;
 | |
|     char *s;
 | |
|     Py_ssize_t i;
 | |
|     PyObject *elem=NULL, *dup=NULL, *t, *f, *dup2, *x;
 | |
|     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) == -1) {
 | |
|         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_AsString(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
 | 
