⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 assocfunc.c

📁 早期freebsd实现
💻 C
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Association table routines. * An association table is a type of value which can be "indexed" by * one or more arbitrary values.  Each element in the table is thus an * association between a particular set of index values and a result value. * The elements in an association table are stored in a hash table for * quick access. */#include "value.h"#define	MINHASHSIZE	31	/* minimum size of hash tables */#define	GROWHASHSIZE	50	/* approximate growth for hash tables */#define	CHAINLENGTH	10	/* desired number of elements on a hash chain */#define	ELEMSIZE(n)	(sizeof(ASSOCELEM) + (sizeof(VALUE) * ((n) - 1)))static ASSOCELEM *elemindex MATH_PROTO((ASSOC *ap, long index));static BOOL compareindices MATH_PROTO((VALUE *v1, VALUE *v2, long dim));static void resize MATH_PROTO((ASSOC *ap, long newsize));static void assoc_elemfree MATH_PROTO((ASSOCELEM *ep));static long nextprime MATH_PROTO((long n));/* * Return the address of the value specified by normal indexing of * an association.  The create flag is TRUE if a value is going to be * assigned into the specified indexing location.  If create is FALSE and * the index value doesn't exist, a pointer to a NULL value is returned. */VALUE *associndex(ap, create, dim, indices)	ASSOC *ap;		/* association to index into */	BOOL create;		/* whether to create the index value */	long dim;		/* dimension of the indexing */	VALUE *indices;		/* table of values being indexed by */{	ASSOCELEM **listhead;	ASSOCELEM *ep;	static VALUE val;	HASH hash;	int i;	if (dim <= 0)		math_error("No dimensions for indexing association");	/*	 * Calculate the hash value to use for this set of indices	 * so that we can first select the correct hash chain, and	 * also so we can quickly compare each element for a match.	 */	hash = 0;	for (i = 0; i < dim; i++)		/* ignore Saber-C warning about Over/underflow */		hash = hash * 67319821 + hashvalue(&indices[i]);	/*	 * Search the correct hash chain for the specified set of indices.	 * If found, return the address of the found element's value.	 */	listhead = &ap->a_table[hash % ap->a_size];	for (ep = *listhead; ep; ep = ep->e_next) {		if ((ep->e_hash != hash) || (ep->e_dim != dim))			continue;		if (compareindices(ep->e_indices, indices, dim))			return &ep->e_value;	}	/*	 * The set of indices was not found.	 * Either return a pointer to a NULL value for a read reference,	 * or allocate a new element in the list for a write reference.	 */	if (!create) {		val.v_type = V_NULL;		return &val;	}	ep = (ASSOCELEM *) malloc(ELEMSIZE(dim));	if (ep == NULL)		math_error("Cannot allocate association element");	ep->e_dim = dim;	ep->e_hash = hash;	ep->e_value.v_type = V_NULL;	for (i = 0; i < dim; i++)		copyvalue(&indices[i], &ep->e_indices[i]);	ep->e_next = *listhead;	*listhead = ep;	ap->a_count++;	resize(ap, ap->a_count / CHAINLENGTH);	return &ep->e_value;}/* * Search an association for the specified value starting at the * specified index.  Returns the element number (zero based) of the * found value, or -1 if the value was not found. */longassocsearch(ap, vp, index)	ASSOC *ap;	VALUE *vp;	long index;{	ASSOCELEM *ep;	if (index < 0)		index = 0;	while (TRUE) {		ep = elemindex(ap, index);		if (ep == NULL)			return -1;		if (!comparevalue(&ep->e_value, vp))			return index;		index++;	}}/* * Search an association backwards for the specified value starting at the * specified index.  Returns the element number (zero based) of the * found value, or -1 if the value was not found. */longassocrsearch(ap, vp, index)	ASSOC *ap;	VALUE *vp;	long index;{	ASSOCELEM *ep;	if (index >= ap->a_count)		index = ap->a_count - 1;	while (TRUE) {		ep = elemindex(ap, index);		if (ep == NULL)			return -1;		if (!comparevalue(&ep->e_value, vp))			return index;		index--;	}}/* * Return the address of an element of an association indexed by the * double-bracket operation. */static ASSOCELEM *elemindex(ap, index)	ASSOC *ap;		/* association to index into */	long index;		/* index of desired element */{	ASSOCELEM *ep;	int i;	if ((index < 0) || (index > ap->a_count))		return NULL;	/*	 * This loop should be made more efficient by remembering	 * previously requested locations within the association.	 */	for (i = 0; i < ap->a_size; i++) {		for (ep = ap->a_table[i]; ep; ep = ep->e_next) {			if (index-- == 0)				return ep;		}	}	return NULL;}/* * Return the address of the value specified by double-bracket indexing * of an association.  Returns NULL if there is no such element. */VALUE *assocfindex(ap, index)	ASSOC *ap;		/* association to index into */	long index;		/* index of desired element */{	ASSOCELEM *ep;	ep = elemindex(ap, index);	if (ep == NULL)		return NULL;	return &ep->e_value;}/* * Compare two associations to see if they are identical. * Returns TRUE if they are different. */BOOLassoccmp(ap1, ap2)	ASSOC *ap1, *ap2;{	ASSOCELEM **table1;	ASSOCELEM *ep1;	ASSOCELEM *ep2;	long size1;	long size2;	HASH hash;	long dim;	if (ap1 == ap2)		return FALSE;	if (ap1->a_count != ap2->a_count)		return TRUE;	table1 = ap1->a_table;	size1 = ap1->a_size;	size2 = ap2->a_size;	while (size1-- > 0) {		for (ep1 = *table1++; ep1; ep1 = ep1->e_next) {			hash = ep1->e_hash;			dim = ep1->e_dim;			for (ep2 = ap2->a_table[hash % size2]; ;				ep2 = ep2->e_next)			{				if (ep2 == NULL)					return TRUE;				if (ep2->e_hash != hash)					continue;				if (ep2->e_dim != dim)					continue;				if (compareindices(ep1->e_indices,					ep2->e_indices, dim))						break;			}			if (comparevalue(&ep1->e_value, &ep2->e_value))				return TRUE;		}	}	return FALSE;}/* * Copy an association value. */ASSOC *assoccopy(oldap)	ASSOC *oldap;{	ASSOC *ap;	ASSOCELEM *oldep;	ASSOCELEM *ep;	ASSOCELEM **listhead;	int oldhi;	int i;	ap = assocalloc(oldap->a_count / CHAINLENGTH);	ap->a_count = oldap->a_count;	for (oldhi = 0; oldhi < oldap->a_size; oldhi++) {		for (oldep = oldap->a_table[oldhi]; oldep;			oldep = oldep->e_next)		{			ep = (ASSOCELEM *) malloc(ELEMSIZE(oldep->e_dim));			if (ep == NULL)				math_error("Cannot allocate association element");			ep->e_dim = oldep->e_dim;			ep->e_hash = oldep->e_hash;			ep->e_value.v_type = V_NULL;			for (i = 0; i < ep->e_dim; i++)				copyvalue(&oldep->e_indices[i], &ep->e_indices[i]);			copyvalue(&oldep->e_value, &ep->e_value);			listhead = &ap->a_table[ep->e_hash % ap->a_size];			ep->e_next = *listhead;			*listhead = ep;		}	}	return ap;}/* * Resize the hash table for an association to be the specified size. * This is only actually done if the growth from the previous size is * enough to make this worthwhile. */static voidresize(ap, newsize)	ASSOC *ap;	long newsize;{	ASSOCELEM **oldtable;	ASSOCELEM **newtable;	ASSOCELEM **oldlist;	ASSOCELEM **newlist;	ASSOCELEM *ep;	int i;	if (newsize < ap->a_size + GROWHASHSIZE)		return;	newsize = nextprime(newsize);	newtable = (ASSOCELEM **) malloc(sizeof(ASSOCELEM *) * newsize);	if (newtable == NULL)		math_error("No memory to grow association");	for (i = 0; i < newsize; i++)		newtable[i] = NULL;	oldtable = ap->a_table;	oldlist = oldtable;	for (i = 0; i < ap->a_size; i++) {		while (*oldlist) {			ep = *oldlist;			*oldlist = ep->e_next;			newlist = &newtable[ep->e_hash % newsize];			ep->e_next = *newlist;			*newlist = ep;		}		oldlist++;	}	ap->a_table = newtable;	ap->a_size = newsize;	free((char *) oldtable);}/* * Free an association element, along with any contained values. */static voidassoc_elemfree(ep)	ASSOCELEM *ep;{	int i;	for (i = 0; i < ep->e_dim; i++)		freevalue(&ep->e_indices[i]);	freevalue(&ep->e_value);	ep->e_dim = 0;	ep->e_next = NULL;	free((char *) ep);}/* * Allocate a new association value with an initial hash table. * The hash table size is set at specified (but at least a minimum size). */ASSOC *assocalloc(initsize)	long initsize;{	register ASSOC *ap;	int i;	if (initsize < MINHASHSIZE)		initsize = MINHASHSIZE;	ap = (ASSOC *) malloc(sizeof(ASSOC));	if (ap == NULL)		math_error("No memory for association");	ap->a_count = 0;	ap->a_size = initsize;	ap->a_table = (ASSOCELEM **) malloc(sizeof(ASSOCELEM *) * initsize);	if (ap->a_table == NULL) {		free((char *) ap);		math_error("No memory for association");	}	for (i = 0; i < initsize; i++)		ap->a_table[i] = NULL;	return ap;}/* * Free an association value, along with all of its elements. */voidassocfree(ap)	register ASSOC *ap;{	ASSOCELEM **listhead;	ASSOCELEM *ep;	ASSOCELEM *nextep;	int i;	listhead = ap->a_table;	for (i = 0; i < ap->a_size; i++) {		nextep = *listhead;		*listhead = NULL;		while (nextep) {			ep = nextep;			nextep = ep->e_next;			assoc_elemfree(ep);		}		listhead++;	}	free((char *) ap->a_table);	ap->a_table = NULL;	free((char *) ap);}/* * Print out an association along with the specified number of * its elements.  The elements are printed out in shortened form. */voidassocprint(ap, max_print)	ASSOC *ap;	long max_print;{	ASSOCELEM *ep;	long index;	long i;	int savemode;	if (max_print <= 0) {		math_fmt("assoc (%ld element%s)", ap->a_count,			((ap->a_count == 1) ? "" : "s"));		return;	}	math_fmt("\n  assoc (%ld element%s):\n", ap->a_count,		((ap->a_count == 1) ? "" : "s"));	for (index = 0; ((index < max_print) && (index < ap->a_count));		index++)	{		ep = elemindex(ap, index);		if (ep == NULL)			continue;		math_str("  [");		for (i = 0; i < ep->e_dim; i++) {			if (i)				math_chr(',');			savemode = math_setmode(MODE_FRAC);			printvalue(&ep->e_indices[i],				(PRINT_SHORT | PRINT_UNAMBIG));			math_setmode(savemode);		}		math_str("] = ");		printvalue(&ep->e_value, PRINT_SHORT | PRINT_UNAMBIG);		math_chr('\n');	}	if (max_print < ap->a_count)		math_str("  ...\n");}/* * Return a trivial hash value for an association. */HASHassochash(ap)	ASSOC *ap;{	return ap->a_count * 700001;}/* * Compare two lists of index values to see if they are identical. * Returns TRUE if they are the same. */static BOOLcompareindices(v1, v2, dim)	VALUE *v1;	VALUE *v2;	long dim;{	int i;	for (i = 0; i < dim; i++)		if (v1[i].v_type != v2[i].v_type)			return FALSE;	while (dim-- > 0)		if (comparevalue(v1++, v2++))			return FALSE;	return TRUE;}/* * Return the next prime number up from the specified value. * This is used to pick a good hash table size. */static longnextprime(n)	long n;{	long i;	if ((n & 0x01) == 0)		n++;	while (TRUE) {		for (i = 3; n % i; i += 2) {			if (i * i > n)				return n;		}		n += 2;	}}/* END CODE */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -