mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			852 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			852 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***********************************************************
 | |
| Copyright 1991-1995 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 */
 | |
| 
 | |
| /* This file should really be called "dictobject.c", since "mapping"
 | |
|   is the generic name for objects with an unorderred arbitrary key
 | |
|   set (just like lists are sequences), but since it improves (and was
 | |
|   originally derived from) a file by that name I had to change its
 | |
|   name.  For the user these objects are still called "dictionaries". */
 | |
| 
 | |
| #include "allobjects.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,
 | |
| the third line is a selection from Knuth, Vol. 3, Sec. 6.1, Table 1,
 | |
| and the next three lines were suggested by Steve Kirsch.
 | |
| The final value is a sentinel.
 | |
| */
 | |
| static long primes[] = {
 | |
| 	3, 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093,
 | |
| 	5987,
 | |
| 	9551, 15683, 19609, 31397,
 | |
| 	65521L, 131071L, 262139L, 524287L, 1048573L, 2097143L,
 | |
| 	4194301L, 8388593L, 16777213L, 33554393L, 67108859L,
 | |
| 	134217689L, 268435399L, 536870909L, 1073741789L,
 | |
| 	0
 | |
| };
 | |
| 
 | |
| /* 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 = 0;
 | |
| 	mp->ma_table = NULL;
 | |
| 	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;
 | |
| 	register int size = mp->ma_size;
 | |
| 	/* 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 % size;
 | |
| 	do {
 | |
| 		sum = 3*sum + 1;
 | |
| 		incr = sum % 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) % 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;
 | |
| {
 | |
| 	object *old_value;
 | |
| 	register mappingentry *ep;
 | |
| 	ep = lookmapping(mp, key, hash);
 | |
| 	if (ep->me_value != NULL) {
 | |
| 		old_value = ep->me_value;
 | |
| 		ep->me_value = value;
 | |
| 		DECREF(old_value); /* which **CAN** re-enter */
 | |
| 		DECREF(key);
 | |
| 	}
 | |
| 	else {
 | |
| 		if (ep->me_key == NULL)
 | |
| 			mp->ma_fill++;
 | |
| 		else
 | |
| 			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 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] <= 0) {
 | |
| 			/* Ran out of primes */
 | |
| 			err_nomem();
 | |
| 			return -1;
 | |
| 		}
 | |
