Merge branch 'ew/hashmap'

Code clean-up of the hashmap API, both users and implementation.

* ew/hashmap:
  hashmap_entry: remove first member requirement from docs
  hashmap: remove type arg from hashmap_{get,put,remove}_entry
  OFFSETOF_VAR macro to simplify hashmap iterators
  hashmap: introduce hashmap_free_entries
  hashmap: hashmap_{put,remove} return hashmap_entry *
  hashmap: use *_entry APIs for iteration
  hashmap_cmp_fn takes hashmap_entry params
  hashmap_get{,_from_hash} return "struct hashmap_entry *"
  hashmap: use *_entry APIs to wrap container_of
  hashmap_get_next returns "struct hashmap_entry *"
  introduce container_of macro
  hashmap_put takes "struct hashmap_entry *"
  hashmap_remove takes "const struct hashmap_entry *"
  hashmap_get takes "const struct hashmap_entry *"
  hashmap_add takes "struct hashmap_entry *"
  hashmap_get_next takes "const struct hashmap_entry *"
  hashmap_entry_init takes "struct hashmap_entry *"
  packfile: use hashmap_entry in delta_base_cache_entry
  coccicheck: detect hashmap_entry.hash assignment
  diff: use hashmap_entry_init on moved_entry.ent
This commit is contained in:
Junio C Hamano
2019-10-15 13:48:01 +09:00
31 changed files with 667 additions and 381 deletions

24
attr.c
View File

