Archived
1
0
Fork 0

proc-cache.c: use hash-table code from hash.c

Use the refactored code from hash.c also
use chaining as the collision strategy instead of
open-adressing, not only becouse the new hash api makes it hard
to do but it is more space efficient.

Since a collision with open-adressing results in two entries
in the hash table but with chaining, we only have one.
the complexity for search/insert/delete is still O(n) for both techniques.
Chaining is better because items that collide only takes up one slot in the
hash table, considering that the best-case for space overflow is 25%. it
is better to have a small table.
This commit is contained in:
Henrik Hautakoski 2012-05-22 11:48:28 +02:00
parent 8a39596268
commit 365b657f9f

View file

@ -28,6 +28,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
#include "env.h" #include "env.h"
#include "llist.h"
#include "hash.h" #include "hash.h"
#include "lockfile.h" #include "lockfile.h"
#include "proc-cache.h" #include "proc-cache.h"
@ -54,6 +55,7 @@ union hash {
struct proc_cache_entry { struct proc_cache_entry {
union hash hash; union hash hash;
unsigned int time; unsigned int time;
struct llist list;
}; };
static struct lockfile lock = LOCKFILE_INIT; static struct lockfile lock = LOCKFILE_INIT;
@ -78,19 +80,53 @@ static void hash(union hash *h, const char *s) {
static struct proc_cache_entry* lookup(const char *key) { static struct proc_cache_entry* lookup(const char *key) {
struct proc_cache_entry *entry;
union hash h; union hash h;
hash(&h, key); hash(&h, key);
return hash_lookup(&table, h.index);
entry = hash_lookup(&table, h.index);
if (entry) {
struct llist *it;
struct proc_cache_entry *e;
llist_foreach(it, entry->list.next) {
e = llist_entry(it, struct proc_cache_entry, list);
if (!memcmp(e->hash.sha1, h.sha1, 20))
return e;
}
}
return NULL;
} }
static void he_insert(struct proc_cache_entry *entry) { static void he_insert(struct proc_cache_entry *entry) {
struct proc_cache_entry *dest; struct proc_cache_entry *dest, *s;
dest = hash_insert(&table, entry->hash.index, entry); s = calloc(1, sizeof(*s));
dest = hash_insert(&table, entry->hash.index, s);
if (dest) { if (dest) {
memcpy(dest, entry, sizeof(*entry)); struct llist *it;
free(entry); struct proc_cache_entry *e;
free(s);
llist_foreach(it, dest->list.next) {
e = llist_entry(it, struct proc_cache_entry, list);
if (!memcmp(entry->hash.sha1, e->hash.sha1, 20)) {
free(entry);
return;
}
}
llist_add(&dest->list, &entry->list);
} else {
s->hash.index = entry->hash.index;
llist_add(&s->list, &entry->list);
} }
} }
@ -204,19 +240,40 @@ void proc_cache_purge(unsigned int timestamp) {
t = now - timestamp; t = now - timestamp;
for(i=0; i < table.size; i++) { for(i=0; i < table.size; i++) {
struct proc_cache_entry *entry = hash_entry(&table, i); struct llist *it, *n;
struct proc_cache_entry *e, *entry = hash_entry(&table, i);
if (!entry) if (!entry)
continue; continue;
if (entry->time <= t) { llist_foreach_safe(it, n, entry->list.next) {
entry = hash_remove(&table, entry->hash.index); e = llist_entry(it, struct proc_cache_entry, list);
if (entry) if (e->time <= t) {
free(entry); llist_del(&entry->list, it);
free(e);
}
}
if (llist_empty(&entry->list)) {
e = hash_remove(&table, entry->hash.index);
if (e)
free(e);
} }
} }
} }
static void write_entry(int fd, struct proc_cache_entry *entry) {
struct proc_cache_entry ondisk;
memcpy(&ondisk.hash, &entry->hash, 20);
ondisk.hash.index = htonl(entry->hash.index);
ondisk.time = htonl(entry->time);
write(fd, &ondisk.hash, 20);
write(fd, &ondisk.time, sizeof(ondisk.time));
}
void proc_cache_close() { void proc_cache_close() {
int i; int i;
@ -226,34 +283,37 @@ void proc_cache_close() {
if (table.size < 1) if (table.size < 1)
return; return;
/* Write header */
hdr.signature = htonl(SIGNATURE); hdr.signature = htonl(SIGNATURE);
hdr.version = htonl(1); hdr.version = htonl(1);
hdr.entries = htonl(table.count); hdr.entries = 0;
write(fd, &hdr, sizeof(hdr)); ftruncate(fd, 0);
lseek(fd, sizeof(hdr), SEEK_SET);
/* Write hash entries */ /* Write hash entries */
for(i=0; i < table.size; i++) { for(i=0; i < table.size; i++) {
struct proc_cache_entry ondisk, *entry = hash_entry(&table, i); struct llist *it;
struct proc_cache_entry *entry = hash_entry(&table, i);
if (!entry) if (!entry)
continue; continue;
memcpy(&ondisk.hash, &entry->hash, 20); llist_foreach(it, entry->list.next) {
ondisk.hash.index = htonl(entry->hash.index); write_entry(fd, llist_entry(it, struct proc_cache_entry, list));
ondisk.time = htonl(entry->time); hdr.entries++;
}
write(fd, &ondisk.hash, 20);
write(fd, &ondisk.time, sizeof(ondisk.time));
free(entry);
} }
hash_free(&table); hdr.entries = htonl(hdr.entries);
/* Write header */
lseek(fd, 0, SEEK_SET);
write(fd, &hdr, sizeof(hdr));
/* Flush it to the real file */ /* Flush it to the real file */
commit_lock(&lock); commit_lock(&lock);
release_lock(&lock); release_lock(&lock);
hash_free(&table);
} }