hashmap: introduce hashmap_free_entries

`hashmap_free_entries' behaves like `container_of' and passes the offset of the hashmap_entry struct to the internal `hashmap_free_' function, allowing the function to free any struct pointer regardless of where the hashmap_entry field is located. `hashmap_free' no longer takes any arguments aside from the hashmap itself. 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 c8e424c9c94d97b18cd335be17f32a8ce94a5b7f
17 files changed +52 -34
blame.c
+1 -1
@@ -433,7 +433,7 @@ static void get_fingerprint(struct fingerprint *result,
433
434 static void free_fingerprint(struct fingerprint *f)
435 {
436 - hashmap_free(&f->map, 0);
436 + hashmap_free(&f->map);
437 free(f->entries);
438 }
439
builtin/fetch.c
+3 -3
@@ -366,7 +366,7 @@ static void find_non_local_tags(const struct ref *refs,
366 item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
367 string_list_insert(&remote_refs_list, ref->name);
368 }
369 - hashmap_free(&existing_refs, 1);
369 + hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
370
371 /*
372 * We may have a final lightweight tag that needs to be
@@ -401,7 +401,7 @@ static void find_non_local_tags(const struct ref *refs,
401 **tail = rm;
402 *tail = &rm->next;
403 }
404 - hashmap_free(&remote_refs, 1);
404 + hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
405 string_list_clear(&remote_refs_list, 0);
406 }
407
@@ -530,7 +530,7 @@ static struct ref *get_ref_map(struct remote *remote,
530 }
531 }
532 }
533 - hashmap_free(&existing_refs, 1);
533 + hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
534
535 return ref_map;
536 }
config.c
+1 -1
@@ -1948,7 +1948,7 @@ void git_configset_clear(struct config_set *cs)
1948 free(entry->key);
1949 string_list_clear(&entry->value_list, 1);
1950 }
1951 - hashmap_free(&cs->config_hash, 1);
1951 + hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
1952 cs->hash_initialized = 0;
1953 free(cs->list.items);
1954 cs->list.nr = 0;
diff.c
+4 -2
@@ -6236,8 +6236,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
6236 if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
6237 dim_moved_lines(o);
6238
6239 - hashmap_free(&add_lines, 1);
6240 - hashmap_free(&del_lines, 1);
6239 + hashmap_free_entries(&add_lines, struct moved_entry,
6240 + ent);
6241 + hashmap_free_entries(&del_lines, struct moved_entry,
6242 + ent);
6243 }
6244
6245 for (i = 0; i < esm.nr; i++)
diffcore-rename.c
+1 -1
@@ -358,7 +358,7 @@ static int find_exact_renames(struct diff_options *options)
358 renames += find_identical_files(&file_table, i, options);
359
360 /* Free the hash data structure and entries */
361 - hashmap_free(&file_table, 1);
361 + hashmap_free_entries(&file_table, struct file_similarity, entry);
362
363 return renames;
364 }
hashmap.c
+8 -3
@@ -171,16 +171,21 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
171 map->do_count_items = 1;
172 }
173
174 -void hashmap_free(struct hashmap *map, int free_entries)
174 +void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
175 {
176 if (!map || !map->table)
177 return;
178 - if (free_entries) {
178 + if (entry_offset >= 0) { /* called by hashmap_free_entries */
179 struct hashmap_iter iter;
180 struct hashmap_entry *e;
181 +
182 hashmap_iter_init(map, &iter);
183 while ((e = hashmap_iter_next(&iter)))
183 - free(e);
184 + /*
185 + * like container_of, but using caller-calculated
186 + * offset (caller being hashmap_free_entries)
187 + */
188 + free((char *)e - entry_offset);
189 }
190 free(map->table);
191 memset(map, 0, sizeof(*map));
hashmap.h
+13 -6
@@ -96,7 +96,7 @@
96 * }
97 *
98 * if (!strcmp("end", action)) {
99 - * hashmap_free(&map, 1);
99 + * hashmap_free_entries(&map, struct long2string, ent);
100 * break;
101 * }
102 * }
@@ -232,13 +232,20 @@ void hashmap_init(struct hashmap *map,
232 const void *equals_function_data,
233 size_t initial_size);
234
235 +/* internal function for freeing hashmap */
236 +void hashmap_free_(struct hashmap *map, ssize_t offset);
237 +
238 /*
236 - * Frees a hashmap structure and allocated memory.
237 - *
238 - * If `free_entries` is true, each hashmap_entry in the map is freed as well
239 - * using stdlibs free().
239 + * Frees a hashmap structure and allocated memory, leaves entries undisturbed
240 + */
241 +#define hashmap_free(map) hashmap_free_(map, -1)
242 +
243 +/*
244 + * Frees @map and all entries. @type is the struct type of the entry
245 + * where @member is the hashmap_entry struct used to associate with @map
246 */
241 -void hashmap_free(struct hashmap *map, int free_entries);
247 +#define hashmap_free_entries(map, type, member) \
248 + hashmap_free_(map, offsetof(type, member));
249
250 /* hashmap_entry functions */
251
merge-recursive.c
+4 -3
@@ -2633,7 +2633,7 @@ static struct string_list *get_renames(struct merge_options *opt,
2633 free(e->target_file);
2634 string_list_clear(&e->source_files, 0);
2635 }
2636 - hashmap_free(&collisions, 1);
2636 + hashmap_free_entries(&collisions, struct collision_entry, ent);
2637 return renames;
2638 }
2639
@@ -2853,7 +2853,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
2853 strbuf_release(&e->new_dir);
2854 /* possible_new_dirs already cleared in get_directory_renames */
2855 }
2856 - hashmap_free(dir_renames, 1);
2856 + hashmap_free_entries(dir_renames, struct dir_rename_entry, ent);
2857 free(dir_renames);
2858
2859 free(pairs->queue);
@@ -3482,7 +3482,8 @@ int merge_trees(struct merge_options *opt,
3482 string_list_clear(entries, 1);
3483 free(entries);
3484
3485 - hashmap_free(&opt->current_file_dir_set, 1);
3485 + hashmap_free_entries(&opt->current_file_dir_set,
3486 + struct path_hashmap_entry, e);
3487
3488 if (clean < 0) {
3489 unpack_trees_finish(opt);
name-hash.c
+2 -2
@@ -728,6 +728,6 @@ void free_name_hash(struct index_state *istate)
728 return;
729 istate->name_hash_initialized = 0;
730
731 - hashmap_free(&istate->name_hash, 0);
732 - hashmap_free(&istate->dir_hash, 1);
731 + hashmap_free(&istate->name_hash);
732 + hashmap_free_entries(&istate->dir_hash, struct dir_entry, ent);
733 }
oidmap.c
+3 -1
@@ -25,7 +25,9 @@ void oidmap_free(struct oidmap *map, int free_entries)
25 {
26 if (!map)
27 return;
28 - hashmap_free(&map->map, free_entries);
28 +
29 + /* TODO: make oidmap itself not depend on struct layouts */
30 + hashmap_free_(&map->map, free_entries ? 0 : -1);
31 }
32
33 void *oidmap_get(const struct oidmap *map, const struct object_id *key)
patch-ids.c
+1 -1
@@ -71,7 +71,7 @@ int init_patch_ids(struct repository *r, struct patch_ids *ids)
71
72 int free_patch_ids(struct patch_ids *ids)
73 {
74 - hashmap_free(&ids->patches, 1);
74 + hashmap_free_entries(&ids->patches, struct patch_id, ent);
75 return 0;
76 }
77
range-diff.c
+1 -1
@@ -241,7 +241,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
241 }
242 }
243
244 - hashmap_free(&map, 0);
244 + hashmap_free(&map);
245 }
246
247 static void diffsize_consume(void *data, char *line, unsigned long len)
ref-filter.c
+2 -1
@@ -2172,7 +2172,8 @@ void ref_array_clear(struct ref_array *array)
2172 used_atom_cnt = 0;
2173
2174 if (ref_to_worktree_map.worktrees) {
2175 - hashmap_free(&(ref_to_worktree_map.map), 1);
2175 + hashmap_free_entries(&(ref_to_worktree_map.map),
2176 + struct ref_to_worktree_entry, ent);
2177 free_worktrees(ref_to_worktree_map.worktrees);
2178 ref_to_worktree_map.worktrees = NULL;
2179 }
revision.c
+1 -1
@@ -136,7 +136,7 @@ static void paths_and_oids_clear(struct hashmap *map)
136 free(entry->path);
137 }
138
139 - hashmap_free(map, 1);
139 + hashmap_free_entries(map, struct path_and_oids_entry, ent);
140 }
141
142 static void paths_and_oids_insert(struct hashmap *map,
sequencer.c
+2 -2
@@ -4772,7 +4772,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
4772
4773 oidmap_free(&commit2todo, 1);
4774 oidmap_free(&state.commit2label, 1);
4775 - hashmap_free(&state.labels, 1);
4775 + hashmap_free_entries(&state.labels, struct labels_entry, entry);
4776 strbuf_release(&state.buf);
4777
4778 return 0;
@@ -5301,7 +5301,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
5301 for (i = 0; i < todo_list->nr; i++)
5302 free(subjects[i]);
5303 free(subjects);
5304 - hashmap_free(&subject2item, 1);
5304 + hashmap_free_entries(&subject2item, struct subject2item_entry, entry);
5305
5306 clear_commit_todo_item(&commit_todo);
5307
submodule-config.c
+2 -2
@@ -103,8 +103,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)
103 struct submodule_entry, ent /* member name */)
104 free_one_config(entry);
105
106 - hashmap_free(&cache->for_path, 1);
107 - hashmap_free(&cache->for_name, 1);
106 + hashmap_free_entries(&cache->for_path, struct submodule_entry, ent);
107 + hashmap_free_entries(&cache->for_name, struct submodule_entry, ent);
108 cache->initialized = 0;
109 cache->gitmodules_read = 0;
110 }
t/helper/test-hashmap.c
+3 -3
@@ -109,7 +109,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
109 hashmap_add(&map, &entries[i]->ent);
110 }
111
112 - hashmap_free(&map, 0);
112 + hashmap_free(&map);
113 }
114 } else {
115 /* test map lookups */
@@ -129,7 +129,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
129 }
130 }
131
132 - hashmap_free(&map, 0);
132 + hashmap_free(&map);
133 }
134 }
135
@@ -266,6 +266,6 @@ int cmd__hashmap(int argc, const char **argv)
266 }
267
268 strbuf_release(&line);
269 - hashmap_free(&map, 1);
269 + hashmap_free_entries(&map, struct test_entry, ent);
270 return 0;
271 }