mirror of
https://github.com/python/cpython.git
synced 2025-10-20 00:13:47 +00:00
2450 lines
57 KiB
C
2450 lines
57 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-2007 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 long 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 long 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, long hash)
|
|||
{
|
|||
|
register setentry *entry;
|
||
typedef setentry *(*lookupfunc)(PySetObject *, PyObject *, long);
|
|||
|
|||
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, long 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;
|
|||
|
|||
assert(so->fill <= so->mask); /* at least one empty slot */
|
|||
n_used = so->used;
|
|||
Py_INCREF(entry->key);
|
|||
if (set_insert_key(so, entry->key, entry->hash) == -1) {
|
|||
Py_DECREF(entry->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 long 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 long 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) {
|
|||
Py_DECREF(keys);
|
|||
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;
|
|||
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];
|
|||
if (entry->key != NULL &&
|
|||
entry->key != dummy) {
|
|||
Py_INCREF(entry->key);
|
|||
if (set_insert_key(so, entry->key, entry->hash) == -1) {
|
|||
Py_DECREF(entry->key);
|
|||
return -1;
|
|||
}
|
|||
}
|
|||
}
|
|||
return 0;
|
|||
}
|
|||
|
|||
static int
|
|||
set_contains_key(PySetObject *so, PyObject *key)
|
|||
{
|
|||
long 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.");
|
|||
|
|||
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 long
|
|||
frozenset_hash(PyObject *self)
|
|||
{
|
|||
PySetObject *so = (PySetObject *)self;
|
|||
long 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_Del(si);
|
|||
}
|
|||
|
|||
|
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_FromLong(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_compare */
|
|||
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, /* tp_flags */
|
|||
0, /* tp_doc */
|
|||
0, /* 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_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;
|
|||
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;
|
||
long 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;
|
|||
}
|
|||
|
|||
/* 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, long hash);
|
|||
setentry tab[PySet_MINSIZE];
|
|||
long 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(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)
|
|||
return (PyObject *)result;
|
|||
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(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) {
|
|||
![]()
* Simplify and speedup logic for tp_print.
* Speed-up intersection whenever PyDict_Next can be used.
|
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;
|
|||
long 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;
|
|||
long 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(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;
|
|||
long hash;
|
|||
while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
|
|||
setentry an_entry;
|
|||
|
|||
an_entry.hash = hash;
|
|||
an_entry.key = key;
|
|||
rv = set_discard_entry(so, &an_entry);
|
|||
if (rv == -1)
|
|||
return NULL;
|
|||
if (rv == DISCARD_NOTFOUND) {
|
|||
if (set_add_entry(so, &an_entry) == -1)
|
|||
return NULL;
|
|||
}
|
|||
}
|
|||
Py_RETURN_NONE;
|
|||
}
|
|||
|
|||
if (PyAnySet_Check(other)) {
|
|||
Py_INCREF(other);
|
|||
otherset = (PySetObject *)other;
|
|||
} else {
|
|||
![]() |
otherset = (PySetObject *)make_new_set(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(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 int
|
|||
set_nocmp(PyObject *self, PyObject *other)
|
|||
{
|
|||
PyErr_SetString(PyExc_TypeError, "cannot compare sets using cmp()");
|
|||
return -1;
|
|||
}
|
|||
|
|||
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, NULL);
|
|||
if (tmpkey == NULL)
|
|||
return -1;
|
|||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
rv = set_contains(so, tmpkey);
|
|||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
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, *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, NULL);
|
||
if (tmpkey == NULL)
|
|||
![]() |
return NULL;
|
||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
|
result = set_remove(so, tmpkey);
|
||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
|
Py_DECREF(tmpkey);
|
||
|
return result;
|
||
} else 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, NULL);
|
||
if (tmpkey == NULL)
|
|||
|
return NULL;
|
||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
|
result = set_discard(so, tmpkey);
|
||
set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key);
|
|||
|
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 (!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_long*/
|
|||
0, /*nb_float*/
|
|||
0, /*nb_inplace_add*/
|
|||
(binaryfunc)set_isub, /*nb_inplace_subtract*/
|
|||
0, /*nb_inplace_multiply*/
|
|||
0, /*nb_inplace_remainder*/
|
|||
0, /*nb_inplace_power*/
|
|||
0, /*nb_inplace_lshift*/
|
|||
0, /*nb_inplace_rshift*/
|
|||
(binaryfunc)set_iand, /*nb_inplace_and*/
|
|||
(binaryfunc)set_ixor, /*nb_inplace_xor*/
|
|||
(binaryfunc)set_ior, /*nb_inplace_or*/
|
|||
};
|
|||
|
|||
PyDoc_STRVAR(set_doc,
|
|||
"set(iterable) --> 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 */
|
|||
set_nocmp, /* tp_compare */
|
|||
(reprfunc)set_repr, /* tp_repr */
|
|||
&set_as_number, /* tp_as_number */
|
|||
&set_as_sequence, /* tp_as_sequence */
|
|||
0, /* tp_as_mapping */
|
|||
(hashfunc)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(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 */
|
|||
![]()
* Simplify and speedup logic for tp_print.
* Speed-up intersection whenever PyDict_Next can be used.
|
0, /* tp_itemsize */
|
||
/* methods */
|
|||
(destructor)set_dealloc, /* tp_dealloc */
|
|||
0, /* tp_print */
|
|||
0, /* tp_getattr */
|
|||
0, /* tp_setattr */
|
|||
set_nocmp, /* tp_compare */
|
|||
(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, long *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;
|
|||
|
long hash;
|
||
|
|
||
/* Verify preconditions and exercise type/size checks */
|
|||
assert(PyAnySet_Check(ob));
|
|||
assert(PyAnySet_CheckExact(ob));
|
|||
assert(!PyFrozenSet_CheckExact(ob));
|
|||
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
|