]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Generalized version of dictionaries, with compatibility hacks.
authorGuido van Rossum <guido@python.org>
Sat, 27 Mar 1993 18:11:32 +0000 (18:11 +0000)
committerGuido van Rossum <guido@python.org>
Sat, 27 Mar 1993 18:11:32 +0000 (18:11 +0000)
Include/mappingobject.h [new file with mode: 0644]
Objects/dictobject.c [new file with mode: 0644]
Objects/mappingobject.c [new file with mode: 0644]

diff --git a/Include/mappingobject.h b/Include/mappingobject.h
new file mode 100644 (file)
index 0000000..83c3380
--- /dev/null
@@ -0,0 +1,43 @@
+/***********************************************************
+Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum,
+Amsterdam, The Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/*
+Mapping object type -- mapping from object to object.
+These functions set errno for errors.  Functions mappingremove() and
+mappinginsert() return nonzero for errors, getmappingsize() returns -1,
+the others NULL.  A successful call to mappinginsert() calls INCREF()
+for the inserted item.
+*/
+
+extern typeobject Mappingtype;
+
+#define is_mappingobject(op) ((op)->ob_type == &Mappingtype)
+
+extern object *newmappingobject PROTO((void));
+extern object *mappinglookup PROTO((object *dp, object *key));
+extern int mappinginsert PROTO((object *dp, object *key, object *item));
+extern int mappingremove PROTO((object *dp, object *key));
+extern int getmappingsize PROTO((object *dp));
+extern object *getmappingkey PROTO((object *dp, int i));
+extern object *getmappingkeys PROTO((object *dp));
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
new file mode 100644 (file)
index 0000000..1cfa308
--- /dev/null
@@ -0,0 +1,705 @@
+/***********************************************************
+Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum,
+Amsterdam, The Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* Mapping object implementation; using a hash table */
+
+#include "allobjects.h"
+#include "mappingobject.h"
+#include "modsupport.h"
+
+
+/*
+Table of primes suitable as keys, in ascending order.
+The first line are the largest primes less than some powers of two,
+the second line is the largest prime less than 6000,
+and the third line is a selection from Knuth, Vol. 3, Sec. 6.1, Table 1.
+The final value is a sentinel and should cause the memory allocation
+of that many entries to fail (if none of the earlier values cause such
+failure already).
+*/
+static unsigned int primes[] = {
+       3, 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093,
+       5987,
+       9551, 15683, 19609, 31397,
+       0xffffffff /* All bits set -- truncation OK */
+};
+
+/* Object used as dummy key to fill deleted entries */
+static object *dummy; /* Initialized by first call to newmappingobject() */
+
+/*
+Invariant for entries: when in use, de_value is not NULL and de_key is
+not NULL and not dummy; when not in use, de_value is NULL and de_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;
+       object *me_key;
+       object *me_value;
+} mappingentry;
+
+/*
+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 {
+       OB_HEAD
+       int ma_fill;
+       int ma_used;
+       int ma_size;
+       mappingentry *ma_table;
+} mappingobject;
+
+object *
+newmappingobject()
+{
+       register mappingobject *mp;
+       if (dummy == NULL) { /* Auto-initialize dummy */
+               dummy = newstringobject("<dummy key>");
+               if (dummy == NULL)
+                       return NULL;
+       }
+       mp = NEWOBJ(mappingobject, &Mappingtype);
+       if (mp == NULL)
+               return NULL;
+       mp->ma_size = primes[0];
+       mp->ma_table = (mappingentry *) calloc(sizeof(mappingentry), mp->ma_size);
+       if (mp->ma_table == NULL) {
+               DEL(mp);
+               return err_nomem();
+       }
+       mp->ma_fill = 0;
+       mp->ma_used = 0;
+       return (object *)mp;
+}
+
+/*
+The basic lookup function used by all operations.
+This is essentially 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).
+
+First a 32-bit hash value, 'sum', is computed from the key string.
+The first character is added an extra time shifted by 8 to avoid hashing
+single-character keys (often heavily used variables) too close together.
+All arithmetic on sum should ignore overflow.
+
+The initial probe index is then computed as sum mod the table size.
+Subsequent probe indices are incr apart (mod table size), where incr
+is also derived from sum, with the additional requirement that it is
+relative prime to the table size (i.e., 1 <= incr < size, since the size
+is a prime number).  My choice for incr is somewhat arbitrary.
+*/
+static mappingentry *lookmapping PROTO((mappingobject *, object *, long));
+static mappingentry *
+lookmapping(mp, key, hash)
+       register mappingobject *mp;
+       object *key;
+       long hash;
+{
+       register int i, incr;
+       register unsigned long sum = (unsigned long) hash;
+       register mappingentry *freeslot = NULL;
+       /* 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 = sum % mp->ma_size;
+       do {
+               sum = sum + sum + sum + 1;
+               incr = sum % mp->ma_size;
+       } while (incr == 0);
+       for (;;) {
+               register mappingentry *ep = &mp->ma_table[i];
+               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_hash == hash &&
+                        cmpobject(ep->me_key, key) == 0) {
+                       return ep;
+               }
+               i = (i + incr) % mp->ma_size;
+       }
+}
+
+/*
+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 insertmapping PROTO((mappingobject *, object *, long, object *));
+static void
+insertmapping(mp, key, hash, value)
+       register mappingobject *mp;
+       object *key;
+       long hash;
+       object *value;
+{
+       register mappingentry *ep;
+       ep = lookmapping(mp, key, hash);
+       if (ep->me_value != NULL) {
+               DECREF(ep->me_value);
+               DECREF(key);
+       }
+       else {
+               if (ep->me_key == NULL)
+                       mp->ma_fill++;
+               else
+                       DECREF(ep->me_key);
+               ep->me_key = key;
+               ep->me_hash = hash;
+               mp->ma_used++;
+       }
+       ep->me_value = value;
+}
+
+/*
+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 mappingresize PROTO((mappingobject *));
+static int
+mappingresize(mp)
+       mappingobject *mp;
+{
+       register int oldsize = mp->ma_size;
+       register int newsize;
+       register mappingentry *oldtable = mp->ma_table;
+       register mappingentry *newtable;
+       register mappingentry *ep;
+       register int i;
+       newsize = mp->ma_size;
+       for (i = 0; ; i++) {
+               if (primes[i] > mp->ma_used*2) {
+                       newsize = primes[i];
+                       break;
+               }
+       }
+       newtable = (mappingentry *) calloc(sizeof(mappingentry), newsize);
+       if (newtable == NULL) {
+               err_nomem();
+               return -1;
+       }
+       mp->ma_size = newsize;
+       mp->ma_table = newtable;
+       mp->ma_fill = 0;
+       mp->ma_used = 0;
+       for (i = 0, ep = oldtable; i < oldsize; i++, ep++) {
+               if (ep->me_value != NULL)
+                       insertmapping(mp,ep->me_key,ep->me_hash,ep->me_value);
+               else {
+                       XDECREF(ep->me_key);
+               }
+       }
+       DEL(oldtable);
+       return 0;
+}
+
+object *
+mappinglookup(op, key)
+       object *op;
+       object *key;
+{
+       long hash;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return NULL;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       return lookmapping((mappingobject *)op, key, hash) -> me_value;
+}
+
+int
+mappinginsert(op, key, value)
+       register object *op;
+       object *key;
+       object *value;
+{
+       register mappingobject *mp;
+       register long hash;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return -1;
+       mp = (mappingobject *)op;
+       /* if fill >= 2/3 size, resize */
+       if (mp->ma_fill*3 >= mp->ma_size*2) {
+               if (mappingresize(mp) != 0) {
+                       if (mp->ma_fill+1 > mp->ma_size)
+                               return -1;
+               }
+       }
+       INCREF(value);
+       INCREF(key);
+       insertmapping(mp, key, hash, value);
+       return 0;
+}
+
+int
+mappingremove(op, key)
+       object *op;
+       object *key;
+{
+       register mappingobject *mp;
+       register long hash;
+       register mappingentry *ep;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return -1;
+       mp = (mappingobject *)op;
+       ep = lookmapping(mp, key, hash);
+       if (ep->me_value == NULL) {
+               err_setval(KeyError, key);
+               return -1;
+       }
+       DECREF(ep->me_key);
+       INCREF(dummy);
+       ep->me_key = dummy;
+       DECREF(ep->me_value);
+       ep->me_value = NULL;
+       mp->ma_used--;
+       return 0;
+}
+
+int
+getmappingsize(op)
+       register object *op;
+{
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       return ((mappingobject *)op) -> ma_size;
+}
+
+object *
+getmappingkey(op, i)
+       object *op;
+       register int i;
+{
+       /* XXX This can't return errors since its callers assume
+          that NULL means there was no key at that point */
+       register mappingobject *mp;
+       if (!is_mappingobject(op)) {
+               /* err_badcall(); */
+               return NULL;
+       }
+       mp = (mappingobject *)op;
+       if (i < 0 || i >= mp->ma_size) {
+               /* err_badarg(); */
+               return NULL;
+       }
+       if (mp->ma_table[i].me_value == NULL) {
+               /* Not an error! */
+               return NULL;
+       }
+       return (object *) mp->ma_table[i].me_key;
+}
+
+/* Methods */
+
+static void
+mapping_dealloc(mp)
+       register mappingobject *mp;
+{
+       register int i;
+       register mappingentry *ep;
+       for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
+               if (ep->me_key != NULL)
+                       DECREF(ep->me_key);
+               if (ep->me_value != NULL)
+                       DECREF(ep->me_value);
+       }
+       if (mp->ma_table != NULL)
+               DEL(mp->ma_table);
+       DEL(mp);
+}
+
+static int
+mapping_print(mp, fp, flags)
+       register mappingobject *mp;
+       register FILE *fp;
+       register int flags;
+{
+       register int i;
+       register int any;
+       register mappingentry *ep;
+       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 (printobject((object *)ep->me_key, fp, flags) != 0)
+                               return -1;
+                       fprintf(fp, ": ");
+                       if (printobject(ep->me_value, fp, flags) != 0)
+                               return -1;
+               }
+       }
+       fprintf(fp, "}");
+       return 0;
+}
+
+static void
+js(pv, w)
+       object **pv;
+       object *w;
+{
+       joinstring(pv, w);
+       XDECREF(w);
+}
+
+static object *
+mapping_repr(mp)
+       mappingobject *mp;
+{
+       auto object *v;
+       object *sepa, *colon;
+       register int i;
+       register int any;
+       register mappingentry *ep;
+       v = newstringobject("{");
+       sepa = newstringobject(", ");
+       colon = newstringobject(": ");
+       any = 0;
+       for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
+               if (ep->me_value != NULL) {
+                       if (any++)
+                               joinstring(&v, sepa);
+                       js(&v, reprobject(ep->me_key));
+                       joinstring(&v, colon);
+                       js(&v, reprobject(ep->me_value));
+               }
+       }
+       js(&v, newstringobject("}"));
+       XDECREF(sepa);
+       XDECREF(colon);
+       return v;
+}
+
+static int
+mapping_length(mp)
+       mappingobject *mp;
+{
+       return mp->ma_used;
+}
+
+static object *
+mapping_subscript(mp, key)
+       mappingobject *mp;
+       register object *key;
+{
+       object *v;
+       long hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       v = lookmapping(mp, key, hash) -> me_value;
+       if (v == NULL)
+               err_setval(KeyError, key);
+       else
+               INCREF(v);
+       return v;
+}
+
+static int
+mapping_ass_sub(mp, v, w)
+       mappingobject *mp;
+       object *v, *w;
+{
+       if (w == NULL)
+               return mappingremove((object *)mp, v);
+       else
+               return mappinginsert((object *)mp, v, w);
+}
+
+static mapping_methods mapping_as_mapping = {
+       mapping_length, /*mp_length*/
+       mapping_subscript,      /*mp_subscript*/
+       mapping_ass_sub,        /*mp_ass_subscript*/
+};
+
+static object *
+mapping_keys(mp, args)
+       register mappingobject *mp;
+       object *args;
+{
+       register object *v;
+       register int i, j;
+       if (!getnoarg(args))
+               return NULL;
+       v = newlistobject(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) {
+                       object *key = mp->ma_table[i].me_key;
+                       INCREF(key);
+                       setlistitem(v, j, key);
+                       j++;
+               }
+       }
+       return v;
+}
+
+object *
+getmappingkeys(mp)
+       object *mp;
+{
+       if (mp == NULL || !is_mappingobject(mp)) {
+               err_badcall();
+               return NULL;
+       }
+       return mapping_keys((mappingobject *)mp, (object *)NULL);
+}
+
+static int
+mapping_compare(a, b)
+       mappingobject *a, *b;
+{
+       object *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 = mapping_keys(a, (object *)NULL);
+       bkeys = mapping_keys(b, (object *)NULL);
+       if (akeys == NULL || bkeys == NULL) {
+               /* Oops, out of memory -- what to do? */
+               /* For now, sort on address! */
+               XDECREF(akeys);
+               XDECREF(bkeys);
+               if (a < b)
+                       return -1;
+               else
+                       return 1;
+       }
+       sortlist(akeys);
+       sortlist(bkeys);
+       n = a->ma_used < b->ma_used ? a->ma_used : b->ma_used; /* smallest */
+       res = 0;
+       for (i = 0; i < n; i++) {
+               object *akey, *bkey, *aval, *bval;
+               long ahash, bhash;
+               akey = getlistitem(akeys, i);
+               bkey = getlistitem(bkeys, i);
+               res = cmpobject(akey, bkey);
+               if (res != 0)
+                       break;
+               ahash = hashobject(akey);
+               if (ahash == -1)
+                       err_clear(); /* Don't want errors here */
+               bhash = hashobject(bkey);
+               if (bhash == -1)
+                       err_clear(); /* Don't want errors here */
+               aval = lookmapping(a, akey, ahash) -> me_value;
+               bval = lookmapping(b, bkey, bhash) -> me_value;
+               res = cmpobject(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;
+       }
+       DECREF(akeys);
+       DECREF(bkeys);
+       return res;
+}
+
+static object *
+mapping_has_key(mp, args)
+       register mappingobject *mp;
+       object *args;
+{
+       object *key;
+       long hash;
+       register long ok;
+       if (!getargs(args, "O", &key))
+               return NULL;
+       hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       ok = lookmapping(mp, key, hash)->me_value != NULL;
+       return newintobject(ok);
+}
+
+static struct methodlist mapp_methods[] = {
+       {"keys",        mapping_keys},
+       {"has_key",     mapping_has_key},
+       {NULL,          NULL}           /* sentinel */
+};
+
+static object *
+mapping_getattr(mp, name)
+       mappingobject *mp;
+       char *name;
+{
+       return findmethod(mapp_methods, (object *)mp, name);
+}
+
+typeobject Mappingtype = {
+       OB_HEAD_INIT(&Typetype)
+       0,
+       "mapping",
+       sizeof(mappingobject),
+       0,
+       mapping_dealloc,        /*tp_dealloc*/
+       mapping_print,          /*tp_print*/
+       mapping_getattr,        /*tp_getattr*/
+       0,                      /*tp_setattr*/
+       mapping_compare,        /*tp_compare*/
+       mapping_repr,           /*tp_repr*/
+       0,                      /*tp_as_number*/
+       0,                      /*tp_as_sequence*/
+       &mapping_as_mapping,    /*tp_as_mapping*/
+};
+
+/* For backward compatibility with old dictionary interface */
+
+static object *last_name_object;
+static char *last_name_char;
+
+object *
+getattro(v, name)
+       object *v;
+       object *name;
+{
+       if (name != last_name_object) {
+               XDECREF(last_name_object);
+               INCREF(name);
+               last_name_object = name;
+               last_name_char = getstringvalue(name);
+       }
+       return getattr(v, last_name_char);
+}
+
+int
+setattro(v, name, value)
+       object *v;
+       object *name;
+       object *value;
+{
+       if (name != last_name_object) {
+               XDECREF(last_name_object);
+               INCREF(name);
+               last_name_object = name;
+               last_name_char = getstringvalue(name);
+       }
+       return setattr(v, last_name_char, value);
+}
+
+object *
+dictlookup(v, key)
+       object *v;
+       char *key;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappinglookup(v, last_name_object);
+}
+
+int
+dictinsert(v, key, item)
+       object *v;
+       char *key;
+       object *item;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappinginsert(v, last_name_object, item);
+}
+
+int
+dictremove(v, key)
+       object *v;
+       char *key;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappingremove(v, last_name_object);
+}
+
+char *
+getdictkey(v, i)
+       object *v;
+       int i;
+{
+       object *key = getmappingkey(v, i);
+       if (key == NULL)
+               return NULL;
+       return getstringvalue(key);
+}
diff --git a/Objects/mappingobject.c b/Objects/mappingobject.c
new file mode 100644 (file)
index 0000000..1cfa308
--- /dev/null
@@ -0,0 +1,705 @@
+/***********************************************************
+Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum,
+Amsterdam, The Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* Mapping object implementation; using a hash table */
+
+#include "allobjects.h"
+#include "mappingobject.h"
+#include "modsupport.h"
+
+
+/*
+Table of primes suitable as keys, in ascending order.
+The first line are the largest primes less than some powers of two,
+the second line is the largest prime less than 6000,
+and the third line is a selection from Knuth, Vol. 3, Sec. 6.1, Table 1.
+The final value is a sentinel and should cause the memory allocation
+of that many entries to fail (if none of the earlier values cause such
+failure already).
+*/
+static unsigned int primes[] = {
+       3, 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093,
+       5987,
+       9551, 15683, 19609, 31397,
+       0xffffffff /* All bits set -- truncation OK */
+};
+
+/* Object used as dummy key to fill deleted entries */
+static object *dummy; /* Initialized by first call to newmappingobject() */
+
+/*
+Invariant for entries: when in use, de_value is not NULL and de_key is
+not NULL and not dummy; when not in use, de_value is NULL and de_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;
+       object *me_key;
+       object *me_value;
+} mappingentry;
+
+/*
+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 {
+       OB_HEAD
+       int ma_fill;
+       int ma_used;
+       int ma_size;
+       mappingentry *ma_table;
+} mappingobject;
+
+object *
+newmappingobject()
+{
+       register mappingobject *mp;
+       if (dummy == NULL) { /* Auto-initialize dummy */
+               dummy = newstringobject("<dummy key>");
+               if (dummy == NULL)
+                       return NULL;
+       }
+       mp = NEWOBJ(mappingobject, &Mappingtype);
+       if (mp == NULL)
+               return NULL;
+       mp->ma_size = primes[0];
+       mp->ma_table = (mappingentry *) calloc(sizeof(mappingentry), mp->ma_size);
+       if (mp->ma_table == NULL) {
+               DEL(mp);
+               return err_nomem();
+       }
+       mp->ma_fill = 0;
+       mp->ma_used = 0;
+       return (object *)mp;
+}
+
+/*
+The basic lookup function used by all operations.
+This is essentially 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).
+
+First a 32-bit hash value, 'sum', is computed from the key string.
+The first character is added an extra time shifted by 8 to avoid hashing
+single-character keys (often heavily used variables) too close together.
+All arithmetic on sum should ignore overflow.
+
+The initial probe index is then computed as sum mod the table size.
+Subsequent probe indices are incr apart (mod table size), where incr
+is also derived from sum, with the additional requirement that it is
+relative prime to the table size (i.e., 1 <= incr < size, since the size
+is a prime number).  My choice for incr is somewhat arbitrary.
+*/
+static mappingentry *lookmapping PROTO((mappingobject *, object *, long));
+static mappingentry *
+lookmapping(mp, key, hash)
+       register mappingobject *mp;
+       object *key;
+       long hash;
+{
+       register int i, incr;
+       register unsigned long sum = (unsigned long) hash;
+       register mappingentry *freeslot = NULL;
+       /* 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 = sum % mp->ma_size;
+       do {
+               sum = sum + sum + sum + 1;
+               incr = sum % mp->ma_size;
+       } while (incr == 0);
+       for (;;) {
+               register mappingentry *ep = &mp->ma_table[i];
+               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_hash == hash &&
+                        cmpobject(ep->me_key, key) == 0) {
+                       return ep;
+               }
+               i = (i + incr) % mp->ma_size;
+       }
+}
+
+/*
+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 insertmapping PROTO((mappingobject *, object *, long, object *));
+static void
+insertmapping(mp, key, hash, value)
+       register mappingobject *mp;
+       object *key;
+       long hash;
+       object *value;
+{
+       register mappingentry *ep;
+       ep = lookmapping(mp, key, hash);
+       if (ep->me_value != NULL) {
+               DECREF(ep->me_value);
+               DECREF(key);
+       }
+       else {
+               if (ep->me_key == NULL)
+                       mp->ma_fill++;
+               else
+                       DECREF(ep->me_key);
+               ep->me_key = key;
+               ep->me_hash = hash;
+               mp->ma_used++;
+       }
+       ep->me_value = value;
+}
+
+/*
+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 mappingresize PROTO((mappingobject *));
+static int
+mappingresize(mp)
+       mappingobject *mp;
+{
+       register int oldsize = mp->ma_size;
+       register int newsize;
+       register mappingentry *oldtable = mp->ma_table;
+       register mappingentry *newtable;
+       register mappingentry *ep;
+       register int i;
+       newsize = mp->ma_size;
+       for (i = 0; ; i++) {
+               if (primes[i] > mp->ma_used*2) {
+                       newsize = primes[i];
+                       break;
+               }
+       }
+       newtable = (mappingentry *) calloc(sizeof(mappingentry), newsize);
+       if (newtable == NULL) {
+               err_nomem();
+               return -1;
+       }
+       mp->ma_size = newsize;
+       mp->ma_table = newtable;
+       mp->ma_fill = 0;
+       mp->ma_used = 0;
+       for (i = 0, ep = oldtable; i < oldsize; i++, ep++) {
+               if (ep->me_value != NULL)
+                       insertmapping(mp,ep->me_key,ep->me_hash,ep->me_value);
+               else {
+                       XDECREF(ep->me_key);
+               }
+       }
+       DEL(oldtable);
+       return 0;
+}
+
+object *
+mappinglookup(op, key)
+       object *op;
+       object *key;
+{
+       long hash;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return NULL;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       return lookmapping((mappingobject *)op, key, hash) -> me_value;
+}
+
+int
+mappinginsert(op, key, value)
+       register object *op;
+       object *key;
+       object *value;
+{
+       register mappingobject *mp;
+       register long hash;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return -1;
+       mp = (mappingobject *)op;
+       /* if fill >= 2/3 size, resize */
+       if (mp->ma_fill*3 >= mp->ma_size*2) {
+               if (mappingresize(mp) != 0) {
+                       if (mp->ma_fill+1 > mp->ma_size)
+                               return -1;
+               }
+       }
+       INCREF(value);
+       INCREF(key);
+       insertmapping(mp, key, hash, value);
+       return 0;
+}
+
+int
+mappingremove(op, key)
+       object *op;
+       object *key;
+{
+       register mappingobject *mp;
+       register long hash;
+       register mappingentry *ep;
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       hash = hashobject(key);
+       if (hash == -1)
+               return -1;
+       mp = (mappingobject *)op;
+       ep = lookmapping(mp, key, hash);
+       if (ep->me_value == NULL) {
+               err_setval(KeyError, key);
+               return -1;
+       }
+       DECREF(ep->me_key);
+       INCREF(dummy);
+       ep->me_key = dummy;
+       DECREF(ep->me_value);
+       ep->me_value = NULL;
+       mp->ma_used--;
+       return 0;
+}
+
+int
+getmappingsize(op)
+       register object *op;
+{
+       if (!is_mappingobject(op)) {
+               err_badcall();
+               return -1;
+       }
+       return ((mappingobject *)op) -> ma_size;
+}
+
+object *
+getmappingkey(op, i)
+       object *op;
+       register int i;
+{
+       /* XXX This can't return errors since its callers assume
+          that NULL means there was no key at that point */
+       register mappingobject *mp;
+       if (!is_mappingobject(op)) {
+               /* err_badcall(); */
+               return NULL;
+       }
+       mp = (mappingobject *)op;
+       if (i < 0 || i >= mp->ma_size) {
+               /* err_badarg(); */
+               return NULL;
+       }
+       if (mp->ma_table[i].me_value == NULL) {
+               /* Not an error! */
+               return NULL;
+       }
+       return (object *) mp->ma_table[i].me_key;
+}
+
+/* Methods */
+
+static void
+mapping_dealloc(mp)
+       register mappingobject *mp;
+{
+       register int i;
+       register mappingentry *ep;
+       for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
+               if (ep->me_key != NULL)
+                       DECREF(ep->me_key);
+               if (ep->me_value != NULL)
+                       DECREF(ep->me_value);
+       }
+       if (mp->ma_table != NULL)
+               DEL(mp->ma_table);
+       DEL(mp);
+}
+
+static int
+mapping_print(mp, fp, flags)
+       register mappingobject *mp;
+       register FILE *fp;
+       register int flags;
+{
+       register int i;
+       register int any;
+       register mappingentry *ep;
+       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 (printobject((object *)ep->me_key, fp, flags) != 0)
+                               return -1;
+                       fprintf(fp, ": ");
+                       if (printobject(ep->me_value, fp, flags) != 0)
+                               return -1;
+               }
+       }
+       fprintf(fp, "}");
+       return 0;
+}
+
+static void
+js(pv, w)
+       object **pv;
+       object *w;
+{
+       joinstring(pv, w);
+       XDECREF(w);
+}
+
+static object *
+mapping_repr(mp)
+       mappingobject *mp;
+{
+       auto object *v;
+       object *sepa, *colon;
+       register int i;
+       register int any;
+       register mappingentry *ep;
+       v = newstringobject("{");
+       sepa = newstringobject(", ");
+       colon = newstringobject(": ");
+       any = 0;
+       for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
+               if (ep->me_value != NULL) {
+                       if (any++)
+                               joinstring(&v, sepa);
+                       js(&v, reprobject(ep->me_key));
+                       joinstring(&v, colon);
+                       js(&v, reprobject(ep->me_value));
+               }
+       }
+       js(&v, newstringobject("}"));
+       XDECREF(sepa);
+       XDECREF(colon);
+       return v;
+}
+
+static int
+mapping_length(mp)
+       mappingobject *mp;
+{
+       return mp->ma_used;
+}
+
+static object *
+mapping_subscript(mp, key)
+       mappingobject *mp;
+       register object *key;
+{
+       object *v;
+       long hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       v = lookmapping(mp, key, hash) -> me_value;
+       if (v == NULL)
+               err_setval(KeyError, key);
+       else
+               INCREF(v);
+       return v;
+}
+
+static int
+mapping_ass_sub(mp, v, w)
+       mappingobject *mp;
+       object *v, *w;
+{
+       if (w == NULL)
+               return mappingremove((object *)mp, v);
+       else
+               return mappinginsert((object *)mp, v, w);
+}
+
+static mapping_methods mapping_as_mapping = {
+       mapping_length, /*mp_length*/
+       mapping_subscript,      /*mp_subscript*/
+       mapping_ass_sub,        /*mp_ass_subscript*/
+};
+
+static object *
+mapping_keys(mp, args)
+       register mappingobject *mp;
+       object *args;
+{
+       register object *v;
+       register int i, j;
+       if (!getnoarg(args))
+               return NULL;
+       v = newlistobject(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) {
+                       object *key = mp->ma_table[i].me_key;
+                       INCREF(key);
+                       setlistitem(v, j, key);
+                       j++;
+               }
+       }
+       return v;
+}
+
+object *
+getmappingkeys(mp)
+       object *mp;
+{
+       if (mp == NULL || !is_mappingobject(mp)) {
+               err_badcall();
+               return NULL;
+       }
+       return mapping_keys((mappingobject *)mp, (object *)NULL);
+}
+
+static int
+mapping_compare(a, b)
+       mappingobject *a, *b;
+{
+       object *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 = mapping_keys(a, (object *)NULL);
+       bkeys = mapping_keys(b, (object *)NULL);
+       if (akeys == NULL || bkeys == NULL) {
+               /* Oops, out of memory -- what to do? */
+               /* For now, sort on address! */
+               XDECREF(akeys);
+               XDECREF(bkeys);
+               if (a < b)
+                       return -1;
+               else
+                       return 1;
+       }
+       sortlist(akeys);
+       sortlist(bkeys);
+       n = a->ma_used < b->ma_used ? a->ma_used : b->ma_used; /* smallest */
+       res = 0;
+       for (i = 0; i < n; i++) {
+               object *akey, *bkey, *aval, *bval;
+               long ahash, bhash;
+               akey = getlistitem(akeys, i);
+               bkey = getlistitem(bkeys, i);
+               res = cmpobject(akey, bkey);
+               if (res != 0)
+                       break;
+               ahash = hashobject(akey);
+               if (ahash == -1)
+                       err_clear(); /* Don't want errors here */
+               bhash = hashobject(bkey);
+               if (bhash == -1)
+                       err_clear(); /* Don't want errors here */
+               aval = lookmapping(a, akey, ahash) -> me_value;
+               bval = lookmapping(b, bkey, bhash) -> me_value;
+               res = cmpobject(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;
+       }
+       DECREF(akeys);
+       DECREF(bkeys);
+       return res;
+}
+
+static object *
+mapping_has_key(mp, args)
+       register mappingobject *mp;
+       object *args;
+{
+       object *key;
+       long hash;
+       register long ok;
+       if (!getargs(args, "O", &key))
+               return NULL;
+       hash = hashobject(key);
+       if (hash == -1)
+               return NULL;
+       ok = lookmapping(mp, key, hash)->me_value != NULL;
+       return newintobject(ok);
+}
+
+static struct methodlist mapp_methods[] = {
+       {"keys",        mapping_keys},
+       {"has_key",     mapping_has_key},
+       {NULL,          NULL}           /* sentinel */
+};
+
+static object *
+mapping_getattr(mp, name)
+       mappingobject *mp;
+       char *name;
+{
+       return findmethod(mapp_methods, (object *)mp, name);
+}
+
+typeobject Mappingtype = {
+       OB_HEAD_INIT(&Typetype)
+       0,
+       "mapping",
+       sizeof(mappingobject),
+       0,
+       mapping_dealloc,        /*tp_dealloc*/
+       mapping_print,          /*tp_print*/
+       mapping_getattr,        /*tp_getattr*/
+       0,                      /*tp_setattr*/
+       mapping_compare,        /*tp_compare*/
+       mapping_repr,           /*tp_repr*/
+       0,                      /*tp_as_number*/
+       0,                      /*tp_as_sequence*/
+       &mapping_as_mapping,    /*tp_as_mapping*/
+};
+
+/* For backward compatibility with old dictionary interface */
+
+static object *last_name_object;
+static char *last_name_char;
+
+object *
+getattro(v, name)
+       object *v;
+       object *name;
+{
+       if (name != last_name_object) {
+               XDECREF(last_name_object);
+               INCREF(name);
+               last_name_object = name;
+               last_name_char = getstringvalue(name);
+       }
+       return getattr(v, last_name_char);
+}
+
+int
+setattro(v, name, value)
+       object *v;
+       object *name;
+       object *value;
+{
+       if (name != last_name_object) {
+               XDECREF(last_name_object);
+               INCREF(name);
+               last_name_object = name;
+               last_name_char = getstringvalue(name);
+       }
+       return setattr(v, last_name_char, value);
+}
+
+object *
+dictlookup(v, key)
+       object *v;
+       char *key;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappinglookup(v, last_name_object);
+}
+
+int
+dictinsert(v, key, item)
+       object *v;
+       char *key;
+       object *item;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappinginsert(v, last_name_object, item);
+}
+
+int
+dictremove(v, key)
+       object *v;
+       char *key;
+{
+       if (key != last_name_char) {
+               XDECREF(last_name_object);
+               last_name_object = newstringobject(key);
+               if (last_name_object == NULL) {
+                       last_name_char = NULL;
+                       return NULL;
+               }
+               last_name_char = key;
+       }
+       return mappingremove(v, last_name_object);
+}
+
+char *
+getdictkey(v, i)
+       object *v;
+       int i;
+{
+       object *key = getmappingkey(v, i);
+       if (key == NULL)
+               return NULL;
+       return getstringvalue(key);
+}