blob: 3fb994455ad323fa5e18bff25b65e8eca6edd7e7 [file] [log] [blame]
/* $Header$
*
* Copyright (c) 1989, Larry Wall
*
* You may distribute under the terms of the GNU General Public License
* as specified in the README file that comes with the perl 3.0 kit.
*
* $Log$
* Revision 1.2 2004/09/01 14:33:24 criswell
* Migrating test suite out of the source tree.
*
* Revision 1.1 2004/02/17 22:21:16 criswell
* Initial commit of the perl Malloc Benchmark. I've cheated a little by
* generating the yacc output files and committing them directly, but it was
* easier than disabling the Bison Voodoo that gets executed by default.
*
* Revision 4.0 91/03/20 01:22:26 lwall
* 4.0 baseline.
*
*/
#include "EXTERN.h"
#include "perl.h"
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;
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;
}
bool
hstore(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)
;
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;
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;
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;
str = str_mortal(entry->hent_val);
hentfree(entry);
if (i)
tb->tbl_fill--;
#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
}
hsplit(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;
Renew(a, newsize, HENT*);
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;
}
void
hentfree(hent)
register HENT *hent;
{
if (!hent)
return;
str_free(hent->hent_val);
Safefree(hent->hent_key);
Safefree(hent);
}
void
hentdelayfree(hent)
register HENT *hent;
{
if (!hent)
return;
str_2mortal(hent->hent_val); /* free between statements */
Safefree(hent->hent_key);
Safefree(hent);
}
void
hclear(tb,dodbm)
register HASH *tb;
int dodbm;
{
if (!tb)
return;
hfreeentries(tb,dodbm);
tb->tbl_fill = 0;
#ifndef lint
if (tb->tbl_array)
(void)bzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));
#endif
}
static void
hfreeentries(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);
while (hent = hiternext(tb)) { /* concise but not very efficient */
hentfree(ohent);
ohent = hent;
}
hentfree(ohent);
#ifdef SOME_DBM
tb->tbl_dbm = old_dbm;
#endif
}
void
hfree(tb,dodbm)
register HASH *tb;
int dodbm;
{
if (!tb)
return;
hfreeentries(tb,dodbm);
Safefree(tb->tbl_array);
Safefree(tb);
}
int
hiterinit(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_ODBM
static int dbmrefcnt = 0;
#endif
bool
hdbmopen(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;
}
void
hdbmclose(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");
}
bool
hdbmstore(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 */