NeoMutt  2025-12-11-980-ge38c27
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;
161 struct HashElem *last = NULL;
162
163 for (tmp = table->table[hash], last = NULL; tmp; last = tmp, tmp = tmp->next)
164 {
165 const int rc = table->cmp_key(tmp->key, key);
166 if (rc == 0)
167 {
168 FREE(&he);
169 return NULL;
170 }
171 if (rc > 0)
172 break;
173 }
174 if (last)
175 last->next = he;
176 else
177 table->table[hash] = he;
178 he->next = tmp;
179 }
180 return he;
181}
182
189static struct HashElem *union_hash_find_elem(const struct HashTable *table, union HashKey key)
190{
191 if (!table)
192 return NULL; // LCOV_EXCL_LINE
193
194 size_t hash = table->gen_hash(key, table->num_elems);
195 struct HashElem *he = table->table[hash];
196 for (; he; he = he->next)
197 {
198 if (table->cmp_key(key, he->key) == 0)
199 return he;
200 }
201 return NULL;
202}
203
210static void *union_hash_find(const struct HashTable *table, union HashKey key)
211{
212 if (!table)
213 return NULL; // LCOV_EXCL_LINE
214 struct HashElem *he = union_hash_find_elem(table, key);
215 if (he)
216 return he->data;
217 return NULL;
218}
219
226static void union_hash_delete(struct HashTable *table, union HashKey key, const void *data)
227{
228 if (!table)
229 return; // LCOV_EXCL_LINE
230
231 size_t hash = table->gen_hash(key, table->num_elems);
232 struct HashElem *he = table->table[hash];
233 struct HashElem **he_last = &table->table[hash];
234
235 while (he)
236 {
237 if (((data == he->data) || !data) && (table->cmp_key(he->key, key) == 0))
238 {
239 *he_last = he->next;
240 if (table->hdata_free)
241 table->hdata_free(he->type, he->data, table->hdata);
242 if (table->strdup_keys)
243 FREE(&he->key.strkey);
244 FREE(&he);
245
246 he = *he_last;
247 }
248 else
249 {
250 he_last = &he->next;
251 he = he->next;
252 }
253 }
254}
255
263{
265 if (flags & MUTT_HASH_STRCASECMP)
266 {
267 table->gen_hash = gen_hash_case_string;
268 table->cmp_key = cmp_key_case_string;
269 }
270 else
271 {
272 table->gen_hash = gen_hash_string;
273 table->cmp_key = cmp_key_string;
274 }
275 if (flags & MUTT_HASH_STRDUP_KEYS)
276 table->strdup_keys = true;
277 if (flags & MUTT_HASH_ALLOW_DUPS)
278 table->allow_dups = true;
279 return table;
280}
281
289{
291 table->gen_hash = gen_hash_int;
292 table->cmp_key = cmp_key_int;
293 if (flags & MUTT_HASH_ALLOW_DUPS)
294 table->allow_dups = true;
295 return table;
296}
297
305{
306 if (!table)
307 return;
308 table->hdata_free = fn;
309 table->hdata = fn_data;
310}
311
321 const char *strkey, int type, void *data)
322{
323 if (!table || !strkey)
324 return NULL;
325
326 union HashKey key = { 0 };
327 key.strkey = table->strdup_keys ? mutt_str_dup(strkey) : strkey;
328 return union_hash_insert(table, key, type, data);
329}
330
338struct HashElem *mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
339{
340 return mutt_hash_typed_insert(table, strkey, -1, data);
341}
342
350struct HashElem *mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
351{
352 if (!table)
353 return NULL;
354 union HashKey key = { 0 };
355 key.intkey = intkey;
356 return union_hash_insert(table, key, -1, data);
357}
358
365void *mutt_hash_find(const struct HashTable *table, const char *strkey)
366{
367 if (!table || !strkey)
368 return NULL;
369 union HashKey key = { 0 };
370 key.strkey = strkey;
371 return union_hash_find(table, key);
372}
373
380struct HashElem *mutt_hash_find_elem(const struct HashTable *table, const char *strkey)
381{
382 if (!table || !strkey)
383 return NULL;
384 union HashKey key = { 0 };
385 key.strkey = strkey;
386 return union_hash_find_elem(table, key);
387}
388
395void *mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
396{
397 if (!table)
398 return NULL;
399 union HashKey key = { 0 };
400 key.intkey = intkey;
401 return union_hash_find(table, key);
402}
403
412struct HashElem *mutt_hash_find_bucket(const struct HashTable *table, const char *strkey)
413{
414 if (!table || !strkey)
415 return NULL;
416
417 union HashKey key = { 0 };
418
419 key.strkey = strkey;
420 size_t hash = table->gen_hash(key, table->num_elems);
421 return table->table[hash];
422}
423
430void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
431{
432 if (!table || !strkey || (strkey[0] == '\0'))
433 return;
434 union HashKey key = { 0 };
435 // Copy the key because union_hash_delete() may use it after the HashElem is freed.
437 union_hash_delete(table, key, data);
438 FREE(&key.strkey);
439}
440
447void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
448{
449 if (!table)
450 return;
451 union HashKey key = { 0 };
452 key.intkey = intkey;
453 union_hash_delete(table, key, data);
454}
455
460void mutt_hash_free(struct HashTable **ptr)
461{
462 if (!ptr || !*ptr)
463 return;
464
465 struct HashTable *table = *ptr;
466 struct HashElem *he = NULL;
467 struct HashElem *tmp = NULL;
468
469 for (size_t i = 0; i < table->num_elems; i++)
470 {
471 for (he = table->table[i]; he;)
472 {
473 tmp = he;
474 he = he->next;
475 if (table->hdata_free && tmp->data)
476 table->hdata_free(tmp->type, tmp->data, table->hdata);
477 if (table->strdup_keys)
478 FREE(&tmp->key.strkey);
479 FREE(&tmp);
480 }
481 }
482 FREE(&table->table);
483 FREE(ptr);
484}
485
493struct HashElem *mutt_hash_walk(const struct HashTable *table, struct HashWalkState *state)
494{
495 if (!table || !state)
496 return NULL;
497
498 if (state->last && state->last->next)
499 {
500 state->last = state->last->next;
501 return state->last;
502 }
503
504 if (state->last)
505 state->index++;
506
507 while (state->index < table->num_elems)
508 {
509 if (table->table[state->index])
510 {
511 state->last = table->table[state->index];
512 return state->last;
513 }
514 state->index++;
515 }
516
517 state->index = 0;
518 state->last = NULL;
519 return NULL;
520}
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:338
void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:430
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:189
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:365
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:210
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:447
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:412
#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:288
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:320
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:493
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:262
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:380
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:304
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:395
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:350
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:460
static void union_hash_delete(struct HashTable *table, union HashKey key, const void *data)
Remove an element from a Hash Table.
Definition hash.c:226
Hash Table data structure.
@ MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition hash.h:118
@ MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition hash.h:116
@ MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:117
uint8_t HashFlags
Definition hash.h:120
void(* hash_hdata_free_t)(int type, void *obj, intptr_t data)
Definition hash.h:63
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:140
size_t index
Current position in table.
Definition hash.h:141
struct HashElem * last
Current element in linked list.
Definition hash.h:142
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