@ -62,7 +62,7 @@ static struct attr_hashmap g_attr_hashmap;
/* The container for objects stored in "struct attr_hashmap" */ /* The container for objects stored in "struct attr_hashmap" */
struct attr_hash_entry { struct attr_hash_entry {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
const char *key; /* the key; memory should be owned by value */ const char *key; /* the key; memory should be owned by value */
size_t keylen; /* length of the key */ size_t keylen; /* length of the key */
void *value; /* the stored value */ void *value; /* the stored value */
@ -70,12 +70,14 @@ struct attr_hash_entry {
/* attr_hashmap comparison function */ /* attr_hashmap comparison function */
static int attr_hash_entry_cmp(const void *unused_cmp_data, static int attr_hash_entry_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct attr_hash_entry *a = entry; const struct attr_hash_entry *a, *b;
const struct attr_hash_entry *b = entry_or_key;
a = container_of(eptr, const struct attr_hash_entry, ent);
b = container_of(entry_or_key, const struct attr_hash_entry, ent);
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen); return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
} }
@ -98,10 +100,10 @@ static void *attr_hashmap_get(struct attr_hashmap *map,
if (!map->map.tablesize) if (!map->map.tablesize)
attr_hashmap_init(map); attr_hashmap_init(map);
hashmap_entry_init(&k, memhash(key, keylen)); hashmap_entry_init(&k.ent, memhash(key, keylen));
k.key = key; k.key = key;
k.keylen = keylen; k.keylen = keylen;
e = hashmap_get(&map->map, &k, NULL); e = hashmap_get_entry(&map->map, &k, ent, NULL);
return e ? e->value : NULL; return e ? e->value : NULL;
} }
@ -117,12 +119,12 @@ static void attr_hashmap_add(struct attr_hashmap *map,
attr_hashmap_init(map); attr_hashmap_init(map);
e = xmalloc(sizeof(struct attr_hash_entry)); e = xmalloc(sizeof(struct attr_hash_entry));
hashmap_entry_init(e, memhash(key, keylen)); hashmap_entry_init(&e->ent, memhash(key, keylen));
e->key = key; e->key = key;
e->keylen = keylen; e->keylen = keylen;
e->value = value; e->value = value;
hashmap_add(&map->map, e); hashmap_add(&map->map, &e->ent);
} }
struct all_attrs_item { struct all_attrs_item {
@ -161,12 +163,12 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
if (size != check->all_attrs_nr) { if (size != check->all_attrs_nr) {
struct attr_hash_entry *e; struct attr_hash_entry *e;
struct hashmap_iter iter; struct hashmap_iter iter;
hashmap_iter_init(&map->map, &iter);
REALLOC_ARRAY(check->all_attrs, size); REALLOC_ARRAY(check->all_attrs, size);
check->all_attrs_nr = size; check->all_attrs_nr = size;
while ((e = hashmap_iter_next(&iter))) { hashmap_for_each_entry(&map->map, &iter, e,
ent /* member name */) {
const struct git_attr *a = e->value; const struct git_attr *a = e->value;
check->all_attrs[a->attr_nr].attr = a; check->all_attrs[a->attr_nr].attr = a;
} }

25
blame.c
View File

@ -417,14 +417,15 @@ static void get_fingerprint(struct fingerprint *result,
/* Ignore whitespace pairs */ /* Ignore whitespace pairs */
if (hash == 0) if (hash == 0)
continue; continue;
hashmap_entry_init(entry, hash); hashmap_entry_init(&entry->entry, hash);
found_entry = hashmap_get(&result->map, entry, NULL); found_entry = hashmap_get_entry(&result->map, entry,
/* member name */ entry, NULL);
if (found_entry) { if (found_entry) {
found_entry->count += 1; found_entry->count += 1;
} else { } else {
entry->count = 1; entry->count = 1;
hashmap_add(&result->map, entry); hashmap_add(&result->map, &entry->entry);
++entry; ++entry;
} }
} }
@ -432,7 +433,7 @@ static void get_fingerprint(struct fingerprint *result,
static void free_fingerprint(struct fingerprint *f) static void free_fingerprint(struct fingerprint *f)
{ {
hashmap_free(&f->map, 0); hashmap_free(&f->map);
free(f->entries); free(f->entries);
} }
@ -449,10 +450,10 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)
struct hashmap_iter iter; struct hashmap_iter iter;
const struct fingerprint_entry *entry_a, *entry_b; const struct fingerprint_entry *entry_a, *entry_b;
hashmap_iter_init(&b->map, &iter); hashmap_for_each_entry(&b->map, &iter, entry_b,
entry /* member name */) {
while ((entry_b = hashmap_iter_next(&iter))) { entry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);
if ((entry_a = hashmap_get(&a->map, entry_b, NULL))) { if (entry_a) {
intersection += entry_a->count < entry_b->count ? intersection += entry_a->count < entry_b->count ?
entry_a->count : entry_b->count; entry_a->count : entry_b->count;
} }
@ -470,10 +471,12 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)
hashmap_iter_init(&b->map, &iter); hashmap_iter_init(&b->map, &iter);
while ((entry_b = hashmap_iter_next(&iter))) { hashmap_for_each_entry(&b->map, &iter, entry_b,
if ((entry_a = hashmap_get(&a->map, entry_b, NULL))) { entry /* member name */) {
entry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);
if (entry_a) {
if (entry_a->count <= entry_b->count) if (entry_a->count <= entry_b->count)
hashmap_remove(&a->map, entry_b, NULL); hashmap_remove(&a->map, &entry_b->entry, NULL);
else else
entry_a->count -= entry_b->count; entry_a->count -= entry_b->count;
} }

View File

@ -63,19 +63,22 @@ static const char *prio_names[] = {
}; };
static int commit_name_neq(const void *unused_cmp_data, static int commit_name_neq(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *peeled) const void *peeled)
{ {
const struct commit_name *cn1 = entry; const struct commit_name *cn1, *cn2;
const struct commit_name *cn2 = entry_or_key;
cn1 = container_of(eptr, const struct commit_name, entry);
cn2 = container_of(entry_or_key, const struct commit_name, entry);
return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled); return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);
} }
static inline struct commit_name *find_commit_name(const struct object_id *peeled) static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{ {
return hashmap_get_from_hash(&names, oidhash(peeled), peeled); return hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled,
struct commit_name, entry);
} }
static int replace_name(struct commit_name *e, static int replace_name(struct commit_name *e,
@ -122,8 +125,8 @@ static void add_to_known_names(const char *path,
if (!e) { if (!e) {
e = xmalloc(sizeof(struct commit_name)); e = xmalloc(sizeof(struct commit_name));
oidcpy(&e->peeled, peeled); oidcpy(&e->peeled, peeled);
hashmap_entry_init(e, oidhash(peeled)); hashmap_entry_init(&e->entry, oidhash(peeled));
hashmap_add(&names, e); hashmap_add(&names, &e->entry);
e->path = NULL; e->path = NULL;
} }
e->tag = tag; e->tag = tag;
@ -329,8 +332,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
struct commit_name *n; struct commit_name *n;
init_commit_names(&commit_names); init_commit_names(&commit_names);
n = hashmap_iter_first(&names, &iter); hashmap_for_each_entry(&names, &iter, n,
for (; n; n = hashmap_iter_next(&iter)) { entry /* member name */) {
c = lookup_commit_reference_gently(the_repository, c = lookup_commit_reference_gently(the_repository,
&n->peeled, 1); &n->peeled, 1);
if (c) if (c)

View File

@ -125,12 +125,15 @@ struct working_tree_entry {
}; };
static int working_tree_entry_cmp(const void *unused_cmp_data, static int working_tree_entry_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct working_tree_entry *a = entry; const struct working_tree_entry *a, *b;
const struct working_tree_entry *b = entry_or_key;
a = container_of(eptr, const struct working_tree_entry, entry);
b = container_of(entry_or_key, const struct working_tree_entry, entry);
return strcmp(a->path, b->path); return strcmp(a->path, b->path);
} }
@ -145,12 +148,14 @@ struct pair_entry {
}; };
static int pair_cmp(const void *unused_cmp_data, static int pair_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct pair_entry *a = entry; const struct pair_entry *a, *b;
const struct pair_entry *b = entry_or_key;
a = container_of(eptr, const struct pair_entry, entry);
b = container_of(entry_or_key, const struct pair_entry, entry);
return strcmp(a->path, b->path); return strcmp(a->path, b->path);
} }
@ -161,14 +166,14 @@ static void add_left_or_right(struct hashmap *map, const char *path,
struct pair_entry *e, *existing; struct pair_entry *e, *existing;
FLEX_ALLOC_STR(e, path, path); FLEX_ALLOC_STR(e, path, path);
hashmap_entry_init(e, strhash(path)); hashmap_entry_init(&e->entry, strhash(path));
existing = hashmap_get(map, e, NULL); existing = hashmap_get_entry(map, e, entry, NULL);
if (existing) { if (existing) {
free(e); free(e);
e = existing; e = existing;
} else { } else {
e->left[0] = e->right[0] = '\0'; e->left[0] = e->right[0] = '\0';
hashmap_add(map, e); hashmap_add(map, &e->entry);
} }
strlcpy(is_right ? e->right : e->left, content, PATH_MAX); strlcpy(is_right ? e->right : e->left, content, PATH_MAX);
} }
@ -179,12 +184,14 @@ struct path_entry {
}; };
static int path_entry_cmp(const void *unused_cmp_data, static int path_entry_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *key) const void *key)
{ {
const struct path_entry *a = entry; const struct path_entry *a, *b;
const struct path_entry *b = entry_or_key;
a = container_of(eptr, const struct path_entry, entry);
b = container_of(entry_or_key, const struct path_entry, entry);
return strcmp(a->path, key ? key : b->path); return strcmp(a->path, key ? key : b->path);
} }
@ -234,8 +241,8 @@ static void changed_files(struct hashmap *result, const char *index_path,
while (!strbuf_getline_nul(&buf, fp)) { while (!strbuf_getline_nul(&buf, fp)) {
struct path_entry *entry; struct path_entry *entry;
FLEX_ALLOC_STR(entry, path, buf.buf); FLEX_ALLOC_STR(entry, path, buf.buf);
hashmap_entry_init(entry, strhash(buf.buf)); hashmap_entry_init(&entry->entry, strhash(buf.buf));
hashmap_add(result, entry); hashmap_add(result, &entry->entry);
} }
fclose(fp); fclose(fp);
if (finish_command(&diff_files)) if (finish_command(&diff_files))
@ -461,12 +468,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Avoid duplicate working_tree entries */ /* Avoid duplicate working_tree entries */
FLEX_ALLOC_STR(entry, path, dst_path); FLEX_ALLOC_STR(entry, path, dst_path);
hashmap_entry_init(entry, strhash(dst_path)); hashmap_entry_init(&entry->entry, strhash(dst_path));
if (hashmap_get(&working_tree_dups, entry, NULL)) { if (hashmap_get(&working_tree_dups, &entry->entry,
NULL)) {
free(entry); free(entry);
continue; continue;
} }
hashmap_add(&working_tree_dups, entry); hashmap_add(&working_tree_dups, &entry->entry);
if (!use_wt_file(workdir, dst_path, &roid)) { if (!use_wt_file(workdir, dst_path, &roid)) {
if (checkout_path(rmode, &roid, dst_path, if (checkout_path(rmode, &roid, dst_path,
@ -530,8 +538,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* temporary file to both the left and right directories to show the * temporary file to both the left and right directories to show the
* change in the recorded SHA1 for the submodule. * change in the recorded SHA1 for the submodule.
*/ */
hashmap_iter_init(&submodules, &iter); hashmap_for_each_entry(&submodules, &iter, entry,
while ((entry = hashmap_iter_next(&iter))) { entry /* member name */) {
if (*entry->left) { if (*entry->left) {
add_path(&ldir, ldir_len, entry->path); add_path(&ldir, ldir_len, entry->path);
ensure_leading_directories(ldir.buf); ensure_leading_directories(ldir.buf);
@ -549,8 +557,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* shows only the link itself, not the contents of the link target. * shows only the link itself, not the contents of the link target.
* This loop replicates that behavior. * This loop replicates that behavior.
*/ */
hashmap_iter_init(&symlinks2, &iter); hashmap_for_each_entry(&symlinks2, &iter, entry,
while ((entry = hashmap_iter_next(&iter))) { entry /* member name */) {
if (*entry->left) { if (*entry->left) {
add_path(&ldir, ldir_len, entry->path); add_path(&ldir, ldir_len, entry->path);
ensure_leading_directories(ldir.buf); ensure_leading_directories(ldir.buf);

View File

@ -127,10 +127,15 @@ struct anonymized_entry {
}; };
static int anonymized_entry_cmp(const void *unused_cmp_data, static int anonymized_entry_cmp(const void *unused_cmp_data,
const void *va, const void *vb, const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct anonymized_entry *a = va, *b = vb; const struct anonymized_entry *a, *b;
a = container_of(eptr, const struct anonymized_entry, hash);
b = container_of(entry_or_key, const struct anonymized_entry, hash);
return a->orig_len != b->orig_len || return a->orig_len != b->orig_len ||
memcmp(a->orig, b->orig, a->orig_len); memcmp(a->orig, b->orig, a->orig_len);
} }
@ -149,10 +154,10 @@ static const void *anonymize_mem(struct hashmap *map,
if (!map->cmpfn) if (!map->cmpfn)
hashmap_init(map, anonymized_entry_cmp, NULL, 0); hashmap_init(map, anonymized_entry_cmp, NULL, 0);
hashmap_entry_init(&key, memhash(orig, *len)); hashmap_entry_init(&key.hash, memhash(orig, *len));
key.orig = orig; key.orig = orig;
key.orig_len = *len; key.orig_len = *len;
ret = hashmap_get(map, &key, NULL); ret = hashmap_get_entry(map, &key, hash, NULL);
if (!ret) { if (!ret) {
ret = xmalloc(sizeof(*ret)); ret = xmalloc(sizeof(*ret));
@ -161,7 +166,7 @@ static const void *anonymize_mem(struct hashmap *map,
ret->orig_len = *len; ret->orig_len = *len;
ret->anon = generate(orig, len); ret->anon = generate(orig, len);
ret->anon_len = *len; ret->anon_len = *len;
hashmap_put(map, ret); hashmap_put(map, &ret->hash);
} }
*len = ret->anon_len; *len = ret->anon_len;

View File

@ -264,20 +264,21 @@ static void create_fetch_oidset(struct ref **head, struct oidset *out)
} }
struct refname_hash_entry { struct refname_hash_entry {
struct hashmap_entry ent; /* must be the first member */ struct hashmap_entry ent;
struct object_id oid; struct object_id oid;
int ignore; int ignore;
char refname[FLEX_ARRAY]; char refname[FLEX_ARRAY];
}; };
static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data, static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
const void *e1_, const struct hashmap_entry *eptr,
const void *e2_, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct refname_hash_entry *e1 = e1_; const struct refname_hash_entry *e1, *e2;
const struct refname_hash_entry *e2 = e2_;
e1 = container_of(eptr, const struct refname_hash_entry, ent);
e2 = container_of(entry_or_key, const struct refname_hash_entry, ent);
return strcmp(e1->refname, keydata ? keydata : e2->refname); return strcmp(e1->refname, keydata ? keydata : e2->refname);
} }
@ -289,9 +290,9 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
size_t len = strlen(refname); size_t len = strlen(refname);
FLEX_ALLOC_MEM(ent, refname, refname, len); FLEX_ALLOC_MEM(ent, refname, refname, len);
hashmap_entry_init(ent, strhash(refname)); hashmap_entry_init(&ent->ent, strhash(refname));
oidcpy(&ent->oid, oid); oidcpy(&ent->oid, oid);
hashmap_add(map, ent); hashmap_add(map, &ent->ent);
return ent; return ent;
} }
@ -380,7 +381,7 @@ static void find_non_local_tags(const struct ref *refs,
item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid); item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
string_list_insert(&remote_refs_list, ref->name); string_list_insert(&remote_refs_list, ref->name);
} }
hashmap_free(&existing_refs, 1); hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
/* /*
* We may have a final lightweight tag that needs to be * We may have a final lightweight tag that needs to be
@ -398,8 +399,10 @@ static void find_non_local_tags(const struct ref *refs,
for_each_string_list_item(remote_ref_item, &remote_refs_list) { for_each_string_list_item(remote_ref_item, &remote_refs_list) {
const char *refname = remote_ref_item->string; const char *refname = remote_ref_item->string;
struct ref *rm; struct ref *rm;
unsigned int hash = strhash(refname);
item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname); item = hashmap_get_entry_from_hash(&remote_refs, hash, refname,
struct refname_hash_entry, ent);
if (!item) if (!item)
BUG("unseen remote ref?"); BUG("unseen remote ref?");
@ -413,7 +416,7 @@ static void find_non_local_tags(const struct ref *refs,
**tail = rm; **tail = rm;
*tail = &rm->next; *tail = &rm->next;
} }
hashmap_free(&remote_refs, 1); hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
string_list_clear(&remote_refs_list, 0); string_list_clear(&remote_refs_list, 0);
oidset_clear(&fetch_oids); oidset_clear(&fetch_oids);
} }
@ -532,17 +535,18 @@ static struct ref *get_ref_map(struct remote *remote,
if (rm->peer_ref) { if (rm->peer_ref) {
const char *refname = rm->peer_ref->name; const char *refname = rm->peer_ref->name;
struct refname_hash_entry *peer_item; struct refname_hash_entry *peer_item;
unsigned int hash = strhash(refname);
peer_item = hashmap_get_from_hash(&existing_refs, peer_item = hashmap_get_entry_from_hash(&existing_refs,
strhash(refname), hash, refname,
refname); struct refname_hash_entry, ent);
if (peer_item) { if (peer_item) {
struct object_id *old_oid = &peer_item->oid; struct object_id *old_oid = &peer_item->oid;
oidcpy(&rm->peer_ref->old_oid, old_oid); oidcpy(&rm->peer_ref->old_oid, old_oid);
} }
} }
} }
hashmap_free(&existing_refs, 1); hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
return ref_map; return ref_map;
} }

View File

@ -1856,9 +1856,9 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
if (git_config_parse_key(key, &normalized_key, NULL)) if (git_config_parse_key(key, &normalized_key, NULL))
return NULL; return NULL;
hashmap_entry_init(&k, strhash(normalized_key)); hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key; k.key = normalized_key;
found_entry = hashmap_get(&cs->config_hash, &k, NULL); found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
free(normalized_key); free(normalized_key);
return found_entry; return found_entry;
} }
@ -1877,10 +1877,10 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
*/ */
if (!e) { if (!e) {
e = xmalloc(sizeof(*e)); e = xmalloc(sizeof(*e));
hashmap_entry_init(e, strhash(key)); hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key); e->key = xstrdup(key);
string_list_init(&e->value_list, 1); string_list_init(&e->value_list, 1);
hashmap_add(&cs->config_hash, e); hashmap_add(&cs->config_hash, &e->ent);
} }
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value)); si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
@ -1908,12 +1908,14 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
} }
static int config_set_element_cmp(const void *unused_cmp_data, static int config_set_element_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct config_set_element *e1 = entry; const struct config_set_element *e1, *e2;
const struct config_set_element *e2 = entry_or_key;
e1 = container_of(eptr, const struct config_set_element, ent);
e2 = container_of(entry_or_key, const struct config_set_element, ent);
return strcmp(e1->key, e2->key); return strcmp(e1->key, e2->key);
} }
@ -1934,12 +1936,12 @@ void git_configset_clear(struct config_set *cs)
if (!cs->hash_initialized) if (!cs->hash_initialized)
return; return;
hashmap_iter_init(&cs->config_hash, &iter); hashmap_for_each_entry(&cs->config_hash, &iter, entry,
while ((entry = hashmap_iter_next(&iter))) { ent /* member name */) {
free(entry->key); free(entry->key);
string_list_clear(&entry->value_list, 1); string_list_clear(&entry->value_list, 1);
} }
hashmap_free(&cs->config_hash, 1); hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
cs->hash_initialized = 0; cs->hash_initialized = 0;
free(cs->list.items); free(cs->list.items);
cs->list.nr = 0; cs->list.nr = 0;

View File

@ -0,0 +1,16 @@
@ hashmap_entry_init_usage @
expression E;
struct hashmap_entry HME;
@@
- HME.hash = E;
+ hashmap_entry_init(&HME, E);
@@
identifier f !~ "^hashmap_entry_init$";
expression E;
struct hashmap_entry *HMEP;
@@
f(...) {<...
- HMEP->hash = E;
+ hashmap_entry_init(HMEP, E);
...>}

31
diff.c
View File

@ -933,16 +933,18 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
} }
static int moved_entry_cmp(const void *hashmap_cmp_fn_data, static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct diff_options *diffopt = hashmap_cmp_fn_data; const struct diff_options *diffopt = hashmap_cmp_fn_data;
const struct moved_entry *a = entry; const struct moved_entry *a, *b;
const struct moved_entry *b = entry_or_key;
unsigned flags = diffopt->color_moved_ws_handling unsigned flags = diffopt->color_moved_ws_handling
& XDF_WHITESPACE_FLAGS; & XDF_WHITESPACE_FLAGS;
a = container_of(eptr, const struct moved_entry, ent);
b = container_of(entry_or_key, const struct moved_entry, ent);
if (diffopt->color_moved_ws_handling & if (diffopt->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
/* /*
@ -964,8 +966,9 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
struct moved_entry *ret = xmalloc(sizeof(*ret)); struct moved_entry *ret = xmalloc(sizeof(*ret));
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no]; struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS; unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
ret->ent.hash = xdiff_hash_string(l->line, l->len, flags); hashmap_entry_init(&ret->ent, hash);
ret->es = l; ret->es = l;
ret->next_line = NULL; ret->next_line = NULL;
@ -1002,7 +1005,7 @@ static void add_lines_to_move_detection(struct diff_options *o,
if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s) if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
prev_line->next_line = key; prev_line->next_line = key;
hashmap_add(hm, key); hashmap_add(hm, &key->ent);
prev_line = key; prev_line = key;
} }
} }
@ -1018,7 +1021,7 @@ static void pmb_advance_or_null(struct diff_options *o,
struct moved_entry *prev = pmb[i].match; struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ? struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL; prev->next_line : NULL;
if (cur && !hm->cmpfn(o, cur, match, NULL)) { if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
pmb[i].match = cur; pmb[i].match = cur;
} else { } else {
pmb[i].match = NULL; pmb[i].match = NULL;
@ -1035,7 +1038,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
int i; int i;
char *got_match = xcalloc(1, pmb_nr); char *got_match = xcalloc(1, pmb_nr);
for (; match; match = hashmap_get_next(hm, match)) { hashmap_for_each_entry_from(hm, match, ent) {
for (i = 0; i < pmb_nr; i++) { for (i = 0; i < pmb_nr; i++) {
struct moved_entry *prev = pmb[i].match; struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ? struct moved_entry *cur = (prev && prev->next_line) ?
@ -1143,13 +1146,13 @@ static void mark_color_as_moved(struct diff_options *o,
case DIFF_SYMBOL_PLUS: case DIFF_SYMBOL_PLUS:
hm = del_lines; hm = del_lines;
key = prepare_entry(o, n); key = prepare_entry(o, n);
match = hashmap_get(hm, key, NULL); match = hashmap_get_entry(hm, key, ent, NULL);
free(key); free(key);
break; break;
case DIFF_SYMBOL_MINUS: case DIFF_SYMBOL_MINUS:
hm = add_lines; hm = add_lines;
key = prepare_entry(o, n); key = prepare_entry(o, n);
match = hashmap_get(hm, key, NULL); match = hashmap_get_entry(hm, key, ent, NULL);
free(key); free(key);
break; break;
default: default:
@ -1188,7 +1191,7 @@ static void mark_color_as_moved(struct diff_options *o,
* The current line is the start of a new block. * The current line is the start of a new block.
* Setup the set of potential blocks. * Setup the set of potential blocks.
*/ */
for (; match; match = hashmap_get_next(hm, match)) { hashmap_for_each_entry_from(hm, match, ent) {
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc); ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
if (o->color_moved_ws_handling & if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) { COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
@ -6230,8 +6233,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM) if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
dim_moved_lines(o); dim_moved_lines(o);
hashmap_free(&add_lines, 1); hashmap_free_entries(&add_lines, struct moved_entry,
hashmap_free(&del_lines, 1); ent);
hashmap_free_entries(&del_lines, struct moved_entry,
ent);
} }
for (i = 0; i < esm.nr; i++) for (i = 0; i < esm.nr; i++)

View File

@ -274,18 +274,17 @@ static int find_identical_files(struct hashmap *srcs,
struct diff_options *options) struct diff_options *options)
{ {
int renames = 0; int renames = 0;
struct diff_filespec *target = rename_dst[dst_index].two; struct diff_filespec *target = rename_dst[dst_index].two;
struct file_similarity *p, *best = NULL; struct file_similarity *p, *best = NULL;
int i = 100, best_score = -1; int i = 100, best_score = -1;
unsigned int hash = hash_filespec(options->repo, target);
/* /*
* Find the best source match for specified destination. * Find the best source match for specified destination.
*/ */
p = hashmap_get_from_hash(srcs, p = hashmap_get_entry_from_hash(srcs, hash, NULL,
hash_filespec(options->repo, target), struct file_similarity, entry);
NULL); hashmap_for_each_entry_from(srcs, p, entry) {
for (; p; p = hashmap_get_next(srcs, p)) {
int score; int score;
struct diff_filespec *source = p->filespec; struct diff_filespec *source = p->filespec;
@ -329,8 +328,8 @@ static void insert_file_table(struct repository *r,
entry->index = index; entry->index = index;
entry->filespec = filespec; entry->filespec = filespec;
hashmap_entry_init(entry, hash_filespec(r, filespec)); hashmap_entry_init(&entry->entry, hash_filespec(r, filespec));
hashmap_add(table, entry); hashmap_add(table, &entry->entry);
} }
/* /*
@ -359,7 +358,7 @@ static int find_exact_renames(struct diff_options *options)
renames += find_identical_files(&file_table, i, options); renames += find_identical_files(&file_table, i, options);
/* Free the hash data structure and entries */ /* Free the hash data structure and entries */
hashmap_free(&file_table, 1); hashmap_free_entries(&file_table, struct file_similarity, entry);
return renames; return renames;
} }

View File

@ -1313,4 +1313,42 @@ void unleak_memory(const void *ptr, size_t len);
*/ */
#include "banned.h" #include "banned.h"
/*
* container_of - Get the address of an object containing a field.
*
* @ptr: pointer to the field.
* @type: type of the object.
* @member: name of the field within the object.
*/
#define container_of(ptr, type, member) \
((type *) ((char *)(ptr) - offsetof(type, member)))
/*
* helper function for `container_of_or_null' to avoid multiple
* evaluation of @ptr
*/
static inline void *container_of_or_null_offset(void *ptr, size_t offset)
{
return ptr ? (char *)ptr - offset : NULL;
}
/*
* like `container_of', but allows returned value to be NULL
*/
#define container_of_or_null(ptr, type, member) \
(type *)container_of_or_null_offset(ptr, offsetof(type, member))
/*
* like offsetof(), but takes a pointer to a a variable of type which
* contains @member, instead of a specified type.
* @ptr is subject to multiple evaluation since we can't rely on __typeof__
* everywhere.
*/
#if defined(__GNUC__) /* clang sets this, too */
#define OFFSETOF_VAR(ptr, member) offsetof(__typeof__(*ptr), member)
#else /* !__GNUC__ */
#define OFFSETOF_VAR(ptr, member) \
((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))
#endif /* !__GNUC__ */
#endif #endif

View File

@ -140,8 +140,8 @@ static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
} }
static int always_equal(const void *unused_cmp_data, static int always_equal(const void *unused_cmp_data,
const void *unused1, const struct hashmap_entry *unused1,
const void *unused2, const struct hashmap_entry *unused2,
const void *unused_keydata) const void *unused_keydata)
{ {
return 0; return 0;
@ -171,41 +171,49 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
map->do_count_items = 1; map->do_count_items = 1;
} }
void hashmap_free(struct hashmap *map, int free_entries) void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
{ {
if (!map || !map->table) if (!map || !map->table)
return; return;
if (free_entries) { if (entry_offset >= 0) { /* called by hashmap_free_entries */
struct hashmap_iter iter; struct hashmap_iter iter;
struct hashmap_entry *e; struct hashmap_entry *e;
hashmap_iter_init(map, &iter); hashmap_iter_init(map, &iter);
while ((e = hashmap_iter_next(&iter))) while ((e = hashmap_iter_next(&iter)))
free(e); /*
* like container_of, but using caller-calculated
* offset (caller being hashmap_free_entries)
*/
free((char *)e - entry_offset);
} }
free(map->table); free(map->table);
memset(map, 0, sizeof(*map)); memset(map, 0, sizeof(*map));
} }
void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata) struct hashmap_entry *hashmap_get(const struct hashmap *map,
const struct hashmap_entry *key,
const void *keydata)
{ {
return *find_entry_ptr(map, key, keydata); return *find_entry_ptr(map, key, keydata);
} }
void *hashmap_get_next(const struct hashmap *map, const void *entry) struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
const struct hashmap_entry *entry)
{ {
struct hashmap_entry *e = ((struct hashmap_entry *) entry)->next; struct hashmap_entry *e = entry->next;
for (; e; e = e->next) for (; e; e = e->next)
if (entry_equals(map, entry, e, NULL)) if (entry_equals(map, entry, e, NULL))
return e; return e;
return NULL; return NULL;
} }
void hashmap_add(struct hashmap *map, void *entry) void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
{ {
unsigned int b = bucket(map, entry); unsigned int b = bucket(map, entry);
/* add entry */ /* add entry */
((struct hashmap_entry *) entry)->next = map->table[b]; entry->next = map->table[b];
map->table[b] = entry; map->table[b] = entry;
/* fix size and rehash if appropriate */ /* fix size and rehash if appropriate */
@ -216,7 +224,9 @@ void hashmap_add(struct hashmap *map, void *entry)
} }
} }
void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata) struct hashmap_entry *hashmap_remove(struct hashmap *map,
const struct hashmap_entry *key,
const void *keydata)
{ {
struct hashmap_entry *old; struct hashmap_entry *old;
struct hashmap_entry **e = find_entry_ptr(map, key, keydata); struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
@ -238,7 +248,8 @@ void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
return old; return old;
} }
void *hashmap_put(struct hashmap *map, void *entry) struct hashmap_entry *hashmap_put(struct hashmap *map,
struct hashmap_entry *entry)
{ {
struct hashmap_entry *old = hashmap_remove(map, entry, NULL); struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
hashmap_add(map, entry); hashmap_add(map, entry);
@ -252,7 +263,7 @@ void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)
iter->next = NULL; iter->next = NULL;
} }
void *hashmap_iter_next(struct hashmap_iter *iter) struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter)
{ {
struct hashmap_entry *current = iter->next; struct hashmap_entry *current = iter->next;
for (;;) { for (;;) {
@ -275,10 +286,15 @@ struct pool_entry {
}; };
static int pool_entry_cmp(const void *unused_cmp_data, static int pool_entry_cmp(const void *unused_cmp_data,
const struct pool_entry *e1, const struct hashmap_entry *eptr,
const struct pool_entry *e2, const struct hashmap_entry *entry_or_key,
const unsigned char *keydata) const void *keydata)
{ {
const struct pool_entry *e1, *e2;
e1 = container_of(eptr, const struct pool_entry, ent);
e2 = container_of(entry_or_key, const struct pool_entry, ent);
return e1->data != keydata && return e1->data != keydata &&
(e1->len != e2->len || memcmp(e1->data, keydata, e1->len)); (e1->len != e2->len || memcmp(e1->data, keydata, e1->len));
} }
@ -290,18 +306,18 @@ const void *memintern(const void *data, size_t len)
/* initialize string pool hashmap */ /* initialize string pool hashmap */
if (!map.tablesize) if (!map.tablesize)
hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, NULL, 0); hashmap_init(&map, pool_entry_cmp, NULL, 0);
/* lookup interned string in pool */ /* lookup interned string in pool */
hashmap_entry_init(&key, memhash(data, len)); hashmap_entry_init(&key.ent, memhash(data, len));
key.len = len; key.len = len;
e = hashmap_get(&map, &key, data); e = hashmap_get_entry(&map, &key, ent, data);
if (!e) { if (!e) {
/* not found: create it */ /* not found: create it */
FLEX_ALLOC_MEM(e, data, data, len); FLEX_ALLOC_MEM(e, data, data, len);
hashmap_entry_init(e, key.ent.hash); hashmap_entry_init(&e->ent, key.ent.hash);
e->len = len; e->len = len;
hashmap_add(&map, e); hashmap_add(&map, &e->ent);
} }
return e->data; return e->data;
} }

