13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
43e12c5d1SDavid du Colombier #include "ndb.h"
53e12c5d1SDavid du Colombier #include "ndbhf.h"
63e12c5d1SDavid du Colombier
73e12c5d1SDavid du Colombier enum {
83e12c5d1SDavid du Colombier Dptr, /* pointer to database file */
93e12c5d1SDavid du Colombier Cptr, /* pointer to first chain entry */
103e12c5d1SDavid du Colombier Cptr1, /* pointer to second chain entry */
113e12c5d1SDavid du Colombier };
123e12c5d1SDavid du Colombier
133e12c5d1SDavid du Colombier /*
143e12c5d1SDavid du Colombier * generate a hash value for an ascii string (val) given
153e12c5d1SDavid du Colombier * a hash table length (hlen)
163e12c5d1SDavid du Colombier */
173e12c5d1SDavid du Colombier ulong
ndbhash(char * vp,int hlen)183e12c5d1SDavid du Colombier ndbhash(char *vp, int hlen)
193e12c5d1SDavid du Colombier {
203e12c5d1SDavid du Colombier ulong hash;
213e12c5d1SDavid du Colombier uchar *val = (uchar*)vp;
223e12c5d1SDavid du Colombier
233e12c5d1SDavid du Colombier for(hash = 0; *val; val++)
243e12c5d1SDavid du Colombier hash = (hash*13) + *val-'a';
253e12c5d1SDavid du Colombier return hash % hlen;
263e12c5d1SDavid du Colombier }
273e12c5d1SDavid du Colombier
283e12c5d1SDavid du Colombier /*
293e12c5d1SDavid du Colombier * read a hash file with buffering
303e12c5d1SDavid du Colombier */
313e12c5d1SDavid du Colombier static uchar*
hfread(Ndbhf * hf,long off,int len)323e12c5d1SDavid du Colombier hfread(Ndbhf *hf, long off, int len)
333e12c5d1SDavid du Colombier {
343e12c5d1SDavid du Colombier if(off < hf->off || off + len > hf->off + hf->len){
353e12c5d1SDavid du Colombier if(seek(hf->fd, off, 0) < 0
363e12c5d1SDavid du Colombier || (hf->len = read(hf->fd, hf->buf, sizeof(hf->buf))) < len){
373e12c5d1SDavid du Colombier hf->off = -1;
383e12c5d1SDavid du Colombier return 0;
393e12c5d1SDavid du Colombier }
403e12c5d1SDavid du Colombier hf->off = off;
413e12c5d1SDavid du Colombier }
423e12c5d1SDavid du Colombier return &hf->buf[off-hf->off];
433e12c5d1SDavid du Colombier }
443e12c5d1SDavid du Colombier
453e12c5d1SDavid du Colombier /*
463e12c5d1SDavid du Colombier * return an opened hash file if one exists for the
473e12c5d1SDavid du Colombier * attribute and if it is current vis-a-vis the data
483e12c5d1SDavid du Colombier * base file
493e12c5d1SDavid du Colombier */
503e12c5d1SDavid du Colombier static Ndbhf*
hfopen(Ndb * db,char * attr)513e12c5d1SDavid du Colombier hfopen(Ndb *db, char *attr)
523e12c5d1SDavid du Colombier {
533e12c5d1SDavid du Colombier Ndbhf *hf;
543e12c5d1SDavid du Colombier char buf[sizeof(hf->attr)+sizeof(db->file)+2];
553e12c5d1SDavid du Colombier uchar *p;
569a747e4fSDavid du Colombier Dir *d;
573e12c5d1SDavid du Colombier
583e12c5d1SDavid du Colombier /* try opening the data base if it's closed */
593e12c5d1SDavid du Colombier if(db->mtime==0 && ndbreopen(db) < 0)
603e12c5d1SDavid du Colombier return 0;
613e12c5d1SDavid du Colombier
623e12c5d1SDavid du Colombier /* if the database has changed, throw out hash files and reopen db */
639a747e4fSDavid du Colombier if((d = dirfstat(Bfildes(&db->b))) == nil || db->qid.path != d->qid.path
649a747e4fSDavid du Colombier || db->qid.vers != d->qid.vers){
659a747e4fSDavid du Colombier if(ndbreopen(db) < 0){
669a747e4fSDavid du Colombier free(d);
673e12c5d1SDavid du Colombier return 0;
687dd7cddfSDavid du Colombier }
699a747e4fSDavid du Colombier }
709a747e4fSDavid du Colombier free(d);
717dd7cddfSDavid du Colombier
727dd7cddfSDavid du Colombier if(db->nohash)
737dd7cddfSDavid du Colombier return 0;
743e12c5d1SDavid du Colombier
753e12c5d1SDavid du Colombier /* see if a hash file exists for this attribute */
763e12c5d1SDavid du Colombier for(hf = db->hf; hf; hf= hf->next){
773e12c5d1SDavid du Colombier if(strcmp(hf->attr, attr) == 0)
783e12c5d1SDavid du Colombier return hf;
793e12c5d1SDavid du Colombier }
803e12c5d1SDavid du Colombier
813e12c5d1SDavid du Colombier /* create a new one */
823e12c5d1SDavid du Colombier hf = (Ndbhf*)malloc(sizeof(Ndbhf));
833e12c5d1SDavid du Colombier if(hf == 0)
843e12c5d1SDavid du Colombier return 0;
857dd7cddfSDavid du Colombier memset(hf, 0, sizeof(Ndbhf));
863e12c5d1SDavid du Colombier
873e12c5d1SDavid du Colombier /* compare it to the database file */
883e12c5d1SDavid du Colombier strncpy(hf->attr, attr, sizeof(hf->attr)-1);
893e12c5d1SDavid du Colombier sprint(buf, "%s.%s", db->file, hf->attr);
903e12c5d1SDavid du Colombier hf->fd = open(buf, OREAD);
913e12c5d1SDavid du Colombier if(hf->fd >= 0){
923e12c5d1SDavid du Colombier hf->len = 0;
933e12c5d1SDavid du Colombier hf->off = 0;
943e12c5d1SDavid du Colombier p = hfread(hf, 0, 2*NDBULLEN);
953e12c5d1SDavid du Colombier if(p){
963e12c5d1SDavid du Colombier hf->dbmtime = NDBGETUL(p);
973e12c5d1SDavid du Colombier hf->hlen = NDBGETUL(p+NDBULLEN);
983e12c5d1SDavid du Colombier if(hf->dbmtime == db->mtime){
993e12c5d1SDavid du Colombier hf->next = db->hf;
1003e12c5d1SDavid du Colombier db->hf = hf;
1013e12c5d1SDavid du Colombier return hf;
1023e12c5d1SDavid du Colombier }
1033e12c5d1SDavid du Colombier }
1043e12c5d1SDavid du Colombier close(hf->fd);
1053e12c5d1SDavid du Colombier }
106219b2ee8SDavid du Colombier
1073e12c5d1SDavid du Colombier free(hf);
1083e12c5d1SDavid du Colombier return 0;
1093e12c5d1SDavid du Colombier }
1103e12c5d1SDavid du Colombier
1113e12c5d1SDavid du Colombier /*
1123e12c5d1SDavid du Colombier * return the first matching entry
1133e12c5d1SDavid du Colombier */
1143e12c5d1SDavid du Colombier Ndbtuple*
ndbsearch(Ndb * db,Ndbs * s,char * attr,char * val)1153e12c5d1SDavid du Colombier ndbsearch(Ndb *db, Ndbs *s, char *attr, char *val)
1163e12c5d1SDavid du Colombier {
1173e12c5d1SDavid du Colombier uchar *p;
1189a747e4fSDavid du Colombier Ndbtuple *t;
1195d459b5aSDavid du Colombier Ndbhf *hf;
1205d459b5aSDavid du Colombier
1215d459b5aSDavid du Colombier hf = hfopen(db, attr);
1223e12c5d1SDavid du Colombier
123219b2ee8SDavid du Colombier memset(s, 0, sizeof(*s));
1249a747e4fSDavid du Colombier if(_ndbcachesearch(db, s, attr, val, &t) == 0){
1259a747e4fSDavid du Colombier /* found in cache */
126*1a4050f5SDavid du Colombier if(t != nil){
127*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&db));
1289a747e4fSDavid du Colombier return t; /* answer from this file */
129*1a4050f5SDavid du Colombier }
1309a747e4fSDavid du Colombier if(db->next == nil)
1319a747e4fSDavid du Colombier return nil;
132*1a4050f5SDavid du Colombier t = ndbsearch(db->next, s, attr, val);
133*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&db));
134*1a4050f5SDavid du Colombier return t;
1359a747e4fSDavid du Colombier }
1369a747e4fSDavid du Colombier
1379a747e4fSDavid du Colombier s->db = db;
1385d459b5aSDavid du Colombier s->hf = hf;
1393e12c5d1SDavid du Colombier if(s->hf){
1403e12c5d1SDavid du Colombier s->ptr = ndbhash(val, s->hf->hlen)*NDBPLEN;
1413e12c5d1SDavid du Colombier p = hfread(s->hf, s->ptr+NDBHLEN, NDBPLEN);
142*1a4050f5SDavid du Colombier if(p == 0){
143*1a4050f5SDavid du Colombier t = _ndbcacheadd(db, s, attr, val, nil);
144*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&db));
145*1a4050f5SDavid du Colombier return t;
146*1a4050f5SDavid du Colombier }
1473e12c5d1SDavid du Colombier s->ptr = NDBGETP(p);
1483e12c5d1SDavid du Colombier s->type = Cptr1;
149219b2ee8SDavid du Colombier } else if(db->length > 128*1024){
150219b2ee8SDavid du Colombier print("Missing or out of date hash file %s.%s.\n", db->file, attr);
151219b2ee8SDavid du Colombier syslog(0, "ndb", "Missing or out of date hash file %s.%s.", db->file, attr);
152219b2ee8SDavid du Colombier
153219b2ee8SDavid du Colombier /* advance search to next db file */
154219b2ee8SDavid du Colombier s->ptr = NDBNAP;
1559a747e4fSDavid du Colombier _ndbcacheadd(db, s, attr, val, nil);
156219b2ee8SDavid du Colombier if(db->next == 0)
1579a747e4fSDavid du Colombier return nil;
158*1a4050f5SDavid du Colombier t = ndbsearch(db->next, s, attr, val);
159*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&db));
160*1a4050f5SDavid du Colombier return t;
1613e12c5d1SDavid du Colombier } else {
1623e12c5d1SDavid du Colombier s->ptr = 0;
1633e12c5d1SDavid du Colombier s->type = Dptr;
1643e12c5d1SDavid du Colombier }
1659a747e4fSDavid du Colombier t = ndbsnext(s, attr, val);
1669a747e4fSDavid du Colombier _ndbcacheadd(db, s, attr, val, (t != nil && s->db == db)?t:nil);
167*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&db));
1689a747e4fSDavid du Colombier return t;
1693e12c5d1SDavid du Colombier }
1703e12c5d1SDavid du Colombier
1713e12c5d1SDavid du Colombier static Ndbtuple*
match(Ndbtuple * t,char * attr,char * val)1723e12c5d1SDavid du Colombier match(Ndbtuple *t, char *attr, char *val)
1733e12c5d1SDavid du Colombier {
1743e12c5d1SDavid du Colombier Ndbtuple *nt;
1753e12c5d1SDavid du Colombier
1763e12c5d1SDavid du Colombier for(nt = t; nt; nt = nt->entry)
1773e12c5d1SDavid du Colombier if(strcmp(attr, nt->attr) == 0
1783e12c5d1SDavid du Colombier && strcmp(val, nt->val) == 0)
1793e12c5d1SDavid du Colombier return nt;
1803e12c5d1SDavid du Colombier return 0;
1813e12c5d1SDavid du Colombier }
1823e12c5d1SDavid du Colombier
1833e12c5d1SDavid du Colombier /*
1843e12c5d1SDavid du Colombier * return the next matching entry in the hash chain
1853e12c5d1SDavid du Colombier */
1863e12c5d1SDavid du Colombier Ndbtuple*
ndbsnext(Ndbs * s,char * attr,char * val)1873e12c5d1SDavid du Colombier ndbsnext(Ndbs *s, char *attr, char *val)
1883e12c5d1SDavid du Colombier {
1893e12c5d1SDavid du Colombier Ndbtuple *t;
1903e12c5d1SDavid du Colombier Ndb *db;
1913e12c5d1SDavid du Colombier uchar *p;
1923e12c5d1SDavid du Colombier
1933e12c5d1SDavid du Colombier db = s->db;
1943e12c5d1SDavid du Colombier if(s->ptr == NDBNAP)
1953e12c5d1SDavid du Colombier goto nextfile;
1963e12c5d1SDavid du Colombier
1973e12c5d1SDavid du Colombier for(;;){
1983e12c5d1SDavid du Colombier if(s->type == Dptr){
199219b2ee8SDavid du Colombier if(Bseek(&db->b, s->ptr, 0) < 0)
2003e12c5d1SDavid du Colombier break;
2013e12c5d1SDavid du Colombier t = ndbparse(db);
202219b2ee8SDavid du Colombier s->ptr = Boffset(&db->b);
2033e12c5d1SDavid du Colombier if(t == 0)
2043e12c5d1SDavid du Colombier break;
205*1a4050f5SDavid du Colombier if(s->t = match(t, attr, val)){
206*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&s));
2073e12c5d1SDavid du Colombier return t;
208*1a4050f5SDavid du Colombier }
2093e12c5d1SDavid du Colombier ndbfree(t);
2103e12c5d1SDavid du Colombier } else if(s->type == Cptr){
211219b2ee8SDavid du Colombier if(Bseek(&db->b, s->ptr, 0) < 0)
2123e12c5d1SDavid du Colombier break;
2133e12c5d1SDavid du Colombier s->ptr = s->ptr1;
2143e12c5d1SDavid du Colombier s->type = Cptr1;
2153e12c5d1SDavid du Colombier t = ndbparse(db);
2163e12c5d1SDavid du Colombier if(t == 0)
2173e12c5d1SDavid du Colombier break;
218*1a4050f5SDavid du Colombier if(s->t = match(t, attr, val)){
219*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&s));
2203e12c5d1SDavid du Colombier return t;
221*1a4050f5SDavid du Colombier }
2223e12c5d1SDavid du Colombier ndbfree(t);
2233e12c5d1SDavid du Colombier } else if(s->type == Cptr1){
2243e12c5d1SDavid du Colombier if(s->ptr & NDBCHAIN){ /* hash chain continuation */
2253e12c5d1SDavid du Colombier s->ptr &= ~NDBCHAIN;
2263e12c5d1SDavid du Colombier p = hfread(s->hf, s->ptr+NDBHLEN, 2*NDBPLEN);
2273e12c5d1SDavid du Colombier if(p == 0)
2283e12c5d1SDavid du Colombier break;
2293e12c5d1SDavid du Colombier s->ptr = NDBGETP(p);
2303e12c5d1SDavid du Colombier s->ptr1 = NDBGETP(p+NDBPLEN);
2313e12c5d1SDavid du Colombier s->type = Cptr;
2323e12c5d1SDavid du Colombier } else { /* end of hash chain */
233219b2ee8SDavid du Colombier if(Bseek(&db->b, s->ptr, 0) < 0)
2343e12c5d1SDavid du Colombier break;
2353e12c5d1SDavid du Colombier s->ptr = NDBNAP;
2363e12c5d1SDavid du Colombier t = ndbparse(db);
2373e12c5d1SDavid du Colombier if(t == 0)
2383e12c5d1SDavid du Colombier break;
239a9f680aeSDavid du Colombier if(s->t = match(t, attr, val)){
240*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&s));
2413e12c5d1SDavid du Colombier return t;
242a9f680aeSDavid du Colombier }
2433e12c5d1SDavid du Colombier ndbfree(t);
2443e12c5d1SDavid du Colombier break;
2453e12c5d1SDavid du Colombier }
2463e12c5d1SDavid du Colombier }
2473e12c5d1SDavid du Colombier }
2483e12c5d1SDavid du Colombier
2493e12c5d1SDavid du Colombier nextfile:
2503e12c5d1SDavid du Colombier
2513e12c5d1SDavid du Colombier /* nothing left to search? */
2523e12c5d1SDavid du Colombier s->ptr = NDBNAP;
2533e12c5d1SDavid du Colombier if(db->next == 0)
2543e12c5d1SDavid du Colombier return 0;
2553e12c5d1SDavid du Colombier
2563e12c5d1SDavid du Colombier /* advance search to next db file */
257a9f680aeSDavid du Colombier t = ndbsearch(db->next, s, attr, val);
258*1a4050f5SDavid du Colombier ndbsetmalloctag(t, getcallerpc(&s));
259a9f680aeSDavid du Colombier return t;
2603e12c5d1SDavid du Colombier }
261