hashmap: use *_entry APIs for iteration
Inspired by list_for_each_entry in the Linux kernel. Once again, these are somewhat compromised usability-wise by compilers lacking __typeof__ support. Signed-off-by: Eric Wong <e@80x24.org> Reviewed-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Eric Wong committed
Oct 6, 2019 at 23:30 UTC
87571c3f71ba41d89eef5202f8589daa26f984ca
13 files changed
+70
-47
attr.c
+3
-2
@@ -163,12 +163,13 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
163
if (size != check->all_attrs_nr) {
164
struct attr_hash_entry *e;
165
struct hashmap_iter iter;
166
- hashmap_iter_init(&map->map, &iter);
166
167
REALLOC_ARRAY(check->all_attrs, size);
168
check->all_attrs_nr = size;
169
171
- while ((e = hashmap_iter_next(&iter))) {
170
+ hashmap_for_each_entry(&map->map, &iter, e,
171
+ struct attr_hash_entry,
172
+ ent /* member name */) {
173
const struct git_attr *a = e->value;
174
check->all_attrs[a->attr_nr].attr = a;
175
}
blame.c
+6
-4
@@ -450,9 +450,9 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)
450
struct hashmap_iter iter;
451
const struct fingerprint_entry *entry_a, *entry_b;
452
453
- hashmap_iter_init(&b->map, &iter);
454
-
455
- while ((entry_b = hashmap_iter_next(&iter))) {
453
+ hashmap_for_each_entry(&b->map, &iter, entry_b,
454
+ const struct fingerprint_entry,
455
+ entry /* member name */) {
456
entry_a = hashmap_get_entry(&a->map, entry_b, NULL,
457
struct fingerprint_entry, entry);
458
if (entry_a) {
@@ -473,7 +473,9 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)
473
474
hashmap_iter_init(&b->map, &iter);
475
476
- while ((entry_b = hashmap_iter_next(&iter))) {
476
+ hashmap_for_each_entry(&b->map, &iter, entry_b,
477
+ const struct fingerprint_entry,
478
+ entry /* member name */) {
479
entry_a = hashmap_get_entry(&a->map, entry_b, NULL,
480
struct fingerprint_entry, entry);
481
if (entry_a) {
builtin/describe.c
+2
-2
@@ -333,8 +333,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
333
struct commit_name *n;
334
335
init_commit_names(&commit_names);
336
- n = hashmap_iter_first(&names, &iter);
337
- for (; n; n = hashmap_iter_next(&iter)) {
336
+ hashmap_for_each_entry(&names, &iter, n, struct commit_name,
337
+ entry /* member name */) {
338
c = lookup_commit_reference_gently(the_repository,
339
&n->peeled, 1);
340
if (c)
builtin/difftool.c
+4
-4
@@ -538,8 +538,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
538
* temporary file to both the left and right directories to show the
539
* change in the recorded SHA1 for the submodule.
540
*/
541
- hashmap_iter_init(&submodules, &iter);
542
- while ((entry = hashmap_iter_next(&iter))) {
541
+ hashmap_for_each_entry(&submodules, &iter, entry,
542
+ struct pair_entry, entry /* member name */) {
543
if (*entry->left) {
544
add_path(&ldir, ldir_len, entry->path);
545
ensure_leading_directories(ldir.buf);
@@ -557,8 +557,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
557
* shows only the link itself, not the contents of the link target.
558
* This loop replicates that behavior.
559
*/
560
- hashmap_iter_init(&symlinks2, &iter);
561
- while ((entry = hashmap_iter_next(&iter))) {
560
+ hashmap_for_each_entry(&symlinks2, &iter, entry,
561
+ struct pair_entry, entry /* member name */) {
562
if (*entry->left) {
563
add_path(&ldir, ldir_len, entry->path);
564
ensure_leading_directories(ldir.buf);
config.c
+3
-2
@@ -1942,8 +1942,9 @@ void git_configset_clear(struct config_set *cs)
1942
if (!cs->hash_initialized)
1943
return;
1944
1945
- hashmap_iter_init(&cs->config_hash, &iter);
1946
- while ((entry = hashmap_iter_next(&iter))) {
1945
+ hashmap_for_each_entry(&cs->config_hash, &iter, entry,
1946
+ struct config_set_element,
1947
+ ent /* member name */) {
1948
free(entry->key);
1949
string_list_clear(&entry->value_list, 1);
1950
}
hashmap.c
+1
-1
@@ -256,7 +256,7 @@ void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)
256
iter->next = NULL;
257
}
258
259
-void *hashmap_iter_next(struct hashmap_iter *iter)
259
+struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter)
260
{
261
struct hashmap_entry *current = iter->next;
262
for (;;) {
hashmap.h
+13
-2
@@ -382,16 +382,27 @@ struct hashmap_iter {
382
void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
383
384
/* Returns the next hashmap_entry, or NULL if there are no more entries. */
385
-void *hashmap_iter_next(struct hashmap_iter *iter);
385
+struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter);
386
387
/* Initializes the iterator and returns the first entry, if any. */
388
-static inline void *hashmap_iter_first(struct hashmap *map,
388
+static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
389
struct hashmap_iter *iter)
390
{
391
hashmap_iter_init(map, iter);
392
return hashmap_iter_next(iter);
393
}
394
395
+#define hashmap_iter_next_entry(iter, type, member) \
396
+ container_of_or_null(hashmap_iter_next(iter), type, member)
397
+
398
+#define hashmap_iter_first_entry(map, iter, type, member) \
399
+ container_of_or_null(hashmap_iter_first(map, iter), type, member)
400
+
401
+#define hashmap_for_each_entry(map, iter, var, type, member) \
402
+ for (var = hashmap_iter_first_entry(map, iter, type, member); \
403
+ var; \
404
+ var = hashmap_iter_next_entry(iter, type, member))
405
+
406
/*
407
* returns a @pointer of @type matching @keyvar, or NULL if nothing found.
408
* @keyvar is a pointer of @type
merge-recursive.c
+15
-10
@@ -2135,8 +2135,9 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
2135
struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
2136
struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
2137
2138
- hashmap_iter_init(dir_re_head, &iter);
2139
- while ((head_ent = hashmap_iter_next(&iter))) {
2138
+ hashmap_for_each_entry(dir_re_head, &iter, head_ent,
2139
+ struct dir_rename_entry,
2140
+ ent /* member name */) {
2141
merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
2142
if (merge_ent &&
2143
!head_ent->non_unique_new_dir &&
@@ -2160,8 +2161,9 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
2161
remove_hashmap_entries(dir_re_head, &remove_from_head);
2162
remove_hashmap_entries(dir_re_merge, &remove_from_merge);
2163
2163
- hashmap_iter_init(dir_re_merge, &iter);
2164
- while ((merge_ent = hashmap_iter_next(&iter))) {
2164
+ hashmap_for_each_entry(dir_re_merge, &iter, merge_ent,
2165
+ struct dir_rename_entry,
2166
+ ent /* member name */) {
2167
head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
2168
if (tree_has_path(opt->repo, merge, merge_ent->dir)) {
2169
/* 2. This wasn't a directory rename after all */
@@ -2265,8 +2267,9 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
2267
* we set non_unique_new_dir. Once we've determined the winner (or
2268
* that there is no winner), we no longer need possible_new_dirs.
2269
*/
2268
- hashmap_iter_init(dir_renames, &iter);
2269
- while ((entry = hashmap_iter_next(&iter))) {
2270
+ hashmap_for_each_entry(dir_renames, &iter, entry,
2271
+ struct dir_rename_entry,
2272
+ ent /* member name */) {
2273
int max = 0;
2274
int bad_max = 0;
2275
char *best = NULL;
@@ -2624,8 +2627,9 @@ static struct string_list *get_renames(struct merge_options *opt,
2627
entries);
2628
}
2629
2627
- hashmap_iter_init(&collisions, &iter);
2628
- while ((e = hashmap_iter_next(&iter))) {
2630
+ hashmap_for_each_entry(&collisions, &iter, e,
2631
+ struct collision_entry,
2632
+ ent /* member name */) {
2633
free(e->target_file);
2634
string_list_clear(&e->source_files, 0);
2635
}
@@ -2842,8 +2846,9 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
2846
struct hashmap_iter iter;
2847
struct dir_rename_entry *e;
2848
2845
- hashmap_iter_init(dir_renames, &iter);
2846
- while ((e = hashmap_iter_next(&iter))) {
2849
+ hashmap_for_each_entry(dir_renames, &iter, e,
2850
+ struct dir_rename_entry,
2851
+ ent /* member name */) {
2852
free(e->dir);
2853
strbuf_release(&e->new_dir);
2854
/* possible_new_dirs already cleared in get_directory_renames */
oidmap.h
+4
-2
@@ -78,14 +78,16 @@ static inline void oidmap_iter_init(struct oidmap *map, struct oidmap_iter *iter
78
79
static inline void *oidmap_iter_next(struct oidmap_iter *iter)
80
{
81
- return hashmap_iter_next(&iter->h_iter);
81
+ /* TODO: this API could be reworked to do compile-time type checks */
82
+ return (void *)hashmap_iter_next(&iter->h_iter);
83
}
84
85
static inline void *oidmap_iter_first(struct oidmap *map,
86
struct oidmap_iter *iter)
87
{
88
oidmap_iter_init(map, iter);
88
- return oidmap_iter_next(iter);
89
+ /* TODO: this API could be reworked to do compile-time type checks */
90
+ return (void *)oidmap_iter_next(iter);
91
}
92
93
#endif
revision.c
+6
-4
@@ -128,9 +128,10 @@ static void paths_and_oids_clear(struct hashmap *map)
128
{
129
struct hashmap_iter iter;
130
struct path_and_oids_entry *entry;
131
- hashmap_iter_init(map, &iter);
131
133
- while ((entry = (struct path_and_oids_entry *)hashmap_iter_next(&iter))) {
132
+ hashmap_for_each_entry(map, &iter, entry,
133
+ struct path_and_oids_entry,
134
+ ent /* member name */) {
135
oidset_clear(&entry->trees);
136
free(entry->path);
137
}
@@ -242,8 +243,9 @@ void mark_trees_uninteresting_sparse(struct repository *r,
243
add_children_by_path(r, tree, &map);
244
}
245
245
- hashmap_iter_init(&map, &map_iter);
246
- while ((entry = hashmap_iter_next(&map_iter)))
246
+ hashmap_for_each_entry(&map, &map_iter, entry,
247
+ struct path_and_oids_entry,
248
+ ent /* member name */)
249
mark_trees_uninteresting_sparse(r, &entry->trees);
250
251
paths_and_oids_clear(&map);
submodule-config.c
+5
-3
@@ -99,8 +99,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)
99
* allocation of struct submodule entries. Each is allocated by
100
* their .gitmodules blob sha1 and submodule name.
101
*/
102
- hashmap_iter_init(&cache->for_name, &iter);
103
- while ((entry = hashmap_iter_next(&iter)))
102
+ hashmap_for_each_entry(&cache->for_name, &iter, entry,
103
+ struct submodule_entry, ent /* member name */)
104
free_one_config(entry);
105
106
hashmap_free(&cache->for_path, 1);
@@ -556,7 +556,9 @@ static const struct submodule *config_from(struct submodule_cache *cache,
556
struct hashmap_iter iter;
557
struct submodule_entry *entry;
558
559
- entry = hashmap_iter_first(&cache->for_name, &iter);
559
+ entry = hashmap_iter_first_entry(&cache->for_name, &iter,
560
+ struct submodule_entry,
561
+ ent /* member name */);
562
if (!entry)
563
return NULL;
564
return entry->config;
t/helper/test-hashmap.c
+4
-3
@@ -222,10 +222,11 @@ int cmd__hashmap(int argc, const char **argv)
222
free(entry);
223
224
} else if (!strcmp("iterate", cmd)) {
225
-
225
struct hashmap_iter iter;
227
- hashmap_iter_init(&map, &iter);
228
- while ((entry = hashmap_iter_next(&iter)))
226
+
227
+ hashmap_for_each_entry(&map, &iter, entry,
228
+ struct test_entry,
229
+ ent /* member name */)
230
printf("%s %s\n", entry->key, get_value(entry));
231
232
} else if (!strcmp("size", cmd)) {
t/helper/test-lazy-init-name-hash.c
+4
-8
@@ -41,17 +41,13 @@ static void dump_run(void)
41
die("non-threaded code path used");
42
}
43
44
- dir = hashmap_iter_first(&the_index.dir_hash, &iter_dir);
45
- while (dir) {
44
+ hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
45
+ struct dir_entry, ent /* member name */)
46
printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
47
- dir = hashmap_iter_next(&iter_dir);
48
- }
47
50
- ce = hashmap_iter_first(&the_index.name_hash, &iter_cache);
51
- while (ce) {
48
+ hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
49
+ struct cache_entry, ent /* member name */)
50
printf("name %08x %s\n", ce->ent.hash, ce->name);
53
- ce = hashmap_iter_next(&iter_cache);
54
- }
51
52
discard_cache();
53
}