168
hashmap.h
View File

@ -13,7 +13,7 @@
* *
* struct hashmap map; * struct hashmap map;
* struct long2string { * struct long2string {
* struct hashmap_entry ent; // must be the first member! * struct hashmap_entry ent;
* long key; * long key;
* char value[FLEX_ARRAY]; // be careful with allocating on stack! * char value[FLEX_ARRAY]; // be careful with allocating on stack!
* }; * };
@ -21,12 +21,16 @@
* #define COMPARE_VALUE 1 * #define COMPARE_VALUE 1
* *
* static int long2string_cmp(const void *hashmap_cmp_fn_data, * static int long2string_cmp(const void *hashmap_cmp_fn_data,
* const struct long2string *e1, * const struct hashmap_entry *eptr,
* const struct long2string *e2, * const struct hashmap_entry *entry_or_key,
* const void *keydata) * const void *keydata)
* { * {
* const char *string = keydata; * const char *string = keydata;
* unsigned flags = *(unsigned *)hashmap_cmp_fn_data; * unsigned flags = *(unsigned *)hashmap_cmp_fn_data;
* const struct long2string *e1, *e2;
*
* e1 = container_of(eptr, const struct long2string, ent);
* e2 = container_of(entry_or_key, const struct long2string, ent);
* *
* if (flags & COMPARE_VALUE) * if (flags & COMPARE_VALUE)
* return e1->key != e2->key || * return e1->key != e2->key ||
@ -41,54 +45,58 @@
* char value[255], action[32]; * char value[255], action[32];
* unsigned flags = 0; * unsigned flags = 0;
* *
* hashmap_init(&map, (hashmap_cmp_fn) long2string_cmp, &flags, 0); * hashmap_init(&map, long2string_cmp, &flags, 0);
* *
* while (scanf("%s %ld %s", action, &key, value)) { * while (scanf("%s %ld %s", action, &key, value)) {
* *
* if (!strcmp("add", action)) { * if (!strcmp("add", action)) {
* struct long2string *e; * struct long2string *e;
* FLEX_ALLOC_STR(e, value, value); * FLEX_ALLOC_STR(e, value, value);
* hashmap_entry_init(e, memhash(&key, sizeof(long))); * hashmap_entry_init(&e->ent, memhash(&key, sizeof(long)));
* e->key = key; * e->key = key;
* hashmap_add(&map, e); * hashmap_add(&map, &e->ent);
* } * }
* *
* if (!strcmp("print_all_by_key", action)) { * if (!strcmp("print_all_by_key", action)) {
* struct long2string k, *e; * struct long2string k, *e;
* hashmap_entry_init(&k, memhash(&key, sizeof(long))); * hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));
* k.key = key; * k.key = key;
* *
* flags &= ~COMPARE_VALUE; * flags &= ~COMPARE_VALUE;
* e = hashmap_get(&map, &k, NULL); * e = hashmap_get_entry(&map, &k, ent, NULL);
* if (e) { * if (e) {
* printf("first: %ld %s\n", e->key, e->value); * printf("first: %ld %s\n", e->key, e->value);
* while ((e = hashmap_get_next(&map, e))) * while ((e = hashmap_get_next_entry(&map, e,
* struct long2string, ent))) {
* printf("found more: %ld %s\n", e->key, e->value); * printf("found more: %ld %s\n", e->key, e->value);
* } * }
* } * }
* }
* *
* if (!strcmp("has_exact_match", action)) { * if (!strcmp("has_exact_match", action)) {
* struct long2string *e; * struct long2string *e;
* FLEX_ALLOC_STR(e, value, value); * FLEX_ALLOC_STR(e, value, value);
* hashmap_entry_init(e, memhash(&key, sizeof(long))); * hashmap_entry_init(&e->ent, memhash(&key, sizeof(long)));
* e->key = key; * e->key = key;
* *
* flags |= COMPARE_VALUE; * flags |= COMPARE_VALUE;
* printf("%sfound\n", hashmap_get(&map, e, NULL) ? "" : "not "); * printf("%sfound\n",
* hashmap_get(&map, &e->ent, NULL) ? "" : "not ");
* free(e); * free(e);
* } * }
* *
* if (!strcmp("has_exact_match_no_heap_alloc", action)) { * if (!strcmp("has_exact_match_no_heap_alloc", action)) {
* struct long2string k; * struct long2string k;
* hashmap_entry_init(&k, memhash(&key, sizeof(long))); * hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));
* k.key = key; * k.key = key;
* *
* flags |= COMPARE_VALUE; * flags |= COMPARE_VALUE;
* printf("%sfound\n", hashmap_get(&map, &k, value) ? "" : "not "); * printf("%sfound\n",
* hashmap_get(&map, &k->ent, value) ? "" : "not ");
* } * }
* *
* if (!strcmp("end", action)) { * if (!strcmp("end", action)) {
* hashmap_free(&map, 1); * hashmap_free_entries(&map, struct long2string, ent);
* break; * break;
* } * }
* } * }
@ -133,7 +141,7 @@ static inline unsigned int oidhash(const struct object_id *oid)
/* /*
* struct hashmap_entry is an opaque structure representing an entry in the * struct hashmap_entry is an opaque structure representing an entry in the
* hash table, which must be used as first member of user data structures. * hash table.
* Ideally it should be followed by an int-sized member to prevent unused * Ideally it should be followed by an int-sized member to prevent unused
* memory on 64-bit systems due to alignment. * memory on 64-bit systems due to alignment.
*/ */
@ -168,7 +176,8 @@ struct hashmap_entry {
* The `hashmap_cmp_fn_data` entry is the pointer given in the init function. * The `hashmap_cmp_fn_data` entry is the pointer given in the init function.
*/ */
typedef int (*hashmap_cmp_fn)(const void *hashmap_cmp_fn_data, typedef int (*hashmap_cmp_fn)(const void *hashmap_cmp_fn_data,
const void *entry, const void *entry_or_key, const struct hashmap_entry *entry,
const struct hashmap_entry *entry_or_key,
const void *keydata); const void *keydata);
/* /*
@ -223,13 +232,20 @@ void hashmap_init(struct hashmap *map,
const void *equals_function_data, const void *equals_function_data,
size_t initial_size); size_t initial_size);
/* internal function for freeing hashmap */
void hashmap_free_(struct hashmap *map, ssize_t offset);
/* /*
* Frees a hashmap structure and allocated memory. * Frees a hashmap structure and allocated memory, leaves entries undisturbed
*
* If `free_entries` is true, each hashmap_entry in the map is freed as well
* using stdlibs free().
*/ */
void hashmap_free(struct hashmap *map, int free_entries); #define hashmap_free(map) hashmap_free_(map, -1)
/*
* Frees @map and all entries. @type is the struct type of the entry
* where @member is the hashmap_entry struct used to associate with @map
*/
#define hashmap_free_entries(map, type, member) \
hashmap_free_(map, offsetof(type, member));
/* hashmap_entry functions */ /* hashmap_entry functions */
@ -244,9 +260,9 @@ void hashmap_free(struct hashmap *map, int free_entries);
* your structure was allocated with xmalloc(), you can just free(3) it, * your structure was allocated with xmalloc(), you can just free(3) it,
* and if it is on stack, you can just let it go out of scope). * and if it is on stack, you can just let it go out of scope).
*/ */
static inline void hashmap_entry_init(void *entry, unsigned int hash) static inline void hashmap_entry_init(struct hashmap_entry *e,
unsigned int hash)
{ {
struct hashmap_entry *e = entry;
e->hash = hash; e->hash = hash;
e->next = NULL; e->next = NULL;
} }
@ -286,7 +302,8 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)
* If an entry with matching hash code is found, `key` and `keydata` are passed * If an entry with matching hash code is found, `key` and `keydata` are passed
* to `hashmap_cmp_fn` to decide whether the entry matches the key. * to `hashmap_cmp_fn` to decide whether the entry matches the key.
*/ */
void *hashmap_get(const struct hashmap *map, const void *key, struct hashmap_entry *hashmap_get(const struct hashmap *map,
const struct hashmap_entry *key,
const void *keydata); const void *keydata);
/* /*
@ -301,7 +318,8 @@ void *hashmap_get(const struct hashmap *map, const void *key,
* `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry * `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry
* structure that should not be used in the comparison. * structure that should not be used in the comparison.
*/ */
static inline void *hashmap_get_from_hash(const struct hashmap *map, static inline struct hashmap_entry *hashmap_get_from_hash(
const struct hashmap *map,
unsigned int hash, unsigned int hash,
const void *keydata) const void *keydata)
{ {
@ -318,7 +336,8 @@ static inline void *hashmap_get_from_hash(const struct hashmap *map,
* `entry` is the hashmap_entry to start the search from, obtained via a previous * `entry` is the hashmap_entry to start the search from, obtained via a previous
* call to `hashmap_get` or `hashmap_get_next`. * call to `hashmap_get` or `hashmap_get_next`.
*/ */
void *hashmap_get_next(const struct hashmap *map, const void *entry); struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
const struct hashmap_entry *entry);
/* /*
* Adds a hashmap entry. This allows to add duplicate entries (i.e. * Adds a hashmap entry. This allows to add duplicate entries (i.e.
@ -327,7 +346,7 @@ void *hashmap_get_next(const struct hashmap *map, const void *entry);
* `map` is the hashmap structure. * `map` is the hashmap structure.
* `entry` is the entry to add. * `entry` is the entry to add.
*/ */
void hashmap_add(struct hashmap *map, void *entry); void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);
/* /*
* Adds or replaces a hashmap entry. If the hashmap contains duplicate * Adds or replaces a hashmap entry. If the hashmap contains duplicate
@ -337,7 +356,20 @@ void hashmap_add(struct hashmap *map, void *entry);
* `entry` is the entry to add or replace. * `entry` is the entry to add or replace.
* Returns the replaced entry, or NULL if not found (i.e. the entry was added). * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
*/ */
void *hashmap_put(struct hashmap *map, void *entry); struct hashmap_entry *hashmap_put(struct hashmap *map,
struct hashmap_entry *entry);
/*
* Adds or replaces a hashmap entry contained within @keyvar,
* where @keyvar is a pointer to a struct containing a
* "struct hashmap_entry" @member.
*
* Returns the replaced pointer which is of the same type as @keyvar,
* or NULL if not found.
*/
#define hashmap_put_entry(map, keyvar, member) \
container_of_or_null_offset(hashmap_put(map, &(keyvar)->member), \
OFFSETOF_VAR(keyvar, member))
/* /*
* Removes a hashmap entry matching the specified key. If the hashmap contains * Removes a hashmap entry matching the specified key. If the hashmap contains
@ -346,9 +378,25 @@ void *hashmap_put(struct hashmap *map, void *entry);
* *
* Argument explanation is the same as in `hashmap_get`. * Argument explanation is the same as in `hashmap_get`.
*/ */
void *hashmap_remove(struct hashmap *map, const void *key, struct hashmap_entry *hashmap_remove(struct hashmap *map,
const struct hashmap_entry *key,
const void *keydata); const void *keydata);
/*
* Removes a hashmap entry contained within @keyvar,
* where @keyvar is a pointer to a struct containing a
* "struct hashmap_entry" @member.
*
* See `hashmap_get` for an explanation of @keydata
*
* Returns the replaced pointer which is of the same type as @keyvar,
* or NULL if not found.
*/
#define hashmap_remove_entry(map, keyvar, member, keydata) \
container_of_or_null_offset( \
hashmap_remove(map, &(keyvar)->member, keydata), \
OFFSETOF_VAR(keyvar, member))
/* /*
* Returns the `bucket` an entry is stored in. * Returns the `bucket` an entry is stored in.
* Useful for multithreaded read access. * Useful for multithreaded read access.
@ -370,16 +418,74 @@ struct hashmap_iter {
void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter); void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
/* Returns the next hashmap_entry, or NULL if there are no more entries. */ /* Returns the next hashmap_entry, or NULL if there are no more entries. */
void *hashmap_iter_next(struct hashmap_iter *iter); struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter);
/* Initializes the iterator and returns the first entry, if any. */ /* Initializes the iterator and returns the first entry, if any. */
static inline void *hashmap_iter_first(struct hashmap *map, static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
struct hashmap_iter *iter) struct hashmap_iter *iter)
{ {
hashmap_iter_init(map, iter); hashmap_iter_init(map, iter);
return hashmap_iter_next(iter); return hashmap_iter_next(iter);
} }
/*
* returns the first entry in @map using @iter, where the entry is of
* @type (e.g. "struct foo") and @member is the name of the
* "struct hashmap_entry" in @type
*/
#define hashmap_iter_first_entry(map, iter, type, member) \
container_of_or_null(hashmap_iter_first(map, iter), type, member)
/* internal macro for hashmap_for_each_entry */
#define hashmap_iter_next_entry_offset(iter, offset) \
container_of_or_null_offset(hashmap_iter_next(iter), offset)
/* internal macro for hashmap_for_each_entry */
#define hashmap_iter_first_entry_offset(map, iter, offset) \
container_of_or_null_offset(hashmap_iter_first(map, iter), offset)
/*
* iterate through @map using @iter, @var is a pointer to a type
* containing a @member which is a "struct hashmap_entry"
*/
#define hashmap_for_each_entry(map, iter, var, member) \
for (var = hashmap_iter_first_entry_offset(map, iter, \
OFFSETOF_VAR(var, member)); \
var; \
var = hashmap_iter_next_entry_offset(iter, \
OFFSETOF_VAR(var, member)))
/*
* returns a pointer of type matching @keyvar, or NULL if nothing found.
* @keyvar is a pointer to a struct containing a
* "struct hashmap_entry" @member.
*/
#define hashmap_get_entry(map, keyvar, member, keydata) \
container_of_or_null_offset( \
hashmap_get(map, &(keyvar)->member, keydata), \
OFFSETOF_VAR(keyvar, member))
#define hashmap_get_entry_from_hash(map, hash, keydata, type, member) \
container_of_or_null(hashmap_get_from_hash(map, hash, keydata), \
type, member)
/*
* returns the next equal pointer to @var, or NULL if not found.
* @var is a pointer of any type containing "struct hashmap_entry"
* @member is the name of the "struct hashmap_entry" field
*/
#define hashmap_get_next_entry(map, var, member) \
container_of_or_null_offset(hashmap_get_next(map, &(var)->member), \
OFFSETOF_VAR(var, member))
/*
* iterate @map starting from @var, where @var is a pointer of @type
* and @member is the name of the "struct hashmap_entry" field in @type
*/
#define hashmap_for_each_entry_from(map, var, member) \
for (; \
var; \
var = hashmap_get_next_entry(map, var, member))
/* /*
* Disable item counting and automatic rehashing when adding/removing items. * Disable item counting and automatic rehashing when adding/removing items.
* *

View File

@ -45,14 +45,16 @@ struct path_hashmap_entry {
}; };
static int path_hashmap_cmp(const void *cmp_data, static int path_hashmap_cmp(const void *cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct path_hashmap_entry *a = entry; const struct path_hashmap_entry *a, *b;
const struct path_hashmap_entry *b = entry_or_key;
const char *key = keydata; const char *key = keydata;
a = container_of(eptr, const struct path_hashmap_entry, e);
b = container_of(entry_or_key, const struct path_hashmap_entry, e);
if (ignore_case) if (ignore_case)
return strcasecmp(a->path, key ? key : b->path); return strcasecmp(a->path, key ? key : b->path);
else else
@ -75,7 +77,7 @@ static unsigned int path_hash(const char *path)
* in get_directory_renames() for details * in get_directory_renames() for details
*/ */
struct dir_rename_entry { struct dir_rename_entry {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
char *dir; char *dir;
unsigned non_unique_new_dir:1; unsigned non_unique_new_dir:1;
struct strbuf new_dir; struct strbuf new_dir;
@ -89,18 +91,20 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
if (dir == NULL) if (dir == NULL)
return NULL; return NULL;
hashmap_entry_init(&key, strhash(dir)); hashmap_entry_init(&key.ent, strhash(dir));
key.dir = dir; key.dir = dir;
return hashmap_get(hashmap, &key, NULL); return hashmap_get_entry(hashmap, &key, ent, NULL);
} }
static int dir_rename_cmp(const void *unused_cmp_data, static int dir_rename_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct dir_rename_entry *e1 = entry; const struct dir_rename_entry *e1, *e2;
const struct dir_rename_entry *e2 = entry_or_key;
e1 = container_of(eptr, const struct dir_rename_entry, ent);
e2 = container_of(entry_or_key, const struct dir_rename_entry, ent);
return strcmp(e1->dir, e2->dir); return strcmp(e1->dir, e2->dir);
} }
@ -113,7 +117,7 @@ static void dir_rename_init(struct hashmap *map)
static void dir_rename_entry_init(struct dir_rename_entry *entry, static void dir_rename_entry_init(struct dir_rename_entry *entry,
char *directory) char *directory)
{ {
hashmap_entry_init(entry, strhash(directory)); hashmap_entry_init(&entry->ent, strhash(directory));
entry->dir = directory; entry->dir = directory;
entry->non_unique_new_dir = 0; entry->non_unique_new_dir = 0;
strbuf_init(&entry->new_dir, 0); strbuf_init(&entry->new_dir, 0);
@ -121,7 +125,7 @@ static void dir_rename_entry_init(struct dir_rename_entry *entry,
} }
struct collision_entry { struct collision_entry {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
char *target_file; char *target_file;
struct string_list source_files; struct string_list source_files;
unsigned reported_already:1; unsigned reported_already:1;
@ -132,22 +136,27 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
{ {
struct collision_entry key; struct collision_entry key;
hashmap_entry_init(&key, strhash(target_file)); hashmap_entry_init(&key.ent, strhash(target_file));
key.target_file = target_file; key.target_file = target_file;
return hashmap_get(hashmap, &key, NULL); return hashmap_get_entry(hashmap, &key, ent, NULL);
} }
static int collision_cmp(void *unused_cmp_data, static int collision_cmp(const void *unused_cmp_data,
const struct collision_entry *e1, const struct hashmap_entry *eptr,
const struct collision_entry *e2, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct collision_entry *e1, *e2;
e1 = container_of(eptr, const struct collision_entry, ent);
e2 = container_of(entry_or_key, const struct collision_entry, ent);
return strcmp(e1->target_file, e2->target_file); return strcmp(e1->target_file, e2->target_file);
} }
static void collision_init(struct hashmap *map) static void collision_init(struct hashmap *map)
{ {
hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0); hashmap_init(map, collision_cmp, NULL, 0);
} }
static void flush_output(struct merge_options *opt) static void flush_output(struct merge_options *opt)
@ -464,8 +473,8 @@ static int save_files_dirs(const struct object_id *oid,
strbuf_addstr(base, path); strbuf_addstr(base, path);
FLEX_ALLOC_MEM(entry, path, base->buf, base->len); FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
hashmap_entry_init(entry, path_hash(entry->path)); hashmap_entry_init(&entry->e, path_hash(entry->path));
hashmap_add(&opt->priv->current_file_dir_set, entry); hashmap_add(&opt->priv->current_file_dir_set, &entry->e);
strbuf_setlen(base, baselen); strbuf_setlen(base, baselen);
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
@ -743,8 +752,8 @@ static char *unique_path(struct merge_options *opt,
} }
FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len); FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
hashmap_entry_init(entry, path_hash(entry->path)); hashmap_entry_init(&entry->e, path_hash(entry->path));
hashmap_add(&opt->priv->current_file_dir_set, entry); hashmap_add(&opt->priv->current_file_dir_set, &entry->e);
return strbuf_detach(&newpath, NULL); return strbuf_detach(&newpath, NULL);
} }
@ -2013,7 +2022,7 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,
for (i = 0; i < items_to_remove->nr; i++) { for (i = 0; i < items_to_remove->nr; i++) {
entry = items_to_remove->items[i].util; entry = items_to_remove->items[i].util;
hashmap_remove(dir_renames, entry, NULL); hashmap_remove(dir_renames, &entry->ent, NULL);
} }
string_list_clear(items_to_remove, 0); string_list_clear(items_to_remove, 0);
} }
@ -2136,8 +2145,8 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
struct string_list remove_from_head = STRING_LIST_INIT_NODUP; struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
struct string_list remove_from_merge = STRING_LIST_INIT_NODUP; struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
hashmap_iter_init(dir_re_head, &iter); hashmap_for_each_entry(dir_re_head, &iter, head_ent,
while ((head_ent = hashmap_iter_next(&iter))) { ent /* member name */) {
merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir); merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
if (merge_ent && if (merge_ent &&
!head_ent->non_unique_new_dir && !head_ent->non_unique_new_dir &&
@ -2161,8 +2170,8 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
remove_hashmap_entries(dir_re_head, &remove_from_head); remove_hashmap_entries(dir_re_head, &remove_from_head);
remove_hashmap_entries(dir_re_merge, &remove_from_merge); remove_hashmap_entries(dir_re_merge, &remove_from_merge);
hashmap_iter_init(dir_re_merge, &iter); hashmap_for_each_entry(dir_re_merge, &iter, merge_ent,
while ((merge_ent = hashmap_iter_next(&iter))) { ent /* member name */) {
head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir); head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
if (tree_has_path(opt->repo, merge, merge_ent->dir)) { if (tree_has_path(opt->repo, merge, merge_ent->dir)) {
/* 2. This wasn't a directory rename after all */ /* 2. This wasn't a directory rename after all */
@ -2241,7 +2250,7 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
if (!entry) { if (!entry) {
entry = xmalloc(sizeof(*entry)); entry = xmalloc(sizeof(*entry));
dir_rename_entry_init(entry, old_dir); dir_rename_entry_init(entry, old_dir);
hashmap_put(dir_renames, entry); hashmap_put(dir_renames, &entry->ent);
} else { } else {
free(old_dir); free(old_dir);
} }
@ -2266,8 +2275,8 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
* we set non_unique_new_dir. Once we've determined the winner (or * we set non_unique_new_dir. Once we've determined the winner (or
* that there is no winner), we no longer need possible_new_dirs. * that there is no winner), we no longer need possible_new_dirs.
*/ */
hashmap_iter_init(dir_renames, &iter); hashmap_for_each_entry(dir_renames, &iter, entry,
while ((entry = hashmap_iter_next(&iter))) { ent /* member name */) {
int max = 0; int max = 0;
int bad_max = 0; int bad_max = 0;
char *best = NULL; char *best = NULL;
@ -2370,8 +2379,9 @@ static void compute_collisions(struct hashmap *collisions,
if (!collision_ent) { if (!collision_ent) {
collision_ent = xcalloc(1, collision_ent = xcalloc(1,
sizeof(struct collision_entry)); sizeof(struct collision_entry));
hashmap_entry_init(collision_ent, strhash(new_path)); hashmap_entry_init(&collision_ent->ent,
hashmap_put(collisions, collision_ent); strhash(new_path));
hashmap_put(collisions, &collision_ent->ent);
collision_ent->target_file = new_path; collision_ent->target_file = new_path;
} else { } else {
free(new_path); free(new_path);
@ -2624,12 +2634,12 @@ static struct string_list *get_renames(struct merge_options *opt,
entries); entries);
} }
hashmap_iter_init(&collisions, &iter); hashmap_for_each_entry(&collisions, &iter, e,
while ((e = hashmap_iter_next(&iter))) { ent /* member name */) {
free(e->target_file); free(e->target_file);
string_list_clear(&e->source_files, 0); string_list_clear(&e->source_files, 0);
} }
hashmap_free(&collisions, 1); hashmap_free_entries(&collisions, struct collision_entry, ent);
return renames; return renames;
} }
@ -2842,13 +2852,13 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
struct hashmap_iter iter; struct hashmap_iter iter;
struct dir_rename_entry *e; struct dir_rename_entry *e;
hashmap_iter_init(dir_renames, &iter); hashmap_for_each_entry(dir_renames, &iter, e,
while ((e = hashmap_iter_next(&iter))) { ent /* member name */) {
free(e->dir); free(e->dir);
strbuf_release(&e->new_dir); strbuf_release(&e->new_dir);
/* possible_new_dirs already cleared in get_directory_renames */ /* possible_new_dirs already cleared in get_directory_renames */
} }
hashmap_free(dir_renames, 1); hashmap_free_entries(dir_renames, struct dir_rename_entry, ent);
free(dir_renames); free(dir_renames);
free(pairs->queue); free(pairs->queue);
@ -3475,7 +3485,8 @@ static int merge_trees_internal(struct merge_options *opt,
string_list_clear(entries, 1); string_list_clear(entries, 1);
free(entries); free(entries);
hashmap_free(&opt->priv->current_file_dir_set, 1); hashmap_free_entries(&opt->priv->current_file_dir_set,
struct path_hashmap_entry, e);
if (clean < 0) { if (clean < 0) {
unpack_trees_finish(opt); unpack_trees_finish(opt);

View File

@ -17,14 +17,16 @@ struct dir_entry {
}; };
static int dir_entry_cmp(const void *unused_cmp_data, static int dir_entry_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct dir_entry *e1 = entry; const struct dir_entry *e1, *e2;
const struct dir_entry *e2 = entry_or_key;
const char *name = keydata; const char *name = keydata;
e1 = container_of(eptr, const struct dir_entry, ent);
e2 = container_of(entry_or_key, const struct dir_entry, ent);
return e1->namelen != e2->namelen || strncasecmp(e1->name, return e1->namelen != e2->namelen || strncasecmp(e1->name,
name ? name : e2->name, e1->namelen); name ? name : e2->name, e1->namelen);
} }
@ -33,9 +35,9 @@ static struct dir_entry *find_dir_entry__hash(struct index_state *istate,
const char *name, unsigned int namelen, unsigned int hash) const char *name, unsigned int namelen, unsigned int hash)
{ {
struct dir_entry key; struct dir_entry key;
hashmap_entry_init(&key, hash); hashmap_entry_init(&key.ent, hash);
key.namelen = namelen; key.namelen = namelen;
return hashmap_get(&istate->dir_hash, &key, name); return hashmap_get_entry(&istate->dir_hash, &key, ent, name);
} }
static struct dir_entry *find_dir_entry(struct index_state *istate, static struct dir_entry *find_dir_entry(struct index_state *istate,
@ -68,9 +70,9 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
if (!dir) { if (!dir) {
/* not found, create it and add to hash table */ /* not found, create it and add to hash table */
FLEX_ALLOC_MEM(dir, name, ce->name, namelen); FLEX_ALLOC_MEM(dir, name, ce->name, namelen);
hashmap_entry_init(dir, memihash(ce->name, namelen)); hashmap_entry_init(&dir->ent, memihash(ce->name, namelen));
dir->namelen = namelen; dir->namelen = namelen;
hashmap_add(&istate->dir_hash, dir); hashmap_add(&istate->dir_hash, &dir->ent);
/* recursively add missing parent directories */ /* recursively add missing parent directories */
dir->parent = hash_dir_entry(istate, ce, namelen); dir->parent = hash_dir_entry(istate, ce, namelen);
@ -95,7 +97,7 @@ static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)
struct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce)); struct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce));
while (dir && !(--dir->nr)) { while (dir && !(--dir->nr)) {
struct dir_entry *parent = dir->parent; struct dir_entry *parent = dir->parent;
hashmap_remove(&istate->dir_hash, dir, NULL); hashmap_remove(&istate->dir_hash, &dir->ent, NULL);
free(dir); free(dir);
dir = parent; dir = parent;
} }
@ -106,20 +108,23 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
if (ce->ce_flags & CE_HASHED) if (ce->ce_flags & CE_HASHED)
return; return;
ce->ce_flags |= CE_HASHED; ce->ce_flags |= CE_HASHED;
hashmap_entry_init(ce, memihash(ce->name, ce_namelen(ce))); hashmap_entry_init(&ce->ent, memihash(ce->name, ce_namelen(ce)));
hashmap_add(&istate->name_hash, ce); hashmap_add(&istate->name_hash, &ce->ent);
if (ignore_case) if (ignore_case)
add_dir_entry(istate, ce); add_dir_entry(istate, ce);
} }
static int cache_entry_cmp(const void *unused_cmp_data, static int cache_entry_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *remove) const void *remove)
{ {
const struct cache_entry *ce1 = entry; const struct cache_entry *ce1, *ce2;
const struct cache_entry *ce2 = entry_or_key;
ce1 = container_of(eptr, const struct cache_entry, ent);
ce2 = container_of(entry_or_key, const struct cache_entry, ent);
/* /*
* For remove_name_hash, find the exact entry (pointer equality); for * For remove_name_hash, find the exact entry (pointer equality); for
* index_file_exists, find all entries with matching hash code and * index_file_exists, find all entries with matching hash code and
@ -280,10 +285,10 @@ static struct dir_entry *hash_dir_entry_with_parent_and_prefix(
dir = find_dir_entry__hash(istate, prefix->buf, prefix->len, hash); dir = find_dir_entry__hash(istate, prefix->buf, prefix->len, hash);
if (!dir) { if (!dir) {
FLEX_ALLOC_MEM(dir, name, prefix->buf, prefix->len); FLEX_ALLOC_MEM(dir, name, prefix->buf, prefix->len);
hashmap_entry_init(dir, hash); hashmap_entry_init(&dir->ent, hash);
dir->namelen = prefix->len; dir->namelen = prefix->len;
dir->parent = parent; dir->parent = parent;
hashmap_add(&istate->dir_hash, dir); hashmap_add(&istate->dir_hash, &dir->ent);
if (parent) { if (parent) {
unlock_dir_mutex(lock_nr); unlock_dir_mutex(lock_nr);
@ -472,8 +477,8 @@ static void *lazy_name_thread_proc(void *_data)
for (k = 0; k < d->istate->cache_nr; k++) { for (k = 0; k < d->istate->cache_nr; k++) {
struct cache_entry *ce_k = d->istate->cache[k]; struct cache_entry *ce_k = d->istate->cache[k];
ce_k->ce_flags |= CE_HASHED; ce_k->ce_flags |= CE_HASHED;
hashmap_entry_init(ce_k, d->lazy_entries[k].hash_name); hashmap_entry_init(&ce_k->ent, d->lazy_entries[k].hash_name);
hashmap_add(&d->istate->name_hash, ce_k); hashmap_add(&d->istate->name_hash, &ce_k->ent);
} }
return NULL; return NULL;
@ -625,7 +630,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)
if (!istate->name_hash_initialized || !(ce->ce_flags & CE_HASHED)) if (!istate->name_hash_initialized || !(ce->ce_flags & CE_HASHED))
return; return;
ce->ce_flags &= ~CE_HASHED; ce->ce_flags &= ~CE_HASHED;
hashmap_remove(&istate->name_hash, ce, ce); hashmap_remove(&istate->name_hash, &ce->ent, ce);
if (ignore_case) if (ignore_case)
remove_dir_entry(istate, ce); remove_dir_entry(istate, ce);
@ -702,15 +707,15 @@ void adjust_dirname_case(struct index_state *istate, char *name)
struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase) struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
{ {
struct cache_entry *ce; struct cache_entry *ce;
unsigned int hash = memihash(name, namelen);
lazy_init_name_hash(istate); lazy_init_name_hash(istate);
ce = hashmap_get_from_hash(&istate->name_hash, ce = hashmap_get_entry_from_hash(&istate->name_hash, hash, NULL,
memihash(name, namelen), NULL); struct cache_entry, ent);
while (ce) { hashmap_for_each_entry_from(&istate->name_hash, ce, ent) {
if (same_name(ce, name, namelen, icase)) if (same_name(ce, name, namelen, icase))
return ce; return ce;
ce = hashmap_get_next(&istate->name_hash, ce);
} }
return NULL; return NULL;
} }
@ -721,6 +726,6 @@ void free_name_hash(struct index_state *istate)
return; return;
istate->name_hash_initialized = 0; istate->name_hash_initialized = 0;
hashmap_free(&istate->name_hash, 0); hashmap_free(&istate->name_hash);
hashmap_free(&istate->dir_hash, 1); hashmap_free_entries(&istate->dir_hash, struct dir_entry, ent);
} }

View File

@ -2,14 +2,18 @@
#include "oidmap.h" #include "oidmap.h"
static int oidmap_neq(const void *hashmap_cmp_fn_data, static int oidmap_neq(const void *hashmap_cmp_fn_data,
const void *entry, const void *entry_or_key, const struct hashmap_entry *e1,
const struct hashmap_entry *e2,
const void *keydata) const void *keydata)
{ {
const struct oidmap_entry *entry_ = entry; const struct oidmap_entry *a, *b;
a = container_of(e1, const struct oidmap_entry, internal_entry);
b = container_of(e2, const struct oidmap_entry, internal_entry);
if (keydata) if (keydata)
return !oideq(&entry_->oid, (const struct object_id *) keydata); return !oideq(&a->oid, (const struct object_id *) keydata);
return !oideq(&entry_->oid, return !oideq(&a->oid, &b->oid);
&((const struct oidmap_entry *) entry_or_key)->oid);
} }
void oidmap_init(struct oidmap *map, size_t initial_size) void oidmap_init(struct oidmap *map, size_t initial_size)
@ -21,7 +25,9 @@ void oidmap_free(struct oidmap *map, int free_entries)
{ {
if (!map) if (!map)
return; return;
hashmap_free(&map->map, free_entries);
/* TODO: make oidmap itself not depend on struct layouts */
hashmap_free_(&map->map, free_entries ? 0 : -1);
} }
void *oidmap_get(const struct oidmap *map, const struct object_id *key) void *oidmap_get(const struct oidmap *map, const struct object_id *key)
@ -51,5 +57,5 @@ void *oidmap_put(struct oidmap *map, void *entry)
oidmap_init(map, 0); oidmap_init(map, 0);
hashmap_entry_init(&to_put->internal_entry, oidhash(&to_put->oid)); hashmap_entry_init(&to_put->internal_entry, oidhash(&to_put->oid));
return hashmap_put(&map->map, to_put); return hashmap_put(&map->map, &to_put->internal_entry);
} }

