2  * Generic implementation of hash-based key value mappings.
 
   7 #define FNV32_BASE ((unsigned int) 0x811c9dc5)
 
   8 #define FNV32_PRIME ((unsigned int) 0x01000193)
 
  10 unsigned int strhash(const char *str)
 
  12         unsigned int c, hash = FNV32_BASE;
 
  13         while ((c = (unsigned char) *str++))
 
  14                 hash = (hash * FNV32_PRIME) ^ c;
 
  18 unsigned int strihash(const char *str)
 
  20         unsigned int c, hash = FNV32_BASE;
 
  21         while ((c = (unsigned char) *str++)) {
 
  22                 if (c >= 'a' && c <= 'z')
 
  24                 hash = (hash * FNV32_PRIME) ^ c;
 
  29 unsigned int memhash(const void *buf, size_t len)
 
  31         unsigned int hash = FNV32_BASE;
 
  32         unsigned char *ucbuf = (unsigned char *) buf;
 
  34                 unsigned int c = *ucbuf++;
 
  35                 hash = (hash * FNV32_PRIME) ^ c;
 
  40 unsigned int memihash(const void *buf, size_t len)
 
  42         unsigned int hash = FNV32_BASE;
 
  43         unsigned char *ucbuf = (unsigned char *) buf;
 
  45                 unsigned int c = *ucbuf++;
 
  46                 if (c >= 'a' && c <= 'z')
 
  48                 hash = (hash * FNV32_PRIME) ^ c;
 
  54  * Incoporate another chunk of data into a memihash
 
  57 unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)
 
  59         unsigned int hash = hash_seed;
 
  60         unsigned char *ucbuf = (unsigned char *) buf;
 
  62                 unsigned int c = *ucbuf++;
 
  63                 if (c >= 'a' && c <= 'z')
 
  65                 hash = (hash * FNV32_PRIME) ^ c;
 
  70 #define HASHMAP_INITIAL_SIZE 64
 
  71 /* grow / shrink by 2^2 */
 
  72 #define HASHMAP_RESIZE_BITS 2
 
  73 /* load factor in percent */
 
  74 #define HASHMAP_LOAD_FACTOR 80
 
  76 static void alloc_table(struct hashmap *map, unsigned int size)
 
  78         map->tablesize = size;
 
  79         map->table = xcalloc(size, sizeof(struct hashmap_entry *));
 
  81         /* calculate resize thresholds for new size */
 
  82         map->grow_at = (unsigned int) ((uint64_t) size * HASHMAP_LOAD_FACTOR / 100);
 
  83         if (size <= HASHMAP_INITIAL_SIZE)
 
  87                  * The shrink-threshold must be slightly smaller than
 
  88                  * (grow-threshold / resize-factor) to prevent erratic resizing,
 
  89                  * thus we divide by (resize-factor + 1).
 
  91                 map->shrink_at = map->grow_at / ((1 << HASHMAP_RESIZE_BITS) + 1);
 
  94 static inline int entry_equals(const struct hashmap *map,
 
  95                 const struct hashmap_entry *e1, const struct hashmap_entry *e2,
 
  99                (e1->hash == e2->hash &&
 
 100                 !map->cmpfn(map->cmpfn_data, e1, e2, keydata));
 
 103 static inline unsigned int bucket(const struct hashmap *map,
 
 104                 const struct hashmap_entry *key)
 
 106         return key->hash & (map->tablesize - 1);
 
 109 int hashmap_bucket(const struct hashmap *map, unsigned int hash)
 
 111         return hash & (map->tablesize - 1);
 
 114 static void rehash(struct hashmap *map, unsigned int newsize)
 
 116         unsigned int i, oldsize = map->tablesize;
 
 117         struct hashmap_entry **oldtable = map->table;
 
 119         alloc_table(map, newsize);
 
 120         for (i = 0; i < oldsize; i++) {
 
 121                 struct hashmap_entry *e = oldtable[i];
 
 123                         struct hashmap_entry *next = e->next;
 
 124                         unsigned int b = bucket(map, e);
 
 125                         e->next = map->table[b];
 
 133 static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
 
 134                 const struct hashmap_entry *key, const void *keydata)
 
 136         struct hashmap_entry **e = &map->table[bucket(map, key)];
 
 137         while (*e && !entry_equals(map, *e, key, keydata))
 
 142 static int always_equal(const void *unused_cmp_data,
 
 145                         const void *unused_keydata)
 
 150 void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
 
 151                 const void *cmpfn_data, size_t initial_size)
 
 153         unsigned int size = HASHMAP_INITIAL_SIZE;
 
 155         memset(map, 0, sizeof(*map));
 
 157         map->cmpfn = equals_function ? equals_function : always_equal;
 
 158         map->cmpfn_data = cmpfn_data;
 
 160         /* calculate initial table size and allocate the table */
 
 161         initial_size = (unsigned int) ((uint64_t) initial_size * 100
 
 162                         / HASHMAP_LOAD_FACTOR);
 
 163         while (initial_size > size)
 
 164                 size <<= HASHMAP_RESIZE_BITS;
 
 165         alloc_table(map, size);
 
 168          * Keep track of the number of items in the map and
 
 169          * allow the map to automatically grow as necessary.
 
 171         map->do_count_items = 1;
 
 174 void hashmap_free(struct hashmap *map, int free_entries)
 
 176         if (!map || !map->table)
 
 179                 struct hashmap_iter iter;
 
 180                 struct hashmap_entry *e;
 
 181                 hashmap_iter_init(map, &iter);
 
 182                 while ((e = hashmap_iter_next(&iter)))
 
 186         memset(map, 0, sizeof(*map));
 
 189 void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)
 
 191         return *find_entry_ptr(map, key, keydata);
 
 194 void *hashmap_get_next(const struct hashmap *map, const void *entry)
 
 196         struct hashmap_entry *e = ((struct hashmap_entry *) entry)->next;
 
 197         for (; e; e = e->next)
 
 198                 if (entry_equals(map, entry, e, NULL))
 
 203 void hashmap_add(struct hashmap *map, void *entry)
 
 205         unsigned int b = bucket(map, entry);
 
 208         ((struct hashmap_entry *) entry)->next = map->table[b];
 
 209         map->table[b] = entry;
 
 211         /* fix size and rehash if appropriate */
 
 212         if (map->do_count_items) {
 
 214                 if (map->private_size > map->grow_at)
 
 215                         rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
 
 219 void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
 
 221         struct hashmap_entry *old;
 
 222         struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
 
 226         /* remove existing entry */
 
 231         /* fix size and rehash if appropriate */
 
 232         if (map->do_count_items) {
 
 234                 if (map->private_size < map->shrink_at)
 
 235                         rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
 
 241 void *hashmap_put(struct hashmap *map, void *entry)
 
 243         struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
 
 244         hashmap_add(map, entry);
 
 248 void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)
 
 255 void *hashmap_iter_next(struct hashmap_iter *iter)
 
 257         struct hashmap_entry *current = iter->next;
 
 260                         iter->next = current->next;
 
 264                 if (iter->tablepos >= iter->map->tablesize)
 
 267                 current = iter->map->table[iter->tablepos++];
 
 272         struct hashmap_entry ent;
 
 274         unsigned char data[FLEX_ARRAY];
 
 277 static int pool_entry_cmp(const void *unused_cmp_data,
 
 278                           const struct pool_entry *e1,
 
 279                           const struct pool_entry *e2,
 
 280                           const unsigned char *keydata)
 
 282         return e1->data != keydata &&
 
 283                (e1->len != e2->len || memcmp(e1->data, keydata, e1->len));
 
 286 const void *memintern(const void *data, size_t len)
 
 288         static struct hashmap map;
 
 289         struct pool_entry key, *e;
 
 291         /* initialize string pool hashmap */
 
 293                 hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, NULL, 0);
 
 295         /* lookup interned string in pool */
 
 296         hashmap_entry_init(&key, memhash(data, len));
 
 298         e = hashmap_get(&map, &key, data);
 
 300                 /* not found: create it */
 
 301                 FLEX_ALLOC_MEM(e, data, data, len);
 
 302                 hashmap_entry_init(e, key.ent.hash);
 
 304                 hashmap_add(&map, e);