/* ARISA - Pack Cache * Copyright (C) 2004 Carl Ritson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "arisa.h" #define INIT_SIZE 32 #define SHRINK_RATIO 4 #define CHAIN_LIMIT 16 #define SIZE_CAP 1024 static uint32_t hash_path(const char *path) { uint32_t hash = 0; int i; assert(path != NULL); for(i = 0; path[i] != '\0'; ++i) hash = hash * 37UL + path[i]; // special case, the hash may never be zero if(hash == 0) hash = 1; return hash; } pack_cache_t *pack_cache_init(void) { pack_cache_t *pc = xalloc(sizeof(pack_cache_t)); int i; LOCK_INIT(&(pc->lock)); pc->table = xalloc(sizeof(pack_t *) * INIT_SIZE); pc->size = INIT_SIZE; pc->no_packs = 0; for(i = 0; i < pc->size; ++i) pc->table[i] = NULL; return pc; } void pack_cache_free(pack_cache_t *pc) { pack_t *p,*n; int i; for(i = 0; i < pc->size; ++i) { p = pc->table[i]; while(p != NULL) { n = p->next; WLOCK(p); p->hash = 0; p->ref_count--; if(p->ref_count == 0) { WUNLOCK(p); free_pack_real(p); } else { WUNLOCK(p); } p = n; } } xfree(pc->table); xfree(pc); } static pack_t *find_pack(pack_cache_t *pc, uint32_t hash, const char *path) { pack_t *p; if(hash == 0) hash = hash_path(path); p = pc->table[hash % pc->size]; while(p != NULL) { RLOCK(p); if(p->hash == hash && strcmp(p->file,path) == 0) { RUNLOCK(p); break; } else RUNLOCK(p); p = p->next; } return p; } static int insert_pack(pack_cache_t *pc, pack_t *p, uint32_t hash) { pack_t *n; int depth = 1; p->next = NULL; p->prev = NULL; if((n = pc->table[hash % pc->size]) != NULL) { while(n->next != NULL) { n = n->next; depth++; } p->prev = n; n->next = p; } else { pc->table[hash % pc->size] = p; } pc->no_packs++; return depth; } static void remove_pack(pack_cache_t *pc, pack_t *p, uint32_t hash) { if(p->prev == NULL) { if(p->next != NULL) p->next->prev = NULL; pc->table[hash % pc->size] = p->next; } else { if(p->next != NULL) p->next->prev = p->prev; p->prev->next = p->next; } pc->no_packs--; } static void resize_cache(pack_cache_t *pc, int new_size) { pack_t *p,*n,**old_table; int i,old_size; if(new_size < INIT_SIZE || new_size > SIZE_CAP) return; old_table = pc->table; old_size = pc->size; pc->no_packs = 0; pc->size = new_size; pc->table = xalloc(sizeof(pack_t *) * new_size); for(i = 0; i < pc->size; ++i) pc->table[i] = NULL; for(i = 0; i < old_size; ++i) { p = old_table[i]; while(p != NULL) { n = p->next; insert_pack(pc,p,p->hash); p = n; } } xfree(old_table); } pack_t *pack_cache_get(pack_cache_t *pc, const char *path, int flags) { uint32_t hash = hash_path(path); pack_t *p; LOCK(pc); p = find_pack(pc,hash,path); if(p == NULL && (flags & PACK_CACHE_CREATE)) { char buffer[512]; pathtofn(path,buffer,sizeof(buffer)); p = alloc_pack(); p->hash = hash; p->file = xstrdup(path); p->label = xstrdup(buffer); if(insert_pack(pc,p,hash) > CHAIN_LIMIT) resize_cache(pc,pc->size * 2); } if(p != NULL) pack_ref(p); UNLOCK(pc); return p; } int pack_cache_present(pack_cache_t *pc, pack_t *p) { int ret = 0; RLOCK(p); if(p->hash != 0) ret = 1; RUNLOCK(p); return ret; } int pack_cache_deref(pack_cache_t *pc, pack_t *p) { uint32_t hash; int ret; LOCK(pc); WLOCK(p); assert(p->hash != 0); ret = --p->ref_count; if(ret == 1) ret = p->ref_count = 0; // remove our ref if(ret == 0) { hash = p->hash; p->hash = 0; remove_pack(pc,p,hash); if(pc->no_packs < (pc->size / SHRINK_RATIO)) resize_cache(pc,pc->size / 2); } WUNLOCK(p); UNLOCK(pc); return ret; } static pack_t *test_and_insert(pack_cache_t *pc, pack_t *p, int consolidate) { if(p->hash == 0) { uint32_t hash = hash_path(p->file); pack_t *o = find_pack(pc,hash,p->file); if(o != NULL) { if(consolidate) o->no_gets += p->no_gets; free_pack(p); p = o; } else { insert_pack(pc,p,hash); p->hash = hash; } p->ref_count++; } return p; } void pack_cache_fill(void) { pack_cache_t *pc = global->pack_cache; int i,j,k; for(i = 0; i < global->no_packlists; ++i) { packlist_t *p = global->packlists[i]; for(j = 0; j < p->no_packs; ++j) p->packs[j] = test_and_insert(pc,p->packs[j],1); } for(i = 0; i < global->no_queues; ++i) { queue_t *q = global->queues[i]; for(j = 0; j < q->no_qsends; ++j) q->qsends[j]->pack = test_and_insert(pc,q->qsends[j]->pack,0); } for(i = 0; i < global->no_interfaces; ++i) { interface_t *intf = global->interfaces[i]; if(intf->type != INTERFACE_SEND) continue; for(j = 0; j < intf->send.no_pools; ++j) { pool_t *p = intf->send.pools[j]; for(k = 0; k < p->no_csends; ++k) { p->csends[k]->pack = test_and_insert(pc,p->csends[k]->pack,0); } } } }