diff options
Diffstat (limited to 'hv.c')
-rw-r--r-- | hv.c | 807 |
1 files changed, 807 insertions, 0 deletions
@@ -0,0 +1,807 @@ +/* $RCSfile: hash.c,v $$Revision: 4.1 $$Date: 92/08/07 18:21:48 $ + * + * 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.1 92/08/07 18:21:48 lwall + * + * 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 void hfreeentries(); + +SV** +hv_fetch(hv,key,klen,lval) +HV *hv; +char *key; +U32 klen; +I32 lval; +{ + register XPVHV* xhv; + register char *s; + register I32 i; + register I32 hash; + register HE *entry; + register I32 maxi; + SV *sv; +#ifdef SOME_DBM + datum dkey,dcontent; +#endif + + if (!hv) + return 0; + xhv = (XPVHV*)SvANY(hv); + if (!xhv->xhv_array) { + if (lval) + Newz(503,xhv->xhv_array, xhv->xhv_max + 1, HE*); + else + return 0; + } + + /* The hash function we use on symbols has to be equal to the first + * character when taken modulo 128, so that sv_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 hv_fetch(), since it + * compares hash values before trying strEQ(). + */ + if (!xhv->xhv_coeffsize && klen) + hash = klen ? *key + 128 * key[1] + 128 * key[klen-1] : 0; + else { /* use normal coefficients */ + if (klen < xhv->xhv_coeffsize) + maxi = klen; + else + maxi = xhv->xhv_coeffsize; + for (s=key, i=0, hash = 0; + i < maxi; /*SUPPRESS 8*/ + s++, i++, hash *= 5) { + hash += *s * coeff[i]; + } + } + + entry = xhv->xhv_array[hash & xhv->xhv_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 (xhv->xhv_dbm) { + dkey.dptr = key; + dkey.dsize = klen; +#ifdef HAS_GDBM + dcontent = gdbm_fetch(xhv->xhv_dbm,dkey); +#else + dcontent = dbm_fetch(xhv->xhv_dbm,dkey); +#endif + if (dcontent.dptr) { /* found one */ + sv = NEWSV(60,dcontent.dsize); + sv_setpvn(sv,dcontent.dptr,dcontent.dsize); + return hv_store(hv,key,klen,sv,hash); /* cache it */ + } + } +#endif + if (lval) { /* gonna assign to this, so it better be there */ + sv = NEWSV(61,0); + return hv_store(hv,key,klen,sv,hash); + } + return 0; +} + +SV** +hv_store(hv,key,klen,val,hash) +HV *hv; +char *key; +U32 klen; +SV *val; +register I32 hash; +{ + register XPVHV* xhv; + register char *s; + register I32 i; + register HE *entry; + register HE **oentry; + register I32 maxi; + + if (!hv) + return 0; + + xhv = (XPVHV*)SvANY(hv); + if (hash) + /*SUPPRESS 530*/ + ; + else if (!xhv->xhv_coeffsize && klen) + hash = klen ? *key + 128 * key[1] + 128 * key[klen-1] : 0; + else { /* use normal coefficients */ + if (klen < xhv->xhv_coeffsize) + maxi = klen; + else + maxi = xhv->xhv_coeffsize; + for (s=key, i=0, hash = 0; + i < maxi; /*SUPPRESS 8*/ + s++, i++, hash *= 5) { + hash += *s * coeff[i]; + } + } + + if (!xhv->xhv_array) + Newz(505,xhv->xhv_array, xhv->xhv_max + 1, HE*); + + oentry = &(xhv->xhv_array[hash & xhv->xhv_max]); + i = 1; + + if (SvMAGICAL(hv)) { + MAGIC* mg = SvMAGIC(hv); + sv_magic(val, (SV*)hv, tolower(mg->mg_type), key, klen); + } + 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; + sv_free(entry->hent_val); + entry->hent_val = val; + return &entry->hent_val; + } + New(501,entry, 1, HE); + + entry->hent_klen = klen; + entry->hent_key = nsavestr(key,klen); + entry->hent_val = val; + entry->hent_hash = hash; + entry->hent_next = *oentry; + *oentry = entry; + + /* hv_dbmstore not necessary here because it's called from sv_setmagic() */ + + if (i) { /* initial entry? */ + xhv->xhv_fill++; +#ifdef SOME_DBM + if (xhv->xhv_dbm && xhv->xhv_max >= DBM_CACHE_MAX) + return &entry->hent_val; +#endif + if (xhv->xhv_fill > xhv->xhv_dosplit) + hsplit(hv); + } +#ifdef SOME_DBM + else if (xhv->xhv_dbm) { /* is this just a cache for dbm file? */ + void he_delayfree(); + HE* ent; + + ent = xhv->xhv_array[hash & xhv->xhv_max]; + oentry = &ent->hent_next; + ent = *oentry; + while (ent) { /* trim chain down to 1 entry */ + *oentry = ent->hent_next; + he_delayfree(ent); /* no doubt they'll want this next, sigh... */ + ent = *oentry; + } + } +#endif + + return &entry->hent_val; +} + +SV * +hv_delete(hv,key,klen) +HV *hv; +char *key; +U32 klen; +{ + register XPVHV* xhv; + register char *s; + register I32 i; + register I32 hash; + register HE *entry; + register HE **oentry; + SV *sv; + I32 maxi; +#ifdef SOME_DBM + datum dkey; +#endif + + if (!hv) + return Nullsv; + xhv = (XPVHV*)SvANY(hv); + if (!xhv->xhv_array) + return Nullsv; + if (!xhv->xhv_coeffsize && klen) + hash = klen ? *key + 128 * key[1] + 128 * key[klen-1] : 0; + else { /* use normal coefficients */ + if (klen < xhv->xhv_coeffsize) + maxi = klen; + else + maxi = xhv->xhv_coeffsize; + for (s=key, i=0, hash = 0; + i < maxi; /*SUPPRESS 8*/ + s++, i++, hash *= 5) { + hash += *s * coeff[i]; + } + } + + oentry = &(xhv->xhv_array[hash & xhv->xhv_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) + xhv->xhv_fill--; + sv = sv_mortalcopy(entry->hent_val); + he_free(entry); +#ifdef SOME_DBM + do_dbm_delete: + if (xhv->xhv_dbm) { + dkey.dptr = key; + dkey.dsize = klen; +#ifdef HAS_GDBM + gdbm_delete(xhv->xhv_dbm,dkey); +#else + dbm_delete(xhv->xhv_dbm,dkey); +#endif + } +#endif + return sv; + } +#ifdef SOME_DBM + sv = Nullsv; + goto do_dbm_delete; +#else + return Nullsv; +#endif +} + +static void +hsplit(hv) +HV *hv; +{ + register XPVHV* xhv = (XPVHV*)SvANY(hv); + I32 oldsize = xhv->xhv_max + 1; + register I32 newsize = oldsize * 2; + register I32 i; + register HE **a; + register HE **b; + register HE *entry; + register HE **oentry; + + a = xhv->xhv_array; + nomemok = TRUE; + Renew(a, newsize, HE*); + nomemok = FALSE; + if (!a) { + xhv->xhv_dosplit = xhv->xhv_max + 1; /* never split again */ + return; + } + Zero(&a[oldsize], oldsize, HE*); /* zero 2nd half*/ + xhv->xhv_max = --newsize; + xhv->xhv_dosplit = xhv->xhv_max * FILLPCT / 100; + xhv->xhv_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) + xhv->xhv_fill++; + *b = entry; + continue; + } + else + oentry = &entry->hent_next; + } + if (!*a) /* everything moved */ + xhv->xhv_fill--; + } +} + +HV * +newHV(lookat) +U32 lookat; +{ + register HV *hv; + register XPVHV* xhv; + + Newz(502,hv, 1, HV); + SvREFCNT(hv) = 1; + sv_upgrade(hv, SVt_PVHV); + xhv = (XPVHV*)SvANY(hv); + SvPOK_off(hv); + SvNOK_off(hv); + if (lookat) { + xhv->xhv_coeffsize = lookat; + xhv->xhv_max = 7; /* it's a normal associative array */ + xhv->xhv_dosplit = xhv->xhv_max * FILLPCT / 100; + } + else { + xhv->xhv_max = 127; /* it's a symbol table */ + xhv->xhv_dosplit = 128; /* so never split */ + } + xhv->xhv_fill = 0; + xhv->xhv_pmroot = 0; +#ifdef SOME_DBM + xhv->xhv_dbm = 0; +#endif + (void)hv_iterinit(hv); /* so each() will start off right */ + return hv; +} + +void +he_free(hent) +register HE *hent; +{ + if (!hent) + return; + sv_free(hent->hent_val); + Safefree(hent->hent_key); + Safefree(hent); +} + +void +he_delayfree(hent) +register HE *hent; +{ + if (!hent) + return; + sv_2mortal(hent->hent_val); /* free between statements */ + Safefree(hent->hent_key); + Safefree(hent); +} + +void +hv_clear(hv,dodbm) +HV *hv; +I32 dodbm; +{ + register XPVHV* xhv; + if (!hv) + return; + xhv = (XPVHV*)SvANY(hv); + hfreeentries(hv,dodbm); + xhv->xhv_fill = 0; +#ifndef lint + if (xhv->xhv_array) + (void)memzero((char*)xhv->xhv_array, (xhv->xhv_max + 1) * sizeof(HE*)); +#endif +} + +static void +hfreeentries(hv,dodbm) +HV *hv; +I32 dodbm; +{ + register XPVHV* xhv; + register HE *hent; + register HE *ohent = Null(HE*); +#ifdef SOME_DBM + datum dkey; + datum nextdkey; +#ifdef HAS_GDBM + GDBM_FILE old_dbm; +#else +#ifdef HAS_NDBM + DBM *old_dbm; +#else + I32 old_dbm; +#endif +#endif +#endif + + if (!hv) + return; + xhv = (XPVHV*)SvANY(hv); + if (!xhv->xhv_array) + return; +#ifdef SOME_DBM + if ((old_dbm = xhv->xhv_dbm) && dodbm) { +#ifdef HAS_GDBM + while (dkey = gdbm_firstkey(xhv->xhv_dbm), dkey.dptr) { +#else + while (dkey = dbm_firstkey(xhv->xhv_dbm), dkey.dptr) { +#endif + do { +#ifdef HAS_GDBM + nextdkey = gdbm_nextkey(xhv->xhv_dbm, dkey); +#else +#ifdef HAS_NDBM +#ifdef _CX_UX + nextdkey = dbm_nextkey(xhv->xhv_dbm, dkey); +#else + nextdkey = dbm_nextkey(xhv->xhv_dbm); +#endif +#else + nextdkey = nextkey(dkey); +#endif +#endif +#ifdef HAS_GDBM + gdbm_delete(xhv->xhv_dbm,dkey); +#else + dbm_delete(xhv->xhv_dbm,dkey); +#endif + dkey = nextdkey; + } while (dkey.dptr); /* one way or another, this works */ + } + } + xhv->xhv_dbm = 0; /* now clear just cache */ +#endif + (void)hv_iterinit(hv); + /*SUPPRESS 560*/ + while (hent = hv_iternext(hv)) { /* concise but not very efficient */ + he_free(ohent); + ohent = hent; + } + he_free(ohent); +#ifdef SOME_DBM + xhv->xhv_dbm = old_dbm; +#endif + if (SvMAGIC(hv)) + mg_clear(hv); +} + +void +hv_undef(hv,dodbm) +HV *hv; +I32 dodbm; +{ + register XPVHV* xhv; + if (!hv) + return; + xhv = (XPVHV*)SvANY(hv); + hfreeentries(hv,dodbm); + Safefree(xhv->xhv_array); + xhv->xhv_array = 0; + if (xhv->xhv_coeffsize) { + xhv->xhv_max = 7; /* it's a normal associative array */ + xhv->xhv_dosplit = xhv->xhv_max * FILLPCT / 100; + } + else { + xhv->xhv_max = 127; /* it's a symbol table */ + xhv->xhv_dosplit = 128; /* so never split */ + } + xhv->xhv_fill = 0; +#ifdef SOME_DBM + xhv->xhv_dbm = 0; +#endif + (void)hv_iterinit(hv); /* so each() will start off right */ +} + +void +hv_free(hv,dodbm) +register HV *hv; +I32 dodbm; +{ + if (!hv) + return; + hfreeentries(hv,dodbm); + Safefree(HvARRAY(hv)); + Safefree(hv); +} + +I32 +hv_iterinit(hv) +HV *hv; +{ + register XPVHV* xhv = (XPVHV*)SvANY(hv); + xhv->xhv_riter = -1; + xhv->xhv_eiter = Null(HE*); + return xhv->xhv_fill; +} + +HE * +hv_iternext(hv) +HV *hv; +{ + register XPVHV* xhv; + register HE *entry; +#ifdef SOME_DBM + datum key; +#endif + + if (!hv) + fatal("Bad associative array"); + xhv = (XPVHV*)SvANY(hv); + entry = xhv->xhv_eiter; +#ifdef SOME_DBM + if (xhv->xhv_dbm) { + if (entry) { +#ifdef HAS_GDBM + key.dptr = entry->hent_key; + key.dsize = entry->hent_klen; + key = gdbm_nextkey(xhv->xhv_dbm, key); +#else +#ifdef HAS_NDBM +#ifdef _CX_UX + key.dptr = entry->hent_key; + key.dsize = entry->hent_klen; + key = dbm_nextkey(xhv->xhv_dbm, key); +#else + key = dbm_nextkey(xhv->xhv_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, HE); + xhv->xhv_eiter = entry; +#ifdef HAS_GDBM + key = gdbm_firstkey(xhv->xhv_dbm); +#else + key = dbm_firstkey(xhv->xhv_dbm); +#endif + } + entry->hent_key = key.dptr; + entry->hent_klen = key.dsize; + if (!key.dptr) { + if (entry->hent_val) + sv_free(entry->hent_val); + Safefree(entry); + xhv->xhv_eiter = Null(HE*); + return Null(HE*); + } + return entry; + } +#endif + if (!xhv->xhv_array) + Newz(506,xhv->xhv_array, xhv->xhv_max + 1, HE*); + do { + if (entry) + entry = entry->hent_next; + if (!entry) { + xhv->xhv_riter++; + if (xhv->xhv_riter > xhv->xhv_max) { + xhv->xhv_riter = -1; + break; + } + entry = xhv->xhv_array[xhv->xhv_riter]; + } + } while (!entry); + + xhv->xhv_eiter = entry; + return entry; +} + +char * +hv_iterkey(entry,retlen) +register HE *entry; +I32 *retlen; +{ + *retlen = entry->hent_klen; + return entry->hent_key; +} + +SV * +hv_iterval(hv,entry) +HV *hv; +register HE *entry; +{ +#ifdef SOME_DBM + register XPVHV* xhv; + datum key, content; + + if (!hv) + fatal("Bad associative array"); + xhv = (XPVHV*)SvANY(hv); + if (xhv->xhv_dbm) { + key.dptr = entry->hent_key; + key.dsize = entry->hent_klen; +#ifdef HAS_GDBM + content = gdbm_fetch(xhv->xhv_dbm,key); +#else + content = dbm_fetch(xhv->xhv_dbm,key); +#endif + if (!entry->hent_val) + entry->hent_val = NEWSV(62,0); + sv_setpvn(entry->hent_val,content.dptr,content.dsize); + } +#endif + return entry->hent_val; +} + +#ifdef SOME_DBM + +#ifndef OP_CREAT +# ifdef I_FCNTL +# include <fcntl.h> +# endif +# ifdef I_SYS_FILE +# include <sys/file.h> +# endif +#endif + +#ifndef OP_RDONLY +#define OP_RDONLY 0 +#endif +#ifndef OP_RDWR +#define OP_RDWR 2 +#endif +#ifndef OP_CREAT +#define OP_CREAT 01000 +#endif + +bool +hv_dbmopen(hv,fname,mode) +HV *hv; +char *fname; +I32 mode; +{ + register XPVHV* xhv; + if (!hv) + return FALSE; + xhv = (XPVHV*)SvANY(hv); +#ifdef HAS_ODBM + if (xhv->xhv_dbm) /* never really closed it */ + return TRUE; +#endif + if (xhv->xhv_dbm) { + hv_dbmclose(hv); + xhv->xhv_dbm = 0; + } + hv_clear(hv, FALSE); /* clear cache */ +#ifdef HAS_GDBM + if (mode >= 0) + xhv->xhv_dbm = gdbm_open(fname, 0, GDBM_WRCREAT,mode, (void *) NULL); + if (!xhv->xhv_dbm) + xhv->xhv_dbm = gdbm_open(fname, 0, GDBM_WRITER, mode, (void *) NULL); + if (!xhv->xhv_dbm) + xhv->xhv_dbm = gdbm_open(fname, 0, GDBM_READER, mode, (void *) NULL); +#else +#ifdef HAS_NDBM + if (mode >= 0) + xhv->xhv_dbm = dbm_open(fname, OP_RDWR|OP_CREAT, mode); + if (!xhv->xhv_dbm) + xhv->xhv_dbm = dbm_open(fname, OP_RDWR, mode); + if (!xhv->xhv_dbm) + xhv->xhv_dbm = dbm_open(fname, OP_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; + } + xhv->xhv_dbm = dbminit(fname) >= 0; +#endif +#endif + if (!xhv->xhv_array && xhv->xhv_dbm != 0) + Newz(507,xhv->xhv_array, xhv->xhv_max + 1, HE*); + hv_magic(hv, 0, 'D'); + return xhv->xhv_dbm != 0; +} + +void +hv_dbmclose(hv) +HV *hv; +{ + register XPVHV* xhv; + if (!hv) + fatal("Bad associative array"); + xhv = (XPVHV*)SvANY(hv); + if (xhv->xhv_dbm) { +#ifdef HAS_GDBM + gdbm_close(xhv->xhv_dbm); + xhv->xhv_dbm = 0; +#else +#ifdef HAS_NDBM + dbm_close(xhv->xhv_dbm); + xhv->xhv_dbm = 0; +#else + /* dbmrefcnt--; */ /* doesn't work, rats */ +#endif +#endif + } + else if (dowarn) + warn("Close on unopened dbm file"); +} + +bool +hv_dbmstore(hv,key,klen,sv) +HV *hv; +char *key; +U32 klen; +register SV *sv; +{ + register XPVHV* xhv; + datum dkey, dcontent; + I32 error; + + if (!hv) + return FALSE; + xhv = (XPVHV*)SvANY(hv); + if (!xhv->xhv_dbm) + return FALSE; + dkey.dptr = key; + dkey.dsize = klen; + dcontent.dptr = SvPVn(sv); + dcontent.dsize = SvCUR(sv); +#ifdef HAS_GDBM + error = gdbm_store(xhv->xhv_dbm, dkey, dcontent, GDBM_REPLACE); +#else + error = dbm_store(xhv->xhv_dbm, dkey, dcontent, DBM_REPLACE); +#endif + if (error) { + if (errno == EPERM) + fatal("No write permission to dbm file"); + fatal("dbm store returned %d, errno %d, key \"%s\"",error,errno,key); +#ifdef HAS_NDBM + dbm_clearerr(xhv->xhv_dbm); +#endif + } + return !error; +} +#endif /* SOME_DBM */ + +#ifdef XXX + magictype = MgTYPE(magic); + switch (magictype) { + case 'E': + environ[0] = Nullch; + break; + case 'S': +#ifndef NSIG +#define NSIG 32 +#endif + for (i = 1; i < NSIG; i++) + signal(i, SIG_DFL); /* crunch, crunch, crunch */ + break; + } + + if (magic) { + sv_magic(tmpstr, (SV*)tmpgv, magic, tmps, SvCUR(sv)); + sv_magicset(tmpstr, magic); + } + + if (hv->hv_sv.sv_rare && !sv->sv_magic) + sv_magic(sv, (GV*)hv, hv->hv_sv.sv_rare, key, keylen); +#endif + +void +hv_magic(hv, gv, how) +HV* hv; +GV* gv; +I32 how; +{ + sv_magic(hv, gv, how, 0, 0); +} |