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
;
53 #define HASHMAP_INITIAL_SIZE 64
54 /* grow / shrink by 2^2 */
55 #define HASHMAP_RESIZE_BITS 2
56 /* load factor in percent */
57 #define HASHMAP_LOAD_FACTOR 80
59 static void alloc_table(struct hashmap
*map
, unsigned int size
)
61 map
->tablesize
= size
;
62 map
->table
= xcalloc(size
, sizeof(struct hashmap_entry
*));
64 /* calculate resize thresholds for new size */
65 map
->grow_at
= (unsigned int) ((uint64_t) size
* HASHMAP_LOAD_FACTOR
/ 100);
66 if (size
<= HASHMAP_INITIAL_SIZE
)
70 * The shrink-threshold must be slightly smaller than
71 * (grow-threshold / resize-factor) to prevent erratic resizing,
72 * thus we divide by (resize-factor + 1).
74 map
->shrink_at
= map
->grow_at
/ ((1 << HASHMAP_RESIZE_BITS
) + 1);
77 static inline int entry_equals(const struct hashmap
*map
,
78 const struct hashmap_entry
*e1
, const struct hashmap_entry
*e2
,
81 return (e1
== e2
) || (e1
->hash
== e2
->hash
&& !map
->cmpfn(e1
, e2
, keydata
));
84 static inline unsigned int bucket(const struct hashmap
*map
,
85 const struct hashmap_entry
*key
)
87 return key
->hash
& (map
->tablesize
- 1);
90 static void rehash(struct hashmap
*map
, unsigned int newsize
)
92 unsigned int i
, oldsize
= map
->tablesize
;
93 struct hashmap_entry
**oldtable
= map
->table
;
95 alloc_table(map
, newsize
);
96 for (i
= 0; i
< oldsize
; i
++) {
97 struct hashmap_entry
*e
= oldtable
[i
];
99 struct hashmap_entry
*next
= e
->next
;
100 unsigned int b
= bucket(map
, e
);
101 e
->next
= map
->table
[b
];
109 static inline struct hashmap_entry
**find_entry_ptr(const struct hashmap
*map
,
110 const struct hashmap_entry
*key
, const void *keydata
)
112 struct hashmap_entry
**e
= &map
->table
[bucket(map
, key
)];
113 while (*e
&& !entry_equals(map
, *e
, key
, keydata
))
118 static int always_equal(const void *unused1
, const void *unused2
, const void *unused3
)
123 void hashmap_init(struct hashmap
*map
, hashmap_cmp_fn equals_function
,
126 unsigned int size
= HASHMAP_INITIAL_SIZE
;
128 map
->cmpfn
= equals_function
? equals_function
: always_equal
;
130 /* calculate initial table size and allocate the table */
131 initial_size
= (unsigned int) ((uint64_t) initial_size
* 100
132 / HASHMAP_LOAD_FACTOR
);
133 while (initial_size
> size
)
134 size
<<= HASHMAP_RESIZE_BITS
;
135 alloc_table(map
, size
);
138 void hashmap_free(struct hashmap
*map
, int free_entries
)
140 if (!map
|| !map
->table
)
143 struct hashmap_iter iter
;
144 struct hashmap_entry
*e
;
145 hashmap_iter_init(map
, &iter
);
146 while ((e
= hashmap_iter_next(&iter
)))
150 memset(map
, 0, sizeof(*map
));
153 void *hashmap_get(const struct hashmap
*map
, const void *key
, const void *keydata
)
155 return *find_entry_ptr(map
, key
, keydata
);
158 void *hashmap_get_next(const struct hashmap
*map
, const void *entry
)
160 struct hashmap_entry
*e
= ((struct hashmap_entry
*) entry
)->next
;
161 for (; e
; e
= e
->next
)
162 if (entry_equals(map
, entry
, e
, NULL
))
167 void hashmap_add(struct hashmap
*map
, void *entry
)
169 unsigned int b
= bucket(map
, entry
);
172 ((struct hashmap_entry
*) entry
)->next
= map
->table
[b
];
173 map
->table
[b
] = entry
;
175 /* fix size and rehash if appropriate */
177 if (map
->size
> map
->grow_at
)
178 rehash(map
, map
->tablesize
<< HASHMAP_RESIZE_BITS
);
181 void *hashmap_remove(struct hashmap
*map
, const void *key
, const void *keydata
)
183 struct hashmap_entry
*old
;
184 struct hashmap_entry
**e
= find_entry_ptr(map
, key
, keydata
);
188 /* remove existing entry */
193 /* fix size and rehash if appropriate */
195 if (map
->size
< map
->shrink_at
)
196 rehash(map
, map
->tablesize
>> HASHMAP_RESIZE_BITS
);
200 void *hashmap_put(struct hashmap
*map
, void *entry
)
202 struct hashmap_entry
*old
= hashmap_remove(map
, entry
, NULL
);
203 hashmap_add(map
, entry
);
207 void hashmap_iter_init(struct hashmap
*map
, struct hashmap_iter
*iter
)
214 void *hashmap_iter_next(struct hashmap_iter
*iter
)
216 struct hashmap_entry
*current
= iter
->next
;
219 iter
->next
= current
->next
;
223 if (iter
->tablepos
>= iter
->map
->tablesize
)
226 current
= iter
->map
->table
[iter
->tablepos
++];
231 struct hashmap_entry ent
;
233 unsigned char data
[FLEX_ARRAY
];
236 static int pool_entry_cmp(const struct pool_entry
*e1
,
237 const struct pool_entry
*e2
,
238 const unsigned char *keydata
)
240 return e1
->data
!= keydata
&&
241 (e1
->len
!= e2
->len
|| memcmp(e1
->data
, keydata
, e1
->len
));
244 const void *memintern(const void *data
, size_t len
)
246 static struct hashmap map
;
247 struct pool_entry key
, *e
;
249 /* initialize string pool hashmap */
251 hashmap_init(&map
, (hashmap_cmp_fn
) pool_entry_cmp
, 0);
253 /* lookup interned string in pool */
254 hashmap_entry_init(&key
, memhash(data
, len
));
256 e
= hashmap_get(&map
, &key
, data
);
258 /* not found: create it */
259 e
= xmallocz(sizeof(struct pool_entry
) + len
);
260 hashmap_entry_init(e
, key
.ent
.hash
);
262 memcpy(e
->data
, data
, len
);
263 hashmap_add(&map
, e
);