#include #include "dat.h" #include "fn.h" enum { Tagsize = sizeof(Hnode), }; static uvlong hash(Str *s) { uvlong h; int i; h = 7; for(i = 0; i < s->n; i++) h = h*31 + s->r[i]; return h; } Hmap* hmapalloc(int nbuckets) { void *store; Hmap *h; int nsz; if(nbuckets < 1) return nil; nsz = Tagsize; if((ulong)nbuckets > (ULONG_MAX-sizeof(*h))/(ulong)nsz) return nil; store = emalloc(sizeof(*h) + (ulong)nbuckets * nsz); h = store; h->nbs = nbuckets; h->nsz = nsz; h->len = h->cap = nbuckets; h->nodes = (uchar*)store + sizeof(*h); return h; } static int keycmp(Hnode *n, Str *key) { char buf[Maxutf]; int len; len = stoutf(key, buf, sizeof(buf)); if(n->klen != len) return 1; return memcmp(n->key, buf, len); } Hnode* hmapget(Hmap *h, Str *key) { Hnode *n; uchar *v; if(h == nil || key == nil || key->n < 0 || key->n > Maxrunes) return nil; v = h->nodes + (hash(key) % h->nbs) * (ulong)h->nsz; for(;;){ n = (Hnode*)v; if(n->filled && keycmp(n, key) == 0) return n; if(n->next == 0) break; v = h->nodes + (ulong)n->next * h->nsz; } return nil; } static char* sdup(Str *s, int *len) { char buf[Maxutf]; char *p; int n; n = stoutf(s, buf, sizeof(buf)); p = emalloc((ulong)n + 1); memmove(p, buf, n); p[n] = '\0'; *len = n; return p; } static char* memdup(const char *src, int n) { char *p; if(n == 0) return nil; p = emalloc((ulong)n + 1); memmove(p, src, n); p[n] = '\0'; return p; } void hmapfree(Hmap *h) { Hnode *n; int i; if(h == nil) return; for(i = 0; i < h->len; i++){ n = (Hnode*)(h->nodes + (ulong)i * h->nsz); if(!n->filled) continue; free(n->key); free(n->val); } free(h); } void hmapset(Hmap **store, Str *key, const char *val, int vlen) { char *newval; Hnode *n; uchar *v; Hmap *h; int next; ulong diff; if(store == nil || *store == nil || key == nil || key->n < 0 || key->n > Maxrunes || vlen < 0 || (vlen > 0 && val == nil)) return; newval = memdup(val, vlen); h = *store; v = h->nodes + (hash(key) % h->nbs) * (ulong)h->nsz; for(;;){ n = (Hnode*)v; next = n->next; if(n->filled == 0) goto replace; if(keycmp(n, key) == 0) goto replace; if(next == 0) break; v = h->nodes + (ulong)next * h->nsz; } if(h->cap == h->len){ diff = v - h->nodes; if(h->cap > INT_MAX/2 || (ulong)h->cap > (ULONG_MAX-sizeof(*h))/(2*(ulong)h->nsz)) die("hash table is too large"); h->cap *= 2; *store = erealloc(*store, sizeof(*h) + (ulong)h->cap * h->nsz); h = *store; h->nodes = (uchar*)*store + sizeof(*h); v = h->nodes + diff; n = (Hnode*)v; } n->next = h->len; memset(h->nodes + (ulong)h->len * h->nsz, 0, h->nsz); h->len++; v = h->nodes + (ulong)n->next * h->nsz; n = (Hnode*)v; replace: if(n->filled == 0){ n->key = sdup(key, &n->klen); n->filled = 1; } n->next = next; free(n->val); n->val = newval; n->vlen = vlen; }