xref: /plan9/sys/src/cmd/upas/bayes/hash.c (revision 2b7fd5ad60cbf9e248990b50b4a1bb48558eb326)
14246b616SDavid du Colombier #include <u.h>
24246b616SDavid du Colombier #include <libc.h>
34246b616SDavid du Colombier #include <bio.h>
44246b616SDavid du Colombier #include "hash.h"
54246b616SDavid du Colombier 
64246b616SDavid du Colombier /***
74246b616SDavid du Colombier  * String hash tables.
84246b616SDavid du Colombier  */
94246b616SDavid du Colombier 
104246b616SDavid du Colombier Stringtab *tfree;
114246b616SDavid du Colombier 
124246b616SDavid du Colombier Stringtab*
taballoc(void)134246b616SDavid du Colombier taballoc(void)
144246b616SDavid du Colombier {
154246b616SDavid du Colombier 	static Stringtab *t;
164246b616SDavid du Colombier 	static uint nt;
174246b616SDavid du Colombier 
184246b616SDavid du Colombier 	if(tfree){
194246b616SDavid du Colombier 		Stringtab *tt = tfree;
204246b616SDavid du Colombier 		tfree = tt->link;
214246b616SDavid du Colombier 		return tt;
224246b616SDavid du Colombier 	}
234246b616SDavid du Colombier 
244246b616SDavid du Colombier 	if(nt == 0){
254246b616SDavid du Colombier 		t = malloc(64000*sizeof(Stringtab));
264246b616SDavid du Colombier 		if(t == 0)
274246b616SDavid du Colombier 			sysfatal("out of memory");
284246b616SDavid du Colombier 		nt = 64000;
294246b616SDavid du Colombier 	}
304246b616SDavid du Colombier 	nt--;
314246b616SDavid du Colombier 	return t++;
324246b616SDavid du Colombier }
334246b616SDavid du Colombier 
344246b616SDavid du Colombier void
tabfree(Stringtab * tt)354246b616SDavid du Colombier tabfree(Stringtab *tt)
364246b616SDavid du Colombier {
374246b616SDavid du Colombier 	tt->link = tfree;
384246b616SDavid du Colombier 	tfree = tt;
394246b616SDavid du Colombier }
404246b616SDavid du Colombier 
414246b616SDavid du Colombier char*
xstrdup(char * s,int len)424246b616SDavid du Colombier xstrdup(char *s, int len)
434246b616SDavid du Colombier {
444246b616SDavid du Colombier 	char *r;
454246b616SDavid du Colombier 	static char *t;
464246b616SDavid du Colombier 	static int nt;
474246b616SDavid du Colombier 
484246b616SDavid du Colombier 	if(nt < len){
494246b616SDavid du Colombier 		t = malloc(512*1024+len);
504246b616SDavid du Colombier 		if(t == 0)
514246b616SDavid du Colombier 			sysfatal("out of memory");
524246b616SDavid du Colombier 		nt = 512*1024;
534246b616SDavid du Colombier 	}
544246b616SDavid du Colombier 	r = t;
554246b616SDavid du Colombier 	t += len;
564246b616SDavid du Colombier 	nt -= len;
574246b616SDavid du Colombier 	memmove(r, s, len);
584246b616SDavid du Colombier 	return r;
594246b616SDavid du Colombier }
604246b616SDavid du Colombier 
614246b616SDavid du Colombier static uint
hash(char * s,int n)624246b616SDavid du Colombier hash(char *s, int n)
634246b616SDavid du Colombier {
644246b616SDavid du Colombier 	uint h;
654246b616SDavid du Colombier 	uchar *p, *ep;
664246b616SDavid du Colombier 	h = 0;
674246b616SDavid du Colombier 	for(p=(uchar*)s, ep=p+n; p<ep; p++)
684246b616SDavid du Colombier 		h = h*37 + *p;
694246b616SDavid du Colombier 	return h;
704246b616SDavid du Colombier }
714246b616SDavid du Colombier 
724246b616SDavid du Colombier static void
rehash(Hash * hh)734246b616SDavid du Colombier rehash(Hash *hh)
744246b616SDavid du Colombier {
754246b616SDavid du Colombier 	int h;
764246b616SDavid du Colombier 	Stringtab *s;
774246b616SDavid du Colombier 
784246b616SDavid du Colombier 	if(hh->nstab == 0)
794246b616SDavid du Colombier 		hh->nstab = 1024;
804246b616SDavid du Colombier 	else
814246b616SDavid du Colombier 		hh->nstab = hh->ntab*2;
824246b616SDavid du Colombier 
834246b616SDavid du Colombier 	free(hh->stab);
844246b616SDavid du Colombier 	hh->stab = mallocz(hh->nstab*sizeof(Stringtab*), 1);
854246b616SDavid du Colombier 	if(hh->stab == nil)
864246b616SDavid du Colombier 		sysfatal("out of memory");
874246b616SDavid du Colombier 
884246b616SDavid du Colombier 	for(s=hh->all; s; s=s->link){
894246b616SDavid du Colombier 		h = hash(s->str, s->n) % hh->nstab;
904246b616SDavid du Colombier 		s->hash = hh->stab[h];
914246b616SDavid du Colombier 		hh->stab[h] = s;
924246b616SDavid du Colombier 	}
934246b616SDavid du Colombier }
944246b616SDavid du Colombier 
954246b616SDavid du Colombier Stringtab*
findstab(Hash * hh,char * str,int n,int create)964246b616SDavid du Colombier findstab(Hash *hh, char *str, int n, int create)
974246b616SDavid du Colombier {
984246b616SDavid du Colombier 	uint h;
994246b616SDavid du Colombier 	Stringtab *tab, **l;
1004246b616SDavid du Colombier 
1014246b616SDavid du Colombier 	if(hh->nstab == 0)
1024246b616SDavid du Colombier 		rehash(hh);
1034246b616SDavid du Colombier 
1044246b616SDavid du Colombier 	h = hash(str, n) % hh->nstab;
1054246b616SDavid du Colombier 	for(tab=hh->stab[h], l=&hh->stab[h]; tab; l=&tab->hash, tab=tab->hash)
1064246b616SDavid du Colombier 		if(n==tab->n && memcmp(str, tab->str, n) == 0){
1074246b616SDavid du Colombier 			*l = tab->hash;
1084246b616SDavid du Colombier 			tab->hash = hh->stab[h];
1094246b616SDavid du Colombier 			hh->stab[h] = tab;
1104246b616SDavid du Colombier 			return tab;
1114246b616SDavid du Colombier 		}
1124246b616SDavid du Colombier 
1134246b616SDavid du Colombier 	if(!create)
1144246b616SDavid du Colombier 		return nil;
1154246b616SDavid du Colombier 
1164246b616SDavid du Colombier 	hh->sorted = 0;
1174246b616SDavid du Colombier 	tab = taballoc();
1184246b616SDavid du Colombier 	tab->str = xstrdup(str, n);
1194246b616SDavid du Colombier 	tab->hash = hh->stab[h];
1204246b616SDavid du Colombier 	tab->link = hh->all;
1214246b616SDavid du Colombier 	hh->all = tab;
1224246b616SDavid du Colombier 	tab->n = n;
1234246b616SDavid du Colombier 	tab->count = 0;
124*2b7fd5adSDavid du Colombier 	tab->date = 0;
1254246b616SDavid du Colombier 	hh->stab[h] = tab;
1264246b616SDavid du Colombier 
1274246b616SDavid du Colombier 	hh->ntab++;
1284246b616SDavid du Colombier 	if(hh->ntab > 2*hh->nstab && !(hh->ntab&(hh->ntab-1)))
1294246b616SDavid du Colombier 		rehash(hh);
1304246b616SDavid du Colombier 	return tab;
1314246b616SDavid du Colombier }
1324246b616SDavid du Colombier 
1334246b616SDavid du Colombier int
scmp(Stringtab * a,Stringtab * b)1344246b616SDavid du Colombier scmp(Stringtab *a, Stringtab *b)
1354246b616SDavid du Colombier {
1364246b616SDavid du Colombier 	int n, x;
1374246b616SDavid du Colombier 
1384246b616SDavid du Colombier 	if(a == 0)
1394246b616SDavid du Colombier 		return 1;
1404246b616SDavid du Colombier 	if(b == 0)
1414246b616SDavid du Colombier 		return -1;
1424246b616SDavid du Colombier 	n = a->n;
1434246b616SDavid du Colombier 	if(n > b->n)
1444246b616SDavid du Colombier 		n = b->n;
1454246b616SDavid du Colombier 	x = memcmp(a->str, b->str, n);
1464246b616SDavid du Colombier 	if(x != 0)
1474246b616SDavid du Colombier 		return x;
1484246b616SDavid du Colombier 	if(a->n < b->n)
1494246b616SDavid du Colombier 		return -1;
1504246b616SDavid du Colombier 	if(a->n > b->n)
1514246b616SDavid du Colombier 		return 1;
1524246b616SDavid du Colombier 	return 0;	/* shouldn't happen */
1534246b616SDavid du Colombier }
1544246b616SDavid du Colombier 
1554246b616SDavid du Colombier Stringtab*
merge(Stringtab * a,Stringtab * b)1564246b616SDavid du Colombier merge(Stringtab *a, Stringtab *b)
1574246b616SDavid du Colombier {
1584246b616SDavid du Colombier 	Stringtab *s, **l;
1594246b616SDavid du Colombier 
1604246b616SDavid du Colombier 	l = &s;
1614246b616SDavid du Colombier 	while(a || b){
1624246b616SDavid du Colombier 		if(scmp(a, b) < 0){
1634246b616SDavid du Colombier 			*l = a;
1644246b616SDavid du Colombier 			l = &a->link;
1654246b616SDavid du Colombier 			a = a->link;
1664246b616SDavid du Colombier 		}else{
1674246b616SDavid du Colombier 			*l = b;
1684246b616SDavid du Colombier 			l = &b->link;
1694246b616SDavid du Colombier 			b = b->link;
1704246b616SDavid du Colombier 		}
1714246b616SDavid du Colombier 	}
1724246b616SDavid du Colombier 	*l = 0;
1734246b616SDavid du Colombier 	return s;
1744246b616SDavid du Colombier }
1754246b616SDavid du Colombier 
1764246b616SDavid du Colombier Stringtab*
mergesort(Stringtab * s)1774246b616SDavid du Colombier mergesort(Stringtab *s)
1784246b616SDavid du Colombier {
1794246b616SDavid du Colombier 	Stringtab *a, *b;
1804246b616SDavid du Colombier 	int delay;
1814246b616SDavid du Colombier 
1824246b616SDavid du Colombier 	if(s == nil)
1834246b616SDavid du Colombier 		return nil;
1844246b616SDavid du Colombier 	if(s->link == nil)
1854246b616SDavid du Colombier 		return s;
1864246b616SDavid du Colombier 
1874246b616SDavid du Colombier 	a = b = s;
1884246b616SDavid du Colombier 	delay = 1;
1894246b616SDavid du Colombier 	while(a && b){
1904246b616SDavid du Colombier 		if(delay)	/* easy way to handle 2-element list */
1914246b616SDavid du Colombier 			delay = 0;
1924246b616SDavid du Colombier 		else
1934246b616SDavid du Colombier 			a = a->link;
1944246b616SDavid du Colombier 		if(b = b->link)
1954246b616SDavid du Colombier 			b = b->link;
1964246b616SDavid du Colombier 	}
1974246b616SDavid du Colombier 
1984246b616SDavid du Colombier 	b = a->link;
1994246b616SDavid du Colombier 	a->link = nil;
2004246b616SDavid du Colombier 
2014246b616SDavid du Colombier 	a = mergesort(s);
2024246b616SDavid du Colombier 	b = mergesort(b);
2034246b616SDavid du Colombier 
2044246b616SDavid du Colombier 	return merge(a, b);
2054246b616SDavid du Colombier }
2064246b616SDavid du Colombier 
2074246b616SDavid du Colombier Stringtab*
sortstab(Hash * hh)2084246b616SDavid du Colombier sortstab(Hash *hh)
2094246b616SDavid du Colombier {
2104246b616SDavid du Colombier 	if(!hh->sorted){
2114246b616SDavid du Colombier 		hh->all = mergesort(hh->all);
2124246b616SDavid du Colombier 		hh->sorted = 1;
2134246b616SDavid du Colombier 	}
2144246b616SDavid du Colombier 	return hh->all;
2154246b616SDavid du Colombier }
2164246b616SDavid du Colombier 
2174246b616SDavid du Colombier int
Bwritehash(Biobuf * b,Hash * hh)2184246b616SDavid du Colombier Bwritehash(Biobuf *b, Hash *hh)
2194246b616SDavid du Colombier {
2204246b616SDavid du Colombier 	Stringtab *s;
221*2b7fd5adSDavid du Colombier 	int now;
2224246b616SDavid du Colombier 
223*2b7fd5adSDavid du Colombier 	now = time(0);
2244246b616SDavid du Colombier 	s = sortstab(hh);
2254246b616SDavid du Colombier 	Bprint(b, "# hash table\n");
2264246b616SDavid du Colombier 	for(; s; s=s->link){
2274246b616SDavid du Colombier 		if(s->count <= 0)
2284246b616SDavid du Colombier 			continue;
229*2b7fd5adSDavid du Colombier 		/*
230*2b7fd5adSDavid du Colombier 		 * Entries that haven't been updated in thirty days get tossed.
231*2b7fd5adSDavid du Colombier 		 */
232*2b7fd5adSDavid du Colombier 		if(s->date+30*86400 < now)
233*2b7fd5adSDavid du Colombier 			continue;
2344246b616SDavid du Colombier 		Bwrite(b, s->str, s->n);
235*2b7fd5adSDavid du Colombier 		Bprint(b, "\t%d %d\n", s->count, s->date);
2364246b616SDavid du Colombier 	}
2374246b616SDavid du Colombier 	if(Bflush(b) == Beof)
2384246b616SDavid du Colombier 		return -1;
2394246b616SDavid du Colombier 	return 0;
2404246b616SDavid du Colombier }
2414246b616SDavid du Colombier 
2424246b616SDavid du Colombier void
Breadhash(Biobuf * b,Hash * hh,int scale)2434246b616SDavid du Colombier Breadhash(Biobuf *b, Hash *hh, int scale)
2444246b616SDavid du Colombier {
2454246b616SDavid du Colombier 	char *s;
2464246b616SDavid du Colombier 	char *t;
2474246b616SDavid du Colombier 	int n;
248*2b7fd5adSDavid du Colombier 	int date;
249*2b7fd5adSDavid du Colombier 	Stringtab *st;
2504246b616SDavid du Colombier 
2514246b616SDavid du Colombier 	s = Brdstr(b, '\n', 1);
2524246b616SDavid du Colombier 	if(s == nil)
2534246b616SDavid du Colombier 		return;
2544246b616SDavid du Colombier 	if(strcmp(s, "# hash table") != 0)
2554246b616SDavid du Colombier 		sysfatal("bad hash table format");
2564246b616SDavid du Colombier 
2574246b616SDavid du Colombier 	while(s = Brdline(b, '\n')){
2584246b616SDavid du Colombier 		s[Blinelen(b)-1] = 0;
2594246b616SDavid du Colombier 		t = strrchr(s, '\t');
2604246b616SDavid du Colombier 		if(t == nil)
2614246b616SDavid du Colombier 			sysfatal("bad hash table format");
2624246b616SDavid du Colombier 		*t++ = '\0';
2634246b616SDavid du Colombier 		if(*t < '0' || *t > '9')
2644246b616SDavid du Colombier 			sysfatal("bad hash table format");
2654246b616SDavid du Colombier 		n = strtol(t, &t, 10);
266*2b7fd5adSDavid du Colombier 		date = time(0);
267*2b7fd5adSDavid du Colombier 		if(*t != 0){
268*2b7fd5adSDavid du Colombier 			if(*t == ' '){
269*2b7fd5adSDavid du Colombier 				t++;
270*2b7fd5adSDavid du Colombier 				date = strtol(t, &t, 10);
271*2b7fd5adSDavid du Colombier 			}
2724246b616SDavid du Colombier 			if(*t != 0)
2734246b616SDavid du Colombier 				sysfatal("bad hash table format");
274*2b7fd5adSDavid du Colombier 		}
275*2b7fd5adSDavid du Colombier 		st = findstab(hh, s, strlen(s), 1);
276*2b7fd5adSDavid du Colombier 		if(date > st->date)
277*2b7fd5adSDavid du Colombier 			st->date = date;
278*2b7fd5adSDavid du Colombier 		st->count += n*scale;
2794246b616SDavid du Colombier 	}
2804246b616SDavid du Colombier }
2814246b616SDavid du Colombier 
2824246b616SDavid du Colombier void
freehash(Hash * h)2834246b616SDavid du Colombier freehash(Hash *h)
2844246b616SDavid du Colombier {
2854246b616SDavid du Colombier 	Stringtab *s, *next;
2864246b616SDavid du Colombier 
2874246b616SDavid du Colombier 	for(s=h->all; s; s=next){
2884246b616SDavid du Colombier 		next = s->link;
2894246b616SDavid du Colombier 		tabfree(s);
2904246b616SDavid du Colombier 	}
2914246b616SDavid du Colombier 	free(h->stab);
2924246b616SDavid du Colombier 	free(h);
2934246b616SDavid du Colombier }
2944246b616SDavid du Colombier 
2954246b616SDavid du Colombier Biobuf*
Bopenlock(char * file,int mode)2964246b616SDavid du Colombier Bopenlock(char *file, int mode)
2974246b616SDavid du Colombier {
2984246b616SDavid du Colombier 	int i;
2994246b616SDavid du Colombier 	Biobuf *b;
3004246b616SDavid du Colombier 	char err[ERRMAX];
3014246b616SDavid du Colombier 
3024246b616SDavid du Colombier 	b = nil;
3034246b616SDavid du Colombier 	for(i=0; i<120; i++){
3044246b616SDavid du Colombier 		if((b = Bopen(file, mode)) != nil)
3054246b616SDavid du Colombier 			break;
3064246b616SDavid du Colombier 		rerrstr(err, sizeof err);
307375daca8SDavid du Colombier 		if(strstr(err, "file is locked")==nil && strstr(err, "exclusive lock")==nil)
3084246b616SDavid du Colombier 			break;
3094246b616SDavid du Colombier 		sleep(1000);
3104246b616SDavid du Colombier 	}
3114246b616SDavid du Colombier 	return b;
3124246b616SDavid du Colombier }
313