NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
hash.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <stdbool.h>
33#include "hash.h"
34#include "ctype2.h"
35#include "memory.h"
36#include "string2.h"
37
39#define SOME_PRIME 149711
40
46static size_t gen_hash_string(union HashKey key, size_t num_elems)
47{
48 size_t hash = 0;
49 const unsigned char *s = (const unsigned char *) key.strkey;
50 if (!s)
51 return 0;
52
53 while (*s != '\0')
54 hash += ((hash << 7) + *s++);
55 hash = (hash * SOME_PRIME) % num_elems;
56
57 return hash;
58}
59
63static int cmp_key_string(union HashKey a, union HashKey b)
64{
65 return mutt_str_cmp(a.strkey, b.strkey);
66}
67
73static size_t gen_hash_case_string(union HashKey key, size_t num_elems)
74{
75 size_t hash = 0;
76 const unsigned char *s = (const unsigned char *) key.strkey;
77 if (!s)
78 return 0;
79
80 while (*s != '\0')
81 hash += ((hash << 7) + mutt_tolower(*s++));
82 hash = (hash * SOME_PRIME) % num_elems;
83
84 return hash;
85}
86
90static int cmp_key_case_string(union HashKey a, union HashKey b)
91{
92 return mutt_istr_cmp(a.strkey, b.strkey);
93}
94
98static size_t gen_hash_int(union HashKey key, size_t num_elems)
99{
100 return (key.intkey % num_elems);
101}
102
106static int cmp_key_int(union HashKey a, union HashKey b)
107{
108 if (a.intkey == b.intkey)
109 return 0;
110 if (a.intkey < b.intkey)
111 return -1;
112 return 1;
113}
114
123static struct HashTable *hash_new(size_t num_elems)
124{
125 struct HashTable *table = MUTT_MEM_CALLOC(1, struct HashTable);
126 if (num_elems == 0)
127 num_elems = 2;
128 table->num_elems = num_elems;
129 table->table = MUTT_MEM_CALLOC(num_elems, struct HashElem *);
130 return table;
131}
132
141static struct HashElem *union_hash_insert(struct HashTable *table,
142 union HashKey key, int type, void *data)
143{
144 if (!table)
145 return NULL; // LCOV_EXCL_LINE
146
147 struct HashElem *he = MUTT_MEM_CALLOC(1, struct HashElem);
148 size_t hash = table->gen_hash(key, table->num_elems);
149 he->key = key;
150 he->data = data;
151 he->type = type;
152
153 if (table->allow_dups)
154 {
155 he->next = table->table[hash];
156 table->table[hash] = he;
157 }
158 else
159 {
160 struct HashElem *tmp = NULL, *last = NULL;
161
162 for (tmp = table->table[hash], last = NULL; tmp; last = tmp, tmp = tmp->next)
163 {
164 const int rc = table->cmp_key(tmp->key, key);
165 if (rc == 0)
166 {
167 FREE(&he);
168 return NULL;
169 }
170 if (rc > 0)
171 break;
172 }
173 if (last)
174 last->next = he;
175 else
176 table->table[hash] = he;
177 he->next = tmp;
178 }
179 return he;
180}
181
188static struct HashElem *union_hash_find_elem(const struct HashTable *table, union HashKey key)
189{
190 if (!table)
191 return NULL; // LCOV_EXCL_LINE
192
193 size_t hash = table->gen_hash(key, table->num_elems);
194 struct HashElem *he = table->table[hash];
195 for (; he; he = he->next)
196 {
197 if (table->cmp_key(key, he->key) == 0)
198 return he;
199 }
200 return NULL;
201}
202
209static void *union_hash_find(const struct HashTable *table, union HashKey key)
210{
211 if (!table)
212 return NULL; // LCOV_EXCL_LINE
213 struct HashElem *he = union_hash_find_elem(table, key);
214 if (he)
215 return he->data;
216 return NULL;
217}
218
225static void union_hash_delete(struct HashTable *table, union HashKey key, const void *data)
226{
227 if (!table)
228 return; // LCOV_EXCL_LINE
229
230 size_t hash = table->gen_hash(key, table->num_elems);
231 struct HashElem *he = table->table[hash];
232 struct HashElem **he_last = &table->table[hash];
233
234 while (he)
235 {
236 if (((data == he->data) || !data) && (table->cmp_key(he->key, key) == 0))
237 {
238 *he_last = he->next;
239 if (table->hdata_free)
240 table->hdata_free(he->type, he->data, table->hdata);
241 if (table->strdup_keys)
242 FREE(&he->key.strkey);
243 FREE(&he);
244
245 he = *he_last;
246 }
247 else
248 {
249 he_last = &he->next;
250 he = he->next;
251 }
252 }
253}
254
262{
264 if (flags & MUTT_HASH_STRCASECMP)
265 {
266 table->gen_hash = gen_hash_case_string;
267 table->cmp_key = cmp_key_case_string;
268 }
269 else
270 {
271 table->gen_hash = gen_hash_string;
272 table->cmp_key = cmp_key_string;
273 }
274 if (flags & MUTT_HASH_STRDUP_KEYS)
275 table->strdup_keys = true;
276 if (flags & MUTT_HASH_ALLOW_DUPS)
277 table->allow_dups = true;
278 return table;
279}
280
288{
290 table->gen_hash = gen_hash_int;
291 table->cmp_key = cmp_key_int;
292 if (flags & MUTT_HASH_ALLOW_DUPS)
293 table->allow_dups = true;
294 return table;
295}
296
304{
305 if (!table)
306 return;
307 table->hdata_free = fn;
308 table->hdata = fn_data;
309}
310
320 const char *strkey, int type, void *data)
321{
322 if (!table || !strkey)
323 return NULL;
324
325 union HashKey key;
326 key.strkey = table->strdup_keys ? mutt_str_dup(strkey) : strkey;
327 return union_hash_insert(table, key, type, data);
328}
329
337struct HashElem *mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
338{
339 return mutt_hash_typed_insert(table, strkey, -1, data);
340}
341
349struct HashElem *mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
350{
351 if (!table)
352 return NULL;
353 union HashKey key;
354 key.intkey = intkey;
355 return union_hash_insert(table, key, -1, data);
356}
357
364void *mutt_hash_find(const struct HashTable *table, const char *strkey)
365{
366 if (!table || !strkey)
367 return NULL;
368 union HashKey key;
369 key.strkey = strkey;
370 return union_hash_find(table, key);
371}
372
379struct HashElem *mutt_hash_find_elem(const struct HashTable *table, const char *strkey)
380{
381 if (!table || !strkey)
382 return NULL;
383 union HashKey key;
384 key.strkey = strkey;
385 return union_hash_find_elem(table, key);
386}
387
394void *mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
395{
396 if (!table)
397 return NULL;
398 union HashKey key;
399 key.intkey = intkey;
400 return union_hash_find(table, key);
401}
402
411struct HashElem *mutt_hash_find_bucket(const struct HashTable *table, const char *strkey)
412{
413 if (!table || !strkey)
414 return NULL;
415
416 union HashKey key;
417
418 key.strkey = strkey;
419 size_t hash = table->gen_hash(key, table->num_elems);
420 return table->table[hash];
421}
422
429void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
430{
431 if (!table || !strkey || (strkey[0] == '\0'))
432 return;
433 union HashKey key;
434 // Copy the key because union_hash_delete() may use it after the HashElem is freed.
436 union_hash_delete(table, key, data);
437 FREE(&key.strkey);
438}
439
446void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
447{
448 if (!table)
449 return;
450 union HashKey key;
451 key.intkey = intkey;
452 union_hash_delete(table, key, data);
453}
454
459void mutt_hash_free(struct HashTable **ptr)
460{
461 if (!ptr || !*ptr)
462 return;
463
464 struct HashTable *table = *ptr;
465 struct HashElem *he = NULL, *tmp = NULL;
466
467 for (size_t i = 0; i < table->num_elems; i++)
468 {
469 for (he = table->table[i]; he;)
470 {
471 tmp = he;
472 he = he->next;
473 if (table->hdata_free && tmp->data)
474 table->hdata_free(tmp->type, tmp->data, table->hdata);
475 if (table->strdup_keys)
476 FREE(&tmp->key.strkey);
477 FREE(&tmp);
478 }
479 }
480 FREE(&table->table);
481 FREE(ptr);
482}
483
491struct HashElem *mutt_hash_walk(const struct HashTable *table, struct HashWalkState *state)
492{
493 if (!table || !state)
494 return NULL;
495
496 if (state->last && state->last->next)
497 {
498 state->last = state->last->next;
499 return state->last;
500 }
501
502 if (state->last)
503 state->index++;
504
505 while (state->index < table->num_elems)
506 {
507 if (table->table[state->index])
508 {
509 state->last = table->table[state->index];
510 return state->last;
511 }
512 state->index++;
513 }
514
515 state->index = 0;
516 state->last = NULL;
517 return NULL;
518}
ctype(3) wrapper functions
int mutt_tolower(int arg)
Wrapper for tolower(3)
Definition ctype.c:126
static int cmp_key_case_string(union HashKey a, union HashKey b)
Compare two string keys (ignore case) - Implements hash_cmp_key_t -.
Definition hash.c:90
static int cmp_key_int(union HashKey a, union HashKey b)
Compare two integer keys - Implements hash_cmp_key_t -.
Definition hash.c:106
static int cmp_key_string(union HashKey a, union HashKey b)
Compare two string keys - Implements hash_cmp_key_t -.
Definition hash.c:63
static size_t gen_hash_string(union HashKey key, size_t num_elems)
Generate a hash from a string - Implements hash_gen_hash_t -.
Definition hash.c:46
static size_t gen_hash_case_string(union HashKey key, size_t num_elems)
Generate a hash from a string (ignore the case) - Implements hash_gen_hash_t -.
Definition hash.c:73
static size_t gen_hash_int(union HashKey key, size_t num_elems)
Generate a hash from an integer - Implements hash_gen_hash_t -.
Definition hash.c:98
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition hash.c:337
void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:429
static struct HashElem * union_hash_find_elem(const struct HashTable *table, union HashKey key)
Find a HashElem in a Hash Table element using a key.
Definition hash.c:188
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:364
static void * union_hash_find(const struct HashTable *table, union HashKey key)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:209
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:446
static struct HashElem * union_hash_insert(struct HashTable *table, union HashKey key, int type, void *data)
Insert into a hash table using a union as a key.
Definition hash.c:141
struct HashElem * mutt_hash_find_bucket(const struct HashTable *table, const char *strkey)
Find the HashElem in a Hash Table element using a key.
Definition hash.c:411
#define SOME_PRIME
A prime number used for hashing.
Definition hash.c:39
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition hash.c:287
struct HashElem * mutt_hash_typed_insert(struct HashTable *table, const char *strkey, int type, void *data)
Insert a string with type info into a Hash Table.
Definition hash.c:319
struct HashElem * mutt_hash_walk(const struct HashTable *table, struct HashWalkState *state)
Iterate through all the HashElem's in a Hash Table.
Definition hash.c:491
static struct HashTable * hash_new(size_t num_elems)
Create a new Hash Table.
Definition hash.c:123
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:261
struct HashElem * mutt_hash_find_elem(const struct HashTable *table, const char *strkey)
Find the HashElem in a Hash Table element using a key.
Definition hash.c:379
void mutt_hash_set_destructor(struct HashTable *table, hash_hdata_free_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition hash.c:303
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:394
struct HashElem * mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
Add a new element to the Hash Table (with integer keys)
Definition hash.c:349
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
static void union_hash_delete(struct HashTable *table, union HashKey key, const void *data)
Remove an element from a Hash Table.
Definition hash.c:225
Hash Table data structure.
uint8_t HashFlags
Flags for mutt_hash_new(), e.g. MUTT_HASH_STRCASECMP.
Definition hash.h:110
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:113
void(* hash_hdata_free_t)(int type, void *obj, intptr_t data)
Definition hash.h:63
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition hash.h:114
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition hash.h:112
Memory management wrappers.
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition string.c:403
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition string.c:416
String manipulation functions.
The item stored in a Hash Table.
Definition hash.h:44
struct HashElem * next
Linked List.
Definition hash.h:48
union HashKey key
Key representing the data.
Definition hash.h:46
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition hash.h:45
void * data
User-supplied data.
Definition hash.h:47
A Hash Table.
Definition hash.h:99
hash_gen_hash_t gen_hash
Function to generate hash id from the key.
Definition hash.h:104
struct HashElem ** table
Array of Hash keys.
Definition hash.h:103
hash_cmp_key_t cmp_key
Function to compare two Hash keys.
Definition hash.h:105
bool allow_dups
if set, duplicate keys are allowed
Definition hash.h:102
size_t num_elems
Number of buckets in the Hash Table.
Definition hash.h:100
bool strdup_keys
if set, the key->strkey is strdup()'d
Definition hash.h:101
intptr_t hdata
Data to pass to the hdata_free() function.
Definition hash.h:106
hash_hdata_free_t hdata_free
Function to free a Hash element.
Definition hash.h:107
Cursor to iterate through a Hash Table.
Definition hash.h:134
size_t index
Current position in table.
Definition hash.h:135
struct HashElem * last
Current element in linked list.
Definition hash.h:136
The data item stored in a HashElem.
Definition hash.h:35
const char * strkey
String key.
Definition hash.h:36
unsigned int intkey
Integer key.
Definition hash.h:37