| #include "a.h" |
| |
| struct Cache |
| { |
| CEntry **hash; |
| int nhash; |
| CEntry *head; |
| CEntry *tail; |
| int nentry; |
| int maxentry; |
| int sizeofentry; |
| void (*cefree)(CEntry*); |
| }; |
| |
| static void |
| nop(CEntry *ce) |
| { |
| } |
| |
| static uint |
| hash(const char *s) |
| { |
| uint h; |
| uchar *p; |
| |
| h = 0; |
| for(p=(uchar*)s; *p; p++) |
| h = h*37 + *p; |
| return h; |
| } |
| |
| Cache* |
| newcache(int sizeofentry, int maxentry, void (*cefree)(CEntry*)) |
| { |
| Cache *c; |
| int i; |
| |
| assert(sizeofentry >= sizeof(CEntry)); |
| c = emalloc(sizeof *c); |
| c->sizeofentry = sizeofentry; |
| c->maxentry = maxentry; |
| c->nentry = 0; |
| for(i=1; i<maxentry; i<<=1) |
| ; |
| c->nhash = i; |
| c->hash = emalloc(c->nhash * sizeof c->hash[0]); |
| if(cefree == nil) |
| cefree = nop; |
| c->cefree = cefree; |
| return c; |
| } |
| |
| static void |
| popout(Cache *c, CEntry *e) |
| { |
| if(e->list.prev) |
| e->list.prev->list.next = e->list.next; |
| else |
| c->head = e->list.next; |
| if(e->list.next) |
| e->list.next->list.prev = e->list.prev; |
| else |
| c->tail = e->list.prev; |
| } |
| |
| static void |
| insertfront(Cache *c, CEntry *e) |
| { |
| e->list.next = c->head; |
| c->head = e; |
| if(e->list.next) |
| e->list.next->list.prev = e; |
| else |
| c->tail = e; |
| } |
| |
| static void |
| movetofront(Cache *c, CEntry *e) |
| { |
| popout(c, e); |
| insertfront(c, e); |
| } |
| |
| static CEntry* |
| evict(Cache *c) |
| { |
| CEntry *e; |
| |
| e = c->tail; |
| popout(c, e); |
| c->cefree(e); |
| free(e->name); |
| e->name = nil; |
| memset(e, 0, c->sizeofentry); |
| insertfront(c, e); |
| return e; |
| } |
| |
| CEntry* |
| cachelookup(Cache *c, char *name, int create) |
| { |
| int h; |
| CEntry *e; |
| |
| h = hash(name) % c->nhash; |
| for(e=c->hash[h]; e; e=e->hash.next){ |
| if(strcmp(name, e->name) == 0){ |
| movetofront(c, e); |
| return e; |
| } |
| } |
| |
| if(!create) |
| return nil; |
| |
| if(c->nentry >= c->maxentry) |
| e = evict(c); |
| else{ |
| e = emalloc(c->sizeofentry); |
| insertfront(c, e); |
| c->nentry++; |
| } |
| e->name = estrdup(name); |
| h = hash(name) % c->nhash; |
| e->hash.next = c->hash[h]; |
| c->hash[h] = e; |
| return e; |
| } |
| |
| void |
| cacheflush(Cache *c, char *substr) |
| { |
| CEntry **l, *e; |
| int i; |
| |
| for(i=0; i<c->nhash; i++){ |
| for(l=&c->hash[i]; (e=*l); ){ |
| if(substr == nil || strstr(e->name, substr)){ |
| *l = e->hash.next; |
| c->nentry--; |
| popout(c, e); |
| c->cefree(e); |
| free(e->name); |
| free(e); |
| }else |
| l = &e->hash.next; |
| } |
| } |
| } |