View File

@ -78,14 +78,16 @@ static inline void oidmap_iter_init(struct oidmap *map, struct oidmap_iter *iter
static inline void *oidmap_iter_next(struct oidmap_iter *iter) static inline void *oidmap_iter_next(struct oidmap_iter *iter)
{ {
return hashmap_iter_next(&iter->h_iter); /* TODO: this API could be reworked to do compile-time type checks */
return (void *)hashmap_iter_next(&iter->h_iter);
} }
static inline void *oidmap_iter_first(struct oidmap *map, static inline void *oidmap_iter_first(struct oidmap *map,
struct oidmap_iter *iter) struct oidmap_iter *iter)
{ {
oidmap_iter_init(map, iter); oidmap_iter_init(map, iter);
return oidmap_iter_next(iter); /* TODO: this API could be reworked to do compile-time type checks */
return (void *)oidmap_iter_next(iter);
} }
#endif #endif

View File

@ -1343,7 +1343,7 @@ struct delta_base_cache_key {
}; };
struct delta_base_cache_entry { struct delta_base_cache_entry {
struct hashmap hash; struct hashmap_entry ent;
struct delta_base_cache_key key; struct delta_base_cache_key key;
struct list_head lru; struct list_head lru;
void *data; void *data;
@ -1363,7 +1363,7 @@ static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)
static struct delta_base_cache_entry * static struct delta_base_cache_entry *
get_delta_base_cache_entry(struct packed_git *p, off_t base_offset) get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
{ {
struct hashmap_entry entry; struct hashmap_entry entry, *e;
struct delta_base_cache_key key; struct delta_base_cache_key key;
if (!delta_base_cache.cmpfn) if (!delta_base_cache.cmpfn)
@ -1372,7 +1372,8 @@ get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
hashmap_entry_init(&entry, pack_entry_hash(p, base_offset)); hashmap_entry_init(&entry, pack_entry_hash(p, base_offset));
key.p = p; key.p = p;
key.base_offset = base_offset; key.base_offset = base_offset;
return hashmap_get(&delta_base_cache, &entry, &key); e = hashmap_get(&delta_base_cache, &entry, &key);
return e ? container_of(e, struct delta_base_cache_entry, ent) : NULL;
} }
static int delta_base_cache_key_eq(const struct delta_base_cache_key *a, static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
@ -1382,11 +1383,16 @@ static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
} }
static int delta_base_cache_hash_cmp(const void *unused_cmp_data, static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
const void *va, const void *vb, const struct hashmap_entry *va,
const struct hashmap_entry *vb,
const void *vkey) const void *vkey)
{ {
const struct delta_base_cache_entry *a = va, *b = vb; const struct delta_base_cache_entry *a, *b;
const struct delta_base_cache_key *key = vkey; const struct delta_base_cache_key *key = vkey;
a = container_of(va, const struct delta_base_cache_entry, ent);
b = container_of(vb, const struct delta_base_cache_entry, ent);
if (key) if (key)
return !delta_base_cache_key_eq(&a->key, key); return !delta_base_cache_key_eq(&a->key, key);
else else
@ -1405,7 +1411,7 @@ static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
*/ */
static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent) static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)
{ {
hashmap_remove(&delta_base_cache, ent, &ent->key); hashmap_remove(&delta_base_cache, &ent->ent, &ent->key);
list_del(&ent->lru); list_del(&ent->lru);
delta_base_cached -= ent->size; delta_base_cached -= ent->size;
free(ent); free(ent);
@ -1469,8 +1475,8 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
if (!delta_base_cache.cmpfn) if (!delta_base_cache.cmpfn)
hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0); hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
hashmap_entry_init(ent, pack_entry_hash(p, base_offset)); hashmap_entry_init(&ent->ent, pack_entry_hash(p, base_offset));
hashmap_add(&delta_base_cache, ent); hashmap_add(&delta_base_cache, &ent->ent);
} }
int packed_object_info(struct repository *r, struct packed_git *p, int packed_object_info(struct repository *r, struct packed_git *p,

View File

@ -36,14 +36,16 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,
* any significance; only that it is non-zero matters. * any significance; only that it is non-zero matters.
*/ */
static int patch_id_neq(const void *cmpfn_data, static int patch_id_neq(const void *cmpfn_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
/* NEEDSWORK: const correctness? */ /* NEEDSWORK: const correctness? */
struct diff_options *opt = (void *)cmpfn_data; struct diff_options *opt = (void *)cmpfn_data;
struct patch_id *a = (void *)entry; struct patch_id *a, *b;
struct patch_id *b = (void *)entry_or_key;
a = container_of(eptr, struct patch_id, ent);
b = container_of(entry_or_key, struct patch_id, ent);
if (is_null_oid(&a->patch_id) && if (is_null_oid(&a->patch_id) &&
commit_patch_id(a->commit, opt, &a->patch_id, 0, 0)) commit_patch_id(a->commit, opt, &a->patch_id, 0, 0))
@ -69,7 +71,7 @@ int init_patch_ids(struct repository *r, struct patch_ids *ids)
int free_patch_ids(struct patch_ids *ids) int free_patch_ids(struct patch_ids *ids)
{ {
hashmap_free(&ids->patches, 1); hashmap_free_entries(&ids->patches, struct patch_id, ent);
return 0; return 0;
} }
@ -83,7 +85,7 @@ static int init_patch_id_entry(struct patch_id *patch,
if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0)) if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))
return -1; return -1;
hashmap_entry_init(patch, oidhash(&header_only_patch_id)); hashmap_entry_init(&patch->ent, oidhash(&header_only_patch_id));
return 0; return 0;
} }
@ -99,7 +101,7 @@ struct patch_id *has_commit_patch_id(struct commit *commit,
if (init_patch_id_entry(&patch, commit, ids)) if (init_patch_id_entry(&patch, commit, ids))
return NULL; return NULL;
return hashmap_get(&ids->patches, &patch, NULL); return hashmap_get_entry(&ids->patches, &patch, ent, NULL);
} }
struct patch_id *add_commit_patch_id(struct commit *commit, struct patch_id *add_commit_patch_id(struct commit *commit,
@ -116,6 +118,6 @@ struct patch_id *add_commit_patch_id(struct commit *commit,
return NULL; return NULL;
} }
hashmap_add(&ids->patches, key); hashmap_add(&ids->patches, &key->ent);
return key; return key;
} }

