strmap: new utility functions

Add strmap as a new struct and associated utility functions, specifically for hashmaps that map strings to some value. The API is taken directly from Peff's proposal at https://lore.kernel.org/git/20180906191203.GA26184@sigill.intra.peff.net/ Note that similar string-list, I have a strdup_strings setting. However, unlike string-list, strmap_init() does not take a parameter for this setting and instead automatically sets it to 1; callers who want to control this detail need to instead call strmap_init_with_options(). (Future patches will add additional parameters to strmap_init_with_options()). Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Nov 2, 2020 at 18:55 UTC ae20bf1ad98bdc716879a8da99e7f329e3cc2730
3 files changed +165
Makefile
+1
@@ -1000,6 +1000,7 @@ LIB_OBJS += stable-qsort.o
1000 LIB_OBJS += strbuf.o
1001 LIB_OBJS += streaming.o
1002 LIB_OBJS += string-list.o
1003 +LIB_OBJS += strmap.o
1004 LIB_OBJS += strvec.o
1005 LIB_OBJS += sub-process.o
1006 LIB_OBJS += submodule-config.o
strmap.c new
+99
@@ -0,0 +1,99 @@
1 +#include "git-compat-util.h"
2 +#include "strmap.h"
3 +
4 +int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
5 + const struct hashmap_entry *entry1,
6 + const struct hashmap_entry *entry2,
7 + const void *keydata)
8 +{
9 + const struct strmap_entry *e1, *e2;
10 +
11 + e1 = container_of(entry1, const struct strmap_entry, ent);
12 + e2 = container_of(entry2, const struct strmap_entry, ent);
13 + return strcmp(e1->key, e2->key);
14 +}
15 +
16 +static struct strmap_entry *find_strmap_entry(struct strmap *map,
17 + const char *str)
18 +{
19 + struct strmap_entry entry;
20 + hashmap_entry_init(&entry.ent, strhash(str));
21 + entry.key = str;
22 + return hashmap_get_entry(&map->map, &entry, ent, NULL);
23 +}
24 +
25 +void strmap_init(struct strmap *map)
26 +{
27 + strmap_init_with_options(map, 1);
28 +}
29 +
30 +void strmap_init_with_options(struct strmap *map,
31 + int strdup_strings)
32 +{
33 + hashmap_init(&map->map, cmp_strmap_entry, NULL, 0);
34 + map->strdup_strings = strdup_strings;
35 +}
36 +
37 +static void strmap_free_entries_(struct strmap *map, int free_values)
38 +{
39 + struct hashmap_iter iter;
40 + struct strmap_entry *e;
41 +
42 + if (!map)
43 + return;
44 +
45 + /*
46 + * We need to iterate over the hashmap entries and free
47 + * e->key and e->value ourselves; hashmap has no API to
48 + * take care of that for us. Since we're already iterating over
49 + * the hashmap, though, might as well free e too and avoid the need
50 + * to make some call into the hashmap API to do that.
51 + */
52 + hashmap_for_each_entry(&map->map, &iter, e, ent) {
53 + if (free_values)
54 + free(e->value);
55 + if (map->strdup_strings)
56 + free((char*)e->key);
57 + free(e);
58 + }
59 +}
60 +
61 +void strmap_clear(struct strmap *map, int free_values)
62 +{
63 + strmap_free_entries_(map, free_values);
64 + hashmap_clear(&map->map);
65 +}
66 +
67 +void *strmap_put(struct strmap *map, const char *str, void *data)
68 +{
69 + struct strmap_entry *entry = find_strmap_entry(map, str);
70 + void *old = NULL;
71 +
72 + if (entry) {
73 + old = entry->value;
74 + entry->value = data;
75 + } else {
76 + const char *key = str;
77 +
78 + entry = xmalloc(sizeof(*entry));
79 + hashmap_entry_init(&entry->ent, strhash(str));
80 +
81 + if (map->strdup_strings)
82 + key = xstrdup(str);
83 + entry->key = key;
84 + entry->value = data;
85 + hashmap_add(&map->map, &entry->ent);
86 + }
87 + return old;
88 +}
89 +
90 +void *strmap_get(struct strmap *map, const char *str)
91 +{
92 + struct strmap_entry *entry = find_strmap_entry(map, str);
93 + return entry ? entry->value : NULL;
94 +}
95 +
96 +int strmap_contains(struct strmap *map, const char *str)
97 +{
98 + return find_strmap_entry(map, str) != NULL;
99 +}
strmap.h new
+65
@@ -0,0 +1,65 @@
1 +#ifndef STRMAP_H
2 +#define STRMAP_H
3 +
4 +#include "hashmap.h"
5 +
6 +struct strmap {
7 + struct hashmap map;
8 + unsigned int strdup_strings:1;
9 +};
10 +
11 +struct strmap_entry {
12 + struct hashmap_entry ent;
13 + const char *key;
14 + void *value;
15 +};
16 +
17 +int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
18 + const struct hashmap_entry *entry1,
19 + const struct hashmap_entry *entry2,
20 + const void *keydata);
21 +
22 +#define STRMAP_INIT { \
23 + .map = HASHMAP_INIT(cmp_strmap_entry, NULL), \
24 + .strdup_strings = 1, \
25 + }
26 +
27 +/*
28 + * Initialize the members of the strmap. Any keys added to the strmap will
29 + * be strdup'ed with their memory managed by the strmap.
30 + */
31 +void strmap_init(struct strmap *map);
32 +
33 +/*
34 + * Same as strmap_init, but for those who want to control the memory management
35 + * carefully instead of using the default of strdup_strings=1.
36 + */
37 +void strmap_init_with_options(struct strmap *map,
38 + int strdup_strings);
39 +
40 +/*
41 + * Remove all entries from the map, releasing any allocated resources.
42 + */
43 +void strmap_clear(struct strmap *map, int free_values);
44 +
45 +/*
46 + * Insert "str" into the map, pointing to "data".
47 + *
48 + * If an entry for "str" already exists, its data pointer is overwritten, and
49 + * the original data pointer returned. Otherwise, returns NULL.
50 + */
51 +void *strmap_put(struct strmap *map, const char *str, void *data);
52 +
53 +/*
54 + * Return the data pointer mapped by "str", or NULL if the entry does not
55 + * exist.
56 + */
57 +void *strmap_get(struct strmap *map, const char *str);
58 +
59 +/*
60 + * Return non-zero iff "str" is present in the map. This differs from
61 + * strmap_get() in that it can distinguish entries with a NULL data pointer.
62 + */
63 +int strmap_contains(struct strmap *map, const char *str);
64 +
65 +#endif /* STRMAP_H */