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