View File

@ -218,8 +218,8 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
util->i = i; util->i = i;
util->patch = a->items[i].string; util->patch = a->items[i].string;
util->diff = util->patch + util->diff_offset; util->diff = util->patch + util->diff_offset;
hashmap_entry_init(util, strhash(util->diff)); hashmap_entry_init(&util->e, strhash(util->diff));
hashmap_add(&map, util); hashmap_add(&map, &util->e);
} }
/* Now try to find exact matches in b */ /* Now try to find exact matches in b */
@ -229,8 +229,8 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
util->i = i; util->i = i;
util->patch = b->items[i].string; util->patch = b->items[i].string;
util->diff = util->patch + util->diff_offset; util->diff = util->patch + util->diff_offset;
hashmap_entry_init(util, strhash(util->diff)); hashmap_entry_init(&util->e, strhash(util->diff));
other = hashmap_remove(&map, util, NULL); other = hashmap_remove_entry(&map, util, e, NULL);
if (other) { if (other) {
if (other->matching >= 0) if (other->matching >= 0)
BUG("already assigned!"); BUG("already assigned!");
@ -240,7 +240,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
} }
} }
hashmap_free(&map, 0); hashmap_free(&map);
} }
static void diffsize_consume(void *data, char *line, unsigned long len) static void diffsize_consume(void *data, char *line, unsigned long len)

