cpython/Objects/dictobject.c

1108 lines
24 KiB
C
Raw Normal View History

/***********************************************************
2000-06-30 20:50:40 -03:00
Copyright (c) 2000, BeOpen.com.
Copyright (c) 1995-2000, Corporation for National Research Initiatives.
Copyright (c) 1990-1995, Stichting Mathematisch Centrum.
All rights reserved.
See the file "Misc/COPYRIGHT" for information on usage and
redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
******************************************************************/
/* Dictionary object implementation using a hash table */
1997-05-02 00:12:38 -03:00
#include "Python.h"
/*
* MINSIZE is the minimum size of a dictionary.
*/
#define MINSIZE 4
/*
Table of irreducible polynomials to efficiently cycle through
GF(2^n)-{0}, 2<=n<=30.
*/
static long polys[] = {
4 + 3,
8 + 3,
16 + 3,
32 + 5,
64 + 3,
128 + 3,
256 + 29,
512 + 17,
1024 + 9,
2048 + 5,
4096 + 83,
8192 + 27,
16384 + 43,
32768 + 3,
65536 + 45,
131072 + 9,
262144 + 39,
524288 + 39,
1048576 + 9,
2097152 + 5,
4194304 + 3,
8388608 + 33,
16777216 + 27,
33554432 + 9,
67108864 + 71,
134217728 + 39,
268435456 + 9,
536870912 + 5,
1073741824 + 83,
0
};
/* Object used as dummy key to fill deleted entries */
static PyObject *dummy; /* Initialized by first call to newdictobject() */
/*
Invariant for entries: when in use, me_value is not NULL and me_key is
not NULL and not dummy; when not in use, me_value is NULL and me_key
is either NULL or dummy. A dummy key value cannot be replaced by
NULL, since otherwise other keys may be lost.
*/
typedef struct {
long me_hash;
1997-05-02 00:12:38 -03:00
PyObject *me_key;
PyObject *me_value;
#ifdef USE_CACHE_ALIGNED
long aligner;
#endif
} dictentry;
/*
To ensure the lookup algorithm terminates, the table size must be a
prime number and there must be at least one NULL key in the table.
The value ma_fill is the number of non-NULL keys; ma_used is the number
of non-NULL, non-dummy keys.
To avoid slowing down lookups on a near-full table, we resize the table
when it is more than half filled.
*/
typedef struct {
1997-05-02 00:12:38 -03:00
PyObject_HEAD
int ma_fill;
int ma_used;
int ma_size;
int ma_poly;
dictentry *ma_table;
} dictobject;
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_New(void)
{
register dictobject *mp;
if (dummy == NULL) { /* Auto-initialize dummy */
1997-05-02 00:12:38 -03:00
dummy = PyString_FromString("<dummy key>");
if (dummy == NULL)
return NULL;
}
mp = PyObject_NEW(dictobject, &PyDict_Type);
if (mp == NULL)
return NULL;
mp->ma_size = 0;
mp->ma_poly = 0;
mp->ma_table = NULL;
mp->ma_fill = 0;
mp->ma_used = 0;
PyObject_GC_Init(mp);
1997-05-02 00:12:38 -03:00
return (PyObject *)mp;
}
/*
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).
However, instead of going through the table at constant steps, we cycle
through the values of GF(2^n)-{0}. This avoids modulo computations, being
much cheaper on RISC machines, without leading to clustering.
The initial probe index is computed as hash mod the table size.
Subsequent probe indices use the values of x^i in GF(2^n) as an offset,
where x is a root. The initial value is derived from hash, too.
All arithmetic on hash should ignore overflow.
(This version is due to Reimer Behrends, some ideas are also due to
Jyrki Alakuijala and Vladimir Marangozov.)
*/
static dictentry *
lookdict(dictobject *mp, PyObject *key, register long hash)
{
register int i;
register unsigned incr;
register dictentry *freeslot;
register unsigned int mask = mp->ma_size-1;
dictentry *ep0 = mp->ma_table;
register dictentry *ep;
/* We must come up with (i, incr) such that 0 <= i < ma_size
and 0 < incr < ma_size and both are a function of hash */
i = (~hash) & mask;
/* We use ~hash instead of hash, as degenerate hash functions, such
as for ints <sigh>, can have lots of leading zeros. It's not
really a performance risk, but better safe than sorry. */
ep = &ep0[i];
if (ep->me_key == NULL || ep->me_key == key)
return ep;
if (ep->me_key == dummy)
freeslot = ep;
else {
if (ep->me_hash == hash &&
PyObject_Compare(ep->me_key, key) == 0)
{
return ep;
}
freeslot = NULL;
1997-01-18 03:55:05 -04:00
}
/* XXX What if PyObject_Compare returned an exception? */
/* Derive incr from hash, just to make it more arbitrary. Note that
incr must not be 0, or we will get into an infinite loop.*/
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
if (!incr)
incr = mask;
for (;;) {
ep = &ep0[(i+incr)&mask];
if (ep->me_key == NULL) {
if (freeslot != NULL)
return freeslot;
else
return ep;
}
if (ep->me_key == dummy) {
if (freeslot == NULL)
freeslot = ep;
}
else if (ep->me_key == key ||
(ep->me_hash == hash &&
1997-05-02 00:12:38 -03:00
PyObject_Compare(ep->me_key, key) == 0)) {
return ep;
}
/* XXX What if PyObject_Compare returned an exception? */
/* Cycle through GF(2^n)-{0} */
incr = incr << 1;
if (incr > mask)
incr ^= mp->ma_poly; /* This will implicitly clear
the highest bit */
}
}
/*
Internal routine to insert a new item into the table.
Used both by the internal resize routine and by the public insert routine.
Eats a reference to key and one to value.
*/
static void
insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value)
{
1997-05-02 00:12:38 -03:00
PyObject *old_value;
register dictentry *ep;
ep = lookdict(mp, key, hash);
if (ep->me_value != NULL) {
old_value = ep->me_value;
ep->me_value = value;
1997-05-02 00:12:38 -03:00
Py_DECREF(old_value); /* which **CAN** re-enter */
Py_DECREF(key);
}
else {
if (ep->me_key == NULL)
mp->ma_fill++;
else
1997-05-02 00:12:38 -03:00
Py_DECREF(ep->me_key);
ep->me_key = key;
ep->me_hash = hash;
ep->me_value = value;
mp->ma_used++;
}
}
/*
Restructure the table by allocating a new table and reinserting all
items again. When entries have been deleted, the new table may
actually be smaller than the old one.
*/
static int
dictresize(dictobject *mp, int minused)
{
register int oldsize = mp->ma_size;
register int newsize, newpoly;
register dictentry *oldtable = mp->ma_table;
register dictentry *newtable;
register dictentry *ep;
register int i;
for (i = 0, newsize = MINSIZE; ; i++, newsize <<= 1) {
if (i > sizeof(polys)/sizeof(polys[0])) {
/* Ran out of polynomials */
1997-05-02 00:12:38 -03:00
PyErr_NoMemory();
1994-08-30 05:27:36 -03:00
return -1;
}
1997-05-28 16:15:28 -03:00
if (newsize > minused) {
newpoly = polys[i];
break;
}
}
newtable = PyMem_NEW(dictentry, newsize);
if (newtable == NULL) {
1997-05-02 00:12:38 -03:00
PyErr_NoMemory();
return -1;
}
memset(newtable, '\0', sizeof(dictentry) * newsize);
mp->ma_size = newsize;
mp->ma_poly = newpoly;
mp->ma_table = newtable;
mp->ma_fill = 0;
mp->ma_used = 0;
/* Make two passes, so we can avoid decrefs
(and possible side effects) till the table is copied */
for (i = 0, ep = oldtable; i < oldsize; i++, ep++) {
if (ep->me_value != NULL)
insertdict(mp,ep->me_key,ep->me_hash,ep->me_value);
}
for (i = 0, ep = oldtable; i < oldsize; i++, ep++) {
if (ep->me_value == NULL) {
1997-05-02 00:12:38 -03:00
Py_XDECREF(ep->me_key);
}
}
if (oldtable != NULL)
PyMem_DEL(oldtable);
return 0;
}
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_GetItem(PyObject *op, PyObject *key)
{
long hash;
1997-05-02 00:12:38 -03:00
if (!PyDict_Check(op)) {
return NULL;
}
if (((dictobject *)op)->ma_table == NULL)
return NULL;
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
if (hash == -1) {
PyErr_Clear();
return NULL;
}
}
return lookdict((dictobject *)op, key, hash) -> me_value;
}
int
PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
{
register dictobject *mp;
register long hash;
1997-05-02 00:12:38 -03:00
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
mp = (dictobject *)op;
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (PyString_Check(key)) {
1997-01-18 03:55:05 -04:00
#ifdef INTERN_STRINGS
1997-05-02 00:12:38 -03:00
if (((PyStringObject *)key)->ob_sinterned != NULL) {
key = ((PyStringObject *)key)->ob_sinterned;
hash = ((PyStringObject *)key)->ob_shash;
1997-01-18 03:55:05 -04:00
}
else
#endif
1997-01-18 03:55:05 -04:00
{
1997-05-02 00:12:38 -03:00
hash = ((PyStringObject *)key)->ob_shash;
1997-01-18 03:55:05 -04:00
if (hash == -1)
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
1997-01-18 03:55:05 -04:00
}
}
else
#endif
{
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
1997-01-18 03:55:05 -04:00
if (hash == -1)
return -1;
}
1997-05-28 16:15:28 -03:00
/* if fill >= 2/3 size, double in size */
if (mp->ma_fill*3 >= mp->ma_size*2) {
1997-05-28 16:15:28 -03:00
if (dictresize(mp, mp->ma_used*2) != 0) {
if (mp->ma_fill+1 > mp->ma_size)
return -1;
}
}
1997-05-02 00:12:38 -03:00
Py_INCREF(value);
Py_INCREF(key);
insertdict(mp, key, hash, value);
return 0;
}
int
PyDict_DelItem(PyObject *op, PyObject *key)
{
register dictobject *mp;
register long hash;
register dictentry *ep;
1997-05-02 00:12:38 -03:00
PyObject *old_value, *old_key;
1997-05-02 00:12:38 -03:00
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
mp = (dictobject *)op;
if (((dictobject *)op)->ma_table == NULL)
goto empty;
ep = lookdict(mp, key, hash);
if (ep->me_value == NULL) {
empty:
1997-05-02 00:12:38 -03:00
PyErr_SetObject(PyExc_KeyError, key);
return -1;
}
old_key = ep->me_key;
1997-05-02 00:12:38 -03:00
Py_INCREF(dummy);
ep->me_key = dummy;
old_value = ep->me_value;
ep->me_value = NULL;
mp->ma_used--;
1997-05-02 00:12:38 -03:00
Py_DECREF(old_value);
Py_DECREF(old_key);
return 0;
}
void
PyDict_Clear(PyObject *op)
{
int i, n;
register dictentry *table;
dictobject *mp;
1997-05-02 00:12:38 -03:00
if (!PyDict_Check(op))
return;
mp = (dictobject *)op;
table = mp->ma_table;
if (table == NULL)
return;
n = mp->ma_size;
mp->ma_size = mp->ma_used = mp->ma_fill = 0;
mp->ma_table = NULL;
for (i = 0; i < n; i++) {
1997-05-02 00:12:38 -03:00
Py_XDECREF(table[i].me_key);
Py_XDECREF(table[i].me_value);
}
1997-05-02 00:12:38 -03:00
PyMem_DEL(table);
}
int
PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
{
int i;
register dictobject *mp;
1997-05-02 00:12:38 -03:00
if (!PyDict_Check(op))
return 0;
mp = (dictobject *)op;
i = *ppos;
if (i < 0)
return 0;
while (i < mp->ma_size && mp->ma_table[i].me_value == NULL)
i++;
*ppos = i+1;
if (i >= mp->ma_size)
return 0;
if (pkey)
*pkey = mp->ma_table[i].me_key;
if (pvalue)
*pvalue = mp->ma_table[i].me_value;
return 1;
}
/* Methods */
static void
dict_dealloc(register dictobject *mp)
{
register int i;
register dictentry *ep;
Py_TRASHCAN_SAFE_BEGIN(mp)
PyObject_GC_Fini(mp);
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
if (ep->me_key != NULL) {
1997-05-02 00:12:38 -03:00
Py_DECREF(ep->me_key);
}
if (ep->me_value != NULL) {
1997-05-02 00:12:38 -03:00
Py_DECREF(ep->me_value);
}
}
if (mp->ma_table != NULL)
PyMem_DEL(mp->ma_table);
2000-06-30 22:00:38 -03:00
mp = (dictobject *) PyObject_AS_GC(mp);
PyObject_DEL(mp);
Py_TRASHCAN_SAFE_END(mp)
}
static int
dict_print(register dictobject *mp, register FILE *fp, register int flags)
{
register int i;
register int any;
register dictentry *ep;
i = Py_ReprEnter((PyObject*)mp);
if (i != 0) {
if (i < 0)
return i;
fprintf(fp, "{...}");
return 0;
}
fprintf(fp, "{");
any = 0;
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
if (ep->me_value != NULL) {
if (any++ > 0)
fprintf(fp, ", ");
if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) {
Py_ReprLeave((PyObject*)mp);
return -1;
}
fprintf(fp, ": ");
if (PyObject_Print(ep->me_value, fp, 0) != 0) {
Py_ReprLeave((PyObject*)mp);
return -1;
}
}
}
fprintf(fp, "}");
Py_ReprLeave((PyObject*)mp);
return 0;
}
1997-05-02 00:12:38 -03:00
static PyObject *
dict_repr(dictobject *mp)
{
1997-05-02 00:12:38 -03:00
auto PyObject *v;
PyObject *sepa, *colon;
register int i;
register int any;
register dictentry *ep;
i = Py_ReprEnter((PyObject*)mp);
if (i != 0) {
if (i > 0)
return PyString_FromString("{...}");
return NULL;
}
1997-05-02 00:12:38 -03:00
v = PyString_FromString("{");
sepa = PyString_FromString(", ");
colon = PyString_FromString(": ");
any = 0;
1994-08-30 05:27:36 -03:00
for (i = 0, ep = mp->ma_table; i < mp->ma_size && v; i++, ep++) {
if (ep->me_value != NULL) {
if (any++)
1997-05-02 00:12:38 -03:00
PyString_Concat(&v, sepa);
PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_key));
PyString_Concat(&v, colon);
PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_value));
}
}
1997-05-02 00:12:38 -03:00
PyString_ConcatAndDel(&v, PyString_FromString("}"));
Py_ReprLeave((PyObject*)mp);
1997-05-02 00:12:38 -03:00
Py_XDECREF(sepa);
Py_XDECREF(colon);
return v;
}
static int
dict_length(dictobject *mp)
{
return mp->ma_used;
}
1997-05-02 00:12:38 -03:00
static PyObject *
dict_subscript(dictobject *mp, register PyObject *key)
{
1997-05-02 00:12:38 -03:00
PyObject *v;
long hash;
if (mp->ma_table == NULL) {
1997-05-02 00:12:38 -03:00
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
v = lookdict(mp, key, hash) -> me_value;
if (v == NULL)
1997-05-02 00:12:38 -03:00
PyErr_SetObject(PyExc_KeyError, key);
else
1997-05-02 00:12:38 -03:00
Py_INCREF(v);
return v;
}
static int
dict_ass_sub(dictobject *mp, PyObject *v, PyObject *w)
{
if (w == NULL)
1997-05-02 00:12:38 -03:00
return PyDict_DelItem((PyObject *)mp, v);
else
1997-05-02 00:12:38 -03:00
return PyDict_SetItem((PyObject *)mp, v, w);
}
static PyMappingMethods dict_as_mapping = {
(inquiry)dict_length, /*mp_length*/
(binaryfunc)dict_subscript, /*mp_subscript*/
(objobjargproc)dict_ass_sub, /*mp_ass_subscript*/
};
1997-05-02 00:12:38 -03:00
static PyObject *
dict_keys(register dictobject *mp, PyObject *args)
{
1997-05-02 00:12:38 -03:00
register PyObject *v;
register int i, j;
1997-05-02 00:12:38 -03:00
if (!PyArg_NoArgs(args))
return NULL;
1997-05-02 00:12:38 -03:00
v = PyList_New(mp->ma_used);
if (v == NULL)
return NULL;
for (i = 0, j = 0; i < mp->ma_size; i++) {
if (mp->ma_table[i].me_value != NULL) {
1997-05-02 00:12:38 -03:00
PyObject *key = mp->ma_table[i].me_key;
Py_INCREF(key);
PyList_SetItem(v, j, key);
j++;
}
}
return v;
}
1997-05-02 00:12:38 -03:00
static PyObject *
dict_values(register dictobject *mp, PyObject *args)
{
1997-05-02 00:12:38 -03:00
register PyObject *v;
register int i, j;
1997-05-02 00:12:38 -03:00
if (!PyArg_NoArgs(args))
return NULL;
1997-05-02 00:12:38 -03:00
v = PyList_New(mp->ma_used);
if (v == NULL)
return NULL;
for (i = 0, j = 0; i < mp->ma_size; i++) {
if (mp->ma_table[i].me_value != NULL) {
1997-05-02 00:12:38 -03:00
PyObject *value = mp->ma_table[i].me_value;
Py_INCREF(value);
PyList_SetItem(v, j, value);
j++;
}
}
return v;
}
1997-05-02 00:12:38 -03:00
static PyObject *
dict_items(register dictobject *mp, PyObject *args)
{
1997-05-02 00:12:38 -03:00
register PyObject *v;
register int i, j;
1997-05-02 00:12:38 -03:00
if (!PyArg_NoArgs(args))
return NULL;
1997-05-02 00:12:38 -03:00
v = PyList_New(mp->ma_used);
if (v == NULL)
return NULL;
for (i = 0, j = 0; i < mp->ma_size; i++) {
if (mp->ma_table[i].me_value != NULL) {
1997-05-02 00:12:38 -03:00
PyObject *key = mp->ma_table[i].me_key;
PyObject *value = mp->ma_table[i].me_value;
PyObject *item = PyTuple_New(2);
if (item == NULL) {
1997-05-02 00:12:38 -03:00
Py_DECREF(v);
return NULL;
}
1997-05-02 00:12:38 -03:00
Py_INCREF(key);
PyTuple_SetItem(item, 0, key);
Py_INCREF(value);
PyTuple_SetItem(item, 1, value);
PyList_SetItem(v, j, item);
j++;
}
}
return v;
}
1997-05-28 16:15:28 -03:00
static PyObject *
dict_update(register dictobject *mp, PyObject *args)
1997-05-28 16:15:28 -03:00
{
register int i;
dictobject *other;
dictentry *entry;
if (!PyArg_Parse(args, "O!", &PyDict_Type, &other))
return NULL;
if (other == mp)
goto done; /* a.update(a); nothing to do */
1997-05-28 16:15:28 -03:00
/* Do one big resize at the start, rather than incrementally
resizing as we insert new items. Expect that there will be
no (or few) overlapping keys. */
if ((mp->ma_fill + other->ma_used)*3 >= mp->ma_size*2) {
if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
return NULL;
}
for (i = 0; i < other->ma_size; i++) {
entry = &other->ma_table[i];
if (entry->me_value != NULL) {
Py_INCREF(entry->me_key);
Py_INCREF(entry->me_value);
insertdict(mp, entry->me_key, entry->me_hash,
entry->me_value);
}
}
done:
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
dict_copy(register dictobject *mp, PyObject *args)
1997-05-28 16:15:28 -03:00
{
if (!PyArg_Parse(args, ""))
return NULL;
return PyDict_Copy((PyObject*)mp);
}
PyObject *
PyDict_Copy(PyObject *o)
{
register dictobject *mp;
1997-05-28 16:15:28 -03:00
register int i;
dictobject *copy;
dictentry *entry;
if (o == NULL || !PyDict_Check(o)) {
PyErr_BadInternalCall();
1997-05-28 16:15:28 -03:00
return NULL;
}
mp = (dictobject *)o;
1997-05-28 16:15:28 -03:00
copy = (dictobject *)PyDict_New();
if (copy == NULL)
return NULL;
if (mp->ma_used > 0) {
if (dictresize(copy, mp->ma_used*3/2) != 0)
return NULL;
for (i = 0; i < mp->ma_size; i++) {
entry = &mp->ma_table[i];
if (entry->me_value != NULL) {
Py_INCREF(entry->me_key);
Py_INCREF(entry->me_value);
insertdict(copy, entry->me_key, entry->me_hash,
entry->me_value);
}
}
}
return (PyObject *)copy;
}
int
PyDict_Size(PyObject *mp)
{
1997-05-02 00:12:38 -03:00
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
return 0;
}
return ((dictobject *)mp)->ma_used;
}
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_Keys(PyObject *mp)
{
1997-05-02 00:12:38 -03:00
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_keys((dictobject *)mp, (PyObject *)NULL);
}
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_Values(PyObject *mp)
{
1997-05-02 00:12:38 -03:00
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_values((dictobject *)mp, (PyObject *)NULL);
}
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_Items(PyObject *mp)
{
1997-05-02 00:12:38 -03:00
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_items((dictobject *)mp, (PyObject *)NULL);
}
#define NEWCMP
#ifdef NEWCMP
/* Subroutine which returns the smallest key in a for which b's value
is different or absent. The value is returned too, through the
pval argument. No reference counts are incremented. */
1997-05-02 00:12:38 -03:00
static PyObject *
characterize(dictobject *a, dictobject *b, PyObject **pval)
{
1997-05-02 00:12:38 -03:00
PyObject *diff = NULL;
int i;
*pval = NULL;
for (i = 0; i < a->ma_size; i++) {
if (a->ma_table[i].me_value != NULL) {
1997-05-02 00:12:38 -03:00
PyObject *key = a->ma_table[i].me_key;
PyObject *aval, *bval;
/* XXX What if PyObject_Compare raises an exception? */
1997-05-02 00:12:38 -03:00
if (diff != NULL && PyObject_Compare(key, diff) > 0)
continue;
aval = a->ma_table[i].me_value;
1997-05-02 00:12:38 -03:00
bval = PyDict_GetItem((PyObject *)b, key);
/* XXX What if PyObject_Compare raises an exception? */
1997-05-02 00:12:38 -03:00
if (bval == NULL || PyObject_Compare(aval, bval) != 0)
{
diff = key;
*pval = aval;
}
}
}
return diff;
}
static int
dict_compare(dictobject *a, dictobject *b)
{
1997-05-02 00:12:38 -03:00
PyObject *adiff, *bdiff, *aval, *bval;
int res;
/* Compare lengths first */
if (a->ma_used < b->ma_used)
return -1; /* a is shorter */
else if (a->ma_used > b->ma_used)
return 1; /* b is shorter */
/* Same length -- check all keys */
adiff = characterize(a, b, &aval);
if (PyErr_Occurred())
return -1;
if (adiff == NULL)
return 0; /* a is a subset with the same length */
bdiff = characterize(b, a, &bval);
if (PyErr_Occurred())
return -1;
/* bdiff == NULL would be impossible now */
1997-05-02 00:12:38 -03:00
res = PyObject_Compare(adiff, bdiff);
if (res == 0)
1997-05-02 00:12:38 -03:00
res = PyObject_Compare(aval, bval);
return res;
}
#else /* !NEWCMP */
static int
dict_compare(dictobject *a, dictobject *b)
{
1997-05-02 00:12:38 -03:00
PyObject *akeys, *bkeys;
int i, n, res;
if (a == b)
return 0;
if (a->ma_used == 0) {
if (b->ma_used != 0)
return -1;
else
return 0;
}
else {
if (b->ma_used == 0)
return 1;
}
akeys = dict_keys(a, (PyObject *)NULL);
bkeys = dict_keys(b, (PyObject *)NULL);
if (akeys == NULL || bkeys == NULL) {
/* Oops, out of memory -- what to do? */
/* For now, sort on address! */
1997-05-02 00:12:38 -03:00
Py_XDECREF(akeys);
Py_XDECREF(bkeys);
if (a < b)
return -1;
else
return 1;
}
1997-05-02 00:12:38 -03:00
PyList_Sort(akeys);
PyList_Sort(bkeys);
n = a->ma_used < b->ma_used ? a->ma_used : b->ma_used; /* smallest */
res = 0;
for (i = 0; i < n; i++) {
1997-05-02 00:12:38 -03:00
PyObject *akey, *bkey, *aval, *bval;
long ahash, bhash;
1997-05-02 00:12:38 -03:00
akey = PyList_GetItem(akeys, i);
bkey = PyList_GetItem(bkeys, i);
res = PyObject_Compare(akey, bkey);
if (res != 0)
break;
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(akey) ||
(ahash = ((PyStringObject *) akey)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
ahash = PyObject_Hash(akey);
if (ahash == -1)
1997-05-02 00:12:38 -03:00
PyErr_Clear(); /* Don't want errors here */
}
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(bkey) ||
(bhash = ((PyStringObject *) bkey)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
bhash = PyObject_Hash(bkey);
if (bhash == -1)
1997-05-02 00:12:38 -03:00
PyErr_Clear(); /* Don't want errors here */
}
aval = lookdict(a, akey, ahash) -> me_value;
bval = lookdict(b, bkey, bhash) -> me_value;
1997-05-02 00:12:38 -03:00
res = PyObject_Compare(aval, bval);
if (res != 0)
break;
}
if (res == 0) {
if (a->ma_used < b->ma_used)
res = -1;
else if (a->ma_used > b->ma_used)
res = 1;
}
1997-05-02 00:12:38 -03:00
Py_DECREF(akeys);
Py_DECREF(bkeys);
return res;
}
#endif /* !NEWCMP */
1997-05-02 00:12:38 -03:00
static PyObject *
dict_has_key(register dictobject *mp, PyObject *args)
{
1997-05-02 00:12:38 -03:00
PyObject *key;
long hash;
register long ok;
if (!PyArg_ParseTuple(args, "O:has_key", &key))
return NULL;
#ifdef CACHE_HASH
1997-05-02 00:12:38 -03:00
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
1997-05-02 00:12:38 -03:00
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ok = mp->ma_size != 0 && lookdict(mp, key, hash)->me_value != NULL;
1997-05-02 00:12:38 -03:00
return PyInt_FromLong(ok);
}
static PyObject *
dict_get(register dictobject *mp, PyObject *args)
{
PyObject *key;
PyObject *failobj = Py_None;
PyObject *val = NULL;
long hash;
if (!PyArg_ParseTuple(args, "O|O:get", &key, &failobj))
return NULL;
if (mp->ma_table == NULL)
goto finally;
#ifdef CACHE_HASH
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
val = lookdict(mp, key, hash)->me_value;
finally:
if (val == NULL)
val = failobj;
Py_INCREF(val);
return val;
}
static PyObject *
dict_setdefault(register dictobject *mp, PyObject *args)
{
PyObject *key;
PyObject *failobj = Py_None;
PyObject *val = NULL;
long hash;
if (!PyArg_ParseTuple(args, "O|O:setdefault", &key, &failobj))
return NULL;
if (mp->ma_table == NULL)
goto finally;
#ifdef CACHE_HASH
if (!PyString_Check(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1)
#endif
{
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
val = lookdict(mp, key, hash)->me_value;
finally:
if (val == NULL) {
val = failobj;
if (PyDict_SetItem((PyObject*)mp, key, failobj))
val = NULL;
}
Py_XINCREF(val);
return val;
}
1997-05-02 00:12:38 -03:00
static PyObject *
dict_clear(register dictobject *mp, PyObject *args)
{
1997-05-02 00:12:38 -03:00
if (!PyArg_NoArgs(args))
return NULL;
1997-05-02 00:12:38 -03:00
PyDict_Clear((PyObject *)mp);
Py_INCREF(Py_None);
return Py_None;
}
static int
dict_traverse(PyObject *op, visitproc visit, void *arg)
{
int i = 0, err;
PyObject *pk;
PyObject *pv;
while (PyDict_Next(op, &i, &pk, &pv)) {
err = visit(pk, arg);
if (err)
return err;
err = visit(pv, arg);
if (err)
return err;
}
return 0;
}
static int
dict_tp_clear(PyObject *op)
{
PyDict_Clear(op);
return 0;
}
1997-05-02 00:12:38 -03:00
static PyMethodDef mapp_methods[] = {
{"has_key", (PyCFunction)dict_has_key, METH_VARARGS},
{"keys", (PyCFunction)dict_keys},
{"items", (PyCFunction)dict_items},
{"values", (PyCFunction)dict_values},
{"update", (PyCFunction)dict_update},
{"clear", (PyCFunction)dict_clear},
{"copy", (PyCFunction)dict_copy},
{"get", (PyCFunction)dict_get, METH_VARARGS},
{"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS},
{NULL, NULL} /* sentinel */
};
1997-05-02 00:12:38 -03:00
static PyObject *
dict_getattr(dictobject *mp, char *name)
{
1997-05-02 00:12:38 -03:00
return Py_FindMethod(mapp_methods, (PyObject *)mp, name);
}
1997-05-02 00:12:38 -03:00
PyTypeObject PyDict_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"dictionary",
sizeof(dictobject) + PyGC_HEAD_SIZE,
0,
(destructor)dict_dealloc, /*tp_dealloc*/
(printfunc)dict_print, /*tp_print*/
(getattrfunc)dict_getattr, /*tp_getattr*/
0, /*tp_setattr*/
(cmpfunc)dict_compare, /*tp_compare*/
(reprfunc)dict_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&dict_as_mapping, /*tp_as_mapping*/
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
0, /* tp_doc */
(traverseproc)dict_traverse, /* tp_traverse */
(inquiry)dict_tp_clear, /* tp_clear */
};
/* For backward compatibility with old dictionary interface */
1997-05-02 00:12:38 -03:00
PyObject *
PyDict_GetItemString(PyObject *v, char *key)
{
PyObject *kv, *rv;
kv = PyString_FromString(key);
if (kv == NULL)
return NULL;
rv = PyDict_GetItem(v, kv);
Py_DECREF(kv);
return rv;
}
int
PyDict_SetItemString(PyObject *v, char *key, PyObject *item)
{
PyObject *kv;
int err;
kv = PyString_FromString(key);
if (kv == NULL)
return -1;
PyString_InternInPlace(&kv); /* XXX Should we really? */
err = PyDict_SetItem(v, kv, item);
Py_DECREF(kv);
return err;
}
int
PyDict_DelItemString(PyObject *v, char *key)
{
PyObject *kv;
int err;
kv = PyString_FromString(key);
if (kv == NULL)
return -1;
err = PyDict_DelItem(v, kv);
Py_DECREF(kv);
return err;
}