Git fork

Merge branch 'en/strmap'

A specialization of hashmap that uses a string as key has been
introduced. Hopefully it will see wider use over time.

* en/strmap:
shortlog: use strset from strmap.h
Use new HASHMAP_INIT macro to simplify hashmap initialization
strmap: take advantage of FLEXPTR_ALLOC_STR when relevant
strmap: enable allocations to come from a mem_pool
strmap: add a strset sub-type
strmap: split create_entry() out of strmap_put()
strmap: add functions facilitating use as a string->int map
strmap: enable faster clearing and reusing of strmaps
strmap: add more utility functions
strmap: new utility functions
hashmap: provide deallocation function names
hashmap: introduce a new hashmap_partial_clear()
hashmap: allow re-use after hashmap_free()
hashmap: adjust spacing to fix argument alignment
hashmap: add usage documentation explaining hashmap_free[_entries]()

+620 -169
+1
Makefile
··· 1004 1004 LIB_OBJS += strbuf.o 1005 1005 LIB_OBJS += streaming.o 1006 1006 LIB_OBJS += string-list.o 1007 + LIB_OBJS += strmap.o 1007 1008 LIB_OBJS += strvec.o 1008 1009 LIB_OBJS += sub-process.o 1009 1010 LIB_OBJS += submodule-config.o
+1 -1
add-interactive.c
··· 557 557 if (ps) 558 558 clear_pathspec(&rev.prune_data); 559 559 } 560 - hashmap_free_entries(&s.file_map, struct pathname_entry, ent); 560 + hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent); 561 561 if (unmerged_count) 562 562 *unmerged_count = s.unmerged_count; 563 563 if (binary_count)
+8 -18
attr.c
··· 52 52 pthread_mutex_unlock(&map->mutex); 53 53 } 54 54 55 - /* 56 - * The global dictionary of all interned attributes. This 57 - * is a singleton object which is shared between threads. 58 - * Access to this dictionary must be surrounded with a mutex. 59 - */ 60 - static struct attr_hashmap g_attr_hashmap; 61 - 62 55 /* The container for objects stored in "struct attr_hashmap" */ 63 56 struct attr_hash_entry { 64 57 struct hashmap_entry ent; ··· 80 73 return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen); 81 74 } 82 75 83 - /* Initialize an 'attr_hashmap' object */ 84 - static void attr_hashmap_init(struct attr_hashmap *map) 85 - { 86 - hashmap_init(&map->map, attr_hash_entry_cmp, NULL, 0); 87 - } 76 + /* 77 + * The global dictionary of all interned attributes. This 78 + * is a singleton object which is shared between threads. 79 + * Access to this dictionary must be surrounded with a mutex. 80 + */ 81 + static struct attr_hashmap g_attr_hashmap = { 82 + HASHMAP_INIT(attr_hash_entry_cmp, NULL) 83 + }; 88 84 89 85 /* 90 86 * Retrieve the 'value' stored in a hashmap given the provided 'key'. ··· 96 92 struct attr_hash_entry k; 97 93 struct attr_hash_entry *e; 98 94 99 - if (!map->map.tablesize) 100 - attr_hashmap_init(map); 101 - 102 95 hashmap_entry_init(&k.ent, memhash(key, keylen)); 103 96 k.key = key; 104 97 k.keylen = keylen; ··· 113 106 void *value) 114 107 { 115 108 struct attr_hash_entry *e; 116 - 117 - if (!map->map.tablesize) 118 - attr_hashmap_init(map); 119 109 120 110 e = xmalloc(sizeof(struct attr_hash_entry)); 121 111 hashmap_entry_init(&e->ent, memhash(key, keylen));
+1 -1
blame.c
··· 435 435 436 436 static void free_fingerprint(struct fingerprint *f) 437 437 { 438 - hashmap_free(&f->map); 438 + hashmap_clear(&f->map); 439 439 free(f->entries); 440 440 } 441 441
+2 -3
bloom.c
··· 229 229 diffcore_std(&diffopt); 230 230 231 231 if (diff_queued_diff.nr <= settings->max_changed_paths) { 232 - struct hashmap pathmap; 232 + struct hashmap pathmap = HASHMAP_INIT(pathmap_cmp, NULL); 233 233 struct pathmap_hash_entry *e; 234 234 struct hashmap_iter iter; 235 - hashmap_init(&pathmap, pathmap_cmp, NULL, 0); 236 235 237 236 for (i = 0; i < diff_queued_diff.nr; i++) { 238 237 const char *path = diff_queued_diff.queue[i]->two->path; ··· 287 286 } 288 287 289 288 cleanup: 290 - hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry); 289 + hashmap_clear_and_free(&pathmap, struct pathmap_hash_entry, entry); 291 290 } else { 292 291 for (i = 0; i < diff_queued_diff.nr; i++) 293 292 diff_free_filepair(diff_queued_diff.queue[i]);
+4 -5
builtin/difftool.c
··· 342 342 const char *workdir, *tmp; 343 343 int ret = 0, i; 344 344 FILE *fp; 345 - struct hashmap working_tree_dups, submodules, symlinks2; 345 + struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp, 346 + NULL); 347 + struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL); 348 + struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL); 346 349 struct hashmap_iter iter; 347 350 struct pair_entry *entry; 348 351 struct index_state wtindex; ··· 382 385 ldir_len = ldir.len; 383 386 rdir_len = rdir.len; 384 387 wtdir_len = wtdir.len; 385 - 386 - hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0); 387 - hashmap_init(&submodules, pair_cmp, NULL, 0); 388 - hashmap_init(&symlinks2, pair_cmp, NULL, 0); 389 388 390 389 child.no_stdin = 1; 391 390 child.git_cmd = 1;
+3 -3
builtin/fetch.c
··· 393 393 item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid); 394 394 string_list_insert(&remote_refs_list, ref->name); 395 395 } 396 - hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent); 396 + hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent); 397 397 398 398 /* 399 399 * We may have a final lightweight tag that needs to be ··· 428 428 **tail = rm; 429 429 *tail = &rm->next; 430 430 } 431 - hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent); 431 + hashmap_clear_and_free(&remote_refs, struct refname_hash_entry, ent); 432 432 string_list_clear(&remote_refs_list, 0); 433 433 oidset_clear(&fetch_oids); 434 434 } ··· 573 573 } 574 574 } 575 575 if (existing_refs_populated) 576 - hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent); 576 + hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent); 577 577 578 578 return ref_map; 579 579 }
+4 -57
builtin/shortlog.c
··· 10 10 #include "shortlog.h" 11 11 #include "parse-options.h" 12 12 #include "trailer.h" 13 + #include "strmap.h" 13 14 14 15 static char const * const shortlog_usage[] = { 15 16 N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"), ··· 169 170 strbuf_release(&oneline); 170 171 } 171 172 172 - struct strset_item { 173 - struct hashmap_entry ent; 174 - char value[FLEX_ARRAY]; 175 - }; 176 - 177 - struct strset { 178 - struct hashmap map; 179 - }; 180 - 181 - #define STRSET_INIT { { NULL } } 182 - 183 - static int strset_item_hashcmp(const void *hash_data, 184 - const struct hashmap_entry *entry, 185 - const struct hashmap_entry *entry_or_key, 186 - const void *keydata) 187 - { 188 - const struct strset_item *a, *b; 189 - 190 - a = container_of(entry, const struct strset_item, ent); 191 - if (keydata) 192 - return strcmp(a->value, keydata); 193 - 194 - b = container_of(entry_or_key, const struct strset_item, ent); 195 - return strcmp(a->value, b->value); 196 - } 197 - 198 - /* 199 - * Adds "str" to the set if it was not already present; returns true if it was 200 - * already there. 201 - */ 202 - static int strset_check_and_add(struct strset *ss, const char *str) 203 - { 204 - unsigned int hash = strhash(str); 205 - struct strset_item *item; 206 - 207 - if (!ss->map.table) 208 - hashmap_init(&ss->map, strset_item_hashcmp, NULL, 0); 209 - 210 - if (hashmap_get_from_hash(&ss->map, hash, str)) 211 - return 1; 212 - 213 - FLEX_ALLOC_STR(item, value, str); 214 - hashmap_entry_init(&item->ent, hash); 215 - hashmap_add(&ss->map, &item->ent); 216 - return 0; 217 - } 218 - 219 - static void strset_clear(struct strset *ss) 220 - { 221 - if (!ss->map.table) 222 - return; 223 - hashmap_free_entries(&ss->map, struct strset_item, ent); 224 - } 225 - 226 173 static void insert_records_from_trailers(struct shortlog *log, 227 174 struct strset *dups, 228 175 struct commit *commit, ··· 253 200 if (!parse_ident(log, &ident, value)) 254 201 value = ident.buf; 255 202 256 - if (strset_check_and_add(dups, value)) 203 + if (!strset_add(dups, value)) 257 204 continue; 258 205 insert_one_record(log, value, oneline); 259 206 } ··· 291 238 log->email ? "%aN <%aE>" : "%aN", 292 239 &ident, &ctx); 293 240 if (!HAS_MULTI_BITS(log->groups) || 294 - !strset_check_and_add(&dups, ident.buf)) 241 + strset_add(&dups, ident.buf)) 295 242 insert_one_record(log, ident.buf, oneline_str); 296 243 } 297 244 if (log->groups & SHORTLOG_GROUP_COMMITTER) { ··· 300 247 log->email ? "%cN <%cE>" : "%cN", 301 248 &ident, &ctx); 302 249 if (!HAS_MULTI_BITS(log->groups) || 303 - !strset_check_and_add(&dups, ident.buf)) 250 + strset_add(&dups, ident.buf)) 304 251 insert_one_record(log, ident.buf, oneline_str); 305 252 } 306 253 if (log->groups & SHORTLOG_GROUP_TRAILER) {
+1 -1
config.c
··· 1963 1963 free(entry->key); 1964 1964 string_list_clear(&entry->value_list, 1); 1965 1965 } 1966 - hashmap_free_entries(&cs->config_hash, struct config_set_element, ent); 1966 + hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent); 1967 1967 cs->hash_initialized = 0; 1968 1968 free(cs->list.items); 1969 1969 cs->list.nr = 0;
+2 -2
diff.c
··· 6315 6315 if (o->color_moved == COLOR_MOVED_ZEBRA_DIM) 6316 6316 dim_moved_lines(o); 6317 6317 6318 - hashmap_free_entries(&add_lines, struct moved_entry, 6318 + hashmap_clear_and_free(&add_lines, struct moved_entry, 6319 6319 ent); 6320 - hashmap_free_entries(&del_lines, struct moved_entry, 6320 + hashmap_clear_and_free(&del_lines, struct moved_entry, 6321 6321 ent); 6322 6322 } 6323 6323
+1 -1
diffcore-rename.c
··· 407 407 renames += find_identical_files(&file_table, i, options); 408 408 409 409 /* Free the hash data structure and entries */ 410 - hashmap_free_entries(&file_table, struct file_similarity, entry); 410 + hashmap_clear_and_free(&file_table, struct file_similarity, entry); 411 411 412 412 return renames; 413 413 }
+4 -4
dir.c
··· 817 817 818 818 clear_hashmaps: 819 819 warning(_("disabling cone pattern matching")); 820 - hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent); 821 - hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent); 820 + hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent); 821 + hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent); 822 822 pl->use_cone_patterns = 0; 823 823 } 824 824 ··· 921 921 free(pl->patterns[i]); 922 922 free(pl->patterns); 923 923 free(pl->filebuf); 924 - hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent); 925 - hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent); 924 + hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent); 925 + hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent); 926 926 927 927 memset(pl, 0, sizeof(*pl)); 928 928 }
+50 -22
hashmap.c
··· 92 92 } 93 93 94 94 static inline int entry_equals(const struct hashmap *map, 95 - const struct hashmap_entry *e1, const struct hashmap_entry *e2, 96 - const void *keydata) 95 + const struct hashmap_entry *e1, 96 + const struct hashmap_entry *e2, 97 + const void *keydata) 97 98 { 98 99 return (e1 == e2) || 99 100 (e1->hash == e2->hash && ··· 101 102 } 102 103 103 104 static inline unsigned int bucket(const struct hashmap *map, 104 - const struct hashmap_entry *key) 105 + const struct hashmap_entry *key) 105 106 { 106 107 return key->hash & (map->tablesize - 1); 107 108 } ··· 113 114 114 115 static void rehash(struct hashmap *map, unsigned int newsize) 115 116 { 117 + /* map->table MUST NOT be NULL when this function is called */ 116 118 unsigned int i, oldsize = map->tablesize; 117 119 struct hashmap_entry **oldtable = map->table; 118 120 ··· 133 135 static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map, 134 136 const struct hashmap_entry *key, const void *keydata) 135 137 { 138 + /* map->table MUST NOT be NULL when this function is called */ 136 139 struct hashmap_entry **e = &map->table[bucket(map, key)]; 137 140 while (*e && !entry_equals(map, *e, key, keydata)) 138 141 e = &(*e)->next; ··· 148 151 } 149 152 150 153 void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, 151 - const void *cmpfn_data, size_t initial_size) 154 + const void *cmpfn_data, size_t initial_size) 152 155 { 153 156 unsigned int size = HASHMAP_INITIAL_SIZE; 154 157 ··· 171 174 map->do_count_items = 1; 172 175 } 173 176 174 - void hashmap_free_(struct hashmap *map, ssize_t entry_offset) 177 + static void free_individual_entries(struct hashmap *map, ssize_t entry_offset) 178 + { 179 + struct hashmap_iter iter; 180 + struct hashmap_entry *e; 181 + 182 + hashmap_iter_init(map, &iter); 183 + while ((e = hashmap_iter_next(&iter))) 184 + /* 185 + * like container_of, but using caller-calculated 186 + * offset (caller being hashmap_clear_and_free) 187 + */ 188 + free((char *)e - entry_offset); 189 + } 190 + 191 + void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset) 175 192 { 176 193 if (!map || !map->table) 177 194 return; 178 - if (entry_offset >= 0) { /* called by hashmap_free_entries */ 179 - struct hashmap_iter iter; 180 - struct hashmap_entry *e; 195 + if (entry_offset >= 0) /* called by hashmap_clear_entries */ 196 + free_individual_entries(map, entry_offset); 197 + memset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *)); 198 + map->shrink_at = 0; 199 + map->private_size = 0; 200 + } 181 201 182 - hashmap_iter_init(map, &iter); 183 - while ((e = hashmap_iter_next(&iter))) 184 - /* 185 - * like container_of, but using caller-calculated 186 - * offset (caller being hashmap_free_entries) 187 - */ 188 - free((char *)e - entry_offset); 189 - } 202 + void hashmap_clear_(struct hashmap *map, ssize_t entry_offset) 203 + { 204 + if (!map || !map->table) 205 + return; 206 + if (entry_offset >= 0) /* called by hashmap_clear_and_free */ 207 + free_individual_entries(map, entry_offset); 190 208 free(map->table); 191 209 memset(map, 0, sizeof(*map)); 192 210 } ··· 195 213 const struct hashmap_entry *key, 196 214 const void *keydata) 197 215 { 216 + if (!map->table) 217 + return NULL; 198 218 return *find_entry_ptr(map, key, keydata); 199 219 } 200 220 201 221 struct hashmap_entry *hashmap_get_next(const struct hashmap *map, 202 - const struct hashmap_entry *entry) 222 + const struct hashmap_entry *entry) 203 223 { 204 224 struct hashmap_entry *e = entry->next; 205 225 for (; e; e = e->next) ··· 210 230 211 231 void hashmap_add(struct hashmap *map, struct hashmap_entry *entry) 212 232 { 213 - unsigned int b = bucket(map, entry); 233 + unsigned int b; 214 234 235 + if (!map->table) 236 + alloc_table(map, HASHMAP_INITIAL_SIZE); 237 + 238 + b = bucket(map, entry); 215 239 /* add entry */ 216 240 entry->next = map->table[b]; 217 241 map->table[b] = entry; ··· 225 249 } 226 250 227 251 struct hashmap_entry *hashmap_remove(struct hashmap *map, 228 - const struct hashmap_entry *key, 229 - const void *keydata) 252 + const struct hashmap_entry *key, 253 + const void *keydata) 230 254 { 231 255 struct hashmap_entry *old; 232 - struct hashmap_entry **e = find_entry_ptr(map, key, keydata); 256 + struct hashmap_entry **e; 257 + 258 + if (!map->table) 259 + return NULL; 260 + e = find_entry_ptr(map, key, keydata); 233 261 if (!*e) 234 262 return NULL; 235 263 ··· 249 277 } 250 278 251 279 struct hashmap_entry *hashmap_put(struct hashmap *map, 252 - struct hashmap_entry *entry) 280 + struct hashmap_entry *entry) 253 281 { 254 282 struct hashmap_entry *old = hashmap_remove(map, entry, NULL); 255 283 hashmap_add(map, entry);
+71 -20
hashmap.h
··· 96 96 * } 97 97 * 98 98 * if (!strcmp("end", action)) { 99 - * hashmap_free_entries(&map, struct long2string, ent); 99 + * hashmap_clear_and_free(&map, struct long2string, ent); 100 100 * break; 101 101 * } 102 102 * } ··· 210 210 211 211 /* hashmap functions */ 212 212 213 + #define HASHMAP_INIT(fn, data) { .cmpfn = fn, .cmpfn_data = data, \ 214 + .do_count_items = 1 } 215 + 213 216 /* 214 217 * Initializes a hashmap structure. 215 218 * ··· 228 231 * prevent expensive resizing. If 0, the table is dynamically resized. 229 232 */ 230 233 void hashmap_init(struct hashmap *map, 231 - hashmap_cmp_fn equals_function, 232 - const void *equals_function_data, 233 - size_t initial_size); 234 + hashmap_cmp_fn equals_function, 235 + const void *equals_function_data, 236 + size_t initial_size); 234 237 235 - /* internal function for freeing hashmap */ 236 - void hashmap_free_(struct hashmap *map, ssize_t offset); 238 + /* internal functions for clearing or freeing hashmap */ 239 + void hashmap_partial_clear_(struct hashmap *map, ssize_t offset); 240 + void hashmap_clear_(struct hashmap *map, ssize_t offset); 241 + 242 + /* 243 + * Frees a hashmap structure and allocated memory for the table, but does not 244 + * free the entries nor anything they point to. 245 + * 246 + * Usage note: 247 + * 248 + * Many callers will need to iterate over all entries and free the data each 249 + * entry points to; in such a case, they can free the entry itself while at it. 250 + * Thus, you might see: 251 + * 252 + * hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) { 253 + * free(e->somefield); 254 + * free(e); 255 + * } 256 + * hashmap_clear(map); 257 + * 258 + * instead of 259 + * 260 + * hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) { 261 + * free(e->somefield); 262 + * } 263 + * hashmap_clear_and_free(map, struct my_entry_struct, hashmap_entry_name); 264 + * 265 + * to avoid the implicit extra loop over the entries. However, if there are 266 + * no special fields in your entry that need to be freed beyond the entry 267 + * itself, it is probably simpler to avoid the explicit loop and just call 268 + * hashmap_clear_and_free(). 269 + */ 270 + #define hashmap_clear(map) hashmap_clear_(map, -1) 271 + 272 + /* 273 + * Similar to hashmap_clear(), except that the table is no deallocated; it 274 + * is merely zeroed out but left the same size as before. If the hashmap 275 + * will be reused, this avoids the overhead of deallocating and 276 + * reallocating map->table. As with hashmap_clear(), you may need to free 277 + * the entries yourself before calling this function. 278 + */ 279 + #define hashmap_partial_clear(map) hashmap_partial_clear_(map, -1) 237 280 238 281 /* 239 - * Frees a hashmap structure and allocated memory, leaves entries undisturbed 282 + * Similar to hashmap_clear() but also frees all entries. @type is the 283 + * struct type of the entry where @member is the hashmap_entry struct used 284 + * to associate with @map. 285 + * 286 + * See usage note above hashmap_clear(). 240 287 */ 241 - #define hashmap_free(map) hashmap_free_(map, -1) 288 + #define hashmap_clear_and_free(map, type, member) \ 289 + hashmap_clear_(map, offsetof(type, member)) 242 290 243 291 /* 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 292 + * Similar to hashmap_partial_clear() but also frees all entries. @type is 293 + * the struct type of the entry where @member is the hashmap_entry struct 294 + * used to associate with @map. 295 + * 296 + * See usage note above hashmap_clear(). 246 297 */ 247 - #define hashmap_free_entries(map, type, member) \ 248 - hashmap_free_(map, offsetof(type, member)); 298 + #define hashmap_partial_clear_and_free(map, type, member) \ 299 + hashmap_partial_clear_(map, offsetof(type, member)) 249 300 250 301 /* hashmap_entry functions */ 251 302 ··· 261 312 * and if it is on stack, you can just let it go out of scope). 262 313 */ 263 314 static inline void hashmap_entry_init(struct hashmap_entry *e, 264 - unsigned int hash) 315 + unsigned int hash) 265 316 { 266 317 e->hash = hash; 267 318 e->next = NULL; ··· 303 354 * to `hashmap_cmp_fn` to decide whether the entry matches the key. 304 355 */ 305 356 struct hashmap_entry *hashmap_get(const struct hashmap *map, 306 - const struct hashmap_entry *key, 307 - const void *keydata); 357 + const struct hashmap_entry *key, 358 + const void *keydata); 308 359 309 360 /* 310 361 * Returns the hashmap entry for the specified hash code and key data, ··· 337 388 * call to `hashmap_get` or `hashmap_get_next`. 338 389 */ 339 390 struct hashmap_entry *hashmap_get_next(const struct hashmap *map, 340 - const struct hashmap_entry *entry); 391 + const struct hashmap_entry *entry); 341 392 342 393 /* 343 394 * Adds a hashmap entry. This allows to add duplicate entries (i.e. ··· 357 408 * Returns the replaced entry, or NULL if not found (i.e. the entry was added). 358 409 */ 359 410 struct hashmap_entry *hashmap_put(struct hashmap *map, 360 - struct hashmap_entry *entry); 411 + struct hashmap_entry *entry); 361 412 362 413 /* 363 414 * Adds or replaces a hashmap entry contained within @keyvar, ··· 379 430 * Argument explanation is the same as in `hashmap_get`. 380 431 */ 381 432 struct hashmap_entry *hashmap_remove(struct hashmap *map, 382 - const struct hashmap_entry *key, 383 - const void *keydata); 433 + const struct hashmap_entry *key, 434 + const void *keydata); 384 435 385 436 /* 386 437 * Removes a hashmap entry contained within @keyvar, ··· 422 473 423 474 /* Initializes the iterator and returns the first entry, if any. */ 424 475 static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map, 425 - struct hashmap_iter *iter) 476 + struct hashmap_iter *iter) 426 477 { 427 478 hashmap_iter_init(map, iter); 428 479 return hashmap_iter_next(iter);
+3 -3
merge-recursive.c
··· 2651 2651 free(e->target_file); 2652 2652 string_list_clear(&e->source_files, 0); 2653 2653 } 2654 - hashmap_free_entries(&collisions, struct collision_entry, ent); 2654 + hashmap_clear_and_free(&collisions, struct collision_entry, ent); 2655 2655 return renames; 2656 2656 } 2657 2657 ··· 2870 2870 strbuf_release(&e->new_dir); 2871 2871 /* possible_new_dirs already cleared in get_directory_renames */ 2872 2872 } 2873 - hashmap_free_entries(dir_renames, struct dir_rename_entry, ent); 2873 + hashmap_clear_and_free(dir_renames, struct dir_rename_entry, ent); 2874 2874 free(dir_renames); 2875 2875 2876 2876 free(pairs->queue); ··· 3497 3497 string_list_clear(entries, 1); 3498 3498 free(entries); 3499 3499 3500 - hashmap_free_entries(&opt->priv->current_file_dir_set, 3500 + hashmap_clear_and_free(&opt->priv->current_file_dir_set, 3501 3501 struct path_hashmap_entry, e); 3502 3502 3503 3503 if (clean < 0) {
+2 -2
name-hash.c
··· 726 726 return; 727 727 istate->name_hash_initialized = 0; 728 728 729 - hashmap_free(&istate->name_hash); 730 - hashmap_free_entries(&istate->dir_hash, struct dir_entry, ent); 729 + hashmap_clear(&istate->name_hash); 730 + hashmap_clear_and_free(&istate->dir_hash, struct dir_entry, ent); 731 731 }
+1 -1
object.c
··· 532 532 close_object_store(o); 533 533 o->packed_git = NULL; 534 534 535 - hashmap_free(&o->pack_map); 535 + hashmap_clear(&o->pack_map); 536 536 } 537 537 538 538 void parsed_object_pool_clear(struct parsed_object_pool *o)
+1 -1
oidmap.c
··· 27 27 return; 28 28 29 29 /* TODO: make oidmap itself not depend on struct layouts */ 30 - hashmap_free_(&map->map, free_entries ? 0 : -1); 30 + hashmap_clear_(&map->map, free_entries ? 0 : -1); 31 31 } 32 32 33 33 void *oidmap_get(const struct oidmap *map, const struct object_id *key)
+1 -1
patch-ids.c
··· 71 71 72 72 int free_patch_ids(struct patch_ids *ids) 73 73 { 74 - hashmap_free_entries(&ids->patches, struct patch_id, ent); 74 + hashmap_clear_and_free(&ids->patches, struct patch_id, ent); 75 75 return 0; 76 76 } 77 77
+2 -4
range-diff.c
··· 232 232 233 233 static void find_exact_matches(struct string_list *a, struct string_list *b) 234 234 { 235 - struct hashmap map; 235 + struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL); 236 236 int i; 237 - 238 - hashmap_init(&map, (hashmap_cmp_fn)patch_util_cmp, NULL, 0); 239 237 240 238 /* First, add the patches of a to a hash map */ 241 239 for (i = 0; i < a->nr; i++) { ··· 266 264 } 267 265 } 268 266 269 - hashmap_free(&map); 267 + hashmap_clear(&map); 270 268 } 271 269 272 270 static void diffsize_consume(void *data, char *line, unsigned long len)
+1 -1
ref-filter.c
··· 2230 2230 used_atom_cnt = 0; 2231 2231 2232 2232 if (ref_to_worktree_map.worktrees) { 2233 - hashmap_free_entries(&(ref_to_worktree_map.map), 2233 + hashmap_clear_and_free(&(ref_to_worktree_map.map), 2234 2234 struct ref_to_worktree_entry, ent); 2235 2235 free_worktrees(ref_to_worktree_map.worktrees); 2236 2236 ref_to_worktree_map.worktrees = NULL;
+2 -9
revision.c
··· 124 124 return strcmp(e1->path, e2->path); 125 125 } 126 126 127 - static void paths_and_oids_init(struct hashmap *map) 128 - { 129 - hashmap_init(map, path_and_oids_cmp, NULL, 0); 130 - } 131 - 132 127 static void paths_and_oids_clear(struct hashmap *map) 133 128 { 134 129 struct hashmap_iter iter; ··· 139 134 free(entry->path); 140 135 } 141 136 142 - hashmap_free_entries(map, struct path_and_oids_entry, ent); 137 + hashmap_clear_and_free(map, struct path_and_oids_entry, ent); 143 138 } 144 139 145 140 static void paths_and_oids_insert(struct hashmap *map, ··· 213 208 struct oidset *trees) 214 209 { 215 210 unsigned has_interesting = 0, has_uninteresting = 0; 216 - struct hashmap map; 211 + struct hashmap map = HASHMAP_INIT(path_and_oids_cmp, NULL); 217 212 struct hashmap_iter map_iter; 218 213 struct path_and_oids_entry *entry; 219 214 struct object_id *oid; ··· 236 231 /* Do not walk unless we have both types of trees. */ 237 232 if (!has_uninteresting || !has_interesting) 238 233 return; 239 - 240 - paths_and_oids_init(&map); 241 234 242 235 oidset_iter_init(trees, &iter); 243 236 while ((oid = oidset_iter_next(&iter))) {
+2 -2
sequencer.c
··· 5085 5085 5086 5086 oidmap_free(&commit2todo, 1); 5087 5087 oidmap_free(&state.commit2label, 1); 5088 - hashmap_free_entries(&state.labels, struct labels_entry, entry); 5088 + hashmap_clear_and_free(&state.labels, struct labels_entry, entry); 5089 5089 strbuf_release(&state.buf); 5090 5090 5091 5091 return 0; ··· 5601 5601 for (i = 0; i < todo_list->nr; i++) 5602 5602 free(subjects[i]); 5603 5603 free(subjects); 5604 - hashmap_free_entries(&subject2item, struct subject2item_entry, entry); 5604 + hashmap_clear_and_free(&subject2item, struct subject2item_entry, entry); 5605 5605 5606 5606 clear_commit_todo_item(&commit_todo); 5607 5607
+178
strmap.c
··· 1 + #include "git-compat-util.h" 2 + #include "strmap.h" 3 + #include "mem-pool.h" 4 + 5 + int cmp_strmap_entry(const void *hashmap_cmp_fn_data, 6 + const struct hashmap_entry *entry1, 7 + const struct hashmap_entry *entry2, 8 + const void *keydata) 9 + { 10 + const struct strmap_entry *e1, *e2; 11 + 12 + e1 = container_of(entry1, const struct strmap_entry, ent); 13 + e2 = container_of(entry2, const struct strmap_entry, ent); 14 + return strcmp(e1->key, e2->key); 15 + } 16 + 17 + static struct strmap_entry *find_strmap_entry(struct strmap *map, 18 + const char *str) 19 + { 20 + struct strmap_entry entry; 21 + hashmap_entry_init(&entry.ent, strhash(str)); 22 + entry.key = str; 23 + return hashmap_get_entry(&map->map, &entry, ent, NULL); 24 + } 25 + 26 + void strmap_init(struct strmap *map) 27 + { 28 + strmap_init_with_options(map, NULL, 1); 29 + } 30 + 31 + void strmap_init_with_options(struct strmap *map, 32 + struct mem_pool *pool, 33 + int strdup_strings) 34 + { 35 + hashmap_init(&map->map, cmp_strmap_entry, NULL, 0); 36 + map->pool = pool; 37 + map->strdup_strings = strdup_strings; 38 + } 39 + 40 + static void strmap_free_entries_(struct strmap *map, int free_values) 41 + { 42 + struct hashmap_iter iter; 43 + struct strmap_entry *e; 44 + 45 + if (!map) 46 + return; 47 + 48 + if (!free_values && map->pool) 49 + /* Memory other than util is owned by and freed with the pool */ 50 + return; 51 + 52 + /* 53 + * We need to iterate over the hashmap entries and free 54 + * e->key and e->value ourselves; hashmap has no API to 55 + * take care of that for us. Since we're already iterating over 56 + * the hashmap, though, might as well free e too and avoid the need 57 + * to make some call into the hashmap API to do that. 58 + */ 59 + hashmap_for_each_entry(&map->map, &iter, e, ent) { 60 + if (free_values) 61 + free(e->value); 62 + if (!map->pool) 63 + free(e); 64 + } 65 + } 66 + 67 + void strmap_clear(struct strmap *map, int free_values) 68 + { 69 + strmap_free_entries_(map, free_values); 70 + hashmap_clear(&map->map); 71 + } 72 + 73 + void strmap_partial_clear(struct strmap *map, int free_values) 74 + { 75 + strmap_free_entries_(map, free_values); 76 + hashmap_partial_clear(&map->map); 77 + } 78 + 79 + static struct strmap_entry *create_entry(struct strmap *map, 80 + const char *str, 81 + void *data) 82 + { 83 + struct strmap_entry *entry; 84 + 85 + if (map->strdup_strings) { 86 + if (!map->pool) { 87 + FLEXPTR_ALLOC_STR(entry, key, str); 88 + } else { 89 + size_t len = st_add(strlen(str), 1); /* include NUL */ 90 + entry = mem_pool_alloc(map->pool, 91 + st_add(sizeof(*entry), len)); 92 + memcpy(entry + 1, str, len); 93 + entry->key = (void *)(entry + 1); 94 + } 95 + } else if (!map->pool) { 96 + entry = xmalloc(sizeof(*entry)); 97 + } else { 98 + entry = mem_pool_alloc(map->pool, sizeof(*entry)); 99 + } 100 + hashmap_entry_init(&entry->ent, strhash(str)); 101 + if (!map->strdup_strings) 102 + entry->key = str; 103 + entry->value = data; 104 + return entry; 105 + } 106 + 107 + void *strmap_put(struct strmap *map, const char *str, void *data) 108 + { 109 + struct strmap_entry *entry = find_strmap_entry(map, str); 110 + 111 + if (entry) { 112 + void *old = entry->value; 113 + entry->value = data; 114 + return old; 115 + } 116 + 117 + entry = create_entry(map, str, data); 118 + hashmap_add(&map->map, &entry->ent); 119 + return NULL; 120 + } 121 + 122 + struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str) 123 + { 124 + return find_strmap_entry(map, str); 125 + } 126 + 127 + void *strmap_get(struct strmap *map, const char *str) 128 + { 129 + struct strmap_entry *entry = find_strmap_entry(map, str); 130 + return entry ? entry->value : NULL; 131 + } 132 + 133 + int strmap_contains(struct strmap *map, const char *str) 134 + { 135 + return find_strmap_entry(map, str) != NULL; 136 + } 137 + 138 + void strmap_remove(struct strmap *map, const char *str, int free_value) 139 + { 140 + struct strmap_entry entry, *ret; 141 + hashmap_entry_init(&entry.ent, strhash(str)); 142 + entry.key = str; 143 + ret = hashmap_remove_entry(&map->map, &entry, ent, NULL); 144 + if (!ret) 145 + return; 146 + if (free_value) 147 + free(ret->value); 148 + if (!map->pool) 149 + free(ret); 150 + } 151 + 152 + void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt) 153 + { 154 + struct strmap_entry *entry = find_strmap_entry(&map->map, str); 155 + if (entry) { 156 + intptr_t *whence = (intptr_t*)&entry->value; 157 + *whence += amt; 158 + } 159 + else 160 + strintmap_set(map, str, map->default_value + amt); 161 + } 162 + 163 + int strset_add(struct strset *set, const char *str) 164 + { 165 + /* 166 + * Cannot use strmap_put() because it'll return NULL in both cases: 167 + * - cannot find str: NULL means "not found" 168 + * - does find str: NULL is the value associated with str 169 + */ 170 + struct strmap_entry *entry = find_strmap_entry(&set->map, str); 171 + 172 + if (entry) 173 + return 0; 174 + 175 + entry = create_entry(&set->map, str, NULL); 176 + hashmap_add(&set->map.map, &entry->ent); 177 + return 1; 178 + }
+268
strmap.h
··· 1 + #ifndef STRMAP_H 2 + #define STRMAP_H 3 + 4 + #include "hashmap.h" 5 + 6 + struct mem_pool; 7 + struct strmap { 8 + struct hashmap map; 9 + struct mem_pool *pool; 10 + unsigned int strdup_strings:1; 11 + }; 12 + 13 + struct strmap_entry { 14 + struct hashmap_entry ent; 15 + const char *key; 16 + void *value; 17 + /* strmap_entry may be allocated extra space to store the key at end */ 18 + }; 19 + 20 + int cmp_strmap_entry(const void *hashmap_cmp_fn_data, 21 + const struct hashmap_entry *entry1, 22 + const struct hashmap_entry *entry2, 23 + const void *keydata); 24 + 25 + #define STRMAP_INIT { \ 26 + .map = HASHMAP_INIT(cmp_strmap_entry, NULL), \ 27 + .strdup_strings = 1, \ 28 + } 29 + #define STRINTMAP_INIT { \ 30 + .map = STRMAP_INIT, \ 31 + .default_value = 0, \ 32 + } 33 + #define STRSET_INIT { .map = STRMAP_INIT } 34 + 35 + /* 36 + * Initialize the members of the strmap. Any keys added to the strmap will 37 + * be strdup'ed with their memory managed by the strmap. 38 + */ 39 + void strmap_init(struct strmap *map); 40 + 41 + /* 42 + * Same as strmap_init, but for those who want to control the memory management 43 + * carefully instead of using the default of strdup_strings=1 and pool=NULL. 44 + */ 45 + void strmap_init_with_options(struct strmap *map, 46 + struct mem_pool *pool, 47 + int strdup_strings); 48 + 49 + /* 50 + * Remove all entries from the map, releasing any allocated resources. 51 + */ 52 + void strmap_clear(struct strmap *map, int free_values); 53 + 54 + /* 55 + * Similar to strmap_clear() but leaves map->map->table allocated and 56 + * pre-sized so that subsequent uses won't need as many rehashings. 57 + */ 58 + void strmap_partial_clear(struct strmap *map, int free_values); 59 + 60 + /* 61 + * Insert "str" into the map, pointing to "data". 62 + * 63 + * If an entry for "str" already exists, its data pointer is overwritten, and 64 + * the original data pointer returned. Otherwise, returns NULL. 65 + */ 66 + void *strmap_put(struct strmap *map, const char *str, void *data); 67 + 68 + /* 69 + * Return the strmap_entry mapped by "str", or NULL if there is not such 70 + * an item in map. 71 + */ 72 + struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str); 73 + 74 + /* 75 + * Return the data pointer mapped by "str", or NULL if the entry does not 76 + * exist. 77 + */ 78 + void *strmap_get(struct strmap *map, const char *str); 79 + 80 + /* 81 + * Return non-zero iff "str" is present in the map. This differs from 82 + * strmap_get() in that it can distinguish entries with a NULL data pointer. 83 + */ 84 + int strmap_contains(struct strmap *map, const char *str); 85 + 86 + /* 87 + * Remove the given entry from the strmap. If the string isn't in the 88 + * strmap, the map is not altered. 89 + */ 90 + void strmap_remove(struct strmap *map, const char *str, int free_value); 91 + 92 + /* 93 + * Return how many entries the strmap has. 94 + */ 95 + static inline unsigned int strmap_get_size(struct strmap *map) 96 + { 97 + return hashmap_get_size(&map->map); 98 + } 99 + 100 + /* 101 + * Return whether the strmap is empty. 102 + */ 103 + static inline int strmap_empty(struct strmap *map) 104 + { 105 + return strmap_get_size(map) == 0; 106 + } 107 + 108 + /* 109 + * iterate through @map using @iter, @var is a pointer to a type strmap_entry 110 + */ 111 + #define strmap_for_each_entry(mystrmap, iter, var) \ 112 + hashmap_for_each_entry(&(mystrmap)->map, iter, var, ent) 113 + 114 + 115 + /* 116 + * strintmap: 117 + * A map of string -> int, typecasting the void* of strmap to an int. 118 + * 119 + * Primary differences: 120 + * 1) Since the void* value is just an int in disguise, there is no value 121 + * to free. (Thus one fewer argument to strintmap_clear) 122 + * 2) strintmap_get() returns an int, or returns the default_value if the 123 + * key is not found in the strintmap. 124 + * 3) No strmap_put() equivalent; strintmap_set() and strintmap_incr() 125 + * instead. 126 + */ 127 + 128 + struct strintmap { 129 + struct strmap map; 130 + int default_value; 131 + }; 132 + 133 + #define strintmap_for_each_entry(mystrmap, iter, var) \ 134 + strmap_for_each_entry(&(mystrmap)->map, iter, var) 135 + 136 + static inline void strintmap_init(struct strintmap *map, int default_value) 137 + { 138 + strmap_init(&map->map); 139 + map->default_value = default_value; 140 + } 141 + 142 + static inline void strintmap_init_with_options(struct strintmap *map, 143 + int default_value, 144 + struct mem_pool *pool, 145 + int strdup_strings) 146 + { 147 + strmap_init_with_options(&map->map, pool, strdup_strings); 148 + map->default_value = default_value; 149 + } 150 + 151 + static inline void strintmap_clear(struct strintmap *map) 152 + { 153 + strmap_clear(&map->map, 0); 154 + } 155 + 156 + static inline void strintmap_partial_clear(struct strintmap *map) 157 + { 158 + strmap_partial_clear(&map->map, 0); 159 + } 160 + 161 + static inline int strintmap_contains(struct strintmap *map, const char *str) 162 + { 163 + return strmap_contains(&map->map, str); 164 + } 165 + 166 + static inline void strintmap_remove(struct strintmap *map, const char *str) 167 + { 168 + return strmap_remove(&map->map, str, 0); 169 + } 170 + 171 + static inline int strintmap_empty(struct strintmap *map) 172 + { 173 + return strmap_empty(&map->map); 174 + } 175 + 176 + static inline unsigned int strintmap_get_size(struct strintmap *map) 177 + { 178 + return strmap_get_size(&map->map); 179 + } 180 + 181 + /* 182 + * Returns the value for str in the map. If str isn't found in the map, 183 + * the map's default_value is returned. 184 + */ 185 + static inline int strintmap_get(struct strintmap *map, const char *str) 186 + { 187 + struct strmap_entry *result = strmap_get_entry(&map->map, str); 188 + if (!result) 189 + return map->default_value; 190 + return (intptr_t)result->value; 191 + } 192 + 193 + static inline void strintmap_set(struct strintmap *map, const char *str, 194 + intptr_t v) 195 + { 196 + strmap_put(&map->map, str, (void *)v); 197 + } 198 + 199 + /* 200 + * Increment the value for str by amt. If str isn't in the map, add it and 201 + * set its value to default_value + amt. 202 + */ 203 + void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt); 204 + 205 + /* 206 + * strset: 207 + * A set of strings. 208 + * 209 + * Primary differences with strmap: 210 + * 1) The value is always NULL, and ignored. As there is no value to free, 211 + * there is one fewer argument to strset_clear 212 + * 2) No strset_get() because there is no value. 213 + * 3) No strset_put(); use strset_add() instead. 214 + */ 215 + 216 + struct strset { 217 + struct strmap map; 218 + }; 219 + 220 + #define strset_for_each_entry(mystrset, iter, var) \ 221 + strmap_for_each_entry(&(mystrset)->map, iter, var) 222 + 223 + static inline void strset_init(struct strset *set) 224 + { 225 + strmap_init(&set->map); 226 + } 227 + 228 + static inline void strset_init_with_options(struct strset *set, 229 + struct mem_pool *pool, 230 + int strdup_strings) 231 + { 232 + strmap_init_with_options(&set->map, pool, strdup_strings); 233 + } 234 + 235 + static inline void strset_clear(struct strset *set) 236 + { 237 + strmap_clear(&set->map, 0); 238 + } 239 + 240 + static inline void strset_partial_clear(struct strset *set) 241 + { 242 + strmap_partial_clear(&set->map, 0); 243 + } 244 + 245 + static inline int strset_contains(struct strset *set, const char *str) 246 + { 247 + return strmap_contains(&set->map, str); 248 + } 249 + 250 + static inline void strset_remove(struct strset *set, const char *str) 251 + { 252 + return strmap_remove(&set->map, str, 0); 253 + } 254 + 255 + static inline int strset_empty(struct strset *set) 256 + { 257 + return strmap_empty(&set->map); 258 + } 259 + 260 + static inline unsigned int strset_get_size(struct strset *set) 261 + { 262 + return strmap_get_size(&set->map); 263 + } 264 + 265 + /* Returns 1 if str is added to the set; returns 0 if str was already in set */ 266 + int strset_add(struct strset *set, const char *str); 267 + 268 + #endif /* STRMAP_H */
+2 -2
submodule-config.c
··· 103 103 ent /* member name */) 104 104 free_one_config(entry); 105 105 106 - hashmap_free_entries(&cache->for_path, struct submodule_entry, ent); 107 - hashmap_free_entries(&cache->for_name, struct submodule_entry, ent); 106 + hashmap_clear_and_free(&cache->for_path, struct submodule_entry, ent); 107 + hashmap_clear_and_free(&cache->for_name, struct submodule_entry, ent); 108 108 cache->initialized = 0; 109 109 cache->gitmodules_read = 0; 110 110 }
+4 -5
t/helper/test-hashmap.c
··· 110 110 hashmap_add(&map, &entries[i]->ent); 111 111 } 112 112 113 - hashmap_free(&map); 113 + hashmap_clear(&map); 114 114 } 115 115 } else { 116 116 /* test map lookups */ ··· 130 130 } 131 131 } 132 132 133 - hashmap_free(&map); 133 + hashmap_clear(&map); 134 134 } 135 135 } 136 136 ··· 151 151 int cmd__hashmap(int argc, const char **argv) 152 152 { 153 153 struct strbuf line = STRBUF_INIT; 154 - struct hashmap map; 155 154 int icase; 155 + struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase); 156 156 157 157 /* init hash map */ 158 158 icase = argc > 1 && !strcmp("ignorecase", argv[1]); 159 - hashmap_init(&map, test_entry_cmp, &icase, 0); 160 159 161 160 /* process commands from stdin */ 162 161 while (strbuf_getline(&line, stdin) != EOF) { ··· 262 261 } 263 262 264 263 strbuf_release(&line); 265 - hashmap_free_entries(&map, struct test_entry, ent); 264 + hashmap_clear_and_free(&map, struct test_entry, ent); 266 265 return 0; 267 266 }