View File

@ -79,17 +79,20 @@ static struct expand_data {
} oi, oi_deref; } oi, oi_deref;
struct ref_to_worktree_entry { struct ref_to_worktree_entry {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
struct worktree *wt; /* key is wt->head_ref */ struct worktree *wt; /* key is wt->head_ref */
}; };
static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
const void *existing_hashmap_entry_to_test, const struct hashmap_entry *eptr,
const void *key, const struct hashmap_entry *kptr,
const void *keydata_aka_refname) const void *keydata_aka_refname)
{ {
const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; const struct ref_to_worktree_entry *e, *k;
const struct ref_to_worktree_entry *k = key;
e = container_of(eptr, const struct ref_to_worktree_entry, ent);
k = container_of(kptr, const struct ref_to_worktree_entry, ent);
return strcmp(e->wt->head_ref, return strcmp(e->wt->head_ref,
keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
} }
@ -1565,9 +1568,10 @@ static void populate_worktree_map(struct hashmap *map, struct worktree **worktre
struct ref_to_worktree_entry *entry; struct ref_to_worktree_entry *entry;
entry = xmalloc(sizeof(*entry)); entry = xmalloc(sizeof(*entry));
entry->wt = worktrees[i]; entry->wt = worktrees[i];
hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); hashmap_entry_init(&entry->ent,
strhash(worktrees[i]->head_ref));
hashmap_add(map, entry); hashmap_add(map, &entry->ent);
} }
} }
} }
@ -1584,18 +1588,20 @@ static void lazy_init_worktree_map(void)
static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
{ {
struct hashmap_entry entry; struct hashmap_entry entry, *e;
struct ref_to_worktree_entry *lookup_result; struct ref_to_worktree_entry *lookup_result;
lazy_init_worktree_map(); lazy_init_worktree_map();
hashmap_entry_init(&entry, strhash(ref->refname)); hashmap_entry_init(&entry, strhash(ref->refname));
lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); e = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
if (lookup_result) if (!e)
return xstrdup(lookup_result->wt->path);
else
return xstrdup(""); return xstrdup("");
lookup_result = container_of(e, struct ref_to_worktree_entry, ent);
return xstrdup(lookup_result->wt->path);
} }
/* /*
@ -2166,7 +2172,8 @@ void ref_array_clear(struct ref_array *array)
used_atom_cnt = 0; used_atom_cnt = 0;
if (ref_to_worktree_map.worktrees) { if (ref_to_worktree_map.worktrees) {
hashmap_free(&(ref_to_worktree_map.map), 1); hashmap_free_entries(&(ref_to_worktree_map.map),
struct ref_to_worktree_entry, ent);
free_worktrees(ref_to_worktree_map.worktrees); free_worktrees(ref_to_worktree_map.worktrees);
ref_to_worktree_map.worktrees = NULL; ref_to_worktree_map.worktrees = NULL;
} }

25
refs.c
View File

@ -1772,7 +1772,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
struct ref_store_hash_entry struct ref_store_hash_entry
{ {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
struct ref_store *refs; struct ref_store *refs;
@ -1781,11 +1781,16 @@ struct ref_store_hash_entry
}; };
static int ref_store_hash_cmp(const void *unused_cmp_data, static int ref_store_hash_cmp(const void *unused_cmp_data,
const void *entry, const void *entry_or_key, const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key; const struct ref_store_hash_entry *e1, *e2;
const char *name = keydata ? keydata : e2->name; const char *name;
e1 = container_of(eptr, const struct ref_store_hash_entry, ent);
e2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);
name = keydata ? keydata : e2->name;
return strcmp(e1->name, name); return strcmp(e1->name, name);
} }
@ -1796,7 +1801,7 @@ static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
struct ref_store_hash_entry *entry; struct ref_store_hash_entry *entry;
FLEX_ALLOC_STR(entry, name, name); FLEX_ALLOC_STR(entry, name, name);
hashmap_entry_init(entry, strhash(name)); hashmap_entry_init(&entry->ent, strhash(name));
entry->refs = refs; entry->refs = refs;
return entry; return entry;
} }
@ -1815,12 +1820,15 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,
const char *name) const char *name)
{ {
struct ref_store_hash_entry *entry; struct ref_store_hash_entry *entry;
unsigned int hash;
if (!map->tablesize) if (!map->tablesize)
/* It's initialized on demand in register_ref_store(). */ /* It's initialized on demand in register_ref_store(). */
return NULL; return NULL;
entry = hashmap_get_from_hash(map, strhash(name), name); hash = strhash(name);
entry = hashmap_get_entry_from_hash(map, hash, name,
struct ref_store_hash_entry, ent);
return entry ? entry->refs : NULL; return entry ? entry->refs : NULL;
} }
@ -1863,10 +1871,13 @@ static void register_ref_store_map(struct hashmap *map,
struct ref_store *refs, struct ref_store *refs,
const char *name) const char *name)
{ {
struct ref_store_hash_entry *entry;
if (!map->tablesize) if (!map->tablesize)
hashmap_init(map, ref_store_hash_cmp, NULL, 0); hashmap_init(map, ref_store_hash_cmp, NULL, 0);
if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs))) entry = alloc_ref_store_hash_entry(name, refs);
if (hashmap_put(map, &entry->ent))
BUG("%s ref_store '%s' initialized twice", type, name); BUG("%s ref_store '%s' initialized twice", type, name);
} }

