Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>
#include "hash.h"

/***
 * String hash tables.
 */

Stringtab *tfree;

Stringtab*
taballoc(void)
{
        static Stringtab *t;
        static uint nt;

        if(tfree){
                Stringtab *tt = tfree;
                tfree = tt->link;
                return tt;
        }

        if(nt == 0){
                t = malloc(64000*sizeof(Stringtab));
                if(t == 0)
                        sysfatal("out of memory");
                nt = 64000;
        }
        nt--;
        return t++;
}

void
tabfree(Stringtab *tt)
{
        tt->link = tfree;
        tfree = tt;
}

char*
xstrdup(char *s, int len)
{
        char *r;
        static char *t;
        static int nt;

        if(nt < len){
                t = malloc(512*1024+len);
                if(t == 0)
                        sysfatal("out of memory");
                nt = 512*1024;
        }
        r = t;
        t += len;
        nt -= len;
        memmove(r, s, len);
        return r;
}

static uint
hash(char *s, int n)
{
        uint h;
        uchar *p, *ep;
        h = 0;
        for(p=(uchar*)s, ep=p+n; p<ep; p++)
                h = h*37 + *p;
        return h;
}

static void
rehash(Hash *hh)
{
        int h;
        Stringtab *s;

        if(hh->nstab == 0)
                hh->nstab = 1024;
        else
                hh->nstab = hh->ntab*2;

        free(hh->stab);
        hh->stab = mallocz(hh->nstab*sizeof(Stringtab*), 1);
        if(hh->stab == nil)
                sysfatal("out of memory");

        for(s=hh->all; s; s=s->link){
                h = hash(s->str, s->n) % hh->nstab;
                s->hash = hh->stab[h];
                hh->stab[h] = s;
        }
}

Stringtab*
findstab(Hash *hh, char *str, int n, int create)
{
        uint h;
        Stringtab *tab, **l;

        if(hh->nstab == 0)
                rehash(hh);

        h = hash(str, n) % hh->nstab;
        for(tab=hh->stab[h], l=&hh->stab[h]; tab; l=&tab->hash, tab=tab->hash)
                if(n==tab->n && memcmp(str, tab->str, n) == 0){
                        *l = tab->hash;
                        tab->hash = hh->stab[h];
                        hh->stab[h] = tab;
                        return tab;
                }

        if(!create)
                return nil;

        hh->sorted = 0;
        tab = taballoc();
        tab->str = xstrdup(str, n);
        tab->hash = hh->stab[h];
        tab->link = hh->all;
        hh->all = tab;
        tab->n = n;
        tab->count = 0;
        tab->date = 0;
        hh->stab[h] = tab;

        hh->ntab++;
        if(hh->ntab > 2*hh->nstab && !(hh->ntab&(hh->ntab-1)))
                rehash(hh);
        return tab;
}

int
scmp(Stringtab *a, Stringtab *b)
{
        int n, x;

        if(a == 0)
                return 1;
        if(b == 0)
                return -1;
        n = a->n;
        if(n > b->n)
                n = b->n;
        x = memcmp(a->str, b->str, n);
        if(x != 0)
                return x;
        if(a->n < b->n)
                return -1;
        if(a->n > b->n)
                return 1;
        return 0;       /* shouldn't happen */
}

Stringtab*
merge(Stringtab *a, Stringtab *b)
{
        Stringtab *s, **l;

        l = &s;
        while(a || b){
                if(scmp(a, b) < 0){
                        *l = a;
                        l = &a->link;
                        a = a->link;
                }else{
                        *l = b;
                        l = &b->link;
                        b = b->link;
                }
        }
        *l = 0;
        return s;
}

Stringtab*
mergesort(Stringtab *s)
{
        Stringtab *a, *b;
        int delay;

        if(s == nil)
                return nil;
        if(s->link == nil)
                return s;

        a = b = s;
        delay = 1;
        while(a && b){
                if(delay)       /* easy way to handle 2-element list */
                        delay = 0;
                else
                        a = a->link;
                if(b = b->link)
                        b = b->link;
        }

        b = a->link;
        a->link = nil;

        a = mergesort(s);
        b = mergesort(b);

        return merge(a, b);
}

Stringtab*
sortstab(Hash *hh)
{
        if(!hh->sorted){
                hh->all = mergesort(hh->all);
                hh->sorted = 1;
        }
        return hh->all;
}

int
Bwritehash(Biobuf *b, Hash *hh)
{
        Stringtab *s;
        int now;

        now = time(0);
        s = sortstab(hh);
        Bprint(b, "# hash table\n");
        for(; s; s=s->link){
                if(s->count <= 0)
                        continue;
                /*
                 * Entries that haven't been updated in thirty days get tossed.
                 */
                if(s->date+30*86400 < now)
                        continue;
                Bwrite(b, s->str, s->n);
                Bprint(b, "\t%d %d\n", s->count, s->date);
        }
        if(Bflush(b) == Beof)
                return -1;
        return 0;
}

void
Breadhash(Biobuf *b, Hash *hh, int scale)
{
        char *s;
        char *t;
        int n;
        int date;
        Stringtab *st;

        s = Brdstr(b, '\n', 1);
        if(s == nil)
                return;
        if(strcmp(s, "# hash table") != 0)
                sysfatal("bad hash table format");

        while(s = Brdline(b, '\n')){
                s[Blinelen(b)-1] = 0;
                t = strrchr(s, '\t');
                if(t == nil)
                        sysfatal("bad hash table format");
                *t++ = '\0';
                if(*t < '0' || *t > '9')
                        sysfatal("bad hash table format");
                n = strtol(t, &t, 10);
                date = time(0);
                if(*t != 0){
                        if(*t == ' '){
                                t++;
                                date = strtol(t, &t, 10);
                        }
                        if(*t != 0)
                                sysfatal("bad hash table format");
                }
                st = findstab(hh, s, strlen(s), 1);
                if(date > st->date)
                        st->date = date;
                st->count += n*scale;
        }
}

void
freehash(Hash *h)
{
        Stringtab *s, *next;

        for(s=h->all; s; s=next){
                next = s->link;
                tabfree(s);
        }
        free(h->stab);
        free(h);
}

Biobuf*
Bopenlock(char *file, int mode)
{
        int i;
        Biobuf *b;
        char err[ERRMAX];

        b = nil;
        for(i=0; i<120; i++){
                if((b = Bopen(file, mode)) != nil)
                        break;
                rerrstr(err, sizeof err);
                if(strstr(err, "file is locked")==nil && strstr(err, "exclusive lock")==nil)
                        break;
                sleep(1000);
        }
        return b;
}