Skip to content

Commit 23dee69

Browse files
Eric Wonggitster
authored andcommitted
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 <[email protected]> Reviewed-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c8e424c commit 23dee69

15 files changed

+56
-45
lines changed

attr.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
168168
check->all_attrs_nr = size;
169169

170170
hashmap_for_each_entry(&map->map, &iter, e,
171-
struct attr_hash_entry,
172171
ent /* member name */) {
173172
const struct git_attr *a = e->value;
174173
check->all_attrs[a->attr_nr].attr = a;

blame.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,6 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)
451451
const struct fingerprint_entry *entry_a, *entry_b;
452452

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

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

builtin/describe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
333333
struct commit_name *n;
334334

335335
init_commit_names(&commit_names);
336-
hashmap_for_each_entry(&names, &iter, n, struct commit_name,
336+
hashmap_for_each_entry(&names, &iter, n,
337337
entry /* member name */) {
338338
c = lookup_commit_reference_gently(the_repository,
339339
&n->peeled, 1);

builtin/difftool.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
539539
* change in the recorded SHA1 for the submodule.
540540
*/
541541
hashmap_for_each_entry(&submodules, &iter, entry,
542-
struct pair_entry, entry /* member name */) {
542+
entry /* member name */) {
543543
if (*entry->left) {
544544
add_path(&ldir, ldir_len, entry->path);
545545
ensure_leading_directories(ldir.buf);
@@ -558,7 +558,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
558558
* This loop replicates that behavior.
559559
*/
560560
hashmap_for_each_entry(&symlinks2, &iter, entry,
561-
struct pair_entry, entry /* member name */) {
561+
entry /* member name */) {
562562
if (*entry->left) {
563563
add_path(&ldir, ldir_len, entry->path);
564564
ensure_leading_directories(ldir.buf);

config.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,6 @@ void git_configset_clear(struct config_set *cs)
19431943
return;
19441944

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

diff.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
10381038
int i;
10391039
char *got_match = xcalloc(1, pmb_nr);
10401040

1041-
hashmap_for_each_entry_from(hm, match, struct moved_entry, ent) {
1041+
hashmap_for_each_entry_from(hm, match, ent) {
10421042
for (i = 0; i < pmb_nr; i++) {
10431043
struct moved_entry *prev = pmb[i].match;
10441044
struct moved_entry *cur = (prev && prev->next_line) ?
@@ -1193,8 +1193,7 @@ static void mark_color_as_moved(struct diff_options *o,
11931193
* The current line is the start of a new block.
11941194
* Setup the set of potential blocks.
11951195
*/
1196-
hashmap_for_each_entry_from(hm, match,
1197-
struct moved_entry, ent) {
1196+
hashmap_for_each_entry_from(hm, match, ent) {
11981197
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
11991198
if (o->color_moved_ws_handling &
12001199
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {

diffcore-rename.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ static int find_identical_files(struct hashmap *srcs,
284284
*/
285285
p = hashmap_get_entry_from_hash(srcs, hash, NULL,
286286
struct file_similarity, entry);
287-
hashmap_for_each_entry_from(srcs, p, struct file_similarity, entry) {
287+
hashmap_for_each_entry_from(srcs, p, entry) {
288288
int score;
289289
struct diff_filespec *source = p->filespec;
290290

git-compat-util.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,4 +1337,17 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
13371337
#define container_of_or_null(ptr, type, member) \
13381338
(type *)container_of_or_null_offset(ptr, offsetof(type, member))
13391339

1340+
/*
1341+
* like offsetof(), but takes a pointer to a a variable of type which
1342+
* contains @member, instead of a specified type.
1343+
* @ptr is subject to multiple evaluation since we can't rely on __typeof__
1344+
* everywhere.
1345+
*/
1346+
#if defined(__GNUC__) /* clang sets this, too */
1347+
#define OFFSETOF_VAR(ptr, member) offsetof(__typeof__(*ptr), member)
1348+
#else /* !__GNUC__ */
1349+
#define OFFSETOF_VAR(ptr, member) \
1350+
((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))
1351+
#endif /* !__GNUC__ */
1352+
13401353
#endif

hashmap.h

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -408,16 +408,32 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
408408
return hashmap_iter_next(iter);
409409
}
410410

411-
#define hashmap_iter_next_entry(iter, type, member) \
412-
container_of_or_null(hashmap_iter_next(iter), type, member)
413-
411+
/*
412+
* returns the first entry in @map using @iter, where the entry is of
413+
* @type (e.g. "struct foo") and @member is the name of the
414+
* "struct hashmap_entry" in @type
415+
*/
414416
#define hashmap_iter_first_entry(map, iter, type, member) \
415417
container_of_or_null(hashmap_iter_first(map, iter), type, member)
416418

417-
#define hashmap_for_each_entry(map, iter, var, type, member) \
418-
for (var = hashmap_iter_first_entry(map, iter, type, member); \
419+
/* internal macro for hashmap_for_each_entry */
420+
#define hashmap_iter_next_entry_offset(iter, offset) \
421+
container_of_or_null_offset(hashmap_iter_next(iter), offset)
422+
423+
/* internal macro for hashmap_for_each_entry */
424+
#define hashmap_iter_first_entry_offset(map, iter, offset) \
425+
container_of_or_null_offset(hashmap_iter_first(map, iter), offset)
426+
427+
/*
428+
* iterate through @map using @iter, @var is a pointer to a type
429+
* containing a @member which is a "struct hashmap_entry"
430+
*/
431+
#define hashmap_for_each_entry(map, iter, var, member) \
432+
for (var = hashmap_iter_first_entry_offset(map, iter, \
433+
OFFSETOF_VAR(var, member)); \
419434
var; \
420-
var = hashmap_iter_next_entry(iter, type, member))
435+
var = hashmap_iter_next_entry_offset(iter, \
436+
OFFSETOF_VAR(var, member)))
421437

422438
/*
423439
* 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,
432448
container_of_or_null(hashmap_get_from_hash(map, hash, keydata), \
433449
type, member)
434450
/*
435-
* returns the next equal @type pointer to @var, or NULL if not found.
436-
* @var is a pointer of @type
437-
* @member is the name of the "struct hashmap_entry" field in @type
451+
* returns the next equal pointer to @var, or NULL if not found.
452+
* @var is a pointer of any type containing "struct hashmap_entry"
453+
* @member is the name of the "struct hashmap_entry" field
438454
*/
439-
#define hashmap_get_next_entry(map, var, type, member) \
440-
container_of_or_null(hashmap_get_next(map, &(var)->member), \
441-
type, member)
455+
#define hashmap_get_next_entry(map, var, member) \
456+
container_of_or_null_offset(hashmap_get_next(map, &(var)->member), \
457+
OFFSETOF_VAR(var, member))
442458

443459
/*
444460
* iterate @map starting from @var, where @var is a pointer of @type
445461
* and @member is the name of the "struct hashmap_entry" field in @type
446462
*/
447-
#define hashmap_for_each_entry_from(map, var, type, member) \
463+
#define hashmap_for_each_entry_from(map, var, member) \
448464
for (; \
449465
var; \
450-
var = hashmap_get_next_entry(map, var, type, member))
466+
var = hashmap_get_next_entry(map, var, member))
451467

452468
/*
453469
* Disable item counting and automatic rehashing when adding/removing items.

merge-recursive.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,7 +2136,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
21362136
struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
21372137

21382138
hashmap_for_each_entry(dir_re_head, &iter, head_ent,
2139-
struct dir_rename_entry,
21402139
ent /* member name */) {
21412140
merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
21422141
if (merge_ent &&
@@ -2162,7 +2161,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,
21622161
remove_hashmap_entries(dir_re_merge, &remove_from_merge);
21632162

21642163
hashmap_for_each_entry(dir_re_merge, &iter, merge_ent,
2165-
struct dir_rename_entry,
21662164
ent /* member name */) {
21672165
head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
21682166
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)
22682266
* that there is no winner), we no longer need possible_new_dirs.
22692267
*/
22702268
hashmap_for_each_entry(dir_renames, &iter, entry,
2271-
struct dir_rename_entry,
22722269
ent /* member name */) {
22732270
int max = 0;
22742271
int bad_max = 0;
@@ -2628,7 +2625,6 @@ static struct string_list *get_renames(struct merge_options *opt,
26282625
}
26292626

26302627
hashmap_for_each_entry(&collisions, &iter, e,
2631-
struct collision_entry,
26322628
ent /* member name */) {
26332629
free(e->target_file);
26342630
string_list_clear(&e->source_files, 0);
@@ -2847,7 +2843,6 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
28472843
struct dir_rename_entry *e;
28482844

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

0 commit comments

Comments
 (0)