View File

@ -111,14 +111,16 @@ struct remotes_hash_key {
}; };
static int remotes_hash_cmp(const void *unused_cmp_data, static int remotes_hash_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct remote *a = entry; const struct remote *a, *b;
const struct remote *b = entry_or_key;
const struct remotes_hash_key *key = keydata; const struct remotes_hash_key *key = keydata;
a = container_of(eptr, const struct remote, ent);
b = container_of(entry_or_key, const struct remote, ent);
if (key) if (key)
return strncmp(a->name, key->str, key->len) || a->name[key->len]; return strncmp(a->name, key->str, key->len) || a->name[key->len];
else else
@ -135,7 +137,7 @@ static struct remote *make_remote(const char *name, int len)
{ {
struct remote *ret, *replaced; struct remote *ret, *replaced;
struct remotes_hash_key lookup; struct remotes_hash_key lookup;
struct hashmap_entry lookup_entry; struct hashmap_entry lookup_entry, *e;
if (!len) if (!len)
len = strlen(name); len = strlen(name);
@ -145,8 +147,9 @@ static struct remote *make_remote(const char *name, int len)
lookup.len = len; lookup.len = len;
hashmap_entry_init(&lookup_entry, memhash(name, len)); hashmap_entry_init(&lookup_entry, memhash(name, len));
if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL) e = hashmap_get(&remotes_hash, &lookup_entry, &lookup);
return ret; if (e)
return container_of(e, struct remote, ent);
ret = xcalloc(1, sizeof(struct remote)); ret = xcalloc(1, sizeof(struct remote));
ret->prune = -1; /* unspecified */ ret->prune = -1; /* unspecified */
@ -158,8 +161,8 @@ static struct remote *make_remote(const char *name, int len)
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
remotes[remotes_nr++] = ret; remotes[remotes_nr++] = ret;
hashmap_entry_init(ret, lookup_entry.hash); hashmap_entry_init(&ret->ent, lookup_entry.hash);
replaced = hashmap_put(&remotes_hash, ret); replaced = hashmap_put_entry(&remotes_hash, ret, ent);
assert(replaced == NULL); /* no previous entry overwritten */ assert(replaced == NULL); /* no previous entry overwritten */
return ret; return ret;
} }

View File

@ -14,7 +14,7 @@ enum {
}; };
struct remote { struct remote {
struct hashmap_entry ent; /* must be first */ struct hashmap_entry ent;
const char *name; const char *name;
int origin, configured_in_repo; int origin, configured_in_repo;

View File

@ -108,30 +108,34 @@ struct path_and_oids_entry {
}; };
static int path_and_oids_cmp(const void *hashmap_cmp_fn_data, static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
const struct path_and_oids_entry *e1, const struct hashmap_entry *eptr,
const struct path_and_oids_entry *e2, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const struct path_and_oids_entry *e1, *e2;
e1 = container_of(eptr, const struct path_and_oids_entry, ent);
e2 = container_of(entry_or_key, const struct path_and_oids_entry, ent);
return strcmp(e1->path, e2->path); return strcmp(e1->path, e2->path);
} }
static void paths_and_oids_init(struct hashmap *map) static void paths_and_oids_init(struct hashmap *map)
{ {
hashmap_init(map, (hashmap_cmp_fn) path_and_oids_cmp, NULL, 0); hashmap_init(map, path_and_oids_cmp, NULL, 0);
} }
static void paths_and_oids_clear(struct hashmap *map) static void paths_and_oids_clear(struct hashmap *map)
{ {
struct hashmap_iter iter; struct hashmap_iter iter;
struct path_and_oids_entry *entry; struct path_and_oids_entry *entry;
hashmap_iter_init(map, &iter);
while ((entry = (struct path_and_oids_entry *)hashmap_iter_next(&iter))) { hashmap_for_each_entry(map, &iter, entry, ent /* member name */) {
oidset_clear(&entry->trees); oidset_clear(&entry->trees);
free(entry->path); free(entry->path);
} }
hashmap_free(map, 1); hashmap_free_entries(map, struct path_and_oids_entry, ent);
} }
static void paths_and_oids_insert(struct hashmap *map, static void paths_and_oids_insert(struct hashmap *map,
@ -142,18 +146,19 @@ static void paths_and_oids_insert(struct hashmap *map,
struct path_and_oids_entry key; struct path_and_oids_entry key;
struct path_and_oids_entry *entry; struct path_and_oids_entry *entry;
hashmap_entry_init(&key, hash); hashmap_entry_init(&key.ent, hash);
/* use a shallow copy for the lookup */ /* use a shallow copy for the lookup */
key.path = (char *)path; key.path = (char *)path;
oidset_init(&key.trees, 0); oidset_init(&key.trees, 0);
if (!(entry = (struct path_and_oids_entry *)hashmap_get(map, &key, NULL))) { entry = hashmap_get_entry(map, &key, ent, NULL);
if (!entry) {
entry = xcalloc(1, sizeof(struct path_and_oids_entry)); entry = xcalloc(1, sizeof(struct path_and_oids_entry));
hashmap_entry_init(entry, hash); hashmap_entry_init(&entry->ent, hash);
entry->path = xstrdup(key.path); entry->path = xstrdup(key.path);
oidset_init(&entry->trees, 16); oidset_init(&entry->trees, 16);
hashmap_put(map, entry); hashmap_put(map, &entry->ent);
} }
oidset_insert(&entry->trees, oid); oidset_insert(&entry->trees, oid);
@ -236,8 +241,7 @@ void mark_trees_uninteresting_sparse(struct repository *r,
add_children_by_path(r, tree, &map); add_children_by_path(r, tree, &map);
} }
hashmap_iter_init(&map, &map_iter); hashmap_for_each_entry(&map, &map_iter, entry, ent /* member name */)
while ((entry = hashmap_iter_next(&map_iter)))
mark_trees_uninteresting_sparse(r, &entry->trees); mark_trees_uninteresting_sparse(r, &entry->trees);
paths_and_oids_clear(&map); paths_and_oids_clear(&map);

View File

@ -4397,9 +4397,14 @@ struct labels_entry {
char label[FLEX_ARRAY]; char label[FLEX_ARRAY];
}; };
static int labels_cmp(const void *fndata, const struct labels_entry *a, static int labels_cmp(const void *fndata, const struct hashmap_entry *eptr,
const struct labels_entry *b, const void *key) const struct hashmap_entry *entry_or_key, const void *key)
{ {
const struct labels_entry *a, *b;
a = container_of(eptr, const struct labels_entry, entry);
b = container_of(entry_or_key, const struct labels_entry, entry);
return key ? strcmp(a->label, key) : strcmp(a->label, b->label); return key ? strcmp(a->label, key) : strcmp(a->label, b->label);
} }
@ -4495,8 +4500,8 @@ static const char *label_oid(struct object_id *oid, const char *label,
} }
FLEX_ALLOC_STR(labels_entry, label, label); FLEX_ALLOC_STR(labels_entry, label, label);
hashmap_entry_init(labels_entry, strihash(label)); hashmap_entry_init(&labels_entry->entry, strihash(label));
hashmap_add(&state->labels, labels_entry); hashmap_add(&state->labels, &labels_entry->entry);
FLEX_ALLOC_STR(string_entry, string, label); FLEX_ALLOC_STR(string_entry, string, label);
oidcpy(&string_entry->entry.oid, oid); oidcpy(&string_entry->entry.oid, oid);
@ -4531,7 +4536,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
oidmap_init(&commit2todo, 0); oidmap_init(&commit2todo, 0);
oidmap_init(&state.commit2label, 0); oidmap_init(&state.commit2label, 0);
hashmap_init(&state.labels, (hashmap_cmp_fn) labels_cmp, NULL, 0); hashmap_init(&state.labels, labels_cmp, NULL, 0);
strbuf_init(&state.buf, 32); strbuf_init(&state.buf, 32);
if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) { if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
@ -4726,7 +4731,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
oidmap_free(&commit2todo, 1); oidmap_free(&commit2todo, 1);
oidmap_free(&state.commit2label, 1); oidmap_free(&state.commit2label, 1);
hashmap_free(&state.labels, 1); hashmap_free_entries(&state.labels, struct labels_entry, entry);
strbuf_release(&state.buf); strbuf_release(&state.buf);
return 0; return 0;
@ -5097,9 +5102,15 @@ struct subject2item_entry {
}; };
static int subject2item_cmp(const void *fndata, static int subject2item_cmp(const void *fndata,
const struct subject2item_entry *a, const struct hashmap_entry *eptr,
const struct subject2item_entry *b, const void *key) const struct hashmap_entry *entry_or_key,
const void *key)
{ {
const struct subject2item_entry *a, *b;
a = container_of(eptr, const struct subject2item_entry, entry);
b = container_of(entry_or_key, const struct subject2item_entry, entry);
return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject); return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject);
} }
@ -5132,8 +5143,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
* In that case, last[i] will indicate the index of the latest item to * In that case, last[i] will indicate the index of the latest item to
* be moved to appear after the i'th. * be moved to appear after the i'th.
*/ */
hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp, hashmap_init(&subject2item, subject2item_cmp, NULL, todo_list->nr);
NULL, todo_list->nr);
ALLOC_ARRAY(next, todo_list->nr); ALLOC_ARRAY(next, todo_list->nr);
ALLOC_ARRAY(tail, todo_list->nr); ALLOC_ARRAY(tail, todo_list->nr);
ALLOC_ARRAY(subjects, todo_list->nr); ALLOC_ARRAY(subjects, todo_list->nr);
@ -5176,8 +5186,11 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
break; break;
} }
if ((entry = hashmap_get_from_hash(&subject2item, entry = hashmap_get_entry_from_hash(&subject2item,
strhash(p), p))) strhash(p), p,
struct subject2item_entry,
entry);
if (entry)
/* found by title */ /* found by title */
i2 = entry->i; i2 = entry->i;
else if (!strchr(p, ' ') && else if (!strchr(p, ' ') &&
@ -5211,8 +5224,9 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
strhash(subject), subject)) { strhash(subject), subject)) {
FLEX_ALLOC_MEM(entry, subject, subject, subject_len); FLEX_ALLOC_MEM(entry, subject, subject, subject_len);
entry->i = i; entry->i = i;
hashmap_entry_init(entry, strhash(entry->subject)); hashmap_entry_init(&entry->entry,
hashmap_put(&subject2item, entry); strhash(entry->subject));
hashmap_put(&subject2item, &entry->entry);
} }
} }
@ -5246,7 +5260,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
for (i = 0; i < todo_list->nr; i++) for (i = 0; i < todo_list->nr; i++)
free(subjects[i]); free(subjects[i]);
free(subjects); free(subjects);
hashmap_free(&subject2item, 1); hashmap_free_entries(&subject2item, struct subject2item_entry, entry);
clear_commit_todo_item(&commit_todo); clear_commit_todo_item(&commit_todo);

View File

@ -6,12 +6,14 @@
#include "pkt-line.h" #include "pkt-line.h"
int cmd2process_cmp(const void *unused_cmp_data, int cmd2process_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct subprocess_entry *e1 = entry; const struct subprocess_entry *e1, *e2;
const struct subprocess_entry *e2 = entry_or_key;
e1 = container_of(eptr, const struct subprocess_entry, ent);
e2 = container_of(entry_or_key, const struct subprocess_entry, ent);
return strcmp(e1->cmd, e2->cmd); return strcmp(e1->cmd, e2->cmd);
} }
@ -20,9 +22,9 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch
{ {
struct subprocess_entry key; struct subprocess_entry key;
hashmap_entry_init(&key, strhash(cmd)); hashmap_entry_init(&key.ent, strhash(cmd));
key.cmd = cmd; key.cmd = cmd;
return hashmap_get(hashmap, &key, NULL); return hashmap_get_entry(hashmap, &key, ent, NULL);
} }
int subprocess_read_status(int fd, struct strbuf *status) int subprocess_read_status(int fd, struct strbuf *status)
@ -58,7 +60,7 @@ void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
kill(entry->process.pid, SIGTERM); kill(entry->process.pid, SIGTERM);
finish_command(&entry->process); finish_command(&entry->process);
hashmap_remove(hashmap, entry, NULL); hashmap_remove(hashmap, &entry->ent, NULL);
} }
static void subprocess_exit_handler(struct child_process *process) static void subprocess_exit_handler(struct child_process *process)
@ -96,7 +98,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
return err; return err;
} }
hashmap_entry_init(entry, strhash(cmd)); hashmap_entry_init(&entry->ent, strhash(cmd));
err = startfn(entry); err = startfn(entry);
if (err) { if (err) {
@ -105,7 +107,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
return err; return err;
} }
hashmap_add(hashmap, entry); hashmap_add(hashmap, &entry->ent);
return 0; return 0;
} }