| 		if (primes[i] > mp->ma_used*2) {
 | |
| 			newsize = primes[i];
 | |
| 			if (newsize != primes[i]) {
 | |
| 				/* Integer truncation */
 | |
| 				err_nomem();
 | |
| 				return -1;
 | |
| 			}
 | |
| 			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;
 | |
| 
 | |
| 	/* 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)
 | |
| 			insertmapping(mp,ep->me_key,ep->me_hash,ep->me_value);
 | |
| 	}
 | |
| 	for (i = 0, ep = oldtable; i < oldsize; i++, ep++) {
 | |
| 		if (ep->me_value == NULL)
 | |
| 			XDECREF(ep->me_key);
 | |
| 	}
 | |
| 
 | |
| 	XDEL(oldtable);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| object *
 | |
| mappinglookup(op, key)
 | |
| 	object *op;
 | |
| 	object *key;
 | |
| {
 | |
| 	long hash;
 | |
| 	if (!is_mappingobject(op)) {
 | |
| 		err_badcall();
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	if (((mappingobject *)op)->ma_table == NULL)
 | |
| 		return NULL;
 | |
| #ifdef CACHE_HASH
 | |
| 	if (!is_stringobject(key) || (hash = ((stringobject *) key)->ob_shash) == -1)
 | |
| #endif
 | |
| 	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;
 | |
| 	}
 | |
| #ifdef CACHE_HASH
 | |
| 	if (!is_stringobject(key) || (hash = ((stringobject *) key)->ob_shash) == -1)
 | |
| #endif
 | |
| 	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;
 | |
| 	object *old_value, *old_key;
 | |
| 
 | |
| 	if (!is_mappingobject(op)) {
 | |
| 		err_badcall();
 | |
| 		return -1;
 | |
| 	}
 | |
| #ifdef CACHE_HASH
 | |
| 	if (!is_stringobject(key) || (hash = ((stringobject *) key)->ob_shash) == -1)
 | |
| #endif
 | |
| 	hash = hashobject(key);
 | |
| 	if (hash == -1)
 | |
| 		return -1;
 | |
| 	mp = (mappingobject *)op;
 | |
| 	if (((mappingobject *)op)->ma_table == NULL)
 | |
| 		goto empty;
 | |
| 	ep = lookmapping(mp, key, hash);
 | |
| 	if (ep->me_value == NULL) {
 | |
| 	empty:
 | |
| 		err_setval(KeyError, key);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	old_key = ep->me_key;
 | |
| 	INCREF(dummy);
 | |
| 	ep->me_key = dummy;
 | |
| 	old_value = ep->me_value;
 | |
| 	ep->me_value = NULL;
 | |
| 	mp->ma_used--;
 | |
| 	DECREF(old_value); 
 | |
| 	DECREF(old_key); 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| mappingclear(op)
 | |
| 	object *op;
 | |
| {
 | |
| 	int i, n;
 | |
| 	register mappingentry *table;
 | |
| 	mappingobject *mp;
 | |
| 	if (!is_mappingobject(op))
 | |
| 		return;
 | |
| 	mp = (mappingobject *)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++) {
 | |
| 		XDECREF(table[i].me_key);
 | |
| 		XDECREF(table[i].me_value);
 | |
| 	}
 | |
| 	DEL(table);
 | |
| }
 | |
| 
 | |
| int
 | |
| mappinggetnext(op, ppos, pkey, pvalue)
 | |
| 	object *op;
 | |
| 	int *ppos;
 | |
| 	object **pkey;
 | |
| 	object **pvalue;
 | |
| {
 | |
| 	int i;
 | |
| 	register mappingobject *mp;
 | |
| 	if (!is_dictobject(op))
 | |
| 		return 0;
 | |
| 	mp = (mappingobject *)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
 | |
| 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);
 | |
| 	}
 | |
| 	XDEL(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, 0) != 0)
 | |
| 				return -1;
 | |
| 			fprintf(fp, ": ");
 | |
| 			if (printobject(ep->me_value, fp, 0) != 0)
 | |
| 				return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	fprintf(fp, "}");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 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 && v; i++, ep++) {
 | |
| 		if (ep->me_value != NULL) {
 | |
| 			if (any++)
 | |
| 				joinstring(&v, sepa);
 | |
| 			joinstring_decref(&v, reprobject(ep->me_key));
 | |
| 			joinstring(&v, colon);
 | |
| 			joinstring_decref(&v, reprobject(ep->me_value));
 | |
| 		}
 | |
| 	}
 | |
| 	joinstring_decref(&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;
 | |
| 	if (mp->ma_table == NULL) {
 | |
| 		err_setval(KeyError, key);
 | |
| 		return NULL;
 | |
| 	}
 | |
| #ifdef CACHE_HASH
 | |
| 	if (!is_stringobject(key) || (hash = ((stringobject *) key)->ob_shash) == -1)
 | |
| #endif
 | |
| 	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 = {
 | |
| 	(inquiry)mapping_length, /*mp_length*/
 | |
| 	(binaryfunc)mapping_subscript, /*mp_subscript*/
 | |
| 	(objobjargproc)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;
 | |
| }
 | |
| 
 | |
| static object *
 | |
| mapping_values(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 *value = mp->ma_table[i].me_value;
 | |
| 			INCREF(value);
 | |
| 			setlistitem(v, j, value);
 | |
| 			j++;
 | |
| 		}
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| static object *
 | |
| mapping_items(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;
 | |
| 			object *value = mp->ma_table[i].me_value;
 | |
| 			object *item = newtupleobject(2);
 | |
| 			if (item == NULL) {
 | |
| 				DECREF(v);
 | |
| 				return NULL;
 | |
| 			}
 | |
| 			INCREF(key);
 | |
| 			settupleitem(item, 0, key);
 | |
| 			INCREF(value);
 | |
| 			settupleitem(item, 1, value);
 | |
| 			setlistitem(v, j, item);
 | |
| 			j++;
 | |
| 		}
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| int
 | |
