hash.c

来自「早期freebsd实现」· C语言 代码 · 共 713 行

C
713
字号
/* $RCSfile: hash.c,v $$Revision: 4.0.1.3 $$Date: 92/06/08 13:26:29 $ * *    Copyright (c) 1991, Larry Wall * *    You may distribute under the terms of either the GNU General Public *    License or the Artistic License, as specified in the README file. * * $Log:	hash.c,v $ * Revision 4.0.1.3  92/06/08  13:26:29  lwall * patch20: removed implicit int declarations on functions * patch20: delete could cause %array to give too low a count of buckets filled * patch20: hash tables now split only if the memory is available to do so *  * Revision 4.0.1.2  91/11/05  17:24:13  lwall * patch11: saberized perl *  * Revision 4.0.1.1  91/06/07  11:10:11  lwall * patch4: new copyright notice *  * Revision 4.0  91/03/20  01:22:26  lwall * 4.0 baseline. *  */#include "EXTERN.h"#include "perl.h"static void hsplit();static char coeff[] = {		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,		61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};static void hfreeentries();STR *hfetch(tb,key,klen,lval)register HASH *tb;char *key;unsigned int klen;int lval;{    register char *s;    register int i;    register int hash;    register HENT *entry;    register int maxi;    STR *str;#ifdef SOME_DBM    datum dkey,dcontent;#endif    if (!tb)	return &str_undef;    if (!tb->tbl_array) {	if (lval)	    Newz(503,tb->tbl_array, tb->tbl_max + 1, HENT*);	else	    return &str_undef;    }    /* The hash function we use on symbols has to be equal to the first     * character when taken modulo 128, so that str_reset() can be implemented     * efficiently.  We throw in the second character and the last character     * (times 128) so that long chains of identifiers starting with the     * same letter don't have to be strEQ'ed within hfetch(), since it     * compares hash values before trying strEQ().     */    if (!tb->tbl_coeffsize)	hash = *key + 128 * key[1] + 128 * key[klen-1];	/* assuming klen > 0 */    else {	/* use normal coefficients */	if (klen < tb->tbl_coeffsize)	    maxi = klen;	else	    maxi = tb->tbl_coeffsize;	for (s=key,		i=0,	hash = 0;			    i < maxi;			/*SUPPRESS 8*/	     s++,		i++,	hash *= 5) {	    hash += *s * coeff[i];	}    }    entry = tb->tbl_array[hash & tb->tbl_max];    for (; entry; entry = entry->hent_next) {	if (entry->hent_hash != hash)		/* strings can't be equal */	    continue;	if (entry->hent_klen != klen)	    continue;	if (bcmp(entry->hent_key,key,klen))	/* is this it? */	    continue;	return entry->hent_val;    }#ifdef SOME_DBM    if (tb->tbl_dbm) {	dkey.dptr = key;	dkey.dsize = klen;#ifdef HAS_GDBM	dcontent = gdbm_fetch(tb->tbl_dbm,dkey);#else	dcontent = dbm_fetch(tb->tbl_dbm,dkey);#endif	if (dcontent.dptr) {			/* found one */	    str = Str_new(60,dcontent.dsize);	    str_nset(str,dcontent.dptr,dcontent.dsize);	    hstore(tb,key,klen,str,hash);		/* cache it */	    return str;	}    }#endif    if (lval) {		/* gonna assign to this, so it better be there */	str = Str_new(61,0);	hstore(tb,key,klen,str,hash);	return str;    }    return &str_undef;}boolhstore(tb,key,klen,val,hash)register HASH *tb;char *key;unsigned int klen;STR *val;register int hash;{    register char *s;    register int i;    register HENT *entry;    register HENT **oentry;    register int maxi;    if (!tb)	return FALSE;    if (hash)	/*SUPPRESS 530*/	;    else if (!tb->tbl_coeffsize)	hash = *key + 128 * key[1] + 128 * key[klen-1];    else {	/* use normal coefficients */	if (klen < tb->tbl_coeffsize)	    maxi = klen;	else	    maxi = tb->tbl_coeffsize;	for (s=key,		i=0,	hash = 0;			    i < maxi;			/*SUPPRESS 8*/	     s++,		i++,	hash *= 5) {	    hash += *s * coeff[i];	}    }    if (!tb->tbl_array)	Newz(505,tb->tbl_array, tb->tbl_max + 1, HENT*);    oentry = &(tb->tbl_array[hash & tb->tbl_max]);    i = 1;    for (entry = *oentry; entry; i=0, entry = entry->hent_next) {	if (entry->hent_hash != hash)		/* strings can't be equal */	    continue;	if (entry->hent_klen != klen)	    continue;	if (bcmp(entry->hent_key,key,klen))	/* is this it? */	    continue;	Safefree(entry->hent_val);	entry->hent_val = val;	return TRUE;    }    New(501,entry, 1, HENT);    entry->hent_klen = klen;    entry->hent_key = nsavestr(key,klen);    entry->hent_val = val;    entry->hent_hash = hash;    entry->hent_next = *oentry;    *oentry = entry;    /* hdbmstore not necessary here because it's called from stabset() */    if (i) {				/* initial entry? */	tb->tbl_fill++;#ifdef SOME_DBM	if (tb->tbl_dbm && tb->tbl_max >= DBM_CACHE_MAX)	    return FALSE;#endif	if (tb->tbl_fill > tb->tbl_dosplit)	    hsplit(tb);    }#ifdef SOME_DBM    else if (tb->tbl_dbm) {		/* is this just a cache for dbm file? */	void hentdelayfree();	entry = tb->tbl_array[hash & tb->tbl_max];	oentry = &entry->hent_next;	entry = *oentry;	while (entry) {	/* trim chain down to 1 entry */	    *oentry = entry->hent_next;	    hentdelayfree(entry);	/* no doubt they'll want this next. */	    entry = *oentry;	}    }#endif    return FALSE;}STR *hdelete(tb,key,klen)register HASH *tb;char *key;unsigned int klen;{    register char *s;    register int i;    register int hash;    register HENT *entry;    register HENT **oentry;    STR *str;    int maxi;#ifdef SOME_DBM    datum dkey;#endif    if (!tb || !tb->tbl_array)	return Nullstr;    if (!tb->tbl_coeffsize)	hash = *key + 128 * key[1] + 128 * key[klen-1];    else {	/* use normal coefficients */	if (klen < tb->tbl_coeffsize)	    maxi = klen;	else	    maxi = tb->tbl_coeffsize;	for (s=key,		i=0,	hash = 0;			    i < maxi;			/*SUPPRESS 8*/	     s++,		i++,	hash *= 5) {	    hash += *s * coeff[i];	}    }    oentry = &(tb->tbl_array[hash & tb->tbl_max]);    entry = *oentry;    i = 1;    for (; entry; i=0, oentry = &entry->hent_next, entry = *oentry) {	if (entry->hent_hash != hash)		/* strings can't be equal */	    continue;	if (entry->hent_klen != klen)	    continue;	if (bcmp(entry->hent_key,key,klen))	/* is this it? */	    continue;	*oentry = entry->hent_next;	if (i && !*oentry)	    tb->tbl_fill--;	str = str_mortal(entry->hent_val);	hentfree(entry);#ifdef SOME_DBM      do_dbm_delete:	if (tb->tbl_dbm) {	    dkey.dptr = key;	    dkey.dsize = klen;#ifdef HAS_GDBM	    gdbm_delete(tb->tbl_dbm,dkey);#else	    dbm_delete(tb->tbl_dbm,dkey);#endif	}#endif	return str;    }#ifdef SOME_DBM    str = Nullstr;    goto do_dbm_delete;#else    return Nullstr;#endif}static voidhsplit(tb)HASH *tb;{    int oldsize = tb->tbl_max + 1;    register int newsize = oldsize * 2;    register int i;    register HENT **a;    register HENT **b;    register HENT *entry;    register HENT **oentry;    a = tb->tbl_array;    nomemok = TRUE;    Renew(a, newsize, HENT*);    nomemok = FALSE;    if (!a) {	tb->tbl_dosplit = tb->tbl_max + 1;	/* never split again */	return;    }    Zero(&a[oldsize], oldsize, HENT*);		/* zero 2nd half*/    tb->tbl_max = --newsize;    tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;    tb->tbl_array = a;    for (i=0; i<oldsize; i++,a++) {	if (!*a)				/* non-existent */	    continue;	b = a+oldsize;	for (oentry = a, entry = *a; entry; entry = *oentry) {	    if ((entry->hent_hash & newsize) != i) {		*oentry = entry->hent_next;		entry->hent_next = *b;		if (!*b)		    tb->tbl_fill++;		*b = entry;		continue;	    }	    else		oentry = &entry->hent_next;	}	if (!*a)				/* everything moved */	    tb->tbl_fill--;    }}HASH *hnew(lookat)unsigned int lookat;{    register HASH *tb;    Newz(502,tb, 1, HASH);    if (lookat) {	tb->tbl_coeffsize = lookat;	tb->tbl_max = 7;		/* it's a normal associative array */	tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;    }    else {	tb->tbl_max = 127;		/* it's a symbol table */	tb->tbl_dosplit = 128;		/* so never split */    }    tb->tbl_fill = 0;#ifdef SOME_DBM    tb->tbl_dbm = 0;#endif    (void)hiterinit(tb);	/* so each() will start off right */    return tb;}voidhentfree(hent)register HENT *hent;{    if (!hent)	return;    str_free(hent->hent_val);    Safefree(hent->hent_key);    Safefree(hent);}voidhentdelayfree(hent)register HENT *hent;{    if (!hent)	return;    str_2mortal(hent->hent_val);	/* free between statements */    Safefree(hent->hent_key);    Safefree(hent);}voidhclear(tb,dodbm)register HASH *tb;int dodbm;{    if (!tb)	return;    hfreeentries(tb,dodbm);    tb->tbl_fill = 0;#ifndef lint    if (tb->tbl_array)	(void)memzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));#endif}static voidhfreeentries(tb,dodbm)register HASH *tb;int dodbm;{    register HENT *hent;    register HENT *ohent = Null(HENT*);#ifdef SOME_DBM    datum dkey;    datum nextdkey;#ifdef HAS_GDBM    GDBM_FILE old_dbm;#else#ifdef HAS_NDBM    DBM *old_dbm;#else    int old_dbm;#endif#endif#endif    if (!tb || !tb->tbl_array)	return;#ifdef SOME_DBM    if ((old_dbm = tb->tbl_dbm) && dodbm) {#ifdef HAS_GDBM	while (dkey = gdbm_firstkey(tb->tbl_dbm), dkey.dptr) {#else	while (dkey = dbm_firstkey(tb->tbl_dbm), dkey.dptr) {#endif	    do {#ifdef HAS_GDBM		nextdkey = gdbm_nextkey(tb->tbl_dbm, dkey);#else#ifdef HAS_NDBM#ifdef _CX_UX		nextdkey = dbm_nextkey(tb->tbl_dbm, dkey);#else		nextdkey = dbm_nextkey(tb->tbl_dbm);#endif#else		nextdkey = nextkey(dkey);#endif#endif#ifdef HAS_GDBM		gdbm_delete(tb->tbl_dbm,dkey);#else		dbm_delete(tb->tbl_dbm,dkey);#endif		dkey = nextdkey;	    } while (dkey.dptr);	/* one way or another, this works */	}    }    tb->tbl_dbm = 0;			/* now clear just cache */#endif    (void)hiterinit(tb);    /*SUPPRESS 560*/    while (hent = hiternext(tb)) {	/* concise but not very efficient */	hentfree(ohent);	ohent = hent;    }    hentfree(ohent);#ifdef SOME_DBM    tb->tbl_dbm = old_dbm;#endif}voidhfree(tb,dodbm)register HASH *tb;int dodbm;{    if (!tb)	return;    hfreeentries(tb,dodbm);    Safefree(tb->tbl_array);    Safefree(tb);}inthiterinit(tb)register HASH *tb;{    tb->tbl_riter = -1;    tb->tbl_eiter = Null(HENT*);    return tb->tbl_fill;}HENT *hiternext(tb)register HASH *tb;{    register HENT *entry;#ifdef SOME_DBM    datum key;#endif    entry = tb->tbl_eiter;#ifdef SOME_DBM    if (tb->tbl_dbm) {	if (entry) {#ifdef HAS_GDBM	    key.dptr = entry->hent_key;	    key.dsize = entry->hent_klen;	    key = gdbm_nextkey(tb->tbl_dbm, key);#else#ifdef HAS_NDBM#ifdef _CX_UX	    key.dptr = entry->hent_key;	    key.dsize = entry->hent_klen;	    key = dbm_nextkey(tb->tbl_dbm, key);#else	    key = dbm_nextkey(tb->tbl_dbm);#endif /* _CX_UX */#else	    key.dptr = entry->hent_key;	    key.dsize = entry->hent_klen;	    key = nextkey(key);#endif#endif	}	else {	    Newz(504,entry, 1, HENT);	    tb->tbl_eiter = entry;#ifdef HAS_GDBM	    key = gdbm_firstkey(tb->tbl_dbm);#else	    key = dbm_firstkey(tb->tbl_dbm);#endif	}	entry->hent_key = key.dptr;	entry->hent_klen = key.dsize;	if (!key.dptr) {	    if (entry->hent_val)		str_free(entry->hent_val);	    Safefree(entry);	    tb->tbl_eiter = Null(HENT*);	    return Null(HENT*);	}	return entry;    }#endif    if (!tb->tbl_array)	Newz(506,tb->tbl_array, tb->tbl_max + 1, HENT*);    do {	if (entry)	    entry = entry->hent_next;	if (!entry) {	    tb->tbl_riter++;	    if (tb->tbl_riter > tb->tbl_max) {		tb->tbl_riter = -1;		break;	    }	    entry = tb->tbl_array[tb->tbl_riter];	}    } while (!entry);    tb->tbl_eiter = entry;    return entry;}char *hiterkey(entry,retlen)register HENT *entry;int *retlen;{    *retlen = entry->hent_klen;    return entry->hent_key;}STR *hiterval(tb,entry)register HASH *tb;register HENT *entry;{#ifdef SOME_DBM    datum key, content;    if (tb->tbl_dbm) {	key.dptr = entry->hent_key;	key.dsize = entry->hent_klen;#ifdef HAS_GDBM	content = gdbm_fetch(tb->tbl_dbm,key);#else	content = dbm_fetch(tb->tbl_dbm,key);#endif	if (!entry->hent_val)	    entry->hent_val = Str_new(62,0);	str_nset(entry->hent_val,content.dptr,content.dsize);    }#endif    return entry->hent_val;}#ifdef SOME_DBM#ifndef O_CREAT#  ifdef I_FCNTL#    include <fcntl.h>#  endif#  ifdef I_SYS_FILE#    include <sys/file.h>#  endif#endif#ifndef O_RDONLY#define O_RDONLY 0#endif#ifndef O_RDWR#define O_RDWR 2#endif#ifndef O_CREAT#define O_CREAT 01000#endif#ifdef HAS_ODBMstatic int dbmrefcnt = 0;#endifboolhdbmopen(tb,fname,mode)register HASH *tb;char *fname;int mode;{    if (!tb)	return FALSE;#ifdef HAS_ODBM    if (tb->tbl_dbm)	/* never really closed it */	return TRUE;#endif    if (tb->tbl_dbm) {	hdbmclose(tb);	tb->tbl_dbm = 0;    }    hclear(tb, FALSE);	/* clear cache */#ifdef HAS_GDBM    if (mode >= 0)	tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRCREAT,mode, (void *) NULL);    if (!tb->tbl_dbm)	tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRITER, mode, (void *) NULL);    if (!tb->tbl_dbm)	tb->tbl_dbm = gdbm_open(fname, 0, GDBM_READER, mode, (void *) NULL);#else#ifdef HAS_NDBM    if (mode >= 0)	tb->tbl_dbm = dbm_open(fname, O_RDWR|O_CREAT, mode);    if (!tb->tbl_dbm)	tb->tbl_dbm = dbm_open(fname, O_RDWR, mode);    if (!tb->tbl_dbm)	tb->tbl_dbm = dbm_open(fname, O_RDONLY, mode);#else    if (dbmrefcnt++)	fatal("Old dbm can only open one database");    sprintf(buf,"%s.dir",fname);    if (stat(buf, &statbuf) < 0) {	if (mode < 0 || close(creat(buf,mode)) < 0)	    return FALSE;	sprintf(buf,"%s.pag",fname);	if (close(creat(buf,mode)) < 0)	    return FALSE;    }    tb->tbl_dbm = dbminit(fname) >= 0;#endif#endif    if (!tb->tbl_array && tb->tbl_dbm != 0)	Newz(507,tb->tbl_array, tb->tbl_max + 1, HENT*);    return tb->tbl_dbm != 0;}voidhdbmclose(tb)register HASH *tb;{    if (tb && tb->tbl_dbm) {#ifdef HAS_GDBM	gdbm_close(tb->tbl_dbm);	tb->tbl_dbm = 0;#else#ifdef HAS_NDBM	dbm_close(tb->tbl_dbm);	tb->tbl_dbm = 0;#else	/* dbmrefcnt--;  */	/* doesn't work, rats */#endif#endif    }    else if (dowarn)	warn("Close on unopened dbm file");}boolhdbmstore(tb,key,klen,str)register HASH *tb;char *key;unsigned int klen;register STR *str;{    datum dkey, dcontent;    int error;    if (!tb || !tb->tbl_dbm)	return FALSE;    dkey.dptr = key;    dkey.dsize = klen;    dcontent.dptr = str_get(str);    dcontent.dsize = str->str_cur;#ifdef HAS_GDBM    error = gdbm_store(tb->tbl_dbm, dkey, dcontent, GDBM_REPLACE);#else    error = dbm_store(tb->tbl_dbm, dkey, dcontent, DBM_REPLACE);#endif    if (error) {	if (errno == EPERM)	    fatal("No write permission to dbm file");	warn("dbm store returned %d, errno %d, key \"%s\"",error,errno,key);#ifdef HAS_NDBM        dbm_clearerr(tb->tbl_dbm);#endif    }    return !error;}#endif /* SOME_DBM */

⌨️ 快捷键说明

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