View File

@ -24,7 +24,7 @@
/* Members should not be accessed directly. */ /* Members should not be accessed directly. */
struct subprocess_entry { struct subprocess_entry {
struct hashmap_entry ent; /* must be the first member! */ struct hashmap_entry ent;
const char *cmd; const char *cmd;
struct child_process process; struct child_process process;
}; };
@ -43,8 +43,8 @@ struct subprocess_capability {
/* Function to test two subprocess hashmap entries for equality. */ /* Function to test two subprocess hashmap entries for equality. */
int cmd2process_cmp(const void *unused_cmp_data, int cmd2process_cmp(const void *unused_cmp_data,
const void *e1, const struct hashmap_entry *e,
const void *e2, const struct hashmap_entry *entry_or_key,
const void *unused_keydata); const void *unused_keydata);
/* /*

View File

@ -38,24 +38,28 @@ enum lookup_type {
}; };
static int config_path_cmp(const void *unused_cmp_data, static int config_path_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct submodule_entry *a = entry; const struct submodule_entry *a, *b;
const struct submodule_entry *b = entry_or_key;
a = container_of(eptr, const struct submodule_entry, ent);
b = container_of(entry_or_key, const struct submodule_entry, ent);
return strcmp(a->config->path, b->config->path) || return strcmp(a->config->path, b->config->path) ||
!oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid); !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
} }
static int config_name_cmp(const void *unused_cmp_data, static int config_name_cmp(const void *unused_cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *unused_keydata) const void *unused_keydata)
{ {
const struct submodule_entry *a = entry; const struct submodule_entry *a, *b;
const struct submodule_entry *b = entry_or_key;
a = container_of(eptr, const struct submodule_entry, ent);
b = container_of(entry_or_key, const struct submodule_entry, ent);
return strcmp(a->config->name, b->config->name) || return strcmp(a->config->name, b->config->name) ||
!oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid); !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
@ -95,12 +99,12 @@ static void submodule_cache_clear(struct submodule_cache *cache)
* allocation of struct submodule entries. Each is allocated by * allocation of struct submodule entries. Each is allocated by
* their .gitmodules blob sha1 and submodule name. * their .gitmodules blob sha1 and submodule name.
*/ */
hashmap_iter_init(&cache->for_name, &iter); hashmap_for_each_entry(&cache->for_name, &iter, entry,
while ((entry = hashmap_iter_next(&iter))) ent /* member name */)
free_one_config(entry); free_one_config(entry);
hashmap_free(&cache->for_path, 1); hashmap_free_entries(&cache->for_path, struct submodule_entry, ent);
hashmap_free(&cache->for_name, 1); hashmap_free_entries(&cache->for_name, struct submodule_entry, ent);
cache->initialized = 0; cache->initialized = 0;
cache->gitmodules_read = 0; cache->gitmodules_read = 0;
} }
@ -123,9 +127,9 @@ static void cache_put_path(struct submodule_cache *cache,
unsigned int hash = hash_oid_string(&submodule->gitmodules_oid, unsigned int hash = hash_oid_string(&submodule->gitmodules_oid,
submodule->path); submodule->path);
struct submodule_entry *e = xmalloc(sizeof(*e)); struct submodule_entry *e = xmalloc(sizeof(*e));
hashmap_entry_init(e, hash); hashmap_entry_init(&e->ent, hash);
e->config = submodule; e->config = submodule;
hashmap_put(&cache->for_path, e); hashmap_put(&cache->for_path, &e->ent);
} }
static void cache_remove_path(struct submodule_cache *cache, static void cache_remove_path(struct submodule_cache *cache,
@ -135,9 +139,9 @@ static void cache_remove_path(struct submodule_cache *cache,
submodule->path); submodule->path);
struct submodule_entry e; struct submodule_entry e;
struct submodule_entry *removed; struct submodule_entry *removed;
hashmap_entry_init(&e, hash); hashmap_entry_init(&e.ent, hash);
e.config = submodule; e.config = submodule;
removed = hashmap_remove(&cache->for_path, &e, NULL); removed = hashmap_remove_entry(&cache->for_path, &e, ent, NULL);
free(removed); free(removed);
} }
@ -147,9 +151,9 @@ static void cache_add(struct submodule_cache *cache,
unsigned int hash = hash_oid_string(&submodule->gitmodules_oid, unsigned int hash = hash_oid_string(&submodule->gitmodules_oid,
submodule->name); submodule->name);
struct submodule_entry *e = xmalloc(sizeof(*e)); struct submodule_entry *e = xmalloc(sizeof(*e));
hashmap_entry_init(e, hash); hashmap_entry_init(&e->ent, hash);
e->config = submodule; e->config = submodule;
hashmap_add(&cache->for_name, e); hashmap_add(&cache->for_name, &e->ent);
} }
static const struct submodule *cache_lookup_path(struct submodule_cache *cache, static const struct submodule *cache_lookup_path(struct submodule_cache *cache,
@ -163,10 +167,10 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache,
oidcpy(&key_config.gitmodules_oid, gitmodules_oid); oidcpy(&key_config.gitmodules_oid, gitmodules_oid);
key_config.path = path; key_config.path = path;
hashmap_entry_init(&key, hash); hashmap_entry_init(&key.ent, hash);
key.config = &key_config; key.config = &key_config;
entry = hashmap_get(&cache->for_path, &key, NULL); entry = hashmap_get_entry(&cache->for_path, &key, ent, NULL);
if (entry) if (entry)
return entry->config; return entry->config;
return NULL; return NULL;
@ -183,10 +187,10 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,
oidcpy(&key_config.gitmodules_oid, gitmodules_oid); oidcpy(&key_config.gitmodules_oid, gitmodules_oid);
key_config.name = name; key_config.name = name;
hashmap_entry_init(&key, hash); hashmap_entry_init(&key.ent, hash);
key.config = &key_config; key.config = &key_config;
entry = hashmap_get(&cache->for_name, &key, NULL); entry = hashmap_get_entry(&cache->for_name, &key, ent, NULL);
if (entry) if (entry)
return entry->config; return entry->config;
return NULL; return NULL;
@ -550,7 +554,9 @@ static const struct submodule *config_from(struct submodule_cache *cache,
struct hashmap_iter iter; struct hashmap_iter iter;
struct submodule_entry *entry; struct submodule_entry *entry;
entry = hashmap_iter_first(&cache->for_name, &iter); entry = hashmap_iter_first_entry(&cache->for_name, &iter,
struct submodule_entry,
ent /* member name */);
if (!entry) if (!entry)
return NULL; return NULL;
return entry->config; return entry->config;

View File

@ -5,6 +5,7 @@
struct test_entry struct test_entry
{ {
int padding; /* hashmap entry no longer needs to be the first member */
struct hashmap_entry ent; struct hashmap_entry ent;
/* key and value as two \0-terminated strings */ /* key and value as two \0-terminated strings */
char key[FLEX_ARRAY]; char key[FLEX_ARRAY];
@ -16,15 +17,17 @@ static const char *get_value(const struct test_entry *e)
} }
static int test_entry_cmp(const void *cmp_data, static int test_entry_cmp(const void *cmp_data,
const void *entry, const struct hashmap_entry *eptr,
const void *entry_or_key, const struct hashmap_entry *entry_or_key,
const void *keydata) const void *keydata)
{ {
const int ignore_case = cmp_data ? *((int *)cmp_data) : 0; const int ignore_case = cmp_data ? *((int *)cmp_data) : 0;
const struct test_entry *e1 = entry; const struct test_entry *e1, *e2;
const struct test_entry *e2 = entry_or_key;
const char *key = keydata; const char *key = keydata;
e1 = container_of(eptr, const struct test_entry, ent);
e2 = container_of(entry_or_key, const struct test_entry, ent);
if (ignore_case) if (ignore_case)
return strcasecmp(e1->key, key ? key : e2->key); return strcasecmp(e1->key, key ? key : e2->key);
else else
@ -37,7 +40,7 @@ static struct test_entry *alloc_test_entry(unsigned int hash,
size_t klen = strlen(key); size_t klen = strlen(key);
size_t vlen = strlen(value); size_t vlen = strlen(value);
struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2)); struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2));
hashmap_entry_init(entry, hash); hashmap_entry_init(&entry->ent, hash);
memcpy(entry->key, key, klen + 1); memcpy(entry->key, key, klen + 1);
memcpy(entry->key + klen + 1, value, vlen + 1); memcpy(entry->key + klen + 1, value, vlen + 1);
return entry; return entry;
@ -103,11 +106,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
/* add entries */ /* add entries */
for (i = 0; i < TEST_SIZE; i++) { for (i = 0; i < TEST_SIZE; i++) {
hashmap_entry_init(entries[i], hashes[i]); hashmap_entry_init(&entries[i]->ent, hashes[i]);
hashmap_add(&map, entries[i]); hashmap_add(&map, &entries[i]->ent);
} }
hashmap_free(&map, 0); hashmap_free(&map);
} }
} else { } else {
/* test map lookups */ /* test map lookups */
@ -116,8 +119,8 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
/* fill the map (sparsely if specified) */ /* fill the map (sparsely if specified) */
j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE; j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
for (i = 0; i < j; i++) { for (i = 0; i < j; i++) {
hashmap_entry_init(entries[i], hashes[i]); hashmap_entry_init(&entries[i]->ent, hashes[i]);
hashmap_add(&map, entries[i]); hashmap_add(&map, &entries[i]->ent);
} }
for (j = 0; j < rounds; j++) { for (j = 0; j < rounds; j++) {
@ -127,7 +130,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
} }
} }
hashmap_free(&map, 0); hashmap_free(&map);
} }
} }
@ -179,7 +182,7 @@ int cmd__hashmap(int argc, const char **argv)
entry = alloc_test_entry(hash, p1, p2); entry = alloc_test_entry(hash, p1, p2);
/* add to hashmap */ /* add to hashmap */
hashmap_add(&map, entry); hashmap_add(&map, &entry->ent);
} else if (!strcmp("put", cmd) && p1 && p2) { } else if (!strcmp("put", cmd) && p1 && p2) {
@ -187,43 +190,44 @@ int cmd__hashmap(int argc, const char **argv)
entry = alloc_test_entry(hash, p1, p2); entry = alloc_test_entry(hash, p1, p2);
/* add / replace entry */ /* add / replace entry */
entry = hashmap_put(&map, entry); entry = hashmap_put_entry(&map, entry, ent);
/* print and free replaced entry, if any */ /* print and free replaced entry, if any */
puts(entry ? get_value(entry) : "NULL"); puts(entry ? get_value(entry) : "NULL");
free(entry); free(entry);
} else if (!strcmp("get", cmd) && p1) { } else if (!strcmp("get", cmd) && p1) {
/* lookup entry in hashmap */ /* lookup entry in hashmap */
entry = hashmap_get_from_hash(&map, hash, p1); entry = hashmap_get_entry_from_hash(&map, hash, p1,
struct test_entry, ent);
/* print result */ /* print result */
if (!entry) if (!entry)
puts("NULL"); puts("NULL");
while (entry) { hashmap_for_each_entry_from(&map, entry, ent)
puts(get_value(entry)); puts(get_value(entry));
entry = hashmap_get_next(&map, entry);
}
} else if (!strcmp("remove", cmd) && p1) { } else if (!strcmp("remove", cmd) && p1) {
/* setup static key */ /* setup static key */
struct hashmap_entry key; struct hashmap_entry key;
struct hashmap_entry *rm;
hashmap_entry_init(&key, hash); hashmap_entry_init(&key, hash);
/* remove entry from hashmap */ /* remove entry from hashmap */
entry = hashmap_remove(&map, &key, p1); rm = hashmap_remove(&map, &key, p1);
entry = rm ? container_of(rm, struct test_entry, ent)
: NULL;
/* print result and free entry*/ /* print result and free entry*/
puts(entry ? get_value(entry) : "NULL"); puts(entry ? get_value(entry) : "NULL");
free(entry); free(entry);
} else if (!strcmp("iterate", cmd)) { } else if (!strcmp("iterate", cmd)) {
struct hashmap_iter iter; struct hashmap_iter iter;
hashmap_iter_init(&map, &iter);
while ((entry = hashmap_iter_next(&iter))) hashmap_for_each_entry(&map, &iter, entry,
ent /* member name */)
printf("%s %s\n", entry->key, get_value(entry)); printf("%s %s\n", entry->key, get_value(entry));
} else if (!strcmp("size", cmd)) { } else if (!strcmp("size", cmd)) {
@ -258,6 +262,6 @@ int cmd__hashmap(int argc, const char **argv)
} }
strbuf_release(&line); strbuf_release(&line);
hashmap_free(&map, 1); hashmap_free_entries(&map, struct test_entry, ent);
return 0; return 0;
} }

View File

@ -41,17 +41,13 @@ static void dump_run(void)
die("non-threaded code path used"); die("non-threaded code path used");
} }
dir = hashmap_iter_first(&the_index.dir_hash, &iter_dir); hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
while (dir) { ent /* member name */)
printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name); printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
dir = hashmap_iter_next(&iter_dir);
}
ce = hashmap_iter_first(&the_index.name_hash, &iter_cache); hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
while (ce) { ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name); printf("name %08x %s\n", ce->ent.hash, ce->name);
ce = hashmap_iter_next(&iter_cache);
}
discard_cache(); discard_cache();
} }