| getmappingsize(mp)
 | |
| 	object *mp;
 | |
| {
 | |
| 	if (mp == NULL || !is_mappingobject(mp)) {
 | |
| 		err_badcall();
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return ((mappingobject *)mp)->ma_used;
 | |
| }
 | |
| 
 | |
| object *
 | |
| getmappingkeys(mp)
 | |
| 	object *mp;
 | |
| {
 | |
| 	if (mp == NULL || !is_mappingobject(mp)) {
 | |
| 		err_badcall();
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return mapping_keys((mappingobject *)mp, (object *)NULL);
 | |
| }
 | |
| 
 | |
| object *
 | |
| getmappingvalues(mp)
 | |
| 	object *mp;
 | |
| {
 | |
| 	if (mp == NULL || !is_mappingobject(mp)) {
 | |
| 		err_badcall();
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return mapping_values((mappingobject *)mp, (object *)NULL);
 | |
| }
 | |
| 
 | |
| object *
 | |
| getmappingitems(mp)
 | |
| 	object *mp;
 | |
| {
 | |
| 	if (mp == NULL || !is_mappingobject(mp)) {
 | |
| 		err_badcall();
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return mapping_items((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;
 | |
| #ifdef CACHE_HASH
 | |
| 		if (!is_stringobject(akey) || (ahash = ((stringobject *) akey)->ob_shash) == -1)
 | |
| #endif
 | |
| 		ahash = hashobject(akey);
 | |
| 		if (ahash == -1)
 | |
| 			err_clear(); /* Don't want errors here */
 | |
| #ifdef CACHE_HASH
 | |
| 		if (!is_stringobject(bkey) || (bhash = ((stringobject *) bkey)->ob_shash) == -1)
 | |
| #endif
 | |
| 		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;
 | |
| #ifdef CACHE_HASH
 | |
| 	if (!is_stringobject(key) || (hash = ((stringobject *) key)->ob_shash) == -1)
 | |
| #endif
 | |
| 	hash = hashobject(key);
 | |
| 	if (hash == -1)
 | |
| 		return NULL;
 | |
| 	ok = mp->ma_size != 0 && lookmapping(mp, key, hash)->me_value != NULL;
 | |
| 	return newintobject(ok);
 | |
| }
 | |
| 
 | |
| static struct methodlist mapp_methods[] = {
 | |
| 	{"has_key",	(method)mapping_has_key},
 | |
| 	{"items",	(method)mapping_items},
 | |
| 	{"keys",	(method)mapping_keys},
 | |
| 	{"values",	(method)mapping_values},
 | |
| 	{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,
 | |
| 	"dictionary",
 | |
| 	sizeof(mappingobject),
 | |
| 	0,
 | |
| 	(destructor)mapping_dealloc, /*tp_dealloc*/
 | |
| 	(printfunc)mapping_print, /*tp_print*/
 | |
| 	(getattrfunc)mapping_getattr, /*tp_getattr*/
 | |
| 	0,			/*tp_setattr*/
 | |
| 	(cmpfunc)mapping_compare, /*tp_compare*/
 | |
| 	(reprfunc)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; /* NULL or == getstringvalue(last_name_object) */
 | |
| 
 | |
| object *
 | |
| getattro(v, name)
 | |
| 	object *v;
 | |
| 	object *name;
 | |
| {
 | |
| 	if (v->ob_type->tp_getattro != NULL)
 | |
| 		return (*v->ob_type->tp_getattro)(v, 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 (v->ob_type->tp_setattro != NULL)
 | |
| 		return (*v->ob_type->tp_setattro)(v, name, 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 = getstringvalue(last_name_object);
 | |
| 	}
 | |
| 	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 -1;
 | |
| 		}
 | |
| 		last_name_char = getstringvalue(last_name_object);
 | |
| 	}
 | |
| 	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 -1;
 | |
| 		}
 | |
| 		last_name_char = getstringvalue(last_name_object);
 | |
| 	}
 | |
| 	return mappingremove(v, last_name_object);
 | |
| }
 | 
