Browse Source

OFFSETOF_VAR macro to simplify hashmap iterators

While we cannot rely on a `__typeof__' operator being portable
to use with `offsetof'; we can calculate the pointer offset
using an existing pointer and the address of a member using
pointer arithmetic for compilers without `__typeof__'.

This allows us to simplify usage of hashmap iterator macros
by not having to specify a type when a pointer of that type
is already given.

In the future, list iterator macros (e.g. list_for_each_entry)
may also be implemented using OFFSETOF_VAR to save hackers the
trouble of using container_of/list_entry macros and without
relying on non-portable `__typeof__'.

v3: use `__typeof__' to avoid clang warnings

Signed-off-by: Eric Wong <e@80x24.org>
Reviewed-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Eric Wong 5 years ago committed by Junio C Hamano
parent
commit
23dee69f53
  1. 1
      attr.c
  2. 2
      blame.c
  3. 2
      builtin/describe.c
  4. 4
      builtin/difftool.c
  5. 1
      config.c
  6. 5
      diff.c
  7. 2
      diffcore-rename.c
  8. 13
      git-compat-util.h
  9. 44
      hashmap.h
  10. 5
      merge-recursive.c
  11. 3
      name-hash.c
  12. 8
      revision.c
  13. 2
      submodule-config.c
  14. 5
      t/helper/test-hashmap.c
  15. 4
      t/helper/test-lazy-init-name-hash.c

1
attr.c

@ -168,7 +168,6 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
check->all_attrs_nr = size; check->all_attrs_nr = size;


hashmap_for_each_entry(&map->map, &iter, e, hashmap_for_each_entry(&map->map, &iter, e,
struct attr_hash_entry,
ent /* member name */) { 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;

2
blame.c

@ -451,7 +451,6 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)
const struct fingerprint_entry *entry_a, *entry_b; const struct fingerprint_entry *entry_a, *entry_b;


hashmap_for_each_entry(&b->map, &iter, entry_b, hashmap_for_each_entry(&b->map, &iter, entry_b,
const struct fingerprint_entry,
entry /* member name */) { entry /* member name */) {
entry_a = hashmap_get_entry(&a->map, entry_b, NULL, entry_a = hashmap_get_entry(&a->map, entry_b, NULL,
struct fingerprint_entry, entry); struct fingerprint_entry, entry);
@ -474,7 +473,6 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)
hashmap_iter_init(&b->map, &iter); hashmap_iter_init(&b->map, &iter);


hashmap_for_each_entry(&b->map, &iter, entry_b, hashmap_for_each_entry(&b->map, &iter, entry_b,
const struct fingerprint_entry,
entry /* member name */) { entry /* member name */) {
entry_a = hashmap_get_entry(&a->map, entry_b, NULL, entry_a = hashmap_get_entry(&a->map, entry_b, NULL,
struct fingerprint_entry, entry); struct fingerprint_entry, entry);

2
builtin/describe.c

@ -333,7 +333,7 @@ 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);
hashmap_for_each_entry(&names, &iter, n, struct commit_name, hashmap_for_each_entry(&names, &iter, n,
entry /* member name */) { entry /* member name */) {
c = lookup_commit_reference_gently(the_repository, c = lookup_commit_reference_gently(the_repository,
&n->peeled, 1); &n->peeled, 1);

4
builtin/difftool.c

@ -539,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* change in the recorded SHA1 for the submodule. * change in the recorded SHA1 for the submodule.
*/ */
hashmap_for_each_entry(&submodules, &iter, entry, hashmap_for_each_entry(&submodules, &iter, entry,
struct pair_entry, entry /* member name */) { 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);
@ -558,7 +558,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* This loop replicates that behavior. * This loop replicates that behavior.
*/ */
hashmap_for_each_entry(&symlinks2, &iter, entry, hashmap_for_each_entry(&symlinks2, &iter, entry,
struct pair_entry, entry /* member name */) { 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);

1
config.c

@ -1943,7 +1943,6 @@ void git_configset_clear(struct config_set *cs)
return; return;


hashmap_for_each_entry(&cs->config_hash, &iter, entry, hashmap_for_each_entry(&cs->config_hash, &iter, entry,
struct config_set_element,
ent /* member name */) { ent /* member name */) {
free(entry->key); free(entry->key);
string_list_clear(&entry->value_list, 1); string_list_clear(&entry->value_list, 1);

5
diff.c

@ -1038,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);


hashmap_for_each_entry_from(hm, match, struct moved_entry, ent) { 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) ?
@ -1193,8 +1193,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.
*/ */
hashmap_for_each_entry_from(hm, match, hashmap_for_each_entry_from(hm, match, ent) {
struct moved_entry, 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) {

2
diffcore-rename.c

@ -284,7 +284,7 @@ static int find_identical_files(struct hashmap *srcs,
*/ */
p = hashmap_get_entry_from_hash(srcs, hash, NULL, p = hashmap_get_entry_from_hash(srcs, hash, NULL,
struct file_similarity, entry); struct file_similarity, entry);
hashmap_for_each_entry_from(srcs, p, struct file_similarity, entry) { hashmap_for_each_entry_from(srcs, p, entry) {
int score; int score;
struct diff_filespec *source = p->filespec; struct diff_filespec *source = p->filespec;



13
git-compat-util.h

@ -1337,4 +1337,17 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
#define container_of_or_null(ptr, type, member) \ #define container_of_or_null(ptr, type, member) \
(type *)container_of_or_null_offset(ptr, offsetof(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

44
hashmap.h

@ -408,16 +408,32 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
return hashmap_iter_next(iter); return hashmap_iter_next(iter);
} }


#define hashmap_iter_next_entry(iter, type, member) \ /*
container_of_or_null(hashmap_iter_next(iter), type, member) * 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) \ #define hashmap_iter_first_entry(map, iter, type, member) \
container_of_or_null(hashmap_iter_first(map, iter), type, member) container_of_or_null(hashmap_iter_first(map, iter), type, member)


#define hashmap_for_each_entry(map, iter, var, type, member) \ /* internal macro for hashmap_for_each_entry */
for (var = hashmap_iter_first_entry(map, iter, type, member); \ #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; \
var = hashmap_iter_next_entry(iter, type, member)) var = hashmap_iter_next_entry_offset(iter, \
OFFSETOF_VAR(var, member)))


/* /*
* returns a @pointer of @type matching @keyvar, or NULL if nothing found. * returns a @pointer of @type matching @keyvar, or NULL if nothing found.
@ -432,22 +448,22 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
container_of_or_null(hashmap_get_from_hash(map, hash, keydata), \ container_of_or_null(hashmap_get_from_hash(map, hash, keydata), \
type, member) type, member)
/* /*
* returns the next equal @type pointer to @var, or NULL if not found. * returns the next equal pointer to @var, or NULL if not found.
* @var is a pointer of @type * @var is a pointer of any type containing "struct hashmap_entry"
* @member is the name of the "struct hashmap_entry" field in @type * @member is the name of the "struct hashmap_entry" field
*/ */
#define hashmap_get_next_entry(map, var, type, member) \ #define hashmap_get_next_entry(map, var, member) \
container_of_or_null(hashmap_get_next(map, &(var)->member), \ container_of_or_null_offset(hashmap_get_next(map, &(var)->member), \
type, member) OFFSETOF_VAR(var, member))


/* /*
* iterate @map starting from @var, where @var is a pointer of @type * 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 * and @member is the name of the "struct hashmap_entry" field in @type
*/ */
#define hashmap_for_each_entry_from(map, var, type, member) \ #define hashmap_for_each_entry_from(map, var, member) \
for (; \ for (; \
var; \ var; \
var = hashmap_get_next_entry(map, var, type, member)) 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.

5
merge-recursive.c

@ -2136,7 +2136,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
struct string_list remove_from_merge = STRING_LIST_INIT_NODUP; struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;


hashmap_for_each_entry(dir_re_head, &iter, head_ent, hashmap_for_each_entry(dir_re_head, &iter, head_ent,
struct dir_rename_entry,
ent /* member name */) { 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 &&
@ -2162,7 +2161,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
remove_hashmap_entries(dir_re_merge, &remove_from_merge); remove_hashmap_entries(dir_re_merge, &remove_from_merge);


hashmap_for_each_entry(dir_re_merge, &iter, merge_ent, hashmap_for_each_entry(dir_re_merge, &iter, merge_ent,
struct dir_rename_entry,
ent /* member name */) { 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)) {
@ -2268,7 +2266,6 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
* 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_for_each_entry(dir_renames, &iter, entry, hashmap_for_each_entry(dir_renames, &iter, entry,
struct dir_rename_entry,
ent /* member name */) { ent /* member name */) {
int max = 0; int max = 0;
int bad_max = 0; int bad_max = 0;
@ -2628,7 +2625,6 @@ static struct string_list *get_renames(struct merge_options *opt,
} }


hashmap_for_each_entry(&collisions, &iter, e, hashmap_for_each_entry(&collisions, &iter, e,
struct collision_entry,
ent /* member name */) { 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);
@ -2847,7 +2843,6 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
struct dir_rename_entry *e; struct dir_rename_entry *e;


hashmap_for_each_entry(dir_renames, &iter, e, hashmap_for_each_entry(dir_renames, &iter, e,
struct dir_rename_entry,
ent /* member name */) { ent /* member name */) {
free(e->dir); free(e->dir);
strbuf_release(&e->new_dir); strbuf_release(&e->new_dir);

3
name-hash.c

@ -714,8 +714,7 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na


ce = hashmap_get_entry_from_hash(&istate->name_hash, hash, NULL, ce = hashmap_get_entry_from_hash(&istate->name_hash, hash, NULL,
struct cache_entry, ent); struct cache_entry, ent);
hashmap_for_each_entry_from(&istate->name_hash, ce, hashmap_for_each_entry_from(&istate->name_hash, ce, ent) {
struct cache_entry, ent) {
if (same_name(ce, name, namelen, icase)) if (same_name(ce, name, namelen, icase))
return ce; return ce;
} }

8
revision.c

@ -129,9 +129,7 @@ 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_for_each_entry(map, &iter, entry, hashmap_for_each_entry(map, &iter, entry, ent /* member name */) {
struct path_and_oids_entry,
ent /* member name */) {
oidset_clear(&entry->trees); oidset_clear(&entry->trees);
free(entry->path); free(entry->path);
} }
@ -243,9 +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_for_each_entry(&map, &map_iter, entry, hashmap_for_each_entry(&map, &map_iter, entry, ent /* member name */)
struct path_and_oids_entry,
ent /* member name */)
mark_trees_uninteresting_sparse(r, &entry->trees); mark_trees_uninteresting_sparse(r, &entry->trees);


paths_and_oids_clear(&map); paths_and_oids_clear(&map);

2
submodule-config.c

@ -100,7 +100,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)
* their .gitmodules blob sha1 and submodule name. * their .gitmodules blob sha1 and submodule name.
*/ */
hashmap_for_each_entry(&cache->for_name, &iter, entry, hashmap_for_each_entry(&cache->for_name, &iter, entry,
struct submodule_entry, ent /* member name */) ent /* member name */)
free_one_config(entry); free_one_config(entry);


hashmap_free_entries(&cache->for_path, struct submodule_entry, ent); hashmap_free_entries(&cache->for_path, struct submodule_entry, ent);

5
t/helper/test-hashmap.c

@ -205,10 +205,8 @@ int cmd__hashmap(int argc, const char **argv)
/* print result */ /* print result */
if (!entry) if (!entry)
puts("NULL"); puts("NULL");
hashmap_for_each_entry_from(&map, entry, hashmap_for_each_entry_from(&map, entry, ent)
struct test_entry, ent) {
puts(get_value(entry)); puts(get_value(entry));
}


} else if (!strcmp("remove", cmd) && p1) { } else if (!strcmp("remove", cmd) && p1) {


@ -230,7 +228,6 @@ int cmd__hashmap(int argc, const char **argv)
struct hashmap_iter iter; struct hashmap_iter iter;


hashmap_for_each_entry(&map, &iter, entry, hashmap_for_each_entry(&map, &iter, entry,
struct test_entry,
ent /* member name */) ent /* member name */)
printf("%s %s\n", entry->key, get_value(entry)); printf("%s %s\n", entry->key, get_value(entry));



4
t/helper/test-lazy-init-name-hash.c

@ -42,11 +42,11 @@ static void dump_run(void)
} }


hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir, hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
struct dir_entry, ent /* member name */) 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);


hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce, hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
struct cache_entry, ent /* member name */) ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name); printf("name %08x %s\n", ce->ent.hash, ce->name);


discard_cache(); discard_cache();

Loading…
Cancel
Save