{"thread":{"id":"51981","subject":"[PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","startedAt":"2019-10-06T23:30:46Z","lastAt":"2019-10-09T01:21:04Z","messageCount":30,"participants":["Eric Wong","Junio C Hamano","Johannes Schindelin","Derrick Stolee"],"isPatch":true,"patchVersion":3,"patchTotal":20},"messages":[{"id":"383530","messageId":"20191006233043.3516-1-e@80x24.org","threadId":"51981","inReplyTo":null,"subject":"[PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:23Z","receivedAt":"2019-10-06T23:30:46Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"v3 changes:\n- use __typeof__ to avoid invalid clang warning on uninitialized var\n- formatting fixes recommended by Stolee\n- add Reviewed-by for Stolee\n- add patch 20 to update docs to drop first member requirement\n\nv2 here:\n  https://public-inbox.org/git/20190924010324.22619-1-e@80x24.org/\n\nThe following changes since commit 745f6812895b31c02b29bdfe4ae8e5498f776c26:\n\n  First batch after Git 2.23 (2019-08-22 12:41:04 -0700)\n\nare available in the Git repository at:\n\n  https://80x24.org/git-svn.git hashmap-wip-v3\n\nfor you to fetch changes up to 4ec5200646d6b9363e64e2002eb374aa888a6c88:\n\n  hashmap_entry: remove first member requirement from docs (2019-10-06 23:05:22 +0000)\n\n----------------------------------------------------------------\nEric Wong (20):\n      diff: use hashmap_entry_init on moved_entry.ent\n      coccicheck: detect hashmap_entry.hash assignment\n      packfile: use hashmap_entry in delta_base_cache_entry\n      hashmap_entry_init takes \"struct hashmap_entry *\"\n      hashmap_get_next takes \"const struct hashmap_entry *\"\n      hashmap_add takes \"struct hashmap_entry *\"\n      hashmap_get takes \"const struct hashmap_entry *\"\n      hashmap_remove takes \"const struct hashmap_entry *\"\n      hashmap_put takes \"struct hashmap_entry *\"\n      introduce container_of macro\n      hashmap_get_next returns \"struct hashmap_entry *\"\n      hashmap: use *_entry APIs to wrap container_of\n      hashmap_get{,_from_hash} return \"struct hashmap_entry *\"\n      hashmap_cmp_fn takes hashmap_entry params\n      hashmap: use *_entry APIs for iteration\n      hashmap: hashmap_{put,remove} return hashmap_entry *\n      hashmap: introduce hashmap_free_entries\n      OFFSETOF_VAR macro to simplify hashmap iterators\n      hashmap: remove type arg from hashmap_{get,put,remove}_entry\n      hashmap_entry: remove first member requirement from docs\n\n attr.c                              |  24 ++---\n blame.c                             |  25 ++---\n builtin/describe.c                  |  21 +++--\n builtin/difftool.c                  |  56 +++++++-----\n builtin/fast-export.c               |  15 ++-\n builtin/fetch.c                     |  32 ++++---\n config.c                            |  24 ++---\n contrib/coccinelle/hashmap.cocci    |  16 ++++\n diff.c                              |  31 ++++---\n diffcore-rename.c                   |  15 ++-\n git-compat-util.h                   |  38 ++++++++\n hashmap.c                           |  58 +++++++-----\n hashmap.h                           | 176 +++++++++++++++++++++++++++++-------\n merge-recursive.c                   |  87 ++++++++++--------\n merge-recursive.h                   |   4 +-\n name-hash.c                         |  57 ++++++------\n oidmap.c                            |  20 ++--\n oidmap.h                            |   6 +-\n packfile.c                          |  22 +++--\n patch-ids.c                         |  18 ++--\n range-diff.c                        |  10 +-\n ref-filter.c                        |  33 ++++---\n refs.c                              |  25 +++--\n remote.c                            |  21 +++--\n remote.h                            |   2 +-\n revision.c                          |  28 +++---\n sequencer.c                         |  44 ++++++---\n sub-process.c                       |  20 ++--\n sub-process.h                       |   6 +-\n submodule-config.c                  |  52 ++++++-----\n t/helper/test-hashmap.c             |  50 +++++-----\n t/helper/test-lazy-init-name-hash.c |  12 +--\n 32 files changed, 667 insertions(+), 381 deletions(-)\n create mode 100644 contrib/coccinelle/hashmap.cocci\n\nRange-diff against v2:\n 1:  51ba91d2f9 !  1:  0f530552de diff: use hashmap_entry_init on moved_entry.ent\n    @@ -10,6 +10,7 @@\n         hashmap_add callers to take \"struct hashmap_entry *\".\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/diff.c b/diff.c\n      --- a/diff.c\n 2:  be4071d33d !  2:  d12ddb4327 coccicheck: detect hashmap_entry.hash assignment\n    @@ -8,6 +8,7 @@\n         hashmap_entry_init, instead.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/contrib/coccinelle/hashmap.cocci b/contrib/coccinelle/hashmap.cocci\n      new file mode 100644\n 3:  ff40a39bad !  3:  624b89fbb4 packfile: use hashmap_entry in delta_base_cache_entry\n    @@ -15,6 +15,7 @@\n         systems.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/packfile.c b/packfile.c\n      --- a/packfile.c\n 4:  d1db0f6472 !  4:  7512f2f2f7 hashmap_entry_init takes \"struct hashmap_entry *\"\n    @@ -8,6 +8,7 @@\n         safety and readability.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n    @@ -212,8 +213,8 @@\n       * and if it is on stack, you can just let it go out of scope).\n       */\n     -static inline void hashmap_entry_init(void *entry, unsigned int hash)\n    -+static inline void\n    -+hashmap_entry_init(struct hashmap_entry *e, unsigned int hash)\n    ++static inline void hashmap_entry_init(struct hashmap_entry *e,\n    ++\t\t\t\t\tunsigned int hash)\n      {\n     -\tstruct hashmap_entry *e = entry;\n      \te->hash = hash;\n 5:  a293445e97 !  5:  dd167a1113 hashmap_get_next takes \"const struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         now detects invalid types being passed.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/diff.c b/diff.c\n      --- a/diff.c\n 6:  beabdab6fe !  6:  53f429ef6f hashmap_add takes \"struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         detects invalid types being passed.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n 7:  99970934c7 !  7:  87739268dc hashmap_get takes \"const struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         now detects invalid types being passed.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n 8:  8148ed70eb !  8:  22226b3bb4 hashmap_remove takes \"const struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         now detects invalid types being passed.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/blame.c b/blame.c\n      --- a/blame.c\n 9:  3756dcc34e !  9:  0b64282a1f hashmap_put takes \"struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         detects invalid types being passed.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/builtin/fast-export.c b/builtin/fast-export.c\n      --- a/builtin/fast-export.c\n10:  ff586aa43c ! 10:  c2e9201838 introduce container_of macro\n    @@ -7,15 +7,16 @@\n         and chained hash tables while allowing the compiler to do\n         type checking.\n     \n    -    I intend to use this to remove the limitation of \"hashmap_entry\"\n    -    being location-dependent and to allow more compile-time type\n    -    checking.\n    +    Later patches will use container_of() to remove the limitation\n    +    of \"hashmap_entry\" being location-dependent. This will complete\n    +    the transition to compile-time type checking for the hashmap API.\n     \n         This macro already exists in our source as \"list_entry\" in\n         list.h and making \"list_entry\" an alias to \"container_of\"\n         as the Linux kernel has done is a possibility.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/git-compat-util.h b/git-compat-util.h\n      --- a/git-compat-util.h\n11:  931356f061 ! 11:  c709e607b1 hashmap_get_next returns \"struct hashmap_entry *\"\n    @@ -6,6 +6,7 @@\n         hashmap_entry being the first field of a struct.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/diff.c b/diff.c\n      --- a/diff.c\n    @@ -14,10 +15,10 @@\n      {\n      \tint i;\n      \tchar *got_match = xcalloc(1, pmb_nr);\n    -+\tstruct hashmap_entry *ent = &match->ent;\n    ++\tstruct hashmap_entry *ent;\n      \n     -\tfor (; match; match = hashmap_get_next(hm, &match->ent)) {\n    -+\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n    ++\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n     +\t\tmatch = container_of(ent, struct moved_entry, ent);\n      \t\tfor (i = 0; i < pmb_nr; i++) {\n      \t\t\tstruct moved_entry *prev = pmb[i].match;\n12:  a748b9b0c4 ! 12:  27ba68a7b8 hashmap: use *_entry APIs to wrap container_of\n    @@ -11,6 +11,7 @@\n         extra parameter to specify the type.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/diff.c b/diff.c\n      --- a/diff.c\n    @@ -19,9 +20,9 @@\n      {\n      \tint i;\n      \tchar *got_match = xcalloc(1, pmb_nr);\n    --\tstruct hashmap_entry *ent = &match->ent;\n    +-\tstruct hashmap_entry *ent;\n      \n    --\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n    +-\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n     -\t\tmatch = container_of(ent, struct moved_entry, ent);\n     +\thashmap_for_each_entry_from(hm, match, struct moved_entry, ent) {\n      \t\tfor (i = 0; i < pmb_nr; i++) {\n    @@ -125,7 +126,6 @@\n      #define container_of(ptr, type, member) \\\n      \t((type *) ((char *)(ptr) - offsetof(type, member)))\n      \n    -+\n     +/*\n     + * helper function for `container_of_or_null' to avoid multiple\n     + * evaluation of @ptr\n13:  e8faa5e12e ! 13:  222b1f07f3 hashmap_get{,_from_hash} return \"struct hashmap_entry *\"\n    @@ -9,6 +9,7 @@\n         hashmap_entry being the first field in a struct.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n    @@ -152,11 +153,13 @@\n      }\n      \n     -void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n    -+struct hashmap_entry *\n    -+hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n    - \t\tconst void *keydata)\n    +-\t\tconst void *keydata)\n    ++struct hashmap_entry *hashmap_get(const struct hashmap *map,\n    ++\t\t\t\tconst struct hashmap_entry *key,\n    ++\t\t\t\tconst void *keydata)\n      {\n      \treturn *find_entry_ptr(map, key, keydata);\n    + }\n     @@\n      \t/* lookup interned string in pool */\n      \thashmap_entry_init(&key.ent, memhash(data, len));\n    @@ -175,21 +178,27 @@\n       * to `hashmap_cmp_fn` to decide whether the entry matches the key.\n       */\n     -void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n    -+struct hashmap_entry *\n    -+hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n    - \t\t\t const void *keydata);\n    +-\t\t\t const void *keydata);\n    ++struct hashmap_entry *hashmap_get(const struct hashmap *map,\n    ++\t\t\t\tconst struct hashmap_entry *key,\n    ++\t\t\t\tconst void *keydata);\n      \n      /*\n    +  * Returns the hashmap entry for the specified hash code and key data,\n     @@\n       * `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry\n       * structure that should not be used in the comparison.\n       */\n     -static inline void *hashmap_get_from_hash(const struct hashmap *map,\n    -+static inline struct hashmap_entry *\n    -+hashmap_get_from_hash(const struct hashmap *map,\n    - \t\t\t\t\t  unsigned int hash,\n    - \t\t\t\t\t  const void *keydata)\n    +-\t\t\t\t\t  unsigned int hash,\n    +-\t\t\t\t\t  const void *keydata)\n    ++static inline struct hashmap_entry *hashmap_get_from_hash(\n    ++\t\t\t\t\tconst struct hashmap *map,\n    ++\t\t\t\t\tunsigned int hash,\n    ++\t\t\t\t\tconst void *keydata)\n      {\n    + \tstruct hashmap_entry key;\n    + \thashmap_entry_init(&key, hash);\n     \n      diff --git a/merge-recursive.c b/merge-recursive.c\n      --- a/merge-recursive.c\n14:  f84bd96b8e ! 14:  c789f98c81 hashmap_cmp_fn takes hashmap_entry params\n    @@ -6,6 +6,7 @@\n         being the first member of a struct.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n15:  23fbd888d9 ! 15:  57ae5b5142 hashmap: use *_entry APIs for iteration\n    @@ -7,6 +7,7 @@\n         by compilers lacking __typeof__ support.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n16:  6908364381 ! 16:  f1e35ba094 hashmap: hashmap_{put,remove} return hashmap_entry *\n    @@ -6,6 +6,7 @@\n         to simplify most callers.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/hashmap.c b/hashmap.c\n      --- a/hashmap.c\n    @@ -15,18 +16,20 @@\n      }\n      \n     -void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n    -+struct hashmap_entry *\n    -+hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n    - \t\tconst void *keydata)\n    +-\t\tconst void *keydata)\n    ++struct hashmap_entry *hashmap_remove(struct hashmap *map,\n    ++\t\t\t\t\tconst struct hashmap_entry *key,\n    ++\t\t\t\t\tconst void *keydata)\n      {\n      \tstruct hashmap_entry *old;\n    + \tstruct hashmap_entry **e = find_entry_ptr(map, key, keydata);\n     @@\n      \treturn old;\n      }\n      \n     -void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n    -+struct hashmap_entry *\n    -+hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n    ++struct hashmap_entry *hashmap_put(struct hashmap *map,\n    ++\t\t\t\tstruct hashmap_entry *entry)\n      {\n      \tstruct hashmap_entry *old = hashmap_remove(map, entry, NULL);\n      \thashmap_add(map, entry);\n    @@ -39,8 +42,8 @@\n       * Returns the replaced entry, or NULL if not found (i.e. the entry was added).\n       */\n     -void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n    -+struct hashmap_entry *\n    -+hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n    ++struct hashmap_entry *hashmap_put(struct hashmap *map,\n    ++\t\t\t\tstruct hashmap_entry *entry);\n     +\n     +#define hashmap_put_entry(map, keyvar, type, member) \\\n     +\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n    @@ -52,17 +55,17 @@\n       * Argument explanation is the same as in `hashmap_get`.\n       */\n     -void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n    -+struct hashmap_entry *\n    -+hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n    - \t\tconst void *keydata);\n    - \n    +-\t\tconst void *keydata);\n    ++struct hashmap_entry *hashmap_remove(struct hashmap *map,\n    ++\t\t\t\t\tconst struct hashmap_entry *key,\n    ++\t\t\t\t\tconst void *keydata);\n    ++\n     +#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n     +\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n     +\t\t\t\ttype, member)\n    -+\n    + \n      /*\n       * Returns the `bucket` an entry is stored in.\n    -  * Useful for multithreaded read access.\n     \n      diff --git a/range-diff.c b/range-diff.c\n      --- a/range-diff.c\n17:  150944128b ! 17:  adc7cb5516 hashmap: introduce hashmap_free_entries\n    @@ -12,6 +12,7 @@\n         the hashmap itself.\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/blame.c b/blame.c\n      --- a/blame.c\n18:  de212aa6d6 ! 18:  1a68e1645d OFFSETOF_VAR macro to simplify hashmap iterators\n    @@ -5,7 +5,7 @@\n         While we cannot rely on a `__typeof__' operator being portable\n         to use with `offsetof'; we can calculate the pointer offset\n         using an existing pointer and the address of a member using\n    -    pointer arithmetic.\n    +    pointer arithmetic for compilers without `__typeof__'.\n     \n         This allows us to simplify usage of hashmap iterator macros\n         by not having to specify a type when a pointer of that type\n    @@ -16,7 +16,10 @@\n         trouble of using container_of/list_entry macros and without\n         relying on non-portable `__typeof__'.\n     \n    +    v3: use `__typeof__' to avoid clang warnings\n    +\n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n    @@ -141,11 +144,17 @@\n      \t(type *)container_of_or_null_offset(ptr, offsetof(type, member))\n      \n     +/*\n    -+ * like offsetof(), but takes a pointer to type instead of the type\n    -+ * @ptr is subject to multiple evaluation since we can't rely on TYPEOF()\n    ++ * like offsetof(), but takes a pointer to a a variable of type which\n    ++ * contains @member, instead of a specified type.\n    ++ * @ptr is subject to multiple evaluation since we can't rely on __typeof__\n    ++ * everywhere.\n     + */\n    ++#if defined(__GNUC__) /* clang sets this, too */\n    ++#define OFFSETOF_VAR(ptr, member) offsetof(__typeof__(*ptr), member)\n    ++#else /* !__GNUC__ */\n     +#define OFFSETOF_VAR(ptr, member) \\\n     +\t((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))\n    ++#endif /* !__GNUC__ */\n     +\n      #endif\n     \n19:  212a596edd ! 19:  fe02b9f839 hashmap: remove type arg from hashmap_{get,put,remove}_entry\n    @@ -10,6 +10,7 @@\n         sequential as they are used as: `keyvar->member'\n     \n         Signed-off-by: Eric Wong <e@80x24.org>\n    +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n     \n      diff --git a/attr.c b/attr.c\n      --- a/attr.c\n    @@ -147,8 +148,8 @@\n       *                 printf(\"first: %ld %s\\n\", e->key, e->value);\n       *                 while ((e = hashmap_get_next_entry(&map, e,\n     @@\n    - struct hashmap_entry *\n    - hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n    + struct hashmap_entry *hashmap_put(struct hashmap *map,\n    + \t\t\t\tstruct hashmap_entry *entry);\n      \n     -#define hashmap_put_entry(map, keyvar, type, member) \\\n     -\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n    @@ -167,8 +168,8 @@\n      /*\n       * Removes a hashmap entry matching the specified key. If the hashmap contains\n     @@\n    - hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n    - \t\tconst void *keydata);\n    + \t\t\t\t\tconst struct hashmap_entry *key,\n    + \t\t\t\t\tconst void *keydata);\n      \n     -#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n     -\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n -:  ---------- > 20:  4ec5200646 hashmap_entry: remove first member requirement from docs\n"},{"id":"383531","messageId":"20191006233043.3516-2-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 01/20] diff: use hashmap_entry_init on moved_entry.ent","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:24Z","receivedAt":"2019-10-06T23:30:48Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Otherwise, the hashmap_entry.next field appears to remain\nuninitialized, which can lead to problems when\nadd_lines_to_move_detection calls hashmap_add.\n\nI found this through manual inspection when converting\nhashmap_add callers to take \"struct hashmap_entry *\".\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n diff.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex efe42b341a..02491ee684 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -964,8 +964,9 @@ static struct moved_entry *prepare_entry(struct diff_options *o,\n \tstruct moved_entry *ret = xmalloc(sizeof(*ret));\n \tstruct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];\n \tunsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;\n+\tunsigned int hash = xdiff_hash_string(l->line, l->len, flags);\n \n-\tret->ent.hash = xdiff_hash_string(l->line, l->len, flags);\n+\thashmap_entry_init(&ret->ent, hash);\n \tret->es = l;\n \tret->next_line = NULL;\n \n"},{"id":"383532","messageId":"20191006233043.3516-3-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 02/20] coccicheck: detect hashmap_entry.hash assignment","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:25Z","receivedAt":"2019-10-06T23:30:56Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Assigning hashmap_entry.hash manually leaves hashmap_entry.next\nuninitialized, which can be dangerous once the hashmap_entry is\ninserted into a hashmap.   Detect those assignments and use\nhashmap_entry_init, instead.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n contrib/coccinelle/hashmap.cocci | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n create mode 100644 contrib/coccinelle/hashmap.cocci\n\ndiff --git a/contrib/coccinelle/hashmap.cocci b/contrib/coccinelle/hashmap.cocci\nnew file mode 100644\nindex 0000000000..d69e120ccf\n--- /dev/null\n+++ b/contrib/coccinelle/hashmap.cocci\n@@ -0,0 +1,16 @@\n+@ hashmap_entry_init_usage @\n+expression E;\n+struct hashmap_entry HME;\n+@@\n+- HME.hash = E;\n++ hashmap_entry_init(&HME, E);\n+\n+@@\n+identifier f !~ \"^hashmap_entry_init$\";\n+expression E;\n+struct hashmap_entry *HMEP;\n+@@\n+  f(...) {<...\n+- HMEP->hash = E;\n++ hashmap_entry_init(HMEP, E);\n+  ...>}\n"},{"id":"383533","messageId":"20191006233043.3516-5-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 04/20] hashmap_entry_init takes \"struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:27Z","receivedAt":"2019-10-06T23:30:56Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"C compilers do type checking to make life easier for us.  So\nrely on that and update all hashmap_entry_init callers to take\n\"struct hashmap_entry *\" to avoid future bugs while improving\nsafety and readability.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                  |  4 ++--\n blame.c                 |  2 +-\n builtin/describe.c      |  2 +-\n builtin/difftool.c      |  6 +++---\n builtin/fast-export.c   |  2 +-\n builtin/fetch.c         |  2 +-\n config.c                |  4 ++--\n diffcore-rename.c       |  2 +-\n hashmap.c               |  4 ++--\n hashmap.h               | 12 ++++++------\n merge-recursive.c       | 13 +++++++------\n name-hash.c             | 10 +++++-----\n packfile.c              |  2 +-\n patch-ids.c             |  2 +-\n range-diff.c            |  4 ++--\n ref-filter.c            |  3 ++-\n refs.c                  |  2 +-\n remote.c                |  2 +-\n revision.c              |  4 ++--\n sequencer.c             |  5 +++--\n sub-process.c           |  4 ++--\n submodule-config.c      | 10 +++++-----\n t/helper/test-hashmap.c |  6 +++---\n 23 files changed, 55 insertions(+), 52 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 93dc16b59c..a8be7a7b4f 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -98,7 +98,7 @@ static void *attr_hashmap_get(struct attr_hashmap *map,\n \tif (!map->map.tablesize)\n \t\tattr_hashmap_init(map);\n \n-\thashmap_entry_init(&k, memhash(key, keylen));\n+\thashmap_entry_init(&k.ent, memhash(key, keylen));\n \tk.key = key;\n \tk.keylen = keylen;\n \te = hashmap_get(&map->map, &k, NULL);\n@@ -117,7 +117,7 @@ static void attr_hashmap_add(struct attr_hashmap *map,\n \t\tattr_hashmap_init(map);\n \n \te = xmalloc(sizeof(struct attr_hash_entry));\n-\thashmap_entry_init(e, memhash(key, keylen));\n+\thashmap_entry_init(&e->ent, memhash(key, keylen));\n \te->key = key;\n \te->keylen = keylen;\n \te->value = value;\ndiff --git a/blame.c b/blame.c\nindex 36a2e7ef11..46059410cd 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -417,7 +417,7 @@ static void get_fingerprint(struct fingerprint *result,\n \t\t/* Ignore whitespace pairs */\n \t\tif (hash == 0)\n \t\t\tcontinue;\n-\t\thashmap_entry_init(entry, hash);\n+\t\thashmap_entry_init(&entry->entry, hash);\n \n \t\tfound_entry = hashmap_get(&result->map, entry, NULL);\n \t\tif (found_entry) {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 200154297d..596ddf89a5 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -123,7 +123,7 @@ static void add_to_known_names(const char *path,\n \t\tif (!e) {\n \t\t\te = xmalloc(sizeof(struct commit_name));\n \t\t\toidcpy(&e->peeled, peeled);\n-\t\t\thashmap_entry_init(e, oidhash(peeled));\n+\t\t\thashmap_entry_init(&e->entry, oidhash(peeled));\n \t\t\thashmap_add(&names, e);\n \t\t\te->path = NULL;\n \t\t}\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 16eb8b70ea..98ffc04c61 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -161,7 +161,7 @@ static void add_left_or_right(struct hashmap *map, const char *path,\n \tstruct pair_entry *e, *existing;\n \n \tFLEX_ALLOC_STR(e, path, path);\n-\thashmap_entry_init(e, strhash(path));\n+\thashmap_entry_init(&e->entry, strhash(path));\n \texisting = hashmap_get(map, e, NULL);\n \tif (existing) {\n \t\tfree(e);\n@@ -234,7 +234,7 @@ static void changed_files(struct hashmap *result, const char *index_path,\n \twhile (!strbuf_getline_nul(&buf, fp)) {\n \t\tstruct path_entry *entry;\n \t\tFLEX_ALLOC_STR(entry, path, buf.buf);\n-\t\thashmap_entry_init(entry, strhash(buf.buf));\n+\t\thashmap_entry_init(&entry->entry, strhash(buf.buf));\n \t\thashmap_add(result, entry);\n \t}\n \tfclose(fp);\n@@ -461,7 +461,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \n \t\t\t/* Avoid duplicate working_tree entries */\n \t\t\tFLEX_ALLOC_STR(entry, path, dst_path);\n-\t\t\thashmap_entry_init(entry, strhash(dst_path));\n+\t\t\thashmap_entry_init(&entry->entry, strhash(dst_path));\n \t\t\tif (hashmap_get(&working_tree_dups, entry, NULL)) {\n \t\t\t\tfree(entry);\n \t\t\t\tcontinue;\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex f541f55d33..287dbd24a3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -148,7 +148,7 @@ static const void *anonymize_mem(struct hashmap *map,\n \tif (!map->cmpfn)\n \t\thashmap_init(map, anonymized_entry_cmp, NULL, 0);\n \n-\thashmap_entry_init(&key, memhash(orig, *len));\n+\thashmap_entry_init(&key.hash, memhash(orig, *len));\n \tkey.orig = orig;\n \tkey.orig_len = *len;\n \tret = hashmap_get(map, &key, NULL);\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 717dd14e89..b7d70eee70 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -276,7 +276,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \tsize_t len = strlen(refname);\n \n \tFLEX_ALLOC_MEM(ent, refname, refname, len);\n-\thashmap_entry_init(ent, strhash(refname));\n+\thashmap_entry_init(&ent->ent, strhash(refname));\n \toidcpy(&ent->oid, oid);\n \thashmap_add(map, ent);\n \treturn ent;\ndiff --git a/config.c b/config.c\nindex 3900e4947b..08d866e7de 100644\n--- a/config.c\n+++ b/config.c\n@@ -1861,7 +1861,7 @@ static struct config_set_element *configset_find_element(struct config_set *cs,\n \tif (git_config_parse_key(key, &normalized_key, NULL))\n \t\treturn NULL;\n \n-\thashmap_entry_init(&k, strhash(normalized_key));\n+\thashmap_entry_init(&k.ent, strhash(normalized_key));\n \tk.key = normalized_key;\n \tfound_entry = hashmap_get(&cs->config_hash, &k, NULL);\n \tfree(normalized_key);\n@@ -1882,7 +1882,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha\n \t */\n \tif (!e) {\n \t\te = xmalloc(sizeof(*e));\n-\t\thashmap_entry_init(e, strhash(key));\n+\t\thashmap_entry_init(&e->ent, strhash(key));\n \t\te->key = xstrdup(key);\n \t\tstring_list_init(&e->value_list, 1);\n \t\thashmap_add(&cs->config_hash, e);\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 9624864858..44a3ab1e31 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -329,7 +329,7 @@ static void insert_file_table(struct repository *r,\n \tentry->index = index;\n \tentry->filespec = filespec;\n \n-\thashmap_entry_init(entry, hash_filespec(r, filespec));\n+\thashmap_entry_init(&entry->entry, hash_filespec(r, filespec));\n \thashmap_add(table, entry);\n }\n \ndiff --git a/hashmap.c b/hashmap.c\nindex d42f01ff5a..6818c65174 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -293,13 +293,13 @@ const void *memintern(const void *data, size_t len)\n \t\thashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, NULL, 0);\n \n \t/* lookup interned string in pool */\n-\thashmap_entry_init(&key, memhash(data, len));\n+\thashmap_entry_init(&key.ent, memhash(data, len));\n \tkey.len = len;\n \te = hashmap_get(&map, &key, data);\n \tif (!e) {\n \t\t/* not found: create it */\n \t\tFLEX_ALLOC_MEM(e, data, data, len);\n-\t\thashmap_entry_init(e, key.ent.hash);\n+\t\thashmap_entry_init(&e->ent, key.ent.hash);\n \t\te->len = len;\n \t\thashmap_add(&map, e);\n \t}\ndiff --git a/hashmap.h b/hashmap.h\nindex 8424911566..54b0b8c698 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -48,14 +48,14 @@\n  *         if (!strcmp(\"add\", action)) {\n  *             struct long2string *e;\n  *             FLEX_ALLOC_STR(e, value, value);\n- *             hashmap_entry_init(e, memhash(&key, sizeof(long)));\n+ *             hashmap_entry_init(&e->ent, memhash(&key, sizeof(long)));\n  *             e->key = key;\n  *             hashmap_add(&map, e);\n  *         }\n  *\n  *         if (!strcmp(\"print_all_by_key\", action)) {\n  *             struct long2string k, *e;\n- *             hashmap_entry_init(&k, memhash(&key, sizeof(long)));\n+ *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));\n  *             k.key = key;\n  *\n  *             flags &= ~COMPARE_VALUE;\n@@ -70,7 +70,7 @@\n  *         if (!strcmp(\"has_exact_match\", action)) {\n  *             struct long2string *e;\n  *             FLEX_ALLOC_STR(e, value, value);\n- *             hashmap_entry_init(e, memhash(&key, sizeof(long)));\n+ *             hashmap_entry_init(&e->ent, memhash(&key, sizeof(long)));\n  *             e->key = key;\n  *\n  *             flags |= COMPARE_VALUE;\n@@ -80,7 +80,7 @@\n  *\n  *         if (!strcmp(\"has_exact_match_no_heap_alloc\", action)) {\n  *             struct long2string k;\n- *             hashmap_entry_init(&k, memhash(&key, sizeof(long)));\n+ *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));\n  *             k.key = key;\n  *\n  *             flags |= COMPARE_VALUE;\n@@ -244,9 +244,9 @@ void hashmap_free(struct hashmap *map, int free_entries);\n  * your structure was allocated with xmalloc(), you can just free(3) it,\n  * and if it is on stack, you can just let it go out of scope).\n  */\n-static inline void hashmap_entry_init(void *entry, unsigned int hash)\n+static inline void hashmap_entry_init(struct hashmap_entry *e,\n+\t\t\t\t\tunsigned int hash)\n {\n-\tstruct hashmap_entry *e = entry;\n \te->hash = hash;\n \te->next = NULL;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6b812d67e3..6bc4f14ff4 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -61,7 +61,7 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,\n \n \tif (dir == NULL)\n \t\treturn NULL;\n-\thashmap_entry_init(&key, strhash(dir));\n+\thashmap_entry_init(&key.ent, strhash(dir));\n \tkey.dir = dir;\n \treturn hashmap_get(hashmap, &key, NULL);\n }\n@@ -85,7 +85,7 @@ static void dir_rename_init(struct hashmap *map)\n static void dir_rename_entry_init(struct dir_rename_entry *entry,\n \t\t\t\t  char *directory)\n {\n-\thashmap_entry_init(entry, strhash(directory));\n+\thashmap_entry_init(&entry->ent, strhash(directory));\n \tentry->dir = directory;\n \tentry->non_unique_new_dir = 0;\n \tstrbuf_init(&entry->new_dir, 0);\n@@ -97,7 +97,7 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,\n {\n \tstruct collision_entry key;\n \n-\thashmap_entry_init(&key, strhash(target_file));\n+\thashmap_entry_init(&key.ent, strhash(target_file));\n \tkey.target_file = target_file;\n \treturn hashmap_get(hashmap, &key, NULL);\n }\n@@ -454,7 +454,7 @@ static int save_files_dirs(const struct object_id *oid,\n \tstrbuf_addstr(base, path);\n \n \tFLEX_ALLOC_MEM(entry, path, base->buf, base->len);\n-\thashmap_entry_init(entry, path_hash(entry->path));\n+\thashmap_entry_init(&entry->e, path_hash(entry->path));\n \thashmap_add(&opt->current_file_dir_set, entry);\n \n \tstrbuf_setlen(base, baselen);\n@@ -731,7 +731,7 @@ static char *unique_path(struct merge_options *opt, const char *path, const char\n \t}\n \n \tFLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);\n-\thashmap_entry_init(entry, path_hash(entry->path));\n+\thashmap_entry_init(&entry->e, path_hash(entry->path));\n \thashmap_add(&opt->current_file_dir_set, entry);\n \treturn strbuf_detach(&newpath, NULL);\n }\n@@ -2358,7 +2358,8 @@ static void compute_collisions(struct hashmap *collisions,\n \t\tif (!collision_ent) {\n \t\t\tcollision_ent = xcalloc(1,\n \t\t\t\t\t\tsizeof(struct collision_entry));\n-\t\t\thashmap_entry_init(collision_ent, strhash(new_path));\n+\t\t\thashmap_entry_init(&collision_ent->ent,\n+\t\t\t\t\t\tstrhash(new_path));\n \t\t\thashmap_put(collisions, collision_ent);\n \t\t\tcollision_ent->target_file = new_path;\n \t\t} else {\ndiff --git a/name-hash.c b/name-hash.c\nindex 695908609f..1ce1417f7e 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -33,7 +33,7 @@ static struct dir_entry *find_dir_entry__hash(struct index_state *istate,\n \t\tconst char *name, unsigned int namelen, unsigned int hash)\n {\n \tstruct dir_entry key;\n-\thashmap_entry_init(&key, hash);\n+\thashmap_entry_init(&key.ent, hash);\n \tkey.namelen = namelen;\n \treturn hashmap_get(&istate->dir_hash, &key, name);\n }\n@@ -68,7 +68,7 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,\n \tif (!dir) {\n \t\t/* not found, create it and add to hash table */\n \t\tFLEX_ALLOC_MEM(dir, name, ce->name, namelen);\n-\t\thashmap_entry_init(dir, memihash(ce->name, namelen));\n+\t\thashmap_entry_init(&dir->ent, memihash(ce->name, namelen));\n \t\tdir->namelen = namelen;\n \t\thashmap_add(&istate->dir_hash, dir);\n \n@@ -106,7 +106,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n \tif (ce->ce_flags & CE_HASHED)\n \t\treturn;\n \tce->ce_flags |= CE_HASHED;\n-\thashmap_entry_init(ce, memihash(ce->name, ce_namelen(ce)));\n+\thashmap_entry_init(&ce->ent, memihash(ce->name, ce_namelen(ce)));\n \thashmap_add(&istate->name_hash, ce);\n \n \tif (ignore_case)\n@@ -280,7 +280,7 @@ static struct dir_entry *hash_dir_entry_with_parent_and_prefix(\n \tdir = find_dir_entry__hash(istate, prefix->buf, prefix->len, hash);\n \tif (!dir) {\n \t\tFLEX_ALLOC_MEM(dir, name, prefix->buf, prefix->len);\n-\t\thashmap_entry_init(dir, hash);\n+\t\thashmap_entry_init(&dir->ent, hash);\n \t\tdir->namelen = prefix->len;\n \t\tdir->parent = parent;\n \t\thashmap_add(&istate->dir_hash, dir);\n@@ -472,7 +472,7 @@ static void *lazy_name_thread_proc(void *_data)\n \tfor (k = 0; k < d->istate->cache_nr; k++) {\n \t\tstruct cache_entry *ce_k = d->istate->cache[k];\n \t\tce_k->ce_flags |= CE_HASHED;\n-\t\thashmap_entry_init(ce_k, d->lazy_entries[k].hash_name);\n+\t\thashmap_entry_init(&ce_k->ent, d->lazy_entries[k].hash_name);\n \t\thashmap_add(&d->istate->name_hash, ce_k);\n \t}\n \ndiff --git a/packfile.c b/packfile.c\nindex 37fe0b73a6..96535eb86b 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1487,7 +1487,7 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,\n \n \tif (!delta_base_cache.cmpfn)\n \t\thashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);\n-\thashmap_entry_init(ent, pack_entry_hash(p, base_offset));\n+\thashmap_entry_init(&ent->ent, pack_entry_hash(p, base_offset));\n \thashmap_add(&delta_base_cache, ent);\n }\n \ndiff --git a/patch-ids.c b/patch-ids.c\nindex e8c150d0c9..a2da711678 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -83,7 +83,7 @@ static int init_patch_id_entry(struct patch_id *patch,\n \tif (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))\n \t\treturn -1;\n \n-\thashmap_entry_init(patch, oidhash(&header_only_patch_id));\n+\thashmap_entry_init(&patch->ent, oidhash(&header_only_patch_id));\n \treturn 0;\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex ba1e9a4265..32b29f9594 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -217,7 +217,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->i = i;\n \t\tutil->patch = a->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n-\t\thashmap_entry_init(util, strhash(util->diff));\n+\t\thashmap_entry_init(&util->e, strhash(util->diff));\n \t\thashmap_add(&map, util);\n \t}\n \n@@ -228,7 +228,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->i = i;\n \t\tutil->patch = b->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n-\t\thashmap_entry_init(util, strhash(util->diff));\n+\t\thashmap_entry_init(&util->e, strhash(util->diff));\n \t\tother = hashmap_remove(&map, util, NULL);\n \t\tif (other) {\n \t\t\tif (other->matching >= 0)\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f27cfc8c3e..206014c93d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1565,7 +1565,8 @@ static void populate_worktree_map(struct hashmap *map, struct worktree **worktre\n \t\t\tstruct ref_to_worktree_entry *entry;\n \t\t\tentry = xmalloc(sizeof(*entry));\n \t\t\tentry->wt = worktrees[i];\n-\t\t\thashmap_entry_init(entry, strhash(worktrees[i]->head_ref));\n+\t\t\thashmap_entry_init(&entry->ent,\n+\t\t\t\t\tstrhash(worktrees[i]->head_ref));\n \n \t\t\thashmap_add(map, entry);\n \t\t}\ndiff --git a/refs.c b/refs.c\nindex cd297ee4bd..c43ec59c6e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1796,7 +1796,7 @@ static struct ref_store_hash_entry *alloc_ref_store_hash_entry(\n \tstruct ref_store_hash_entry *entry;\n \n \tFLEX_ALLOC_STR(entry, name, name);\n-\thashmap_entry_init(entry, strhash(name));\n+\thashmap_entry_init(&entry->ent, strhash(name));\n \tentry->refs = refs;\n \treturn entry;\n }\ndiff --git a/remote.c b/remote.c\nindex e50f7602ed..bd81cb71bc 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -158,7 +158,7 @@ static struct remote *make_remote(const char *name, int len)\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n \n-\thashmap_entry_init(ret, lookup_entry.hash);\n+\thashmap_entry_init(&ret->ent, lookup_entry.hash);\n \treplaced = hashmap_put(&remotes_hash, ret);\n \tassert(replaced == NULL);  /* no previous entry overwritten */\n \treturn ret;\ndiff --git a/revision.c b/revision.c\nindex 07412297f0..3461c78883 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -141,7 +141,7 @@ static void paths_and_oids_insert(struct hashmap *map,\n \tstruct path_and_oids_entry key;\n \tstruct path_and_oids_entry *entry;\n \n-\thashmap_entry_init(&key, hash);\n+\thashmap_entry_init(&key.ent, hash);\n \n \t/* use a shallow copy for the lookup */\n \tkey.path = (char *)path;\n@@ -149,7 +149,7 @@ static void paths_and_oids_insert(struct hashmap *map,\n \n \tif (!(entry = (struct path_and_oids_entry *)hashmap_get(map, &key, NULL))) {\n \t\tentry = xcalloc(1, sizeof(struct path_and_oids_entry));\n-\t\thashmap_entry_init(entry, hash);\n+\t\thashmap_entry_init(&entry->ent, hash);\n \t\tentry->path = xstrdup(key.path);\n \t\toidset_init(&entry->trees, 16);\n \t\thashmap_put(map, entry);\ndiff --git a/sequencer.c b/sequencer.c\nindex 34ebf8ed94..1140cdf526 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4538,7 +4538,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n \t}\n \n \tFLEX_ALLOC_STR(labels_entry, label, label);\n-\thashmap_entry_init(labels_entry, strihash(label));\n+\thashmap_entry_init(&labels_entry->entry, strihash(label));\n \thashmap_add(&state->labels, labels_entry);\n \n \tFLEX_ALLOC_STR(string_entry, string, label);\n@@ -5252,7 +5252,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)\n \t\t\t\t\t\tstrhash(subject), subject)) {\n \t\t\tFLEX_ALLOC_MEM(entry, subject, subject, subject_len);\n \t\t\tentry->i = i;\n-\t\t\thashmap_entry_init(entry, strhash(entry->subject));\n+\t\t\thashmap_entry_init(&entry->entry,\n+\t\t\t\t\tstrhash(entry->subject));\n \t\t\thashmap_put(&subject2item, entry);\n \t\t}\n \t}\ndiff --git a/sub-process.c b/sub-process.c\nindex 3f4af93555..9847dad6fc 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -20,7 +20,7 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch\n {\n \tstruct subprocess_entry key;\n \n-\thashmap_entry_init(&key, strhash(cmd));\n+\thashmap_entry_init(&key.ent, strhash(cmd));\n \tkey.cmd = cmd;\n \treturn hashmap_get(hashmap, &key, NULL);\n }\n@@ -96,7 +96,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co\n \t\treturn err;\n \t}\n \n-\thashmap_entry_init(entry, strhash(cmd));\n+\thashmap_entry_init(&entry->ent, strhash(cmd));\n \n \terr = startfn(entry);\n \tif (err) {\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 4264ee216f..4aa02e280e 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -123,7 +123,7 @@ static void cache_put_path(struct submodule_cache *cache,\n \tunsigned int hash = hash_oid_string(&submodule->gitmodules_oid,\n \t\t\t\t\t    submodule->path);\n \tstruct submodule_entry *e = xmalloc(sizeof(*e));\n-\thashmap_entry_init(e, hash);\n+\thashmap_entry_init(&e->ent, hash);\n \te->config = submodule;\n \thashmap_put(&cache->for_path, e);\n }\n@@ -135,7 +135,7 @@ static void cache_remove_path(struct submodule_cache *cache,\n \t\t\t\t\t    submodule->path);\n \tstruct submodule_entry e;\n \tstruct submodule_entry *removed;\n-\thashmap_entry_init(&e, hash);\n+\thashmap_entry_init(&e.ent, hash);\n \te.config = submodule;\n \tremoved = hashmap_remove(&cache->for_path, &e, NULL);\n \tfree(removed);\n@@ -147,7 +147,7 @@ static void cache_add(struct submodule_cache *cache,\n \tunsigned int hash = hash_oid_string(&submodule->gitmodules_oid,\n \t\t\t\t\t    submodule->name);\n \tstruct submodule_entry *e = xmalloc(sizeof(*e));\n-\thashmap_entry_init(e, hash);\n+\thashmap_entry_init(&e->ent, hash);\n \te->config = submodule;\n \thashmap_add(&cache->for_name, e);\n }\n@@ -163,7 +163,7 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache,\n \toidcpy(&key_config.gitmodules_oid, gitmodules_oid);\n \tkey_config.path = path;\n \n-\thashmap_entry_init(&key, hash);\n+\thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n \tentry = hashmap_get(&cache->for_path, &key, NULL);\n@@ -183,7 +183,7 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,\n \toidcpy(&key_config.gitmodules_oid, gitmodules_oid);\n \tkey_config.name = name;\n \n-\thashmap_entry_init(&key, hash);\n+\thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n \tentry = hashmap_get(&cache->for_name, &key, NULL);\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex aaf17b0ddf..0c9fd7c996 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -37,7 +37,7 @@ static struct test_entry *alloc_test_entry(unsigned int hash,\n \tsize_t klen = strlen(key);\n \tsize_t vlen = strlen(value);\n \tstruct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2));\n-\thashmap_entry_init(entry, hash);\n+\thashmap_entry_init(&entry->ent, hash);\n \tmemcpy(entry->key, key, klen + 1);\n \tmemcpy(entry->key + klen + 1, value, vlen + 1);\n \treturn entry;\n@@ -103,7 +103,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \n \t\t\t/* add entries */\n \t\t\tfor (i = 0; i < TEST_SIZE; i++) {\n-\t\t\t\thashmap_entry_init(entries[i], hashes[i]);\n+\t\t\t\thashmap_entry_init(&entries[i]->ent, hashes[i]);\n \t\t\t\thashmap_add(&map, entries[i]);\n \t\t\t}\n \n@@ -116,7 +116,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \t\t/* fill the map (sparsely if specified) */\n \t\tj = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;\n \t\tfor (i = 0; i < j; i++) {\n-\t\t\thashmap_entry_init(entries[i], hashes[i]);\n+\t\t\thashmap_entry_init(&entries[i]->ent, hashes[i]);\n \t\t\thashmap_add(&map, entries[i]);\n \t\t}\n \n"},{"id":"383534","messageId":"20191006233043.3516-4-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 03/20] packfile: use hashmap_entry in delta_base_cache_entry","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:26Z","receivedAt":"2019-10-06T23:30:56Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This hashmap_entry_init function is intended to take a\nhashmap_entry struct pointer, not a hashmap struct pointer.\n\nThis was not noticed because hashmap_entry_init takes a \"void *\"\narg instead of \"struct hashmap_entry *\", and the hashmap struct\nis larger and can be cast into a hashmap_entry struct without\ndata corruption.\n\nThis has the beneficial side effect of reducing the size of\na delta_base_cache_entry from 104 bytes to 72 bytes on 64-bit\nsystems.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n packfile.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/packfile.c b/packfile.c\nindex fc43a6c52c..37fe0b73a6 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1361,7 +1361,7 @@ struct delta_base_cache_key {\n };\n \n struct delta_base_cache_entry {\n-\tstruct hashmap hash;\n+\tstruct hashmap_entry ent;\n \tstruct delta_base_cache_key key;\n \tstruct list_head lru;\n \tvoid *data;\n"},{"id":"383535","messageId":"20191006233043.3516-6-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 05/20] hashmap_get_next takes \"const struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:28Z","receivedAt":"2019-10-06T23:30:59Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is less error-prone than \"const void *\" as the compiler\nnow detects invalid types being passed.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n diff.c                  | 5 +++--\n diffcore-rename.c       | 2 +-\n hashmap.c               | 5 +++--\n hashmap.h               | 3 ++-\n name-hash.c             | 2 +-\n t/helper/test-hashmap.c | 2 +-\n 6 files changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 02491ee684..1168f0cbb9 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1036,7 +1036,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,\n \tint i;\n \tchar *got_match = xcalloc(1, pmb_nr);\n \n-\tfor (; match; match = hashmap_get_next(hm, match)) {\n+\tfor (; match; match = hashmap_get_next(hm, &match->ent)) {\n \t\tfor (i = 0; i < pmb_nr; i++) {\n \t\t\tstruct moved_entry *prev = pmb[i].match;\n \t\t\tstruct moved_entry *cur = (prev && prev->next_line) ?\n@@ -1189,7 +1189,8 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\t * The current line is the start of a new block.\n \t\t\t * Setup the set of potential blocks.\n \t\t\t */\n-\t\t\tfor (; match; match = hashmap_get_next(hm, match)) {\n+\t\t\tfor (; match; match = hashmap_get_next(hm,\n+\t\t\t\t\t\t\t\t&match->ent)) {\n \t\t\t\tALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);\n \t\t\t\tif (o->color_moved_ws_handling &\n \t\t\t\t    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 44a3ab1e31..2a1449013b 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -285,7 +285,7 @@ static int find_identical_files(struct hashmap *srcs,\n \tp = hashmap_get_from_hash(srcs,\n \t\t\t\t  hash_filespec(options->repo, target),\n \t\t\t\t  NULL);\n-\tfor (; p; p = hashmap_get_next(srcs, p)) {\n+\tfor (; p; p = hashmap_get_next(srcs, &p->entry)) {\n \t\tint score;\n \t\tstruct diff_filespec *source = p->filespec;\n \ndiff --git a/hashmap.c b/hashmap.c\nindex 6818c65174..c1de40eea0 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -191,9 +191,10 @@ void *hashmap_get(const struct hashmap *map, const void *key, const void *keydat\n \treturn *find_entry_ptr(map, key, keydata);\n }\n \n-void *hashmap_get_next(const struct hashmap *map, const void *entry)\n+void *hashmap_get_next(const struct hashmap *map,\n+\t\t\tconst struct hashmap_entry *entry)\n {\n-\tstruct hashmap_entry *e = ((struct hashmap_entry *) entry)->next;\n+\tstruct hashmap_entry *e = entry->next;\n \tfor (; e; e = e->next)\n \t\tif (entry_equals(map, entry, e, NULL))\n \t\t\treturn e;\ndiff --git a/hashmap.h b/hashmap.h\nindex 54b0b8c698..93fb9599ca 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -318,7 +318,8 @@ static inline void *hashmap_get_from_hash(const struct hashmap *map,\n  * `entry` is the hashmap_entry to start the search from, obtained via a previous\n  * call to `hashmap_get` or `hashmap_get_next`.\n  */\n-void *hashmap_get_next(const struct hashmap *map, const void *entry);\n+void *hashmap_get_next(const struct hashmap *map,\n+\t\t\tconst struct hashmap_entry *entry);\n \n /*\n  * Adds a hashmap entry. This allows to add duplicate entries (i.e.\ndiff --git a/name-hash.c b/name-hash.c\nindex 1ce1417f7e..4d84326c58 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -710,7 +710,7 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na\n \twhile (ce) {\n \t\tif (same_name(ce, name, namelen, icase))\n \t\t\treturn ce;\n-\t\tce = hashmap_get_next(&istate->name_hash, ce);\n+\t\tce = hashmap_get_next(&istate->name_hash, &ce->ent);\n \t}\n \treturn NULL;\n }\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 0c9fd7c996..bf063a2521 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -203,7 +203,7 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\t\tputs(\"NULL\");\n \t\t\twhile (entry) {\n \t\t\t\tputs(get_value(entry));\n-\t\t\t\tentry = hashmap_get_next(&map, entry);\n+\t\t\t\tentry = hashmap_get_next(&map, &entry->ent);\n \t\t\t}\n \n \t\t} else if (!strcmp(\"remove\", cmd) && p1) {\n"},{"id":"383536","messageId":"20191006233043.3516-7-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 06/20] hashmap_add takes \"struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:29Z","receivedAt":"2019-10-06T23:31:02Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is less error-prone than \"void *\" as the compiler now\ndetects invalid types being passed.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                  | 2 +-\n blame.c                 | 2 +-\n builtin/describe.c      | 2 +-\n builtin/difftool.c      | 6 +++---\n builtin/fetch.c         | 2 +-\n config.c                | 2 +-\n diff.c                  | 2 +-\n diffcore-rename.c       | 2 +-\n hashmap.c               | 6 +++---\n hashmap.h               | 4 ++--\n merge-recursive.c       | 4 ++--\n name-hash.c             | 8 ++++----\n packfile.c              | 2 +-\n patch-ids.c             | 2 +-\n range-diff.c            | 2 +-\n ref-filter.c            | 2 +-\n sequencer.c             | 2 +-\n sub-process.c           | 2 +-\n submodule-config.c      | 2 +-\n t/helper/test-hashmap.c | 6 +++---\n 20 files changed, 31 insertions(+), 31 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex a8be7a7b4f..fa26a3e3cb 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -122,7 +122,7 @@ static void attr_hashmap_add(struct attr_hashmap *map,\n \te->keylen = keylen;\n \te->value = value;\n \n-\thashmap_add(&map->map, e);\n+\thashmap_add(&map->map, &e->ent);\n }\n \n struct all_attrs_item {\ndiff --git a/blame.c b/blame.c\nindex 46059410cd..4d20aee435 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -424,7 +424,7 @@ static void get_fingerprint(struct fingerprint *result,\n \t\t\tfound_entry->count += 1;\n \t\t} else {\n \t\t\tentry->count = 1;\n-\t\t\thashmap_add(&result->map, entry);\n+\t\t\thashmap_add(&result->map, &entry->entry);\n \t\t\t++entry;\n \t\t}\n \t}\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 596ddf89a5..f5e0a7e033 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -124,7 +124,7 @@ static void add_to_known_names(const char *path,\n \t\t\te = xmalloc(sizeof(struct commit_name));\n \t\t\toidcpy(&e->peeled, peeled);\n \t\t\thashmap_entry_init(&e->entry, oidhash(peeled));\n-\t\t\thashmap_add(&names, e);\n+\t\t\thashmap_add(&names, &e->entry);\n \t\t\te->path = NULL;\n \t\t}\n \t\te->tag = tag;\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 98ffc04c61..82c146718d 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -168,7 +168,7 @@ static void add_left_or_right(struct hashmap *map, const char *path,\n \t\te = existing;\n \t} else {\n \t\te->left[0] = e->right[0] = '\\0';\n-\t\thashmap_add(map, e);\n+\t\thashmap_add(map, &e->entry);\n \t}\n \tstrlcpy(is_right ? e->right : e->left, content, PATH_MAX);\n }\n@@ -235,7 +235,7 @@ static void changed_files(struct hashmap *result, const char *index_path,\n \t\tstruct path_entry *entry;\n \t\tFLEX_ALLOC_STR(entry, path, buf.buf);\n \t\thashmap_entry_init(&entry->entry, strhash(buf.buf));\n-\t\thashmap_add(result, entry);\n+\t\thashmap_add(result, &entry->entry);\n \t}\n \tfclose(fp);\n \tif (finish_command(&diff_files))\n@@ -466,7 +466,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t\t\t\tfree(entry);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\thashmap_add(&working_tree_dups, entry);\n+\t\t\thashmap_add(&working_tree_dups, &entry->entry);\n \n \t\t\tif (!use_wt_file(workdir, dst_path, &roid)) {\n \t\t\t\tif (checkout_path(rmode, &roid, dst_path,\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b7d70eee70..909dbde909 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -278,7 +278,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \tFLEX_ALLOC_MEM(ent, refname, refname, len);\n \thashmap_entry_init(&ent->ent, strhash(refname));\n \toidcpy(&ent->oid, oid);\n-\thashmap_add(map, ent);\n+\thashmap_add(map, &ent->ent);\n \treturn ent;\n }\n \ndiff --git a/config.c b/config.c\nindex 08d866e7de..2243d7c3d6 100644\n--- a/config.c\n+++ b/config.c\n@@ -1885,7 +1885,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha\n \t\thashmap_entry_init(&e->ent, strhash(key));\n \t\te->key = xstrdup(key);\n \t\tstring_list_init(&e->value_list, 1);\n-\t\thashmap_add(&cs->config_hash, e);\n+\t\thashmap_add(&cs->config_hash, &e->ent);\n \t}\n \tsi = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));\n \ndiff --git a/diff.c b/diff.c\nindex 1168f0cbb9..cc7f06d10d 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1003,7 +1003,7 @@ static void add_lines_to_move_detection(struct diff_options *o,\n \t\tif (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)\n \t\t\tprev_line->next_line = key;\n \n-\t\thashmap_add(hm, key);\n+\t\thashmap_add(hm, &key->ent);\n \t\tprev_line = key;\n \t}\n }\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 2a1449013b..4670a40179 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -330,7 +330,7 @@ static void insert_file_table(struct repository *r,\n \tentry->filespec = filespec;\n \n \thashmap_entry_init(&entry->entry, hash_filespec(r, filespec));\n-\thashmap_add(table, entry);\n+\thashmap_add(table, &entry->entry);\n }\n \n /*\ndiff --git a/hashmap.c b/hashmap.c\nindex c1de40eea0..9c2db3e0c8 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -201,12 +201,12 @@ void *hashmap_get_next(const struct hashmap *map,\n \treturn NULL;\n }\n \n-void hashmap_add(struct hashmap *map, void *entry)\n+void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)\n {\n \tunsigned int b = bucket(map, entry);\n \n \t/* add entry */\n-\t((struct hashmap_entry *) entry)->next = map->table[b];\n+\tentry->next = map->table[b];\n \tmap->table[b] = entry;\n \n \t/* fix size and rehash if appropriate */\n@@ -302,7 +302,7 @@ const void *memintern(const void *data, size_t len)\n \t\tFLEX_ALLOC_MEM(e, data, data, len);\n \t\thashmap_entry_init(&e->ent, key.ent.hash);\n \t\te->len = len;\n-\t\thashmap_add(&map, e);\n+\t\thashmap_add(&map, &e->ent);\n \t}\n \treturn e->data;\n }\ndiff --git a/hashmap.h b/hashmap.h\nindex 93fb9599ca..47ee5c00d7 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -50,7 +50,7 @@\n  *             FLEX_ALLOC_STR(e, value, value);\n  *             hashmap_entry_init(&e->ent, memhash(&key, sizeof(long)));\n  *             e->key = key;\n- *             hashmap_add(&map, e);\n+ *             hashmap_add(&map, &e->ent);\n  *         }\n  *\n  *         if (!strcmp(\"print_all_by_key\", action)) {\n@@ -328,7 +328,7 @@ void *hashmap_get_next(const struct hashmap *map,\n  * `map` is the hashmap structure.\n  * `entry` is the entry to add.\n  */\n-void hashmap_add(struct hashmap *map, void *entry);\n+void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);\n \n /*\n  * Adds or replaces a hashmap entry. If the hashmap contains duplicate\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6bc4f14ff4..db9b247ece 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -455,7 +455,7 @@ static int save_files_dirs(const struct object_id *oid,\n \n \tFLEX_ALLOC_MEM(entry, path, base->buf, base->len);\n \thashmap_entry_init(&entry->e, path_hash(entry->path));\n-\thashmap_add(&opt->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, &entry->e);\n \n \tstrbuf_setlen(base, baselen);\n \treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n@@ -732,7 +732,7 @@ static char *unique_path(struct merge_options *opt, const char *path, const char\n \n \tFLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);\n \thashmap_entry_init(&entry->e, path_hash(entry->path));\n-\thashmap_add(&opt->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, &entry->e);\n \treturn strbuf_detach(&newpath, NULL);\n }\n \ndiff --git a/name-hash.c b/name-hash.c\nindex 4d84326c58..faec682bc7 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -70,7 +70,7 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,\n \t\tFLEX_ALLOC_MEM(dir, name, ce->name, namelen);\n \t\thashmap_entry_init(&dir->ent, memihash(ce->name, namelen));\n \t\tdir->namelen = namelen;\n-\t\thashmap_add(&istate->dir_hash, dir);\n+\t\thashmap_add(&istate->dir_hash, &dir->ent);\n \n \t\t/* recursively add missing parent directories */\n \t\tdir->parent = hash_dir_entry(istate, ce, namelen);\n@@ -107,7 +107,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n \t\treturn;\n \tce->ce_flags |= CE_HASHED;\n \thashmap_entry_init(&ce->ent, memihash(ce->name, ce_namelen(ce)));\n-\thashmap_add(&istate->name_hash, ce);\n+\thashmap_add(&istate->name_hash, &ce->ent);\n \n \tif (ignore_case)\n \t\tadd_dir_entry(istate, ce);\n@@ -283,7 +283,7 @@ static struct dir_entry *hash_dir_entry_with_parent_and_prefix(\n \t\thashmap_entry_init(&dir->ent, hash);\n \t\tdir->namelen = prefix->len;\n \t\tdir->parent = parent;\n-\t\thashmap_add(&istate->dir_hash, dir);\n+\t\thashmap_add(&istate->dir_hash, &dir->ent);\n \n \t\tif (parent) {\n \t\t\tunlock_dir_mutex(lock_nr);\n@@ -473,7 +473,7 @@ static void *lazy_name_thread_proc(void *_data)\n \t\tstruct cache_entry *ce_k = d->istate->cache[k];\n \t\tce_k->ce_flags |= CE_HASHED;\n \t\thashmap_entry_init(&ce_k->ent, d->lazy_entries[k].hash_name);\n-\t\thashmap_add(&d->istate->name_hash, ce_k);\n+\t\thashmap_add(&d->istate->name_hash, &ce_k->ent);\n \t}\n \n \treturn NULL;\ndiff --git a/packfile.c b/packfile.c\nindex 96535eb86b..f7402c470b 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1488,7 +1488,7 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,\n \tif (!delta_base_cache.cmpfn)\n \t\thashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);\n \thashmap_entry_init(&ent->ent, pack_entry_hash(p, base_offset));\n-\thashmap_add(&delta_base_cache, ent);\n+\thashmap_add(&delta_base_cache, &ent->ent);\n }\n \n int packed_object_info(struct repository *r, struct packed_git *p,\ndiff --git a/patch-ids.c b/patch-ids.c\nindex a2da711678..f87b62bf58 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -116,6 +116,6 @@ struct patch_id *add_commit_patch_id(struct commit *commit,\n \t\treturn NULL;\n \t}\n \n-\thashmap_add(&ids->patches, key);\n+\thashmap_add(&ids->patches, &key->ent);\n \treturn key;\n }\ndiff --git a/range-diff.c b/range-diff.c\nindex 32b29f9594..96f955d84d 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -218,7 +218,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->patch = a->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n \t\thashmap_entry_init(&util->e, strhash(util->diff));\n-\t\thashmap_add(&map, util);\n+\t\thashmap_add(&map, &util->e);\n \t}\n \n \t/* Now try to find exact matches in b */\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 206014c93d..d939ebc6bb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1568,7 +1568,7 @@ static void populate_worktree_map(struct hashmap *map, struct worktree **worktre\n \t\t\thashmap_entry_init(&entry->ent,\n \t\t\t\t\tstrhash(worktrees[i]->head_ref));\n \n-\t\t\thashmap_add(map, entry);\n+\t\t\thashmap_add(map, &entry->ent);\n \t\t}\n \t}\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 1140cdf526..ee11cda7e7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4539,7 +4539,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n \n \tFLEX_ALLOC_STR(labels_entry, label, label);\n \thashmap_entry_init(&labels_entry->entry, strihash(label));\n-\thashmap_add(&state->labels, labels_entry);\n+\thashmap_add(&state->labels, &labels_entry->entry);\n \n \tFLEX_ALLOC_STR(string_entry, string, label);\n \toidcpy(&string_entry->entry.oid, oid);\ndiff --git a/sub-process.c b/sub-process.c\nindex 9847dad6fc..d58e069855 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -105,7 +105,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co\n \t\treturn err;\n \t}\n \n-\thashmap_add(hashmap, entry);\n+\thashmap_add(hashmap, &entry->ent);\n \treturn 0;\n }\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex 4aa02e280e..a3bbd9fd6f 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -149,7 +149,7 @@ static void cache_add(struct submodule_cache *cache,\n \tstruct submodule_entry *e = xmalloc(sizeof(*e));\n \thashmap_entry_init(&e->ent, hash);\n \te->config = submodule;\n-\thashmap_add(&cache->for_name, e);\n+\thashmap_add(&cache->for_name, &e->ent);\n }\n \n static const struct submodule *cache_lookup_path(struct submodule_cache *cache,\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex bf063a2521..49e715f1cd 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -104,7 +104,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \t\t\t/* add entries */\n \t\t\tfor (i = 0; i < TEST_SIZE; i++) {\n \t\t\t\thashmap_entry_init(&entries[i]->ent, hashes[i]);\n-\t\t\t\thashmap_add(&map, entries[i]);\n+\t\t\t\thashmap_add(&map, &entries[i]->ent);\n \t\t\t}\n \n \t\t\thashmap_free(&map, 0);\n@@ -117,7 +117,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \t\tj = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;\n \t\tfor (i = 0; i < j; i++) {\n \t\t\thashmap_entry_init(&entries[i]->ent, hashes[i]);\n-\t\t\thashmap_add(&map, entries[i]);\n+\t\t\thashmap_add(&map, &entries[i]->ent);\n \t\t}\n \n \t\tfor (j = 0; j < rounds; j++) {\n@@ -179,7 +179,7 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tentry = alloc_test_entry(hash, p1, p2);\n \n \t\t\t/* add to hashmap */\n-\t\t\thashmap_add(&map, entry);\n+\t\t\thashmap_add(&map, &entry->ent);\n \n \t\t} else if (!strcmp(\"put\", cmd) && p1 && p2) {\n \n"},{"id":"383537","messageId":"20191006233043.3516-8-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 07/20] hashmap_get takes \"const struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:30Z","receivedAt":"2019-10-06T23:31:05Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is less error-prone than \"const void *\" as the compiler\nnow detects invalid types being passed.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                | 2 +-\n blame.c               | 6 +++---\n builtin/difftool.c    | 5 +++--\n builtin/fast-export.c | 2 +-\n config.c              | 2 +-\n diff.c                | 4 ++--\n hashmap.c             | 5 +++--\n hashmap.h             | 8 +++++---\n merge-recursive.c     | 4 ++--\n name-hash.c           | 2 +-\n patch-ids.c           | 2 +-\n revision.c            | 3 ++-\n sub-process.c         | 2 +-\n submodule-config.c    | 4 ++--\n 14 files changed, 28 insertions(+), 23 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex fa26a3e3cb..9bdef61cc3 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -101,7 +101,7 @@ static void *attr_hashmap_get(struct attr_hashmap *map,\n \thashmap_entry_init(&k.ent, memhash(key, keylen));\n \tk.key = key;\n \tk.keylen = keylen;\n-\te = hashmap_get(&map->map, &k, NULL);\n+\te = hashmap_get(&map->map, &k.ent, NULL);\n \n \treturn e ? e->value : NULL;\n }\ndiff --git a/blame.c b/blame.c\nindex 4d20aee435..73ffb59403 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -419,7 +419,7 @@ static void get_fingerprint(struct fingerprint *result,\n \t\t\tcontinue;\n \t\thashmap_entry_init(&entry->entry, hash);\n \n-\t\tfound_entry = hashmap_get(&result->map, entry, NULL);\n+\t\tfound_entry = hashmap_get(&result->map, &entry->entry, NULL);\n \t\tif (found_entry) {\n \t\t\tfound_entry->count += 1;\n \t\t} else {\n@@ -452,7 +452,7 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)\n \thashmap_iter_init(&b->map, &iter);\n \n \twhile ((entry_b = hashmap_iter_next(&iter))) {\n-\t\tif ((entry_a = hashmap_get(&a->map, entry_b, NULL))) {\n+\t\tif ((entry_a = hashmap_get(&a->map, &entry_b->entry, NULL))) {\n \t\t\tintersection += entry_a->count < entry_b->count ?\n \t\t\t\t\tentry_a->count : entry_b->count;\n \t\t}\n@@ -471,7 +471,7 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \thashmap_iter_init(&b->map, &iter);\n \n \twhile ((entry_b = hashmap_iter_next(&iter))) {\n-\t\tif ((entry_a = hashmap_get(&a->map, entry_b, NULL))) {\n+\t\tif ((entry_a = hashmap_get(&a->map, &entry_b->entry, NULL))) {\n \t\t\tif (entry_a->count <= entry_b->count)\n \t\t\t\thashmap_remove(&a->map, entry_b, NULL);\n \t\t\telse\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 82c146718d..f41298d199 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -162,7 +162,7 @@ static void add_left_or_right(struct hashmap *map, const char *path,\n \n \tFLEX_ALLOC_STR(e, path, path);\n \thashmap_entry_init(&e->entry, strhash(path));\n-\texisting = hashmap_get(map, e, NULL);\n+\texisting = hashmap_get(map, &e->entry, NULL);\n \tif (existing) {\n \t\tfree(e);\n \t\te = existing;\n@@ -462,7 +462,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t\t\t/* Avoid duplicate working_tree entries */\n \t\t\tFLEX_ALLOC_STR(entry, path, dst_path);\n \t\t\thashmap_entry_init(&entry->entry, strhash(dst_path));\n-\t\t\tif (hashmap_get(&working_tree_dups, entry, NULL)) {\n+\t\t\tif (hashmap_get(&working_tree_dups, &entry->entry,\n+\t\t\t\t\tNULL)) {\n \t\t\t\tfree(entry);\n \t\t\t\tcontinue;\n \t\t\t}\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 287dbd24a3..c693cf6a8c 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -151,7 +151,7 @@ static const void *anonymize_mem(struct hashmap *map,\n \thashmap_entry_init(&key.hash, memhash(orig, *len));\n \tkey.orig = orig;\n \tkey.orig_len = *len;\n-\tret = hashmap_get(map, &key, NULL);\n+\tret = hashmap_get(map, &key.hash, NULL);\n \n \tif (!ret) {\n \t\tret = xmalloc(sizeof(*ret));\ndiff --git a/config.c b/config.c\nindex 2243d7c3d6..1a1b6675fd 100644\n--- a/config.c\n+++ b/config.c\n@@ -1863,7 +1863,7 @@ static struct config_set_element *configset_find_element(struct config_set *cs,\n \n \thashmap_entry_init(&k.ent, strhash(normalized_key));\n \tk.key = normalized_key;\n-\tfound_entry = hashmap_get(&cs->config_hash, &k, NULL);\n+\tfound_entry = hashmap_get(&cs->config_hash, &k.ent, NULL);\n \tfree(normalized_key);\n \treturn found_entry;\n }\ndiff --git a/diff.c b/diff.c\nindex cc7f06d10d..72d3c6aa19 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1144,13 +1144,13 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\tcase DIFF_SYMBOL_PLUS:\n \t\t\thm = del_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get(hm, key, NULL);\n+\t\t\tmatch = hashmap_get(hm, &key->ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tcase DIFF_SYMBOL_MINUS:\n \t\t\thm = add_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get(hm, key, NULL);\n+\t\t\tmatch = hashmap_get(hm, &key->ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tdefault:\ndiff --git a/hashmap.c b/hashmap.c\nindex 9c2db3e0c8..092236c09a 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -186,7 +186,8 @@ void hashmap_free(struct hashmap *map, int free_entries)\n \tmemset(map, 0, sizeof(*map));\n }\n \n-void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)\n+void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n+\t\tconst void *keydata)\n {\n \treturn *find_entry_ptr(map, key, keydata);\n }\n@@ -296,7 +297,7 @@ const void *memintern(const void *data, size_t len)\n \t/* lookup interned string in pool */\n \thashmap_entry_init(&key.ent, memhash(data, len));\n \tkey.len = len;\n-\te = hashmap_get(&map, &key, data);\n+\te = hashmap_get(&map, &key.ent, data);\n \tif (!e) {\n \t\t/* not found: create it */\n \t\tFLEX_ALLOC_MEM(e, data, data, len);\ndiff --git a/hashmap.h b/hashmap.h\nindex 47ee5c00d7..ef83f9431d 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -74,7 +74,8 @@\n  *             e->key = key;\n  *\n  *             flags |= COMPARE_VALUE;\n- *             printf(\"%sfound\\n\", hashmap_get(&map, e, NULL) ? \"\" : \"not \");\n+ *             printf(\"%sfound\\n\",\n+ *                    hashmap_get(&map, &e->ent, NULL) ? \"\" : \"not \");\n  *             free(e);\n  *         }\n  *\n@@ -84,7 +85,8 @@\n  *             k.key = key;\n  *\n  *             flags |= COMPARE_VALUE;\n- *             printf(\"%sfound\\n\", hashmap_get(&map, &k, value) ? \"\" : \"not \");\n+ *             printf(\"%sfound\\n\",\n+ *                    hashmap_get(&map, &k->ent, value) ? \"\" : \"not \");\n  *         }\n  *\n  *         if (!strcmp(\"end\", action)) {\n@@ -286,7 +288,7 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)\n  * If an entry with matching hash code is found, `key` and `keydata` are passed\n  * to `hashmap_cmp_fn` to decide whether the entry matches the key.\n  */\n-void *hashmap_get(const struct hashmap *map, const void *key,\n+void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n \t\t\t const void *keydata);\n \n /*\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex db9b247ece..2d31a3e279 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -63,7 +63,7 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,\n \t\treturn NULL;\n \thashmap_entry_init(&key.ent, strhash(dir));\n \tkey.dir = dir;\n-\treturn hashmap_get(hashmap, &key, NULL);\n+\treturn hashmap_get(hashmap, &key.ent, NULL);\n }\n \n static int dir_rename_cmp(const void *unused_cmp_data,\n@@ -99,7 +99,7 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,\n \n \thashmap_entry_init(&key.ent, strhash(target_file));\n \tkey.target_file = target_file;\n-\treturn hashmap_get(hashmap, &key, NULL);\n+\treturn hashmap_get(hashmap, &key.ent, NULL);\n }\n \n static int collision_cmp(void *unused_cmp_data,\ndiff --git a/name-hash.c b/name-hash.c\nindex faec682bc7..4eaeded775 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -35,7 +35,7 @@ static struct dir_entry *find_dir_entry__hash(struct index_state *istate,\n \tstruct dir_entry key;\n \thashmap_entry_init(&key.ent, hash);\n \tkey.namelen = namelen;\n-\treturn hashmap_get(&istate->dir_hash, &key, name);\n+\treturn hashmap_get(&istate->dir_hash, &key.ent, name);\n }\n \n static struct dir_entry *find_dir_entry(struct index_state *istate,\ndiff --git a/patch-ids.c b/patch-ids.c\nindex f87b62bf58..437f29e42c 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -99,7 +99,7 @@ struct patch_id *has_commit_patch_id(struct commit *commit,\n \tif (init_patch_id_entry(&patch, commit, ids))\n \t\treturn NULL;\n \n-\treturn hashmap_get(&ids->patches, &patch, NULL);\n+\treturn hashmap_get(&ids->patches, &patch.ent, NULL);\n }\n \n struct patch_id *add_commit_patch_id(struct commit *commit,\ndiff --git a/revision.c b/revision.c\nindex 3461c78883..4336281286 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -147,7 +147,8 @@ static void paths_and_oids_insert(struct hashmap *map,\n \tkey.path = (char *)path;\n \toidset_init(&key.trees, 0);\n \n-\tif (!(entry = (struct path_and_oids_entry *)hashmap_get(map, &key, NULL))) {\n+\tentry = hashmap_get(map, &key.ent, NULL);\n+\tif (!entry) {\n \t\tentry = xcalloc(1, sizeof(struct path_and_oids_entry));\n \t\thashmap_entry_init(&entry->ent, hash);\n \t\tentry->path = xstrdup(key.path);\ndiff --git a/sub-process.c b/sub-process.c\nindex d58e069855..debd86bb68 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -22,7 +22,7 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch\n \n \thashmap_entry_init(&key.ent, strhash(cmd));\n \tkey.cmd = cmd;\n-\treturn hashmap_get(hashmap, &key, NULL);\n+\treturn hashmap_get(hashmap, &key.ent, NULL);\n }\n \n int subprocess_read_status(int fd, struct strbuf *status)\ndiff --git a/submodule-config.c b/submodule-config.c\nindex a3bbd9fd6f..58d585cd7d 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -166,7 +166,7 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get(&cache->for_path, &key, NULL);\n+\tentry = hashmap_get(&cache->for_path, &key.ent, NULL);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\n@@ -186,7 +186,7 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get(&cache->for_name, &key, NULL);\n+\tentry = hashmap_get(&cache->for_name, &key.ent, NULL);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\n"},{"id":"383538","messageId":"20191006233043.3516-9-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 08/20] hashmap_remove takes \"const struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:31Z","receivedAt":"2019-10-06T23:31:08Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is less error-prone than \"const void *\" as the compiler\nnow detects invalid types being passed.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n blame.c            | 2 +-\n hashmap.c          | 3 ++-\n hashmap.h          | 2 +-\n merge-recursive.c  | 2 +-\n name-hash.c        | 4 ++--\n packfile.c         | 2 +-\n range-diff.c       | 2 +-\n sub-process.c      | 2 +-\n submodule-config.c | 2 +-\n 9 files changed, 11 insertions(+), 10 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 73ffb59403..00f8f3fb0a 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -473,7 +473,7 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \twhile ((entry_b = hashmap_iter_next(&iter))) {\n \t\tif ((entry_a = hashmap_get(&a->map, &entry_b->entry, NULL))) {\n \t\t\tif (entry_a->count <= entry_b->count)\n-\t\t\t\thashmap_remove(&a->map, entry_b, NULL);\n+\t\t\t\thashmap_remove(&a->map, &entry_b->entry, NULL);\n \t\t\telse\n \t\t\t\tentry_a->count -= entry_b->count;\n \t\t}\ndiff --git a/hashmap.c b/hashmap.c\nindex 092236c09a..bdf33e0381 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -218,7 +218,8 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)\n \t}\n }\n \n-void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)\n+void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n+\t\tconst void *keydata)\n {\n \tstruct hashmap_entry *old;\n \tstruct hashmap_entry **e = find_entry_ptr(map, key, keydata);\ndiff --git a/hashmap.h b/hashmap.h\nindex ef83f9431d..c4c31b462f 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -349,7 +349,7 @@ void *hashmap_put(struct hashmap *map, void *entry);\n  *\n  * Argument explanation is the same as in `hashmap_get`.\n  */\n-void *hashmap_remove(struct hashmap *map, const void *key,\n+void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n \t\tconst void *keydata);\n \n /*\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2d31a3e279..f60451d396 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2001,7 +2001,7 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,\n \n \tfor (i = 0; i < items_to_remove->nr; i++) {\n \t\tentry = items_to_remove->items[i].util;\n-\t\thashmap_remove(dir_renames, entry, NULL);\n+\t\thashmap_remove(dir_renames, &entry->ent, NULL);\n \t}\n \tstring_list_clear(items_to_remove, 0);\n }\ndiff --git a/name-hash.c b/name-hash.c\nindex 4eaeded775..44d788f1ce 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -95,7 +95,7 @@ static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)\n \tstruct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce));\n \twhile (dir && !(--dir->nr)) {\n \t\tstruct dir_entry *parent = dir->parent;\n-\t\thashmap_remove(&istate->dir_hash, dir, NULL);\n+\t\thashmap_remove(&istate->dir_hash, &dir->ent, NULL);\n \t\tfree(dir);\n \t\tdir = parent;\n \t}\n@@ -625,7 +625,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)\n \tif (!istate->name_hash_initialized || !(ce->ce_flags & CE_HASHED))\n \t\treturn;\n \tce->ce_flags &= ~CE_HASHED;\n-\thashmap_remove(&istate->name_hash, ce, ce);\n+\thashmap_remove(&istate->name_hash, &ce->ent, ce);\n \n \tif (ignore_case)\n \t\tremove_dir_entry(istate, ce);\ndiff --git a/packfile.c b/packfile.c\nindex f7402c470b..3edd648de0 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1423,7 +1423,7 @@ static int in_delta_base_cache(struct packed_git *p, off_t base_offset)\n  */\n static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)\n {\n-\thashmap_remove(&delta_base_cache, ent, &ent->key);\n+\thashmap_remove(&delta_base_cache, &ent->ent, &ent->key);\n \tlist_del(&ent->lru);\n \tdelta_base_cached -= ent->size;\n \tfree(ent);\ndiff --git a/range-diff.c b/range-diff.c\nindex 96f955d84d..c51cfd5556 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -229,7 +229,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->patch = b->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n \t\thashmap_entry_init(&util->e, strhash(util->diff));\n-\t\tother = hashmap_remove(&map, util, NULL);\n+\t\tother = hashmap_remove(&map, &util->e, NULL);\n \t\tif (other) {\n \t\t\tif (other->matching >= 0)\n \t\t\t\tBUG(\"already assigned!\");\ndiff --git a/sub-process.c b/sub-process.c\nindex debd86bb68..99fccef592 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -58,7 +58,7 @@ void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)\n \tkill(entry->process.pid, SIGTERM);\n \tfinish_command(&entry->process);\n \n-\thashmap_remove(hashmap, entry, NULL);\n+\thashmap_remove(hashmap, &entry->ent, NULL);\n }\n \n static void subprocess_exit_handler(struct child_process *process)\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 58d585cd7d..7486745a6a 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -137,7 +137,7 @@ static void cache_remove_path(struct submodule_cache *cache,\n \tstruct submodule_entry *removed;\n \thashmap_entry_init(&e.ent, hash);\n \te.config = submodule;\n-\tremoved = hashmap_remove(&cache->for_path, &e, NULL);\n+\tremoved = hashmap_remove(&cache->for_path, &e.ent, NULL);\n \tfree(removed);\n }\n \n"},{"id":"383539","messageId":"20191006233043.3516-10-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 09/20] hashmap_put takes \"struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:32Z","receivedAt":"2019-10-06T23:31:11Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is less error-prone than \"void *\" as the compiler now\ndetects invalid types being passed.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n builtin/fast-export.c   | 2 +-\n hashmap.c               | 2 +-\n hashmap.h               | 2 +-\n merge-recursive.c       | 4 ++--\n oidmap.c                | 2 +-\n refs.c                  | 5 ++++-\n remote.c                | 2 +-\n revision.c              | 2 +-\n sequencer.c             | 2 +-\n submodule-config.c      | 2 +-\n t/helper/test-hashmap.c | 2 +-\n 11 files changed, 15 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex c693cf6a8c..192e21dae4 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -160,7 +160,7 @@ static const void *anonymize_mem(struct hashmap *map,\n \t\tret->orig_len = *len;\n \t\tret->anon = generate(orig, len);\n \t\tret->anon_len = *len;\n-\t\thashmap_put(map, ret);\n+\t\thashmap_put(map, &ret->hash);\n \t}\n \n \t*len = ret->anon_len;\ndiff --git a/hashmap.c b/hashmap.c\nindex bdf33e0381..9b83e73d03 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -241,7 +241,7 @@ void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n \treturn old;\n }\n \n-void *hashmap_put(struct hashmap *map, void *entry)\n+void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n {\n \tstruct hashmap_entry *old = hashmap_remove(map, entry, NULL);\n \thashmap_add(map, entry);\ndiff --git a/hashmap.h b/hashmap.h\nindex c4c31b462f..d122c75d0f 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -340,7 +340,7 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);\n  * `entry` is the entry to add or replace.\n  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).\n  */\n-void *hashmap_put(struct hashmap *map, void *entry);\n+void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n \n /*\n  * Removes a hashmap entry matching the specified key. If the hashmap contains\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f60451d396..a685b4fb69 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2229,7 +2229,7 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)\n \t\tif (!entry) {\n \t\t\tentry = xmalloc(sizeof(*entry));\n \t\t\tdir_rename_entry_init(entry, old_dir);\n-\t\t\thashmap_put(dir_renames, entry);\n+\t\t\thashmap_put(dir_renames, &entry->ent);\n \t\t} else {\n \t\t\tfree(old_dir);\n \t\t}\n@@ -2360,7 +2360,7 @@ static void compute_collisions(struct hashmap *collisions,\n \t\t\t\t\t\tsizeof(struct collision_entry));\n \t\t\thashmap_entry_init(&collision_ent->ent,\n \t\t\t\t\t\tstrhash(new_path));\n-\t\t\thashmap_put(collisions, collision_ent);\n+\t\t\thashmap_put(collisions, &collision_ent->ent);\n \t\t\tcollision_ent->target_file = new_path;\n \t\t} else {\n \t\t\tfree(new_path);\ndiff --git a/oidmap.c b/oidmap.c\nindex 6d6e840d03..cd22b3a8bf 100644\n--- a/oidmap.c\n+++ b/oidmap.c\n@@ -51,5 +51,5 @@ void *oidmap_put(struct oidmap *map, void *entry)\n \t\toidmap_init(map, 0);\n \n \thashmap_entry_init(&to_put->internal_entry, oidhash(&to_put->oid));\n-\treturn hashmap_put(&map->map, to_put);\n+\treturn hashmap_put(&map->map, &to_put->internal_entry);\n }\ndiff --git a/refs.c b/refs.c\nindex c43ec59c6e..3e55031256 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1863,10 +1863,13 @@ static void register_ref_store_map(struct hashmap *map,\n \t\t\t\t   struct ref_store *refs,\n \t\t\t\t   const char *name)\n {\n+\tstruct ref_store_hash_entry *entry;\n+\n \tif (!map->tablesize)\n \t\thashmap_init(map, ref_store_hash_cmp, NULL, 0);\n \n-\tif (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))\n+\tentry = alloc_ref_store_hash_entry(name, refs);\n+\tif (hashmap_put(map, &entry->ent))\n \t\tBUG(\"%s ref_store '%s' initialized twice\", type, name);\n }\n \ndiff --git a/remote.c b/remote.c\nindex bd81cb71bc..8ca23d95dc 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -159,7 +159,7 @@ static struct remote *make_remote(const char *name, int len)\n \tremotes[remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\treplaced = hashmap_put(&remotes_hash, ret);\n+\treplaced = hashmap_put(&remotes_hash, &ret->ent);\n \tassert(replaced == NULL);  /* no previous entry overwritten */\n \treturn ret;\n }\ndiff --git a/revision.c b/revision.c\nindex 4336281286..a7e2339064 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -153,7 +153,7 @@ static void paths_and_oids_insert(struct hashmap *map,\n \t\thashmap_entry_init(&entry->ent, hash);\n \t\tentry->path = xstrdup(key.path);\n \t\toidset_init(&entry->trees, 16);\n-\t\thashmap_put(map, entry);\n+\t\thashmap_put(map, &entry->ent);\n \t}\n \n \toidset_insert(&entry->trees, oid);\ndiff --git a/sequencer.c b/sequencer.c\nindex ee11cda7e7..b4ef70e260 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5254,7 +5254,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)\n \t\t\tentry->i = i;\n \t\t\thashmap_entry_init(&entry->entry,\n \t\t\t\t\tstrhash(entry->subject));\n-\t\t\thashmap_put(&subject2item, entry);\n+\t\t\thashmap_put(&subject2item, &entry->entry);\n \t\t}\n \t}\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex 7486745a6a..9248c5ea5b 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -125,7 +125,7 @@ static void cache_put_path(struct submodule_cache *cache,\n \tstruct submodule_entry *e = xmalloc(sizeof(*e));\n \thashmap_entry_init(&e->ent, hash);\n \te->config = submodule;\n-\thashmap_put(&cache->for_path, e);\n+\thashmap_put(&cache->for_path, &e->ent);\n }\n \n static void cache_remove_path(struct submodule_cache *cache,\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 49e715f1cd..de2bd083b9 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -187,7 +187,7 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tentry = alloc_test_entry(hash, p1, p2);\n \n \t\t\t/* add / replace entry */\n-\t\t\tentry = hashmap_put(&map, entry);\n+\t\t\tentry = hashmap_put(&map, &entry->ent);\n \n \t\t\t/* print and free replaced entry, if any */\n \t\t\tputs(entry ? get_value(entry) : \"NULL\");\n"},{"id":"383540","messageId":"20191006233043.3516-11-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 10/20] introduce container_of macro","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:33Z","receivedAt":"2019-10-06T23:31:14Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This macro is popular within the Linux kernel for supporting\nintrusive data structures such as linked lists, red-black trees,\nand chained hash tables while allowing the compiler to do\ntype checking.\n\nLater patches will use container_of() to remove the limitation\nof \"hashmap_entry\" being location-dependent. This will complete\nthe transition to compile-time type checking for the hashmap API.\n\nThis macro already exists in our source as \"list_entry\" in\nlist.h and making \"list_entry\" an alias to \"container_of\"\nas the Linux kernel has done is a possibility.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n git-compat-util.h | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 83be89de0a..4cc2c8283a 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1312,4 +1312,14 @@ void unleak_memory(const void *ptr, size_t len);\n  */\n #include \"banned.h\"\n \n+/*\n+ * container_of - Get the address of an object containing a field.\n+ *\n+ * @ptr: pointer to the field.\n+ * @type: type of the object.\n+ * @member: name of the field within the object.\n+ */\n+#define container_of(ptr, type, member) \\\n+\t((type *) ((char *)(ptr) - offsetof(type, member)))\n+\n #endif\n"},{"id":"383541","messageId":"20191006233043.3516-12-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 11/20] hashmap_get_next returns \"struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:34Z","receivedAt":"2019-10-06T23:31:17Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is a step towards removing the requirement for\nhashmap_entry being the first field of a struct.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n diff.c                  | 19 ++++++++++++-------\n diffcore-rename.c       | 11 +++++++----\n hashmap.c               |  2 +-\n hashmap.h               | 12 ++++++++----\n name-hash.c             |  8 +++++---\n t/helper/test-hashmap.c | 10 ++++++----\n 6 files changed, 39 insertions(+), 23 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 72d3c6aa19..0978814086 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1035,8 +1035,10 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,\n {\n \tint i;\n \tchar *got_match = xcalloc(1, pmb_nr);\n+\tstruct hashmap_entry *ent;\n \n-\tfor (; match; match = hashmap_get_next(hm, &match->ent)) {\n+\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n+\t\tmatch = container_of(ent, struct moved_entry, ent);\n \t\tfor (i = 0; i < pmb_nr; i++) {\n \t\t\tstruct moved_entry *prev = pmb[i].match;\n \t\t\tstruct moved_entry *cur = (prev && prev->next_line) ?\n@@ -1135,8 +1137,9 @@ static void mark_color_as_moved(struct diff_options *o,\n \n \tfor (n = 0; n < o->emitted_symbols->nr; n++) {\n \t\tstruct hashmap *hm = NULL;\n+\t\tstruct hashmap_entry *ent = NULL;\n \t\tstruct moved_entry *key;\n-\t\tstruct moved_entry *match = NULL;\n+\t\tstruct moved_entry *match;\n \t\tstruct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];\n \t\tenum diff_symbol last_symbol = 0;\n \n@@ -1144,20 +1147,20 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\tcase DIFF_SYMBOL_PLUS:\n \t\t\thm = del_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get(hm, &key->ent, NULL);\n+\t\t\tent = hashmap_get(hm, &key->ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tcase DIFF_SYMBOL_MINUS:\n \t\t\thm = add_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get(hm, &key->ent, NULL);\n+\t\t\tent = hashmap_get(hm, &key->ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tflipped_block = 0;\n \t\t}\n \n-\t\tif (!match) {\n+\t\tif (!ent) {\n \t\t\tint i;\n \n \t\t\tadjust_last_block(o, n, block_length);\n@@ -1169,6 +1172,7 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\tlast_symbol = l->s;\n \t\t\tcontinue;\n \t\t}\n+\t\tmatch = container_of(ent, struct moved_entry, ent);\n \n \t\tif (o->color_moved == COLOR_MOVED_PLAIN) {\n \t\t\tlast_symbol = l->s;\n@@ -1189,8 +1193,9 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\t * The current line is the start of a new block.\n \t\t\t * Setup the set of potential blocks.\n \t\t\t */\n-\t\t\tfor (; match; match = hashmap_get_next(hm,\n-\t\t\t\t\t\t\t\t&match->ent)) {\n+\t\t\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n+\t\t\t\tmatch = container_of(ent, struct moved_entry,\n+\t\t\t\t\t\t\tent);\n \t\t\t\tALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);\n \t\t\t\tif (o->color_moved_ws_handling &\n \t\t\t\t    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 4670a40179..71aa240a68 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -274,7 +274,7 @@ static int find_identical_files(struct hashmap *srcs,\n \t\t\t\tstruct diff_options *options)\n {\n \tint renames = 0;\n-\n+\tstruct hashmap_entry *ent;\n \tstruct diff_filespec *target = rename_dst[dst_index].two;\n \tstruct file_similarity *p, *best = NULL;\n \tint i = 100, best_score = -1;\n@@ -282,12 +282,15 @@ static int find_identical_files(struct hashmap *srcs,\n \t/*\n \t * Find the best source match for specified destination.\n \t */\n-\tp = hashmap_get_from_hash(srcs,\n+\tent = hashmap_get_from_hash(srcs,\n \t\t\t\t  hash_filespec(options->repo, target),\n \t\t\t\t  NULL);\n-\tfor (; p; p = hashmap_get_next(srcs, &p->entry)) {\n+\tfor (; ent; ent = hashmap_get_next(srcs, ent)) {\n \t\tint score;\n-\t\tstruct diff_filespec *source = p->filespec;\n+\t\tstruct diff_filespec *source;\n+\n+\t\tp = container_of(ent, struct file_similarity, entry);\n+\t\tsource = p->filespec;\n \n \t\t/* False hash collision? */\n \t\tif (!oideq(&source->oid, &target->oid))\ndiff --git a/hashmap.c b/hashmap.c\nindex 9b83e73d03..22bc7c5b3b 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -192,7 +192,7 @@ void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n \treturn *find_entry_ptr(map, key, keydata);\n }\n \n-void *hashmap_get_next(const struct hashmap *map,\n+struct hashmap_entry *hashmap_get_next(const struct hashmap *map,\n \t\t\tconst struct hashmap_entry *entry)\n {\n \tstruct hashmap_entry *e = entry->next;\ndiff --git a/hashmap.h b/hashmap.h\nindex d122c75d0f..21bc8aff2b 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -55,15 +55,19 @@\n  *\n  *         if (!strcmp(\"print_all_by_key\", action)) {\n  *             struct long2string k, *e;\n+ *             struct hashmap_entry *ent;\n  *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));\n  *             k.key = key;\n  *\n  *             flags &= ~COMPARE_VALUE;\n- *             e = hashmap_get(&map, &k, NULL);\n- *             if (e) {\n+ *             ent = hashmap_get(&map, &k, NULL);\n+ *             if (ent) {\n+ *                 e = container_of(ent, struct long2string, ent);\n  *                 printf(\"first: %ld %s\\n\", e->key, e->value);\n- *                 while ((e = hashmap_get_next(&map, e)))\n+ *                 while ((ent = hashmap_get_next(&map, ent))) {\n+ *                     e = container_of(ent, struct long2string, ent);\n  *                     printf(\"found more: %ld %s\\n\", e->key, e->value);\n+ *                 }\n  *             }\n  *         }\n  *\n@@ -320,7 +324,7 @@ static inline void *hashmap_get_from_hash(const struct hashmap *map,\n  * `entry` is the hashmap_entry to start the search from, obtained via a previous\n  * call to `hashmap_get` or `hashmap_get_next`.\n  */\n-void *hashmap_get_next(const struct hashmap *map,\n+struct hashmap_entry *hashmap_get_next(const struct hashmap *map,\n \t\t\tconst struct hashmap_entry *entry);\n \n /*\ndiff --git a/name-hash.c b/name-hash.c\nindex 44d788f1ce..dbacb34f50 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -702,15 +702,17 @@ void adjust_dirname_case(struct index_state *istate, char *name)\n struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)\n {\n \tstruct cache_entry *ce;\n+\tstruct hashmap_entry *ent;\n \n \tlazy_init_name_hash(istate);\n \n-\tce = hashmap_get_from_hash(&istate->name_hash,\n+\tent = hashmap_get_from_hash(&istate->name_hash,\n \t\t\t\t   memihash(name, namelen), NULL);\n-\twhile (ce) {\n+\twhile (ent) {\n+\t\tce = container_of(ent, struct cache_entry, ent);\n \t\tif (same_name(ce, name, namelen, icase))\n \t\t\treturn ce;\n-\t\tce = hashmap_get_next(&istate->name_hash, &ce->ent);\n+\t\tent = hashmap_get_next(&istate->name_hash, ent);\n \t}\n \treturn NULL;\n }\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex de2bd083b9..d85b8dc58e 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -194,16 +194,18 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tfree(entry);\n \n \t\t} else if (!strcmp(\"get\", cmd) && p1) {\n+\t\t\tstruct hashmap_entry *e;\n \n \t\t\t/* lookup entry in hashmap */\n-\t\t\tentry = hashmap_get_from_hash(&map, hash, p1);\n+\t\t\te = hashmap_get_from_hash(&map, hash, p1);\n \n \t\t\t/* print result */\n-\t\t\tif (!entry)\n+\t\t\tif (!e)\n \t\t\t\tputs(\"NULL\");\n-\t\t\twhile (entry) {\n+\t\t\twhile (e) {\n+\t\t\t\tentry = container_of(e, struct test_entry, ent);\n \t\t\t\tputs(get_value(entry));\n-\t\t\t\tentry = hashmap_get_next(&map, &entry->ent);\n+\t\t\t\te = hashmap_get_next(&map, e);\n \t\t\t}\n \n \t\t} else if (!strcmp(\"remove\", cmd) && p1) {\n"},{"id":"383542","messageId":"20191006233043.3516-13-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 12/20] hashmap: use *_entry APIs to wrap container_of","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:35Z","receivedAt":"2019-10-06T23:31:20Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Using `container_of' can be verbose and choosing names for\nintermediate \"struct hashmap_entry\" pointers is a hard problem.\nSo introduce \"*_entry\" APIs inspired by similar linked-list\nAPIs in the Linux kernel.\n\nUnfortunately, `__typeof__' is not portable C, so we need an\nextra parameter to specify the type.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n diff.c                  | 21 +++++++++------------\n diffcore-rename.c       | 14 +++++---------\n git-compat-util.h       | 15 +++++++++++++++\n hashmap.h               | 40 ++++++++++++++++++++++++++++++++++------\n name-hash.c             | 11 +++++------\n t/helper/test-hashmap.c | 12 +++++-------\n 6 files changed, 73 insertions(+), 40 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 0978814086..66cdf4e9ca 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1035,10 +1035,8 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,\n {\n \tint i;\n \tchar *got_match = xcalloc(1, pmb_nr);\n-\tstruct hashmap_entry *ent;\n \n-\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n-\t\tmatch = container_of(ent, struct moved_entry, ent);\n+\thashmap_for_each_entry_from(hm, match, struct moved_entry, ent) {\n \t\tfor (i = 0; i < pmb_nr; i++) {\n \t\t\tstruct moved_entry *prev = pmb[i].match;\n \t\t\tstruct moved_entry *cur = (prev && prev->next_line) ?\n@@ -1137,9 +1135,8 @@ static void mark_color_as_moved(struct diff_options *o,\n \n \tfor (n = 0; n < o->emitted_symbols->nr; n++) {\n \t\tstruct hashmap *hm = NULL;\n-\t\tstruct hashmap_entry *ent = NULL;\n \t\tstruct moved_entry *key;\n-\t\tstruct moved_entry *match;\n+\t\tstruct moved_entry *match = NULL;\n \t\tstruct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];\n \t\tenum diff_symbol last_symbol = 0;\n \n@@ -1147,20 +1144,22 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\tcase DIFF_SYMBOL_PLUS:\n \t\t\thm = del_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tent = hashmap_get(hm, &key->ent, NULL);\n+\t\t\tmatch = hashmap_get_entry(hm, key, NULL,\n+\t\t\t\t\t\tstruct moved_entry, ent);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tcase DIFF_SYMBOL_MINUS:\n \t\t\thm = add_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tent = hashmap_get(hm, &key->ent, NULL);\n+\t\t\tmatch = hashmap_get_entry(hm, key, NULL,\n+\t\t\t\t\t\tstruct moved_entry, ent);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tflipped_block = 0;\n \t\t}\n \n-\t\tif (!ent) {\n+\t\tif (!match) {\n \t\t\tint i;\n \n \t\t\tadjust_last_block(o, n, block_length);\n@@ -1172,7 +1171,6 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\tlast_symbol = l->s;\n \t\t\tcontinue;\n \t\t}\n-\t\tmatch = container_of(ent, struct moved_entry, ent);\n \n \t\tif (o->color_moved == COLOR_MOVED_PLAIN) {\n \t\t\tlast_symbol = l->s;\n@@ -1193,9 +1191,8 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\t * The current line is the start of a new block.\n \t\t\t * Setup the set of potential blocks.\n \t\t\t */\n-\t\t\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n-\t\t\t\tmatch = container_of(ent, struct moved_entry,\n-\t\t\t\t\t\t\tent);\n+\t\t\thashmap_for_each_entry_from(hm, match,\n+\t\t\t\t\t\tstruct moved_entry, ent) {\n \t\t\t\tALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);\n \t\t\t\tif (o->color_moved_ws_handling &\n \t\t\t\t    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 71aa240a68..611b08f463 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -274,23 +274,19 @@ static int find_identical_files(struct hashmap *srcs,\n \t\t\t\tstruct diff_options *options)\n {\n \tint renames = 0;\n-\tstruct hashmap_entry *ent;\n \tstruct diff_filespec *target = rename_dst[dst_index].two;\n \tstruct file_similarity *p, *best = NULL;\n \tint i = 100, best_score = -1;\n+\tunsigned int hash = hash_filespec(options->repo, target);\n \n \t/*\n \t * Find the best source match for specified destination.\n \t */\n-\tent = hashmap_get_from_hash(srcs,\n-\t\t\t\t  hash_filespec(options->repo, target),\n-\t\t\t\t  NULL);\n-\tfor (; ent; ent = hashmap_get_next(srcs, ent)) {\n+\tp = hashmap_get_entry_from_hash(srcs, hash, NULL,\n+\t\t\t\t\tstruct file_similarity, entry);\n+\thashmap_for_each_entry_from(srcs, p, struct file_similarity, entry) {\n \t\tint score;\n-\t\tstruct diff_filespec *source;\n-\n-\t\tp = container_of(ent, struct file_similarity, entry);\n-\t\tsource = p->filespec;\n+\t\tstruct diff_filespec *source = p->filespec;\n \n \t\t/* False hash collision? */\n \t\tif (!oideq(&source->oid, &target->oid))\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 4cc2c8283a..4a23b9090b 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1322,4 +1322,19 @@ void unleak_memory(const void *ptr, size_t len);\n #define container_of(ptr, type, member) \\\n \t((type *) ((char *)(ptr) - offsetof(type, member)))\n \n+/*\n+ * helper function for `container_of_or_null' to avoid multiple\n+ * evaluation of @ptr\n+ */\n+static inline void *container_of_or_null_offset(void *ptr, size_t offset)\n+{\n+\treturn ptr ? (char *)ptr - offset : NULL;\n+}\n+\n+/*\n+ * like `container_of', but allows returned value to be NULL\n+ */\n+#define container_of_or_null(ptr, type, member) \\\n+\t(type *)container_of_or_null_offset(ptr, offsetof(type, member))\n+\n #endif\ndiff --git a/hashmap.h b/hashmap.h\nindex 21bc8aff2b..cd42dcc15c 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -55,17 +55,15 @@\n  *\n  *         if (!strcmp(\"print_all_by_key\", action)) {\n  *             struct long2string k, *e;\n- *             struct hashmap_entry *ent;\n  *             hashmap_entry_init(&k->ent, memhash(&key, sizeof(long)));\n  *             k.key = key;\n  *\n  *             flags &= ~COMPARE_VALUE;\n- *             ent = hashmap_get(&map, &k, NULL);\n- *             if (ent) {\n- *                 e = container_of(ent, struct long2string, ent);\n+ *             e = hashmap_get_entry(&map, &k, NULL, struct long2string, ent);\n+ *             if (e) {\n  *                 printf(\"first: %ld %s\\n\", e->key, e->value);\n- *                 while ((ent = hashmap_get_next(&map, ent))) {\n- *                     e = container_of(ent, struct long2string, ent);\n+ *                 while ((e = hashmap_get_next_entry(&map, e,\n+ *                                              struct long2string, ent))) {\n  *                     printf(\"found more: %ld %s\\n\", e->key, e->value);\n  *                 }\n  *             }\n@@ -387,6 +385,36 @@ static inline void *hashmap_iter_first(struct hashmap *map,\n \treturn hashmap_iter_next(iter);\n }\n \n+/*\n+ * returns a @pointer of @type matching @keyvar, or NULL if nothing found.\n+ * @keyvar is a pointer of @type\n+ * @member is the name of the \"struct hashmap_entry\" field in @type\n+ */\n+#define hashmap_get_entry(map, keyvar, keydata, type, member) \\\n+\tcontainer_of_or_null(hashmap_get(map, &(keyvar)->member, keydata), \\\n+\t\t\t\ttype, member)\n+\n+#define hashmap_get_entry_from_hash(map, hash, keydata, type, member) \\\n+\tcontainer_of_or_null(hashmap_get_from_hash(map, hash, keydata), \\\n+\t\t\t\ttype, member)\n+/*\n+ * returns the next equal @type pointer to @var, or NULL if not found.\n+ * @var is a pointer of @type\n+ * @member is the name of the \"struct hashmap_entry\" field in @type\n+ */\n+#define hashmap_get_next_entry(map, var, type, member) \\\n+\tcontainer_of_or_null(hashmap_get_next(map, &(var)->member), \\\n+\t\t\t\ttype, member)\n+\n+/*\n+ * iterate @map starting from @var, where @var is a pointer of @type\n+ * and @member is the name of the \"struct hashmap_entry\" field in @type\n+ */\n+#define hashmap_for_each_entry_from(map, var, type, member) \\\n+\tfor (; \\\n+\t\tvar; \\\n+\t\tvar = hashmap_get_next_entry(map, var, type, member))\n+\n /*\n  * Disable item counting and automatic rehashing when adding/removing items.\n  *\ndiff --git a/name-hash.c b/name-hash.c\nindex dbacb34f50..73b83adf3d 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -702,17 +702,16 @@ void adjust_dirname_case(struct index_state *istate, char *name)\n struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)\n {\n \tstruct cache_entry *ce;\n-\tstruct hashmap_entry *ent;\n+\tunsigned int hash = memihash(name, namelen);\n \n \tlazy_init_name_hash(istate);\n \n-\tent = hashmap_get_from_hash(&istate->name_hash,\n-\t\t\t\t   memihash(name, namelen), NULL);\n-\twhile (ent) {\n-\t\tce = container_of(ent, struct cache_entry, ent);\n+\tce = hashmap_get_entry_from_hash(&istate->name_hash, hash, NULL,\n+\t\t\t\t\t struct cache_entry, ent);\n+\thashmap_for_each_entry_from(&istate->name_hash, ce,\n+\t\t\t\t\tstruct cache_entry, ent) {\n \t\tif (same_name(ce, name, namelen, icase))\n \t\t\treturn ce;\n-\t\tent = hashmap_get_next(&istate->name_hash, ent);\n \t}\n \treturn NULL;\n }\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex d85b8dc58e..e82cbfdee2 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -194,18 +194,16 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tfree(entry);\n \n \t\t} else if (!strcmp(\"get\", cmd) && p1) {\n-\t\t\tstruct hashmap_entry *e;\n-\n \t\t\t/* lookup entry in hashmap */\n-\t\t\te = hashmap_get_from_hash(&map, hash, p1);\n+\t\t\tentry = hashmap_get_entry_from_hash(&map, hash, p1,\n+\t\t\t\t\t\t\tstruct test_entry, ent);\n \n \t\t\t/* print result */\n-\t\t\tif (!e)\n+\t\t\tif (!entry)\n \t\t\t\tputs(\"NULL\");\n-\t\t\twhile (e) {\n-\t\t\t\tentry = container_of(e, struct test_entry, ent);\n+\t\t\thashmap_for_each_entry_from(&map, entry,\n+\t\t\t\t\t\tstruct test_entry, ent) {\n \t\t\t\tputs(get_value(entry));\n-\t\t\t\te = hashmap_get_next(&map, e);\n \t\t\t}\n \n \t\t} else if (!strcmp(\"remove\", cmd) && p1) {\n"},{"id":"383543","messageId":"20191006233043.3516-14-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 13/20] hashmap_get{,_from_hash} return \"struct hashmap_entry *\"","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:36Z","receivedAt":"2019-10-06T23:31:23Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Update callers to use hashmap_get_entry, hashmap_get_entry_from_hash\nor container_of as appropriate.\n\nThis is another step towards eliminating the requirement of\nhashmap_entry being the first field in a struct.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                |  2 +-\n blame.c               | 11 ++++++++---\n builtin/describe.c    |  3 ++-\n builtin/difftool.c    |  2 +-\n builtin/fast-export.c |  2 +-\n builtin/fetch.c       | 11 +++++++----\n config.c              |  3 ++-\n hashmap.c             |  7 ++++---\n hashmap.h             | 12 +++++++-----\n merge-recursive.c     |  6 ++++--\n name-hash.c           |  3 ++-\n packfile.c            |  5 +++--\n patch-ids.c           |  3 ++-\n ref-filter.c          | 12 +++++++-----\n refs.c                |  5 ++++-\n remote.c              |  7 ++++---\n revision.c            |  3 ++-\n sequencer.c           |  7 +++++--\n sub-process.c         |  3 ++-\n submodule-config.c    |  6 ++++--\n 20 files changed, 72 insertions(+), 41 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 9bdef61cc3..4230bee63d 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -101,7 +101,7 @@ static void *attr_hashmap_get(struct attr_hashmap *map,\n \thashmap_entry_init(&k.ent, memhash(key, keylen));\n \tk.key = key;\n \tk.keylen = keylen;\n-\te = hashmap_get(&map->map, &k.ent, NULL);\n+\te = hashmap_get_entry(&map->map, &k, NULL, struct attr_hash_entry, ent);\n \n \treturn e ? e->value : NULL;\n }\ndiff --git a/blame.c b/blame.c\nindex 00f8f3fb0a..aa46c7ec52 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -419,7 +419,8 @@ static void get_fingerprint(struct fingerprint *result,\n \t\t\tcontinue;\n \t\thashmap_entry_init(&entry->entry, hash);\n \n-\t\tfound_entry = hashmap_get(&result->map, &entry->entry, NULL);\n+\t\tfound_entry = hashmap_get_entry(&result->map, entry, NULL,\n+\t\t\t\t\tstruct fingerprint_entry, entry);\n \t\tif (found_entry) {\n \t\t\tfound_entry->count += 1;\n \t\t} else {\n@@ -452,7 +453,9 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)\n \thashmap_iter_init(&b->map, &iter);\n \n \twhile ((entry_b = hashmap_iter_next(&iter))) {\n-\t\tif ((entry_a = hashmap_get(&a->map, &entry_b->entry, NULL))) {\n+\t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n+\t\t\t\t\tstruct fingerprint_entry, entry);\n+\t\tif (entry_a) {\n \t\t\tintersection += entry_a->count < entry_b->count ?\n \t\t\t\t\tentry_a->count : entry_b->count;\n \t\t}\n@@ -471,7 +474,9 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \thashmap_iter_init(&b->map, &iter);\n \n \twhile ((entry_b = hashmap_iter_next(&iter))) {\n-\t\tif ((entry_a = hashmap_get(&a->map, &entry_b->entry, NULL))) {\n+\t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n+\t\t\t\t\tstruct fingerprint_entry, entry);\n+\t\tif (entry_a) {\n \t\t\tif (entry_a->count <= entry_b->count)\n \t\t\t\thashmap_remove(&a->map, &entry_b->entry, NULL);\n \t\t\telse\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex f5e0a7e033..c6d2386b64 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -76,7 +76,8 @@ static int commit_name_neq(const void *unused_cmp_data,\n \n static inline struct commit_name *find_commit_name(const struct object_id *peeled)\n {\n-\treturn hashmap_get_from_hash(&names, oidhash(peeled), peeled);\n+\treturn hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled,\n+\t\t\t\t\t\tstruct commit_name, entry);\n }\n \n static int replace_name(struct commit_name *e,\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex f41298d199..fa9c862e3a 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -162,7 +162,7 @@ static void add_left_or_right(struct hashmap *map, const char *path,\n \n \tFLEX_ALLOC_STR(e, path, path);\n \thashmap_entry_init(&e->entry, strhash(path));\n-\texisting = hashmap_get(map, &e->entry, NULL);\n+\texisting = hashmap_get_entry(map, e, NULL, struct pair_entry, entry);\n \tif (existing) {\n \t\tfree(e);\n \t\te = existing;\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 192e21dae4..25195badd4 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -151,7 +151,7 @@ static const void *anonymize_mem(struct hashmap *map,\n \thashmap_entry_init(&key.hash, memhash(orig, *len));\n \tkey.orig = orig;\n \tkey.orig_len = *len;\n-\tret = hashmap_get(map, &key.hash, NULL);\n+\tret = hashmap_get_entry(map, &key, NULL, struct anonymized_entry, hash);\n \n \tif (!ret) {\n \t\tret = xmalloc(sizeof(*ret));\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 909dbde909..d06f2b98aa 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -383,8 +383,10 @@ static void find_non_local_tags(const struct ref *refs,\n \tfor_each_string_list_item(remote_ref_item, &remote_refs_list) {\n \t\tconst char *refname = remote_ref_item->string;\n \t\tstruct ref *rm;\n+\t\tunsigned int hash = strhash(refname);\n \n-\t\titem = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);\n+\t\titem = hashmap_get_entry_from_hash(&remote_refs, hash, refname,\n+\t\t\t\t\tstruct refname_hash_entry, ent);\n \t\tif (!item)\n \t\t\tBUG(\"unseen remote ref?\");\n \n@@ -516,10 +518,11 @@ static struct ref *get_ref_map(struct remote *remote,\n \t\tif (rm->peer_ref) {\n \t\t\tconst char *refname = rm->peer_ref->name;\n \t\t\tstruct refname_hash_entry *peer_item;\n+\t\t\tunsigned int hash = strhash(refname);\n \n-\t\t\tpeer_item = hashmap_get_from_hash(&existing_refs,\n-\t\t\t\t\t\t\t  strhash(refname),\n-\t\t\t\t\t\t\t  refname);\n+\t\t\tpeer_item = hashmap_get_entry_from_hash(&existing_refs,\n+\t\t\t\t\t\thash, refname,\n+\t\t\t\t\t\tstruct refname_hash_entry, ent);\n \t\t\tif (peer_item) {\n \t\t\t\tstruct object_id *old_oid = &peer_item->oid;\n \t\t\t\toidcpy(&rm->peer_ref->old_oid, old_oid);\ndiff --git a/config.c b/config.c\nindex 1a1b6675fd..4952d1cc9e 100644\n--- a/config.c\n+++ b/config.c\n@@ -1863,7 +1863,8 @@ static struct config_set_element *configset_find_element(struct config_set *cs,\n \n \thashmap_entry_init(&k.ent, strhash(normalized_key));\n \tk.key = normalized_key;\n-\tfound_entry = hashmap_get(&cs->config_hash, &k.ent, NULL);\n+\tfound_entry = hashmap_get_entry(&cs->config_hash, &k, NULL,\n+\t\t\t\tstruct config_set_element, ent);\n \tfree(normalized_key);\n \treturn found_entry;\n }\ndiff --git a/hashmap.c b/hashmap.c\nindex 22bc7c5b3b..5662bee10a 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -186,8 +186,9 @@ void hashmap_free(struct hashmap *map, int free_entries)\n \tmemset(map, 0, sizeof(*map));\n }\n \n-void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n-\t\tconst void *keydata)\n+struct hashmap_entry *hashmap_get(const struct hashmap *map,\n+\t\t\t\tconst struct hashmap_entry *key,\n+\t\t\t\tconst void *keydata)\n {\n \treturn *find_entry_ptr(map, key, keydata);\n }\n@@ -298,7 +299,7 @@ const void *memintern(const void *data, size_t len)\n \t/* lookup interned string in pool */\n \thashmap_entry_init(&key.ent, memhash(data, len));\n \tkey.len = len;\n-\te = hashmap_get(&map, &key.ent, data);\n+\te = hashmap_get_entry(&map, &key, data, struct pool_entry, ent);\n \tif (!e) {\n \t\t/* not found: create it */\n \t\tFLEX_ALLOC_MEM(e, data, data, len);\ndiff --git a/hashmap.h b/hashmap.h\nindex cd42dcc15c..82ddb0ef41 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -290,8 +290,9 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)\n  * If an entry with matching hash code is found, `key` and `keydata` are passed\n  * to `hashmap_cmp_fn` to decide whether the entry matches the key.\n  */\n-void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n-\t\t\t const void *keydata);\n+struct hashmap_entry *hashmap_get(const struct hashmap *map,\n+\t\t\t\tconst struct hashmap_entry *key,\n+\t\t\t\tconst void *keydata);\n \n /*\n  * Returns the hashmap entry for the specified hash code and key data,\n@@ -305,9 +306,10 @@ void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n  * `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry\n  * structure that should not be used in the comparison.\n  */\n-static inline void *hashmap_get_from_hash(const struct hashmap *map,\n-\t\t\t\t\t  unsigned int hash,\n-\t\t\t\t\t  const void *keydata)\n+static inline struct hashmap_entry *hashmap_get_from_hash(\n+\t\t\t\t\tconst struct hashmap *map,\n+\t\t\t\t\tunsigned int hash,\n+\t\t\t\t\tconst void *keydata)\n {\n \tstruct hashmap_entry key;\n \thashmap_entry_init(&key, hash);\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex a685b4fb69..8274828c4d 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -63,7 +63,8 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,\n \t\treturn NULL;\n \thashmap_entry_init(&key.ent, strhash(dir));\n \tkey.dir = dir;\n-\treturn hashmap_get(hashmap, &key.ent, NULL);\n+\treturn hashmap_get_entry(hashmap, &key, NULL,\n+\t\t\t\tstruct dir_rename_entry, ent);\n }\n \n static int dir_rename_cmp(const void *unused_cmp_data,\n@@ -99,7 +100,8 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,\n \n \thashmap_entry_init(&key.ent, strhash(target_file));\n \tkey.target_file = target_file;\n-\treturn hashmap_get(hashmap, &key.ent, NULL);\n+\treturn hashmap_get_entry(hashmap, &key, NULL,\n+\t\t\t\tstruct collision_entry, ent);\n }\n \n static int collision_cmp(void *unused_cmp_data,\ndiff --git a/name-hash.c b/name-hash.c\nindex 73b83adf3d..aa8253ddd5 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -35,7 +35,8 @@ static struct dir_entry *find_dir_entry__hash(struct index_state *istate,\n \tstruct dir_entry key;\n \thashmap_entry_init(&key.ent, hash);\n \tkey.namelen = namelen;\n-\treturn hashmap_get(&istate->dir_hash, &key.ent, name);\n+\treturn hashmap_get_entry(&istate->dir_hash, &key, name,\n+\t\t\t\t\tstruct dir_entry, ent);\n }\n \n static struct dir_entry *find_dir_entry(struct index_state *istate,\ndiff --git a/packfile.c b/packfile.c\nindex 3edd648de0..f2aa34bb49 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1381,7 +1381,7 @@ static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)\n static struct delta_base_cache_entry *\n get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)\n {\n-\tstruct hashmap_entry entry;\n+\tstruct hashmap_entry entry, *e;\n \tstruct delta_base_cache_key key;\n \n \tif (!delta_base_cache.cmpfn)\n@@ -1390,7 +1390,8 @@ get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)\n \thashmap_entry_init(&entry, pack_entry_hash(p, base_offset));\n \tkey.p = p;\n \tkey.base_offset = base_offset;\n-\treturn hashmap_get(&delta_base_cache, &entry, &key);\n+\te = hashmap_get(&delta_base_cache, &entry, &key);\n+\treturn e ? container_of(e, struct delta_base_cache_entry, ent) : NULL;\n }\n \n static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,\ndiff --git a/patch-ids.c b/patch-ids.c\nindex 437f29e42c..176c47d967 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -99,7 +99,8 @@ struct patch_id *has_commit_patch_id(struct commit *commit,\n \tif (init_patch_id_entry(&patch, commit, ids))\n \t\treturn NULL;\n \n-\treturn hashmap_get(&ids->patches, &patch.ent, NULL);\n+\treturn hashmap_get_entry(&ids->patches, &patch, NULL,\n+\t\t\t\t\tstruct patch_id, ent);\n }\n \n struct patch_id *add_commit_patch_id(struct commit *commit,\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d939ebc6bb..9999426914 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1585,18 +1585,20 @@ static void lazy_init_worktree_map(void)\n \n static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)\n {\n-\tstruct hashmap_entry entry;\n+\tstruct hashmap_entry entry, *e;\n \tstruct ref_to_worktree_entry *lookup_result;\n \n \tlazy_init_worktree_map();\n \n \thashmap_entry_init(&entry, strhash(ref->refname));\n-\tlookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);\n+\te = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);\n \n-\tif (lookup_result)\n-\t\treturn xstrdup(lookup_result->wt->path);\n-\telse\n+\tif (!e)\n \t\treturn xstrdup(\"\");\n+\n+\tlookup_result = container_of(e, struct ref_to_worktree_entry, ent);\n+\n+\treturn xstrdup(lookup_result->wt->path);\n }\n \n /*\ndiff --git a/refs.c b/refs.c\nindex 3e55031256..43a95105f1 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1815,12 +1815,15 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,\n \t\t\t\t\t      const char *name)\n {\n \tstruct ref_store_hash_entry *entry;\n+\tunsigned int hash;\n \n \tif (!map->tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n \n-\tentry = hashmap_get_from_hash(map, strhash(name), name);\n+\thash = strhash(name);\n+\tentry = hashmap_get_entry_from_hash(map, hash, name,\n+\t\t\t\t\tstruct ref_store_hash_entry, ent);\n \treturn entry ? entry->refs : NULL;\n }\n \ndiff --git a/remote.c b/remote.c\nindex 8ca23d95dc..ed95ae6ed6 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -135,7 +135,7 @@ static struct remote *make_remote(const char *name, int len)\n {\n \tstruct remote *ret, *replaced;\n \tstruct remotes_hash_key lookup;\n-\tstruct hashmap_entry lookup_entry;\n+\tstruct hashmap_entry lookup_entry, *e;\n \n \tif (!len)\n \t\tlen = strlen(name);\n@@ -145,8 +145,9 @@ static struct remote *make_remote(const char *name, int len)\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\tif ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL)\n-\t\treturn ret;\n+\te = hashmap_get(&remotes_hash, &lookup_entry, &lookup);\n+\tif (e)\n+\t\treturn container_of(e, struct remote, ent);\n \n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\ndiff --git a/revision.c b/revision.c\nindex a7e2339064..d5f534209d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -147,7 +147,8 @@ static void paths_and_oids_insert(struct hashmap *map,\n \tkey.path = (char *)path;\n \toidset_init(&key.trees, 0);\n \n-\tentry = hashmap_get(map, &key.ent, NULL);\n+\tentry = hashmap_get_entry(map, &key, NULL,\n+\t\t\t\tstruct path_and_oids_entry, ent);\n \tif (!entry) {\n \t\tentry = xcalloc(1, sizeof(struct path_and_oids_entry));\n \t\thashmap_entry_init(&entry->ent, hash);\ndiff --git a/sequencer.c b/sequencer.c\nindex b4ef70e260..aea2cb12cc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5217,8 +5217,11 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)\n \t\t\t\t\tbreak;\n \t\t\t}\n \n-\t\t\tif ((entry = hashmap_get_from_hash(&subject2item,\n-\t\t\t\t\t\t\t   strhash(p), p)))\n+\t\t\tentry = hashmap_get_entry_from_hash(&subject2item,\n+\t\t\t\t\t\tstrhash(p), p,\n+\t\t\t\t\t\tstruct subject2item_entry,\n+\t\t\t\t\t\tentry);\n+\t\t\tif (entry)\n \t\t\t\t/* found by title */\n \t\t\t\ti2 = entry->i;\n \t\t\telse if (!strchr(p, ' ') &&\ndiff --git a/sub-process.c b/sub-process.c\nindex 99fccef592..f2fcc16c3e 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -22,7 +22,8 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch\n \n \thashmap_entry_init(&key.ent, strhash(cmd));\n \tkey.cmd = cmd;\n-\treturn hashmap_get(hashmap, &key.ent, NULL);\n+\treturn hashmap_get_entry(hashmap, &key, NULL,\n+\t\t\t\tstruct subprocess_entry, ent);\n }\n \n int subprocess_read_status(int fd, struct strbuf *status)\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 9248c5ea5b..b031884789 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -166,7 +166,8 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get(&cache->for_path, &key.ent, NULL);\n+\tentry = hashmap_get_entry(&cache->for_path, &key, NULL,\n+\t\t\t\tstruct submodule_entry, ent);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\n@@ -186,7 +187,8 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get(&cache->for_name, &key.ent, NULL);\n+\tentry = hashmap_get_entry(&cache->for_name, &key, NULL,\n+\t\t\t\tstruct submodule_entry, ent);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\n"},{"id":"383544","messageId":"20191006233043.3516-15-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 14/20] hashmap_cmp_fn takes hashmap_entry params","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:37Z","receivedAt":"2019-10-06T23:31:26Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Another step in eliminating the requirement of hashmap_entry\nbeing the first member of a struct.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                  | 10 ++++++----\n builtin/describe.c      | 10 ++++++----\n builtin/difftool.c      | 31 +++++++++++++++++++------------\n builtin/fast-export.c   |  9 +++++++--\n builtin/fetch.c         |  9 +++++----\n config.c                | 10 ++++++----\n diff.c                  | 12 +++++++-----\n hashmap.c               | 17 +++++++++++------\n hashmap.h               | 13 +++++++++----\n merge-recursive.c       | 33 +++++++++++++++++++++------------\n name-hash.c             | 21 +++++++++++++--------\n oidmap.c                | 14 +++++++++-----\n packfile.c              |  9 +++++++--\n patch-ids.c             | 10 ++++++----\n ref-filter.c            | 11 +++++++----\n refs.c                  | 11 ++++++++---\n remote.c                | 10 ++++++----\n revision.c              | 11 ++++++++---\n sequencer.c             | 24 +++++++++++++++++-------\n sub-process.c           | 10 ++++++----\n sub-process.h           |  4 ++--\n submodule-config.c      | 20 ++++++++++++--------\n t/helper/test-hashmap.c | 10 ++++++----\n 23 files changed, 204 insertions(+), 115 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4230bee63d..6053481610 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -70,12 +70,14 @@ struct attr_hash_entry {\n \n /* attr_hashmap comparison function */\n static int attr_hash_entry_cmp(const void *unused_cmp_data,\n-\t\t\t       const void *entry,\n-\t\t\t       const void *entry_or_key,\n+\t\t\t       const struct hashmap_entry *eptr,\n+\t\t\t       const struct hashmap_entry *entry_or_key,\n \t\t\t       const void *unused_keydata)\n {\n-\tconst struct attr_hash_entry *a = entry;\n-\tconst struct attr_hash_entry *b = entry_or_key;\n+\tconst struct attr_hash_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct attr_hash_entry, ent);\n+\tb = container_of(entry_or_key, const struct attr_hash_entry, ent);\n \treturn (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);\n }\n \ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex c6d2386b64..e9267b5c9c 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -64,12 +64,14 @@ static const char *prio_names[] = {\n };\n \n static int commit_name_neq(const void *unused_cmp_data,\n-\t\t\t   const void *entry,\n-\t\t\t   const void *entry_or_key,\n+\t\t\t   const struct hashmap_entry *eptr,\n+\t\t\t   const struct hashmap_entry *entry_or_key,\n \t\t\t   const void *peeled)\n {\n-\tconst struct commit_name *cn1 = entry;\n-\tconst struct commit_name *cn2 = entry_or_key;\n+\tconst struct commit_name *cn1, *cn2;\n+\n+\tcn1 = container_of(eptr, const struct commit_name, entry);\n+\tcn2 = container_of(entry_or_key, const struct commit_name, entry);\n \n \treturn !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);\n }\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex fa9c862e3a..4a37b3edee 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -125,12 +125,15 @@ struct working_tree_entry {\n };\n \n static int working_tree_entry_cmp(const void *unused_cmp_data,\n-\t\t\t\t  const void *entry,\n-\t\t\t\t  const void *entry_or_key,\n+\t\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t\t  const void *unused_keydata)\n {\n-\tconst struct working_tree_entry *a = entry;\n-\tconst struct working_tree_entry *b = entry_or_key;\n+\tconst struct working_tree_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct working_tree_entry, entry);\n+\tb = container_of(entry_or_key, const struct working_tree_entry, entry);\n+\n \treturn strcmp(a->path, b->path);\n }\n \n@@ -145,12 +148,14 @@ struct pair_entry {\n };\n \n static int pair_cmp(const void *unused_cmp_data,\n-\t\t    const void *entry,\n-\t\t    const void *entry_or_key,\n+\t\t    const struct hashmap_entry *eptr,\n+\t\t    const struct hashmap_entry *entry_or_key,\n \t\t    const void *unused_keydata)\n {\n-\tconst struct pair_entry *a = entry;\n-\tconst struct pair_entry *b = entry_or_key;\n+\tconst struct pair_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct pair_entry, entry);\n+\tb = container_of(entry_or_key, const struct pair_entry, entry);\n \n \treturn strcmp(a->path, b->path);\n }\n@@ -179,12 +184,14 @@ struct path_entry {\n };\n \n static int path_entry_cmp(const void *unused_cmp_data,\n-\t\t\t  const void *entry,\n-\t\t\t  const void *entry_or_key,\n+\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t  const void *key)\n {\n-\tconst struct path_entry *a = entry;\n-\tconst struct path_entry *b = entry_or_key;\n+\tconst struct path_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct path_entry, entry);\n+\tb = container_of(entry_or_key, const struct path_entry, entry);\n \n \treturn strcmp(a->path, key ? key : b->path);\n }\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 25195badd4..ef0578bf90 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -126,10 +126,15 @@ struct anonymized_entry {\n };\n \n static int anonymized_entry_cmp(const void *unused_cmp_data,\n-\t\t\t\tconst void *va, const void *vb,\n+\t\t\t\tconst struct hashmap_entry *eptr,\n+\t\t\t\tconst struct hashmap_entry *entry_or_key,\n \t\t\t\tconst void *unused_keydata)\n {\n-\tconst struct anonymized_entry *a = va, *b = vb;\n+\tconst struct anonymized_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct anonymized_entry, hash);\n+\tb = container_of(entry_or_key, const struct anonymized_entry, hash);\n+\n \treturn a->orig_len != b->orig_len ||\n \t\tmemcmp(a->orig, b->orig, a->orig_len);\n }\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex d06f2b98aa..476c2416e3 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -258,13 +258,14 @@ struct refname_hash_entry {\n };\n \n static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,\n-\t\t\t\t  const void *e1_,\n-\t\t\t\t  const void *e2_,\n+\t\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t\t  const void *keydata)\n {\n-\tconst struct refname_hash_entry *e1 = e1_;\n-\tconst struct refname_hash_entry *e2 = e2_;\n+\tconst struct refname_hash_entry *e1, *e2;\n \n+\te1 = container_of(eptr, const struct refname_hash_entry, ent);\n+\te2 = container_of(entry_or_key, const struct refname_hash_entry, ent);\n \treturn strcmp(e1->refname, keydata ? keydata : e2->refname);\n }\n \ndiff --git a/config.c b/config.c\nindex 4952d1cc9e..33043ee73c 100644\n--- a/config.c\n+++ b/config.c\n@@ -1914,12 +1914,14 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha\n }\n \n static int config_set_element_cmp(const void *unused_cmp_data,\n-\t\t\t\t  const void *entry,\n-\t\t\t\t  const void *entry_or_key,\n+\t\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t\t  const void *unused_keydata)\n {\n-\tconst struct config_set_element *e1 = entry;\n-\tconst struct config_set_element *e2 = entry_or_key;\n+\tconst struct config_set_element *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct config_set_element, ent);\n+\te2 = container_of(entry_or_key, const struct config_set_element, ent);\n \n \treturn strcmp(e1->key, e2->key);\n }\ndiff --git a/diff.c b/diff.c\nindex 66cdf4e9ca..5eaf689fcc 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -933,16 +933,18 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,\n }\n \n static int moved_entry_cmp(const void *hashmap_cmp_fn_data,\n-\t\t\t   const void *entry,\n-\t\t\t   const void *entry_or_key,\n+\t\t\t   const struct hashmap_entry *eptr,\n+\t\t\t   const struct hashmap_entry *entry_or_key,\n \t\t\t   const void *keydata)\n {\n \tconst struct diff_options *diffopt = hashmap_cmp_fn_data;\n-\tconst struct moved_entry *a = entry;\n-\tconst struct moved_entry *b = entry_or_key;\n+\tconst struct moved_entry *a, *b;\n \tunsigned flags = diffopt->color_moved_ws_handling\n \t\t\t & XDF_WHITESPACE_FLAGS;\n \n+\ta = container_of(eptr, const struct moved_entry, ent);\n+\tb = container_of(entry_or_key, const struct moved_entry, ent);\n+\n \tif (diffopt->color_moved_ws_handling &\n \t    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)\n \t\t/*\n@@ -1019,7 +1021,7 @@ static void pmb_advance_or_null(struct diff_options *o,\n \t\tstruct moved_entry *prev = pmb[i].match;\n \t\tstruct moved_entry *cur = (prev && prev->next_line) ?\n \t\t\t\tprev->next_line : NULL;\n-\t\tif (cur && !hm->cmpfn(o, cur, match, NULL)) {\n+\t\tif (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {\n \t\t\tpmb[i].match = cur;\n \t\t} else {\n \t\t\tpmb[i].match = NULL;\ndiff --git a/hashmap.c b/hashmap.c\nindex 5662bee10a..64f6accfff 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -140,8 +140,8 @@ static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,\n }\n \n static int always_equal(const void *unused_cmp_data,\n-\t\t\tconst void *unused1,\n-\t\t\tconst void *unused2,\n+\t\t\tconst struct hashmap_entry *unused1,\n+\t\t\tconst struct hashmap_entry *unused2,\n \t\t\tconst void *unused_keydata)\n {\n \treturn 0;\n@@ -279,10 +279,15 @@ struct pool_entry {\n };\n \n static int pool_entry_cmp(const void *unused_cmp_data,\n-\t\t\t  const struct pool_entry *e1,\n-\t\t\t  const struct pool_entry *e2,\n-\t\t\t  const unsigned char *keydata)\n+\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t  const struct hashmap_entry *entry_or_key,\n+\t\t\t  const void *keydata)\n {\n+\tconst struct pool_entry *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct pool_entry, ent);\n+\te2 = container_of(entry_or_key, const struct pool_entry, ent);\n+\n \treturn e1->data != keydata &&\n \t       (e1->len != e2->len || memcmp(e1->data, keydata, e1->len));\n }\n@@ -294,7 +299,7 @@ const void *memintern(const void *data, size_t len)\n \n \t/* initialize string pool hashmap */\n \tif (!map.tablesize)\n-\t\thashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, NULL, 0);\n+\t\thashmap_init(&map, pool_entry_cmp, NULL, 0);\n \n \t/* lookup interned string in pool */\n \thashmap_entry_init(&key.ent, memhash(data, len));\ndiff --git a/hashmap.h b/hashmap.h\nindex 82ddb0ef41..8f5c163d56 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -21,12 +21,16 @@\n  * #define COMPARE_VALUE 1\n  *\n  * static int long2string_cmp(const void *hashmap_cmp_fn_data,\n- *                            const struct long2string *e1,\n- *                            const struct long2string *e2,\n+ *                            const struct hashmap_entry *eptr,\n+ *                            const struct hashmap_entry *entry_or_key,\n  *                            const void *keydata)\n  * {\n  *     const char *string = keydata;\n  *     unsigned flags = *(unsigned *)hashmap_cmp_fn_data;\n+ *     const struct long2string *e1, *e2;\n+ *\n+ *     e1 = container_of(eptr, const struct long2string, ent);\n+ *     e2 = container_of(entry_or_key, const struct long2string, ent);\n  *\n  *     if (flags & COMPARE_VALUE)\n  *         return e1->key != e2->key ||\n@@ -41,7 +45,7 @@\n  *     char value[255], action[32];\n  *     unsigned flags = 0;\n  *\n- *     hashmap_init(&map, (hashmap_cmp_fn) long2string_cmp, &flags, 0);\n+ *     hashmap_init(&map, long2string_cmp, &flags, 0);\n  *\n  *     while (scanf(\"%s %ld %s\", action, &key, value)) {\n  *\n@@ -172,7 +176,8 @@ struct hashmap_entry {\n  * The `hashmap_cmp_fn_data` entry is the pointer given in the init function.\n  */\n typedef int (*hashmap_cmp_fn)(const void *hashmap_cmp_fn_data,\n-\t\t\t      const void *entry, const void *entry_or_key,\n+\t\t\t      const struct hashmap_entry *entry,\n+\t\t\t      const struct hashmap_entry *entry_or_key,\n \t\t\t      const void *keydata);\n \n /*\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8274828c4d..b06e9f7f0b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -35,14 +35,16 @@ struct path_hashmap_entry {\n };\n \n static int path_hashmap_cmp(const void *cmp_data,\n-\t\t\t    const void *entry,\n-\t\t\t    const void *entry_or_key,\n+\t\t\t    const struct hashmap_entry *eptr,\n+\t\t\t    const struct hashmap_entry *entry_or_key,\n \t\t\t    const void *keydata)\n {\n-\tconst struct path_hashmap_entry *a = entry;\n-\tconst struct path_hashmap_entry *b = entry_or_key;\n+\tconst struct path_hashmap_entry *a, *b;\n \tconst char *key = keydata;\n \n+\ta = container_of(eptr, const struct path_hashmap_entry, e);\n+\tb = container_of(entry_or_key, const struct path_hashmap_entry, e);\n+\n \tif (ignore_case)\n \t\treturn strcasecmp(a->path, key ? key : b->path);\n \telse\n@@ -68,12 +70,14 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,\n }\n \n static int dir_rename_cmp(const void *unused_cmp_data,\n-\t\t\t  const void *entry,\n-\t\t\t  const void *entry_or_key,\n+\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t  const void *unused_keydata)\n {\n-\tconst struct dir_rename_entry *e1 = entry;\n-\tconst struct dir_rename_entry *e2 = entry_or_key;\n+\tconst struct dir_rename_entry *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct dir_rename_entry, ent);\n+\te2 = container_of(entry_or_key, const struct dir_rename_entry, ent);\n \n \treturn strcmp(e1->dir, e2->dir);\n }\n@@ -104,17 +108,22 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,\n \t\t\t\tstruct collision_entry, ent);\n }\n \n-static int collision_cmp(void *unused_cmp_data,\n-\t\t\t const struct collision_entry *e1,\n-\t\t\t const struct collision_entry *e2,\n+static int collision_cmp(const void *unused_cmp_data,\n+\t\t\t const struct hashmap_entry *eptr,\n+\t\t\t const struct hashmap_entry *entry_or_key,\n \t\t\t const void *unused_keydata)\n {\n+\tconst struct collision_entry *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct collision_entry, ent);\n+\te2 = container_of(entry_or_key, const struct collision_entry, ent);\n+\n \treturn strcmp(e1->target_file, e2->target_file);\n }\n \n static void collision_init(struct hashmap *map)\n {\n-\thashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);\n+\thashmap_init(map, collision_cmp, NULL, 0);\n }\n \n static void flush_output(struct merge_options *opt)\ndiff --git a/name-hash.c b/name-hash.c\nindex aa8253ddd5..85a1ce982c 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -17,14 +17,16 @@ struct dir_entry {\n };\n \n static int dir_entry_cmp(const void *unused_cmp_data,\n-\t\t\t const void *entry,\n-\t\t\t const void *entry_or_key,\n+\t\t\t const struct hashmap_entry *eptr,\n+\t\t\t const struct hashmap_entry *entry_or_key,\n \t\t\t const void *keydata)\n {\n-\tconst struct dir_entry *e1 = entry;\n-\tconst struct dir_entry *e2 = entry_or_key;\n+\tconst struct dir_entry *e1, *e2;\n \tconst char *name = keydata;\n \n+\te1 = container_of(eptr, const struct dir_entry, ent);\n+\te2 = container_of(entry_or_key, const struct dir_entry, ent);\n+\n \treturn e1->namelen != e2->namelen || strncasecmp(e1->name,\n \t\t\tname ? name : e2->name, e1->namelen);\n }\n@@ -115,12 +117,15 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n }\n \n static int cache_entry_cmp(const void *unused_cmp_data,\n-\t\t\t   const void *entry,\n-\t\t\t   const void *entry_or_key,\n+\t\t\t   const struct hashmap_entry *eptr,\n+\t\t\t   const struct hashmap_entry *entry_or_key,\n \t\t\t   const void *remove)\n {\n-\tconst struct cache_entry *ce1 = entry;\n-\tconst struct cache_entry *ce2 = entry_or_key;\n+\tconst struct cache_entry *ce1, *ce2;\n+\n+\tce1 = container_of(eptr, const struct cache_entry, ent);\n+\tce2 = container_of(entry_or_key, const struct cache_entry, ent);\n+\n \t/*\n \t * For remove_name_hash, find the exact entry (pointer equality); for\n \t * index_file_exists, find all entries with matching hash code and\ndiff --git a/oidmap.c b/oidmap.c\nindex cd22b3a8bf..4942599391 100644\n--- a/oidmap.c\n+++ b/oidmap.c\n@@ -2,14 +2,18 @@\n #include \"oidmap.h\"\n \n static int oidmap_neq(const void *hashmap_cmp_fn_data,\n-\t\t      const void *entry, const void *entry_or_key,\n+\t\t      const struct hashmap_entry *e1,\n+\t\t      const struct hashmap_entry *e2,\n \t\t      const void *keydata)\n {\n-\tconst struct oidmap_entry *entry_ = entry;\n+\tconst struct oidmap_entry *a, *b;\n+\n+\ta = container_of(e1, const struct oidmap_entry, internal_entry);\n+\tb = container_of(e2, const struct oidmap_entry, internal_entry);\n+\n \tif (keydata)\n-\t\treturn !oideq(&entry_->oid, (const struct object_id *) keydata);\n-\treturn !oideq(&entry_->oid,\n-\t\t      &((const struct oidmap_entry *) entry_or_key)->oid);\n+\t\treturn !oideq(&a->oid, (const struct object_id *) keydata);\n+\treturn !oideq(&a->oid, &b->oid);\n }\n \n void oidmap_init(struct oidmap *map, size_t initial_size)\ndiff --git a/packfile.c b/packfile.c\nindex f2aa34bb49..675d5f2287 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -1401,11 +1401,16 @@ static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,\n }\n \n static int delta_base_cache_hash_cmp(const void *unused_cmp_data,\n-\t\t\t\t     const void *va, const void *vb,\n+\t\t\t\t     const struct hashmap_entry *va,\n+\t\t\t\t     const struct hashmap_entry *vb,\n \t\t\t\t     const void *vkey)\n {\n-\tconst struct delta_base_cache_entry *a = va, *b = vb;\n+\tconst struct delta_base_cache_entry *a, *b;\n \tconst struct delta_base_cache_key *key = vkey;\n+\n+\ta = container_of(va, const struct delta_base_cache_entry, ent);\n+\tb = container_of(vb, const struct delta_base_cache_entry, ent);\n+\n \tif (key)\n \t\treturn !delta_base_cache_key_eq(&a->key, key);\n \telse\ndiff --git a/patch-ids.c b/patch-ids.c\nindex 176c47d967..75f8c9f1a1 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -36,14 +36,16 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,\n  * any significance; only that it is non-zero matters.\n  */\n static int patch_id_neq(const void *cmpfn_data,\n-\t\t\tconst void *entry,\n-\t\t\tconst void *entry_or_key,\n+\t\t\tconst struct hashmap_entry *eptr,\n+\t\t\tconst struct hashmap_entry *entry_or_key,\n \t\t\tconst void *unused_keydata)\n {\n \t/* NEEDSWORK: const correctness? */\n \tstruct diff_options *opt = (void *)cmpfn_data;\n-\tstruct patch_id *a = (void *)entry;\n-\tstruct patch_id *b = (void *)entry_or_key;\n+\tstruct patch_id *a, *b;\n+\n+\ta = container_of(eptr, struct patch_id, ent);\n+\tb = container_of(entry_or_key, struct patch_id, ent);\n \n \tif (is_null_oid(&a->patch_id) &&\n \t    commit_patch_id(a->commit, opt, &a->patch_id, 0, 0))\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9999426914..4613df8826 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -84,12 +84,15 @@ struct ref_to_worktree_entry {\n };\n \n static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,\n-\t\t\t\t      const void *existing_hashmap_entry_to_test,\n-\t\t\t\t      const void *key,\n+\t\t\t\t      const struct hashmap_entry *eptr,\n+\t\t\t\t      const struct hashmap_entry *kptr,\n \t\t\t\t      const void *keydata_aka_refname)\n {\n-\tconst struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;\n-\tconst struct ref_to_worktree_entry *k = key;\n+\tconst struct ref_to_worktree_entry *e, *k;\n+\n+\te = container_of(eptr, const struct ref_to_worktree_entry, ent);\n+\tk = container_of(kptr, const struct ref_to_worktree_entry, ent);\n+\n \treturn strcmp(e->wt->head_ref,\n \t\tkeydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);\n }\ndiff --git a/refs.c b/refs.c\nindex 43a95105f1..2d3eb40f39 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1781,11 +1781,16 @@ struct ref_store_hash_entry\n };\n \n static int ref_store_hash_cmp(const void *unused_cmp_data,\n-\t\t\t      const void *entry, const void *entry_or_key,\n+\t\t\t      const struct hashmap_entry *eptr,\n+\t\t\t      const struct hashmap_entry *entry_or_key,\n \t\t\t      const void *keydata)\n {\n-\tconst struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;\n-\tconst char *name = keydata ? keydata : e2->name;\n+\tconst struct ref_store_hash_entry *e1, *e2;\n+\tconst char *name;\n+\n+\te1 = container_of(eptr, const struct ref_store_hash_entry, ent);\n+\te2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);\n+\tname = keydata ? keydata : e2->name;\n \n \treturn strcmp(e1->name, name);\n }\ndiff --git a/remote.c b/remote.c\nindex ed95ae6ed6..fa9cadcfbd 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -111,14 +111,16 @@ struct remotes_hash_key {\n };\n \n static int remotes_hash_cmp(const void *unused_cmp_data,\n-\t\t\t    const void *entry,\n-\t\t\t    const void *entry_or_key,\n+\t\t\t    const struct hashmap_entry *eptr,\n+\t\t\t    const struct hashmap_entry *entry_or_key,\n \t\t\t    const void *keydata)\n {\n-\tconst struct remote *a = entry;\n-\tconst struct remote *b = entry_or_key;\n+\tconst struct remote *a, *b;\n \tconst struct remotes_hash_key *key = keydata;\n \n+\ta = container_of(eptr, const struct remote, ent);\n+\tb = container_of(entry_or_key, const struct remote, ent);\n+\n \tif (key)\n \t\treturn strncmp(a->name, key->str, key->len) || a->name[key->len];\n \telse\ndiff --git a/revision.c b/revision.c\nindex d5f534209d..f32fbc5e2e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -107,16 +107,21 @@ struct path_and_oids_entry {\n };\n \n static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,\n-\t\t\t     const struct path_and_oids_entry *e1,\n-\t\t\t     const struct path_and_oids_entry *e2,\n+\t\t\t     const struct hashmap_entry *eptr,\n+\t\t\t     const struct hashmap_entry *entry_or_key,\n \t\t\t     const void *keydata)\n {\n+\tconst struct path_and_oids_entry *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct path_and_oids_entry, ent);\n+\te2 = container_of(entry_or_key, const struct path_and_oids_entry, ent);\n+\n \treturn strcmp(e1->path, e2->path);\n }\n \n static void paths_and_oids_init(struct hashmap *map)\n {\n-\thashmap_init(map, (hashmap_cmp_fn) path_and_oids_cmp, NULL, 0);\n+\thashmap_init(map, path_and_oids_cmp, NULL, 0);\n }\n \n static void paths_and_oids_clear(struct hashmap *map)\ndiff --git a/sequencer.c b/sequencer.c\nindex aea2cb12cc..b3e7319b55 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4440,9 +4440,14 @@ struct labels_entry {\n \tchar label[FLEX_ARRAY];\n };\n \n-static int labels_cmp(const void *fndata, const struct labels_entry *a,\n-\t\t      const struct labels_entry *b, const void *key)\n+static int labels_cmp(const void *fndata, const struct hashmap_entry *eptr,\n+\t\t      const struct hashmap_entry *entry_or_key, const void *key)\n {\n+\tconst struct labels_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct labels_entry, entry);\n+\tb = container_of(entry_or_key, const struct labels_entry, entry);\n+\n \treturn key ? strcmp(a->label, key) : strcmp(a->label, b->label);\n }\n \n@@ -4573,7 +4578,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \n \toidmap_init(&commit2todo, 0);\n \toidmap_init(&state.commit2label, 0);\n-\thashmap_init(&state.labels, (hashmap_cmp_fn) labels_cmp, NULL, 0);\n+\thashmap_init(&state.labels, labels_cmp, NULL, 0);\n \tstrbuf_init(&state.buf, 32);\n \n \tif (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {\n@@ -5138,9 +5143,15 @@ struct subject2item_entry {\n };\n \n static int subject2item_cmp(const void *fndata,\n-\t\t\t    const struct subject2item_entry *a,\n-\t\t\t    const struct subject2item_entry *b, const void *key)\n+\t\t\t    const struct hashmap_entry *eptr,\n+\t\t\t    const struct hashmap_entry *entry_or_key,\n+\t\t\t    const void *key)\n {\n+\tconst struct subject2item_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct subject2item_entry, entry);\n+\tb = container_of(entry_or_key, const struct subject2item_entry, entry);\n+\n \treturn key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject);\n }\n \n@@ -5173,8 +5184,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)\n \t * In that case, last[i] will indicate the index of the latest item to\n \t * be moved to appear after the i'th.\n \t */\n-\thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list->nr);\n+\thashmap_init(&subject2item, subject2item_cmp, NULL, todo_list->nr);\n \tALLOC_ARRAY(next, todo_list->nr);\n \tALLOC_ARRAY(tail, todo_list->nr);\n \tALLOC_ARRAY(subjects, todo_list->nr);\ndiff --git a/sub-process.c b/sub-process.c\nindex f2fcc16c3e..ad94f72665 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -6,12 +6,14 @@\n #include \"pkt-line.h\"\n \n int cmd2process_cmp(const void *unused_cmp_data,\n-\t\t    const void *entry,\n-\t\t    const void *entry_or_key,\n+\t\t    const struct hashmap_entry *eptr,\n+\t\t    const struct hashmap_entry *entry_or_key,\n \t\t    const void *unused_keydata)\n {\n-\tconst struct subprocess_entry *e1 = entry;\n-\tconst struct subprocess_entry *e2 = entry_or_key;\n+\tconst struct subprocess_entry *e1, *e2;\n+\n+\te1 = container_of(eptr, const struct subprocess_entry, ent);\n+\te2 = container_of(entry_or_key, const struct subprocess_entry, ent);\n \n \treturn strcmp(e1->cmd, e2->cmd);\n }\ndiff --git a/sub-process.h b/sub-process.h\nindex 5c182fad98..0d12708b8c 100644\n--- a/sub-process.h\n+++ b/sub-process.h\n@@ -43,8 +43,8 @@ struct subprocess_capability {\n \n /* Function to test two subprocess hashmap entries for equality. */\n int cmd2process_cmp(const void *unused_cmp_data,\n-\t\t    const void *e1,\n-\t\t    const void *e2,\n+\t\t    const struct hashmap_entry *e,\n+\t\t    const struct hashmap_entry *entry_or_key,\n \t\t    const void *unused_keydata);\n \n /*\ndiff --git a/submodule-config.c b/submodule-config.c\nindex b031884789..5463729ab8 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -38,24 +38,28 @@ enum lookup_type {\n };\n \n static int config_path_cmp(const void *unused_cmp_data,\n-\t\t\t   const void *entry,\n-\t\t\t   const void *entry_or_key,\n+\t\t\t   const struct hashmap_entry *eptr,\n+\t\t\t   const struct hashmap_entry *entry_or_key,\n \t\t\t   const void *unused_keydata)\n {\n-\tconst struct submodule_entry *a = entry;\n-\tconst struct submodule_entry *b = entry_or_key;\n+\tconst struct submodule_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct submodule_entry, ent);\n+\tb = container_of(entry_or_key, const struct submodule_entry, ent);\n \n \treturn strcmp(a->config->path, b->config->path) ||\n \t       !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);\n }\n \n static int config_name_cmp(const void *unused_cmp_data,\n-\t\t\t   const void *entry,\n-\t\t\t   const void *entry_or_key,\n+\t\t\t   const struct hashmap_entry *eptr,\n+\t\t\t   const struct hashmap_entry *entry_or_key,\n \t\t\t   const void *unused_keydata)\n {\n-\tconst struct submodule_entry *a = entry;\n-\tconst struct submodule_entry *b = entry_or_key;\n+\tconst struct submodule_entry *a, *b;\n+\n+\ta = container_of(eptr, const struct submodule_entry, ent);\n+\tb = container_of(entry_or_key, const struct submodule_entry, ent);\n \n \treturn strcmp(a->config->name, b->config->name) ||\n \t       !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex e82cbfdee2..56846da64c 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -16,15 +16,17 @@ static const char *get_value(const struct test_entry *e)\n }\n \n static int test_entry_cmp(const void *cmp_data,\n-\t\t\t  const void *entry,\n-\t\t\t  const void *entry_or_key,\n+\t\t\t  const struct hashmap_entry *eptr,\n+\t\t\t  const struct hashmap_entry *entry_or_key,\n \t\t\t  const void *keydata)\n {\n \tconst int ignore_case = cmp_data ? *((int *)cmp_data) : 0;\n-\tconst struct test_entry *e1 = entry;\n-\tconst struct test_entry *e2 = entry_or_key;\n+\tconst struct test_entry *e1, *e2;\n \tconst char *key = keydata;\n \n+\te1 = container_of(eptr, const struct test_entry, ent);\n+\te2 = container_of(entry_or_key, const struct test_entry, ent);\n+\n \tif (ignore_case)\n \t\treturn strcasecmp(e1->key, key ? key : e2->key);\n \telse\n"},{"id":"383545","messageId":"20191006233043.3516-16-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 15/20] hashmap: use *_entry APIs for iteration","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:38Z","receivedAt":"2019-10-06T23:31:29Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Inspired by list_for_each_entry in the Linux kernel.\nOnce again, these are somewhat compromised usability-wise\nby compilers lacking __typeof__ support.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                              |  5 +++--\n blame.c                             | 10 ++++++----\n builtin/describe.c                  |  4 ++--\n builtin/difftool.c                  |  8 ++++----\n config.c                            |  5 +++--\n hashmap.c                           |  2 +-\n hashmap.h                           | 15 +++++++++++++--\n merge-recursive.c                   | 25 +++++++++++++++----------\n oidmap.h                            |  6 ++++--\n revision.c                          | 10 ++++++----\n submodule-config.c                  |  8 +++++---\n t/helper/test-hashmap.c             |  7 ++++---\n t/helper/test-lazy-init-name-hash.c | 12 ++++--------\n 13 files changed, 70 insertions(+), 47 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 6053481610..ca8be46e8e 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -163,12 +163,13 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)\n \tif (size != check->all_attrs_nr) {\n \t\tstruct attr_hash_entry *e;\n \t\tstruct hashmap_iter iter;\n-\t\thashmap_iter_init(&map->map, &iter);\n \n \t\tREALLOC_ARRAY(check->all_attrs, size);\n \t\tcheck->all_attrs_nr = size;\n \n-\t\twhile ((e = hashmap_iter_next(&iter))) {\n+\t\thashmap_for_each_entry(&map->map, &iter, e,\n+\t\t\t\t\tstruct attr_hash_entry,\n+\t\t\t\t\tent /* member name */) {\n \t\t\tconst struct git_attr *a = e->value;\n \t\t\tcheck->all_attrs[a->attr_nr].attr = a;\n \t\t}\ndiff --git a/blame.c b/blame.c\nindex aa46c7ec52..3d8accf902 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -450,9 +450,9 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)\n \tstruct hashmap_iter iter;\n \tconst struct fingerprint_entry *entry_a, *entry_b;\n \n-\thashmap_iter_init(&b->map, &iter);\n-\n-\twhile ((entry_b = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&b->map, &iter, entry_b,\n+\t\t\t\tconst struct fingerprint_entry,\n+\t\t\t\tentry /* member name */) {\n \t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n \t\t\t\t\tstruct fingerprint_entry, entry);\n \t\tif (entry_a) {\n@@ -473,7 +473,9 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \n \thashmap_iter_init(&b->map, &iter);\n \n-\twhile ((entry_b = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&b->map, &iter, entry_b,\n+\t\t\t\tconst struct fingerprint_entry,\n+\t\t\t\tentry /* member name */) {\n \t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n \t\t\t\t\tstruct fingerprint_entry, entry);\n \t\tif (entry_a) {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex e9267b5c9c..8cf2cd992d 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -333,8 +333,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)\n \t\tstruct commit_name *n;\n \n \t\tinit_commit_names(&commit_names);\n-\t\tn = hashmap_iter_first(&names, &iter);\n-\t\tfor (; n; n = hashmap_iter_next(&iter)) {\n+\t\thashmap_for_each_entry(&names, &iter, n, struct commit_name,\n+\t\t\t\t\tentry /* member name */) {\n \t\t\tc = lookup_commit_reference_gently(the_repository,\n \t\t\t\t\t\t\t   &n->peeled, 1);\n \t\t\tif (c)\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 4a37b3edee..dd94179b68 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -538,8 +538,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t * temporary file to both the left and right directories to show the\n \t * change in the recorded SHA1 for the submodule.\n \t */\n-\thashmap_iter_init(&submodules, &iter);\n-\twhile ((entry = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&submodules, &iter, entry,\n+\t\t\t\tstruct pair_entry, entry /* member name */) {\n \t\tif (*entry->left) {\n \t\t\tadd_path(&ldir, ldir_len, entry->path);\n \t\t\tensure_leading_directories(ldir.buf);\n@@ -557,8 +557,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t * shows only the link itself, not the contents of the link target.\n \t * This loop replicates that behavior.\n \t */\n-\thashmap_iter_init(&symlinks2, &iter);\n-\twhile ((entry = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&symlinks2, &iter, entry,\n+\t\t\t\tstruct pair_entry, entry /* member name */) {\n \t\tif (*entry->left) {\n \t\t\tadd_path(&ldir, ldir_len, entry->path);\n \t\t\tensure_leading_directories(ldir.buf);\ndiff --git a/config.c b/config.c\nindex 33043ee73c..8433f74371 100644\n--- a/config.c\n+++ b/config.c\n@@ -1942,8 +1942,9 @@ void git_configset_clear(struct config_set *cs)\n \tif (!cs->hash_initialized)\n \t\treturn;\n \n-\thashmap_iter_init(&cs->config_hash, &iter);\n-\twhile ((entry = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&cs->config_hash, &iter, entry,\n+\t\t\t\tstruct config_set_element,\n+\t\t\t\tent /* member name */) {\n \t\tfree(entry->key);\n \t\tstring_list_clear(&entry->value_list, 1);\n \t}\ndiff --git a/hashmap.c b/hashmap.c\nindex 64f6accfff..deb5fdf28c 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -256,7 +256,7 @@ void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)\n \titer->next = NULL;\n }\n \n-void *hashmap_iter_next(struct hashmap_iter *iter)\n+struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter)\n {\n \tstruct hashmap_entry *current = iter->next;\n \tfor (;;) {\ndiff --git a/hashmap.h b/hashmap.h\nindex 8f5c163d56..8d4b3907b4 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -382,16 +382,27 @@ struct hashmap_iter {\n void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);\n \n /* Returns the next hashmap_entry, or NULL if there are no more entries. */\n-void *hashmap_iter_next(struct hashmap_iter *iter);\n+struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter);\n \n /* Initializes the iterator and returns the first entry, if any. */\n-static inline void *hashmap_iter_first(struct hashmap *map,\n+static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,\n \t\tstruct hashmap_iter *iter)\n {\n \thashmap_iter_init(map, iter);\n \treturn hashmap_iter_next(iter);\n }\n \n+#define hashmap_iter_next_entry(iter, type, member) \\\n+\tcontainer_of_or_null(hashmap_iter_next(iter), type, member)\n+\n+#define hashmap_iter_first_entry(map, iter, type, member) \\\n+\tcontainer_of_or_null(hashmap_iter_first(map, iter), type, member)\n+\n+#define hashmap_for_each_entry(map, iter, var, type, member) \\\n+\tfor (var = hashmap_iter_first_entry(map, iter, type, member); \\\n+\t\tvar; \\\n+\t\tvar = hashmap_iter_next_entry(iter, type, member))\n+\n /*\n  * returns a @pointer of @type matching @keyvar, or NULL if nothing found.\n  * @keyvar is a pointer of @type\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b06e9f7f0b..73c7750448 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2135,8 +2135,9 @@ static void handle_directory_level_conflicts(struct merge_options *opt,\n \tstruct string_list remove_from_head = STRING_LIST_INIT_NODUP;\n \tstruct string_list remove_from_merge = STRING_LIST_INIT_NODUP;\n \n-\thashmap_iter_init(dir_re_head, &iter);\n-\twhile ((head_ent = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(dir_re_head, &iter, head_ent,\n+\t\t\t\tstruct dir_rename_entry,\n+\t\t\t\tent /* member name */) {\n \t\tmerge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);\n \t\tif (merge_ent &&\n \t\t    !head_ent->non_unique_new_dir &&\n@@ -2160,8 +2161,9 @@ static void handle_directory_level_conflicts(struct merge_options *opt,\n \tremove_hashmap_entries(dir_re_head, &remove_from_head);\n \tremove_hashmap_entries(dir_re_merge, &remove_from_merge);\n \n-\thashmap_iter_init(dir_re_merge, &iter);\n-\twhile ((merge_ent = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(dir_re_merge, &iter, merge_ent,\n+\t\t\t\tstruct dir_rename_entry,\n+\t\t\t\tent /* member name */) {\n \t\thead_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);\n \t\tif (tree_has_path(opt->repo, merge, merge_ent->dir)) {\n \t\t\t/* 2. This wasn't a directory rename after all */\n@@ -2265,8 +2267,9 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)\n \t * we set non_unique_new_dir.  Once we've determined the winner (or\n \t * that there is no winner), we no longer need possible_new_dirs.\n \t */\n-\thashmap_iter_init(dir_renames, &iter);\n-\twhile ((entry = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(dir_renames, &iter, entry,\n+\t\t\t\tstruct dir_rename_entry,\n+\t\t\t\tent /* member name */) {\n \t\tint max = 0;\n \t\tint bad_max = 0;\n \t\tchar *best = NULL;\n@@ -2624,8 +2627,9 @@ static struct string_list *get_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t     entries);\n \t}\n \n-\thashmap_iter_init(&collisions, &iter);\n-\twhile ((e = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(&collisions, &iter, e,\n+\t\t\t\tstruct collision_entry,\n+\t\t\t\tent /* member name */) {\n \t\tfree(e->target_file);\n \t\tstring_list_clear(&e->source_files, 0);\n \t}\n@@ -2842,8 +2846,9 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,\n \tstruct hashmap_iter iter;\n \tstruct dir_rename_entry *e;\n \n-\thashmap_iter_init(dir_renames, &iter);\n-\twhile ((e = hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(dir_renames, &iter, e,\n+\t\t\t\tstruct dir_rename_entry,\n+\t\t\t\tent /* member name */) {\n \t\tfree(e->dir);\n \t\tstrbuf_release(&e->new_dir);\n \t\t/* possible_new_dirs already cleared in get_directory_renames */\ndiff --git a/oidmap.h b/oidmap.h\nindex 7a939461ff..c66a83ab1d 100644\n--- a/oidmap.h\n+++ b/oidmap.h\n@@ -78,14 +78,16 @@ static inline void oidmap_iter_init(struct oidmap *map, struct oidmap_iter *iter\n \n static inline void *oidmap_iter_next(struct oidmap_iter *iter)\n {\n-\treturn hashmap_iter_next(&iter->h_iter);\n+\t/* TODO: this API could be reworked to do compile-time type checks */\n+\treturn (void *)hashmap_iter_next(&iter->h_iter);\n }\n \n static inline void *oidmap_iter_first(struct oidmap *map,\n \t\t\t\t      struct oidmap_iter *iter)\n {\n \toidmap_iter_init(map, iter);\n-\treturn oidmap_iter_next(iter);\n+\t/* TODO: this API could be reworked to do compile-time type checks */\n+\treturn (void *)oidmap_iter_next(iter);\n }\n \n #endif\ndiff --git a/revision.c b/revision.c\nindex f32fbc5e2e..f28cbe5de8 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -128,9 +128,10 @@ static void paths_and_oids_clear(struct hashmap *map)\n {\n \tstruct hashmap_iter iter;\n \tstruct path_and_oids_entry *entry;\n-\thashmap_iter_init(map, &iter);\n \n-\twhile ((entry = (struct path_and_oids_entry *)hashmap_iter_next(&iter))) {\n+\thashmap_for_each_entry(map, &iter, entry,\n+\t\t\t\tstruct path_and_oids_entry,\n+\t\t\t\tent /* member name */) {\n \t\toidset_clear(&entry->trees);\n \t\tfree(entry->path);\n \t}\n@@ -242,8 +243,9 @@ void mark_trees_uninteresting_sparse(struct repository *r,\n \t\tadd_children_by_path(r, tree, &map);\n \t}\n \n-\thashmap_iter_init(&map, &map_iter);\n-\twhile ((entry = hashmap_iter_next(&map_iter)))\n+\thashmap_for_each_entry(&map, &map_iter, entry,\n+\t\t\t\tstruct path_and_oids_entry,\n+\t\t\t\tent /* member name */)\n \t\tmark_trees_uninteresting_sparse(r, &entry->trees);\n \n \tpaths_and_oids_clear(&map);\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 5463729ab8..5319933e1d 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -99,8 +99,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)\n \t * allocation of struct submodule entries. Each is allocated by\n \t * their .gitmodules blob sha1 and submodule name.\n \t */\n-\thashmap_iter_init(&cache->for_name, &iter);\n-\twhile ((entry = hashmap_iter_next(&iter)))\n+\thashmap_for_each_entry(&cache->for_name, &iter, entry,\n+\t\t\t\tstruct submodule_entry, ent /* member name */)\n \t\tfree_one_config(entry);\n \n \thashmap_free(&cache->for_path, 1);\n@@ -556,7 +556,9 @@ static const struct submodule *config_from(struct submodule_cache *cache,\n \t\tstruct hashmap_iter iter;\n \t\tstruct submodule_entry *entry;\n \n-\t\tentry = hashmap_iter_first(&cache->for_name, &iter);\n+\t\tentry = hashmap_iter_first_entry(&cache->for_name, &iter,\n+\t\t\t\t\t\tstruct submodule_entry,\n+\t\t\t\t\t\tent /* member name */);\n \t\tif (!entry)\n \t\t\treturn NULL;\n \t\treturn entry->config;\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 56846da64c..4ec5e11556 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -222,10 +222,11 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tfree(entry);\n \n \t\t} else if (!strcmp(\"iterate\", cmd)) {\n-\n \t\t\tstruct hashmap_iter iter;\n-\t\t\thashmap_iter_init(&map, &iter);\n-\t\t\twhile ((entry = hashmap_iter_next(&iter)))\n+\n+\t\t\thashmap_for_each_entry(&map, &iter, entry,\n+\t\t\t\t\t\tstruct test_entry,\n+\t\t\t\t\t\tent /* member name */)\n \t\t\t\tprintf(\"%s %s\\n\", entry->key, get_value(entry));\n \n \t\t} else if (!strcmp(\"size\", cmd)) {\ndiff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c\nindex b99a37080d..9d4664d6a4 100644\n--- a/t/helper/test-lazy-init-name-hash.c\n+++ b/t/helper/test-lazy-init-name-hash.c\n@@ -41,17 +41,13 @@ static void dump_run(void)\n \t\t\tdie(\"non-threaded code path used\");\n \t}\n \n-\tdir = hashmap_iter_first(&the_index.dir_hash, &iter_dir);\n-\twhile (dir) {\n+\thashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,\n+\t\t\t\tstruct dir_entry, ent /* member name */)\n \t\tprintf(\"dir %08x %7d %s\\n\", dir->ent.hash, dir->nr, dir->name);\n-\t\tdir = hashmap_iter_next(&iter_dir);\n-\t}\n \n-\tce = hashmap_iter_first(&the_index.name_hash, &iter_cache);\n-\twhile (ce) {\n+\thashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,\n+\t\t\t\tstruct cache_entry, ent /* member name */)\n \t\tprintf(\"name %08x %s\\n\", ce->ent.hash, ce->name);\n-\t\tce = hashmap_iter_next(&iter_cache);\n-\t}\n \n \tdiscard_cache();\n }\n"},{"id":"383546","messageId":"20191006233043.3516-17-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 16/20] hashmap: hashmap_{put,remove} return hashmap_entry *","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:39Z","receivedAt":"2019-10-06T23:31:32Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"And add *_entry variants to perform container_of as necessary\nto simplify most callers.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n hashmap.c               |  8 +++++---\n hashmap.h               | 15 ++++++++++++---\n range-diff.c            |  4 +++-\n remote.c                |  3 ++-\n submodule-config.c      |  4 +++-\n t/helper/test-hashmap.c |  9 +++++++--\n 6 files changed, 32 insertions(+), 11 deletions(-)\n\ndiff --git a/hashmap.c b/hashmap.c\nindex deb5fdf28c..1b60f97cf2 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -219,8 +219,9 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)\n \t}\n }\n \n-void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n-\t\tconst void *keydata)\n+struct hashmap_entry *hashmap_remove(struct hashmap *map,\n+\t\t\t\t\tconst struct hashmap_entry *key,\n+\t\t\t\t\tconst void *keydata)\n {\n \tstruct hashmap_entry *old;\n \tstruct hashmap_entry **e = find_entry_ptr(map, key, keydata);\n@@ -242,7 +243,8 @@ void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n \treturn old;\n }\n \n-void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n+struct hashmap_entry *hashmap_put(struct hashmap *map,\n+\t\t\t\tstruct hashmap_entry *entry)\n {\n \tstruct hashmap_entry *old = hashmap_remove(map, entry, NULL);\n \thashmap_add(map, entry);\ndiff --git a/hashmap.h b/hashmap.h\nindex 8d4b3907b4..bc3b10e097 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -349,7 +349,11 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);\n  * `entry` is the entry to add or replace.\n  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).\n  */\n-void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n+struct hashmap_entry *hashmap_put(struct hashmap *map,\n+\t\t\t\tstruct hashmap_entry *entry);\n+\n+#define hashmap_put_entry(map, keyvar, type, member) \\\n+\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n \n /*\n  * Removes a hashmap entry matching the specified key. If the hashmap contains\n@@ -358,8 +362,13 @@ void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n  *\n  * Argument explanation is the same as in `hashmap_get`.\n  */\n-void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n-\t\tconst void *keydata);\n+struct hashmap_entry *hashmap_remove(struct hashmap *map,\n+\t\t\t\t\tconst struct hashmap_entry *key,\n+\t\t\t\t\tconst void *keydata);\n+\n+#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n+\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n+\t\t\t\ttype, member)\n \n /*\n  * Returns the `bucket` an entry is stored in.\ndiff --git a/range-diff.c b/range-diff.c\nindex c51cfd5556..e5e7820bfe 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -229,7 +229,9 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->patch = b->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n \t\thashmap_entry_init(&util->e, strhash(util->diff));\n-\t\tother = hashmap_remove(&map, &util->e, NULL);\n+\t\tother = hashmap_remove_entry(&map, util, NULL,\n+\t\t\t\t\tstruct patch_util,\n+\t\t\t\t\te /* member name */);\n \t\tif (other) {\n \t\t\tif (other->matching >= 0)\n \t\t\t\tBUG(\"already assigned!\");\ndiff --git a/remote.c b/remote.c\nindex fa9cadcfbd..5fcddcd88d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -162,7 +162,8 @@ static struct remote *make_remote(const char *name, int len)\n \tremotes[remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\treplaced = hashmap_put(&remotes_hash, &ret->ent);\n+\treplaced = hashmap_put_entry(&remotes_hash, ret, struct remote,\n+\t\t\t\t\tent /* member name */);\n \tassert(replaced == NULL);  /* no previous entry overwritten */\n \treturn ret;\n }\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 5319933e1d..a289d195f6 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -141,7 +141,9 @@ static void cache_remove_path(struct submodule_cache *cache,\n \tstruct submodule_entry *removed;\n \thashmap_entry_init(&e.ent, hash);\n \te.config = submodule;\n-\tremoved = hashmap_remove(&cache->for_path, &e.ent, NULL);\n+\tremoved = hashmap_remove_entry(&cache->for_path, &e, NULL,\n+\t\t\t\t\tstruct submodule_entry,\n+\t\t\t\t\tent /* member name */);\n \tfree(removed);\n }\n \ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 4ec5e11556..07a93a2aec 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -189,7 +189,9 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tentry = alloc_test_entry(hash, p1, p2);\n \n \t\t\t/* add / replace entry */\n-\t\t\tentry = hashmap_put(&map, &entry->ent);\n+\t\t\tentry = hashmap_put_entry(&map, entry,\n+\t\t\t\t\t\tstruct test_entry,\n+\t\t\t\t\t\tent /* member name */);\n \n \t\t\t/* print and free replaced entry, if any */\n \t\t\tputs(entry ? get_value(entry) : \"NULL\");\n@@ -212,10 +214,13 @@ int cmd__hashmap(int argc, const char **argv)\n \n \t\t\t/* setup static key */\n \t\t\tstruct hashmap_entry key;\n+\t\t\tstruct hashmap_entry *rm;\n \t\t\thashmap_entry_init(&key, hash);\n \n \t\t\t/* remove entry from hashmap */\n-\t\t\tentry = hashmap_remove(&map, &key, p1);\n+\t\t\trm = hashmap_remove(&map, &key, p1);\n+\t\t\tentry = rm ? container_of(rm, struct test_entry, ent)\n+\t\t\t\t\t: NULL;\n \n \t\t\t/* print result and free entry*/\n \t\t\tputs(entry ? get_value(entry) : \"NULL\");\n"},{"id":"383547","messageId":"20191006233043.3516-18-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 17/20] hashmap: introduce hashmap_free_entries","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:40Z","receivedAt":"2019-10-06T23:31:35Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"`hashmap_free_entries' behaves like `container_of' and passes\nthe offset of the hashmap_entry struct to the internal\n`hashmap_free_' function, allowing the function to free any\nstruct pointer regardless of where the hashmap_entry field\nis located.\n\n`hashmap_free' no longer takes any arguments aside from\nthe hashmap itself.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n blame.c                 |  2 +-\n builtin/fetch.c         |  6 +++---\n config.c                |  2 +-\n diff.c                  |  6 ++++--\n diffcore-rename.c       |  2 +-\n hashmap.c               | 11 ++++++++---\n hashmap.h               | 19 +++++++++++++------\n merge-recursive.c       |  7 ++++---\n name-hash.c             |  4 ++--\n oidmap.c                |  4 +++-\n patch-ids.c             |  2 +-\n range-diff.c            |  2 +-\n ref-filter.c            |  3 ++-\n revision.c              |  2 +-\n sequencer.c             |  4 ++--\n submodule-config.c      |  4 ++--\n t/helper/test-hashmap.c |  6 +++---\n 17 files changed, 52 insertions(+), 34 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 3d8accf902..f33af0da9f 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -433,7 +433,7 @@ static void get_fingerprint(struct fingerprint *result,\n \n static void free_fingerprint(struct fingerprint *f)\n {\n-\thashmap_free(&f->map, 0);\n+\thashmap_free(&f->map);\n \tfree(f->entries);\n }\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 476c2416e3..09f7170616 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -366,7 +366,7 @@ static void find_non_local_tags(const struct ref *refs,\n \t\titem = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);\n \t\tstring_list_insert(&remote_refs_list, ref->name);\n \t}\n-\thashmap_free(&existing_refs, 1);\n+\thashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);\n \n \t/*\n \t * We may have a final lightweight tag that needs to be\n@@ -401,7 +401,7 @@ static void find_non_local_tags(const struct ref *refs,\n \t\t**tail = rm;\n \t\t*tail = &rm->next;\n \t}\n-\thashmap_free(&remote_refs, 1);\n+\thashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);\n \tstring_list_clear(&remote_refs_list, 0);\n }\n \n@@ -530,7 +530,7 @@ static struct ref *get_ref_map(struct remote *remote,\n \t\t\t}\n \t\t}\n \t}\n-\thashmap_free(&existing_refs, 1);\n+\thashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);\n \n \treturn ref_map;\n }\ndiff --git a/config.c b/config.c\nindex 8433f74371..4d05dbc15a 100644\n--- a/config.c\n+++ b/config.c\n@@ -1948,7 +1948,7 @@ void git_configset_clear(struct config_set *cs)\n \t\tfree(entry->key);\n \t\tstring_list_clear(&entry->value_list, 1);\n \t}\n-\thashmap_free(&cs->config_hash, 1);\n+\thashmap_free_entries(&cs->config_hash, struct config_set_element, ent);\n \tcs->hash_initialized = 0;\n \tfree(cs->list.items);\n \tcs->list.nr = 0;\ndiff --git a/diff.c b/diff.c\nindex 5eaf689fcc..f94d9f96af 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -6236,8 +6236,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)\n \t\t\tif (o->color_moved == COLOR_MOVED_ZEBRA_DIM)\n \t\t\t\tdim_moved_lines(o);\n \n-\t\t\thashmap_free(&add_lines, 1);\n-\t\t\thashmap_free(&del_lines, 1);\n+\t\t\thashmap_free_entries(&add_lines, struct moved_entry,\n+\t\t\t\t\t\tent);\n+\t\t\thashmap_free_entries(&del_lines, struct moved_entry,\n+\t\t\t\t\t\tent);\n \t\t}\n \n \t\tfor (i = 0; i < esm.nr; i++)\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 611b08f463..994609ed58 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -358,7 +358,7 @@ static int find_exact_renames(struct diff_options *options)\n \t\trenames += find_identical_files(&file_table, i, options);\n \n \t/* Free the hash data structure and entries */\n-\thashmap_free(&file_table, 1);\n+\thashmap_free_entries(&file_table, struct file_similarity, entry);\n \n \treturn renames;\n }\ndiff --git a/hashmap.c b/hashmap.c\nindex 1b60f97cf2..65b447f6cd 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -171,16 +171,21 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,\n \tmap->do_count_items = 1;\n }\n \n-void hashmap_free(struct hashmap *map, int free_entries)\n+void hashmap_free_(struct hashmap *map, ssize_t entry_offset)\n {\n \tif (!map || !map->table)\n \t\treturn;\n-\tif (free_entries) {\n+\tif (entry_offset >= 0) { /* called by hashmap_free_entries */\n \t\tstruct hashmap_iter iter;\n \t\tstruct hashmap_entry *e;\n+\n \t\thashmap_iter_init(map, &iter);\n \t\twhile ((e = hashmap_iter_next(&iter)))\n-\t\t\tfree(e);\n+\t\t\t/*\n+\t\t\t * like container_of, but using caller-calculated\n+\t\t\t * offset (caller being hashmap_free_entries)\n+\t\t\t */\n+\t\t\tfree((char *)e - entry_offset);\n \t}\n \tfree(map->table);\n \tmemset(map, 0, sizeof(*map));\ndiff --git a/hashmap.h b/hashmap.h\nindex bc3b10e097..171d6ddb76 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -96,7 +96,7 @@\n  *         }\n  *\n  *         if (!strcmp(\"end\", action)) {\n- *             hashmap_free(&map, 1);\n+ *             hashmap_free_entries(&map, struct long2string, ent);\n  *             break;\n  *         }\n  *     }\n@@ -232,13 +232,20 @@ void hashmap_init(struct hashmap *map,\n \t\t\t const void *equals_function_data,\n \t\t\t size_t initial_size);\n \n+/* internal function for freeing hashmap */\n+void hashmap_free_(struct hashmap *map, ssize_t offset);\n+\n /*\n- * Frees a hashmap structure and allocated memory.\n- *\n- * If `free_entries` is true, each hashmap_entry in the map is freed as well\n- * using stdlibs free().\n+ * Frees a hashmap structure and allocated memory, leaves entries undisturbed\n+ */\n+#define hashmap_free(map) hashmap_free_(map, -1)\n+\n+/*\n+ * Frees @map and all entries.  @type is the struct type of the entry\n+ * where @member is the hashmap_entry struct used to associate with @map\n  */\n-void hashmap_free(struct hashmap *map, int free_entries);\n+#define hashmap_free_entries(map, type, member) \\\n+\thashmap_free_(map, offsetof(type, member));\n \n /* hashmap_entry functions */\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 73c7750448..34b3d54154 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2633,7 +2633,7 @@ static struct string_list *get_renames(struct merge_options *opt,\n \t\tfree(e->target_file);\n \t\tstring_list_clear(&e->source_files, 0);\n \t}\n-\thashmap_free(&collisions, 1);\n+\thashmap_free_entries(&collisions, struct collision_entry, ent);\n \treturn renames;\n }\n \n@@ -2853,7 +2853,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,\n \t\tstrbuf_release(&e->new_dir);\n \t\t/* possible_new_dirs already cleared in get_directory_renames */\n \t}\n-\thashmap_free(dir_renames, 1);\n+\thashmap_free_entries(dir_renames, struct dir_rename_entry, ent);\n \tfree(dir_renames);\n \n \tfree(pairs->queue);\n@@ -3482,7 +3482,8 @@ int merge_trees(struct merge_options *opt,\n \t\tstring_list_clear(entries, 1);\n \t\tfree(entries);\n \n-\t\thashmap_free(&opt->current_file_dir_set, 1);\n+\t\thashmap_free_entries(&opt->current_file_dir_set,\n+\t\t\t\t\tstruct path_hashmap_entry, e);\n \n \t\tif (clean < 0) {\n \t\t\tunpack_trees_finish(opt);\ndiff --git a/name-hash.c b/name-hash.c\nindex 85a1ce982c..c86fe0f1df 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -728,6 +728,6 @@ void free_name_hash(struct index_state *istate)\n \t\treturn;\n \tistate->name_hash_initialized = 0;\n \n-\thashmap_free(&istate->name_hash, 0);\n-\thashmap_free(&istate->dir_hash, 1);\n+\thashmap_free(&istate->name_hash);\n+\thashmap_free_entries(&istate->dir_hash, struct dir_entry, ent);\n }\ndiff --git a/oidmap.c b/oidmap.c\nindex 4942599391..423aa014a3 100644\n--- a/oidmap.c\n+++ b/oidmap.c\n@@ -25,7 +25,9 @@ void oidmap_free(struct oidmap *map, int free_entries)\n {\n \tif (!map)\n \t\treturn;\n-\thashmap_free(&map->map, free_entries);\n+\n+\t/* TODO: make oidmap itself not depend on struct layouts */\n+\thashmap_free_(&map->map, free_entries ? 0 : -1);\n }\n \n void *oidmap_get(const struct oidmap *map, const struct object_id *key)\ndiff --git a/patch-ids.c b/patch-ids.c\nindex 75f8c9f1a1..af17828e33 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -71,7 +71,7 @@ int init_patch_ids(struct repository *r, struct patch_ids *ids)\n \n int free_patch_ids(struct patch_ids *ids)\n {\n-\thashmap_free(&ids->patches, 1);\n+\thashmap_free_entries(&ids->patches, struct patch_id, ent);\n \treturn 0;\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex e5e7820bfe..9df53569bb 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -241,7 +241,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\t}\n \t}\n \n-\thashmap_free(&map, 0);\n+\thashmap_free(&map);\n }\n \n static void diffsize_consume(void *data, char *line, unsigned long len)\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 4613df8826..0950b789e3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2172,7 +2172,8 @@ void ref_array_clear(struct ref_array *array)\n \tused_atom_cnt = 0;\n \n \tif (ref_to_worktree_map.worktrees) {\n-\t\thashmap_free(&(ref_to_worktree_map.map), 1);\n+\t\thashmap_free_entries(&(ref_to_worktree_map.map),\n+\t\t\t\t\tstruct ref_to_worktree_entry, ent);\n \t\tfree_worktrees(ref_to_worktree_map.worktrees);\n \t\tref_to_worktree_map.worktrees = NULL;\n \t}\ndiff --git a/revision.c b/revision.c\nindex f28cbe5de8..8a5f866ae6 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -136,7 +136,7 @@ static void paths_and_oids_clear(struct hashmap *map)\n \t\tfree(entry->path);\n \t}\n \n-\thashmap_free(map, 1);\n+\thashmap_free_entries(map, struct path_and_oids_entry, ent);\n }\n \n static void paths_and_oids_insert(struct hashmap *map,\ndiff --git a/sequencer.c b/sequencer.c\nindex b3e7319b55..694b463518 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4772,7 +4772,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \n \toidmap_free(&commit2todo, 1);\n \toidmap_free(&state.commit2label, 1);\n-\thashmap_free(&state.labels, 1);\n+\thashmap_free_entries(&state.labels, struct labels_entry, entry);\n \tstrbuf_release(&state.buf);\n \n \treturn 0;\n@@ -5301,7 +5301,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)\n \tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n-\thashmap_free(&subject2item, 1);\n+\thashmap_free_entries(&subject2item, struct subject2item_entry, entry);\n \n \tclear_commit_todo_item(&commit_todo);\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex a289d195f6..5462acc8ec 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -103,8 +103,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)\n \t\t\t\tstruct submodule_entry, ent /* member name */)\n \t\tfree_one_config(entry);\n \n-\thashmap_free(&cache->for_path, 1);\n-\thashmap_free(&cache->for_name, 1);\n+\thashmap_free_entries(&cache->for_path, struct submodule_entry, ent);\n+\thashmap_free_entries(&cache->for_name, struct submodule_entry, ent);\n \tcache->initialized = 0;\n \tcache->gitmodules_read = 0;\n }\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 07a93a2aec..6f2530dcc8 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -109,7 +109,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \t\t\t\thashmap_add(&map, &entries[i]->ent);\n \t\t\t}\n \n-\t\t\thashmap_free(&map, 0);\n+\t\t\thashmap_free(&map);\n \t\t}\n \t} else {\n \t\t/* test map lookups */\n@@ -129,7 +129,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)\n \t\t\t}\n \t\t}\n \n-\t\thashmap_free(&map, 0);\n+\t\thashmap_free(&map);\n \t}\n }\n \n@@ -266,6 +266,6 @@ int cmd__hashmap(int argc, const char **argv)\n \t}\n \n \tstrbuf_release(&line);\n-\thashmap_free(&map, 1);\n+\thashmap_free_entries(&map, struct test_entry, ent);\n \treturn 0;\n }\n"},{"id":"383548","messageId":"20191006233043.3516-19-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 18/20] OFFSETOF_VAR macro to simplify hashmap iterators","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:41Z","receivedAt":"2019-10-06T23:31:39Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"While we cannot rely on a `__typeof__' operator being portable\nto use with `offsetof'; we can calculate the pointer offset\nusing an existing pointer and the address of a member using\npointer arithmetic for compilers without `__typeof__'.\n\nThis allows us to simplify usage of hashmap iterator macros\nby not having to specify a type when a pointer of that type\nis already given.\n\nIn the future, list iterator macros (e.g. list_for_each_entry)\nmay also be implemented using OFFSETOF_VAR to save hackers the\ntrouble of using container_of/list_entry macros and without\nrelying on non-portable `__typeof__'.\n\nv3: use `__typeof__' to avoid clang warnings\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                              |  1 -\n blame.c                             |  2 --\n builtin/describe.c                  |  2 +-\n builtin/difftool.c                  |  4 +--\n config.c                            |  1 -\n diff.c                              |  5 ++--\n diffcore-rename.c                   |  2 +-\n git-compat-util.h                   | 13 +++++++++\n hashmap.h                           | 44 ++++++++++++++++++++---------\n merge-recursive.c                   |  5 ----\n name-hash.c                         |  3 +-\n revision.c                          |  8 ++----\n submodule-config.c                  |  2 +-\n t/helper/test-hashmap.c             |  5 +---\n t/helper/test-lazy-init-name-hash.c |  4 +--\n 15 files changed, 56 insertions(+), 45 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex ca8be46e8e..9849106627 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -168,7 +168,6 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)\n \t\tcheck->all_attrs_nr = size;\n \n \t\thashmap_for_each_entry(&map->map, &iter, e,\n-\t\t\t\t\tstruct attr_hash_entry,\n \t\t\t\t\tent /* member name */) {\n \t\t\tconst struct git_attr *a = e->value;\n \t\t\tcheck->all_attrs[a->attr_nr].attr = a;\ndiff --git a/blame.c b/blame.c\nindex f33af0da9f..90b247abf9 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -451,7 +451,6 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)\n \tconst struct fingerprint_entry *entry_a, *entry_b;\n \n \thashmap_for_each_entry(&b->map, &iter, entry_b,\n-\t\t\t\tconst struct fingerprint_entry,\n \t\t\t\tentry /* member name */) {\n \t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n \t\t\t\t\tstruct fingerprint_entry, entry);\n@@ -474,7 +473,6 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \thashmap_iter_init(&b->map, &iter);\n \n \thashmap_for_each_entry(&b->map, &iter, entry_b,\n-\t\t\t\tconst struct fingerprint_entry,\n \t\t\t\tentry /* member name */) {\n \t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n \t\t\t\t\tstruct fingerprint_entry, entry);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 8cf2cd992d..1caf98f716 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -333,7 +333,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)\n \t\tstruct commit_name *n;\n \n \t\tinit_commit_names(&commit_names);\n-\t\thashmap_for_each_entry(&names, &iter, n, struct commit_name,\n+\t\thashmap_for_each_entry(&names, &iter, n,\n \t\t\t\t\tentry /* member name */) {\n \t\t\tc = lookup_commit_reference_gently(the_repository,\n \t\t\t\t\t\t\t   &n->peeled, 1);\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex dd94179b68..f2d4d1e0f8 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -539,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t * change in the recorded SHA1 for the submodule.\n \t */\n \thashmap_for_each_entry(&submodules, &iter, entry,\n-\t\t\t\tstruct pair_entry, entry /* member name */) {\n+\t\t\t\tentry /* member name */) {\n \t\tif (*entry->left) {\n \t\t\tadd_path(&ldir, ldir_len, entry->path);\n \t\t\tensure_leading_directories(ldir.buf);\n@@ -558,7 +558,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t * This loop replicates that behavior.\n \t */\n \thashmap_for_each_entry(&symlinks2, &iter, entry,\n-\t\t\t\tstruct pair_entry, entry /* member name */) {\n+\t\t\t\tentry /* member name */) {\n \t\tif (*entry->left) {\n \t\t\tadd_path(&ldir, ldir_len, entry->path);\n \t\t\tensure_leading_directories(ldir.buf);\ndiff --git a/config.c b/config.c\nindex 4d05dbc15a..77ed00bfbf 100644\n--- a/config.c\n+++ b/config.c\n@@ -1943,7 +1943,6 @@ void git_configset_clear(struct config_set *cs)\n \t\treturn;\n \n \thashmap_for_each_entry(&cs->config_hash, &iter, entry,\n-\t\t\t\tstruct config_set_element,\n \t\t\t\tent /* member name */) {\n \t\tfree(entry->key);\n \t\tstring_list_clear(&entry->value_list, 1);\ndiff --git a/diff.c b/diff.c\nindex f94d9f96af..051de9832d 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1038,7 +1038,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,\n \tint i;\n \tchar *got_match = xcalloc(1, pmb_nr);\n \n-\thashmap_for_each_entry_from(hm, match, struct moved_entry, ent) {\n+\thashmap_for_each_entry_from(hm, match, ent) {\n \t\tfor (i = 0; i < pmb_nr; i++) {\n \t\t\tstruct moved_entry *prev = pmb[i].match;\n \t\t\tstruct moved_entry *cur = (prev && prev->next_line) ?\n@@ -1193,8 +1193,7 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\t\t * The current line is the start of a new block.\n \t\t\t * Setup the set of potential blocks.\n \t\t\t */\n-\t\t\thashmap_for_each_entry_from(hm, match,\n-\t\t\t\t\t\tstruct moved_entry, ent) {\n+\t\t\thashmap_for_each_entry_from(hm, match, ent) {\n \t\t\t\tALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);\n \t\t\t\tif (o->color_moved_ws_handling &\n \t\t\t\t    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 994609ed58..9ad4dc395a 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -284,7 +284,7 @@ static int find_identical_files(struct hashmap *srcs,\n \t */\n \tp = hashmap_get_entry_from_hash(srcs, hash, NULL,\n \t\t\t\t\tstruct file_similarity, entry);\n-\thashmap_for_each_entry_from(srcs, p, struct file_similarity, entry) {\n+\thashmap_for_each_entry_from(srcs, p, entry) {\n \t\tint score;\n \t\tstruct diff_filespec *source = p->filespec;\n \ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 4a23b9090b..8605cb4202 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1337,4 +1337,17 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)\n #define container_of_or_null(ptr, type, member) \\\n \t(type *)container_of_or_null_offset(ptr, offsetof(type, member))\n \n+/*\n+ * like offsetof(), but takes a pointer to a a variable of type which\n+ * contains @member, instead of a specified type.\n+ * @ptr is subject to multiple evaluation since we can't rely on __typeof__\n+ * everywhere.\n+ */\n+#if defined(__GNUC__) /* clang sets this, too */\n+#define OFFSETOF_VAR(ptr, member) offsetof(__typeof__(*ptr), member)\n+#else /* !__GNUC__ */\n+#define OFFSETOF_VAR(ptr, member) \\\n+\t((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))\n+#endif /* !__GNUC__ */\n+\n #endif\ndiff --git a/hashmap.h b/hashmap.h\nindex 171d6ddb76..96786c724a 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -408,16 +408,32 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,\n \treturn hashmap_iter_next(iter);\n }\n \n-#define hashmap_iter_next_entry(iter, type, member) \\\n-\tcontainer_of_or_null(hashmap_iter_next(iter), type, member)\n-\n+/*\n+ * returns the first entry in @map using @iter, where the entry is of\n+ * @type (e.g. \"struct foo\") and @member is the name of the\n+ * \"struct hashmap_entry\" in @type\n+ */\n #define hashmap_iter_first_entry(map, iter, type, member) \\\n \tcontainer_of_or_null(hashmap_iter_first(map, iter), type, member)\n \n-#define hashmap_for_each_entry(map, iter, var, type, member) \\\n-\tfor (var = hashmap_iter_first_entry(map, iter, type, member); \\\n+/* internal macro for hashmap_for_each_entry */\n+#define hashmap_iter_next_entry_offset(iter, offset) \\\n+\tcontainer_of_or_null_offset(hashmap_iter_next(iter), offset)\n+\n+/* internal macro for hashmap_for_each_entry */\n+#define hashmap_iter_first_entry_offset(map, iter, offset) \\\n+\tcontainer_of_or_null_offset(hashmap_iter_first(map, iter), offset)\n+\n+/*\n+ * iterate through @map using @iter, @var is a pointer to a type\n+ * containing a @member which is a \"struct hashmap_entry\"\n+ */\n+#define hashmap_for_each_entry(map, iter, var, member) \\\n+\tfor (var = hashmap_iter_first_entry_offset(map, iter, \\\n+\t\t\t\t\t\tOFFSETOF_VAR(var, member)); \\\n \t\tvar; \\\n-\t\tvar = hashmap_iter_next_entry(iter, type, member))\n+\t\tvar = hashmap_iter_next_entry_offset(iter, \\\n+\t\t\t\t\t\tOFFSETOF_VAR(var, member)))\n \n /*\n  * returns a @pointer of @type matching @keyvar, or NULL if nothing found.\n@@ -432,22 +448,22 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,\n \tcontainer_of_or_null(hashmap_get_from_hash(map, hash, keydata), \\\n \t\t\t\ttype, member)\n /*\n- * returns the next equal @type pointer to @var, or NULL if not found.\n- * @var is a pointer of @type\n- * @member is the name of the \"struct hashmap_entry\" field in @type\n+ * returns the next equal pointer to @var, or NULL if not found.\n+ * @var is a pointer of any type containing \"struct hashmap_entry\"\n+ * @member is the name of the \"struct hashmap_entry\" field\n  */\n-#define hashmap_get_next_entry(map, var, type, member) \\\n-\tcontainer_of_or_null(hashmap_get_next(map, &(var)->member), \\\n-\t\t\t\ttype, member)\n+#define hashmap_get_next_entry(map, var, member) \\\n+\tcontainer_of_or_null_offset(hashmap_get_next(map, &(var)->member), \\\n+\t\t\t\tOFFSETOF_VAR(var, member))\n \n /*\n  * iterate @map starting from @var, where @var is a pointer of @type\n  * and @member is the name of the \"struct hashmap_entry\" field in @type\n  */\n-#define hashmap_for_each_entry_from(map, var, type, member) \\\n+#define hashmap_for_each_entry_from(map, var, member) \\\n \tfor (; \\\n \t\tvar; \\\n-\t\tvar = hashmap_get_next_entry(map, var, type, member))\n+\t\tvar = hashmap_get_next_entry(map, var, member))\n \n /*\n  * Disable item counting and automatic rehashing when adding/removing items.\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 34b3d54154..3abba3a618 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2136,7 +2136,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,\n \tstruct string_list remove_from_merge = STRING_LIST_INIT_NODUP;\n \n \thashmap_for_each_entry(dir_re_head, &iter, head_ent,\n-\t\t\t\tstruct dir_rename_entry,\n \t\t\t\tent /* member name */) {\n \t\tmerge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);\n \t\tif (merge_ent &&\n@@ -2162,7 +2161,6 @@ static void handle_directory_level_conflicts(struct merge_options *opt,\n \tremove_hashmap_entries(dir_re_merge, &remove_from_merge);\n \n \thashmap_for_each_entry(dir_re_merge, &iter, merge_ent,\n-\t\t\t\tstruct dir_rename_entry,\n \t\t\t\tent /* member name */) {\n \t\thead_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);\n \t\tif (tree_has_path(opt->repo, merge, merge_ent->dir)) {\n@@ -2268,7 +2266,6 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)\n \t * that there is no winner), we no longer need possible_new_dirs.\n \t */\n \thashmap_for_each_entry(dir_renames, &iter, entry,\n-\t\t\t\tstruct dir_rename_entry,\n \t\t\t\tent /* member name */) {\n \t\tint max = 0;\n \t\tint bad_max = 0;\n@@ -2628,7 +2625,6 @@ static struct string_list *get_renames(struct merge_options *opt,\n \t}\n \n \thashmap_for_each_entry(&collisions, &iter, e,\n-\t\t\t\tstruct collision_entry,\n \t\t\t\tent /* member name */) {\n \t\tfree(e->target_file);\n \t\tstring_list_clear(&e->source_files, 0);\n@@ -2847,7 +2843,6 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,\n \tstruct dir_rename_entry *e;\n \n \thashmap_for_each_entry(dir_renames, &iter, e,\n-\t\t\t\tstruct dir_rename_entry,\n \t\t\t\tent /* member name */) {\n \t\tfree(e->dir);\n \t\tstrbuf_release(&e->new_dir);\ndiff --git a/name-hash.c b/name-hash.c\nindex c86fe0f1df..3cda22b657 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -714,8 +714,7 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na\n \n \tce = hashmap_get_entry_from_hash(&istate->name_hash, hash, NULL,\n \t\t\t\t\t struct cache_entry, ent);\n-\thashmap_for_each_entry_from(&istate->name_hash, ce,\n-\t\t\t\t\tstruct cache_entry, ent) {\n+\thashmap_for_each_entry_from(&istate->name_hash, ce, ent) {\n \t\tif (same_name(ce, name, namelen, icase))\n \t\t\treturn ce;\n \t}\ndiff --git a/revision.c b/revision.c\nindex 8a5f866ae6..5abd4a1fe7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -129,9 +129,7 @@ static void paths_and_oids_clear(struct hashmap *map)\n \tstruct hashmap_iter iter;\n \tstruct path_and_oids_entry *entry;\n \n-\thashmap_for_each_entry(map, &iter, entry,\n-\t\t\t\tstruct path_and_oids_entry,\n-\t\t\t\tent /* member name */) {\n+\thashmap_for_each_entry(map, &iter, entry, ent /* member name */) {\n \t\toidset_clear(&entry->trees);\n \t\tfree(entry->path);\n \t}\n@@ -243,9 +241,7 @@ void mark_trees_uninteresting_sparse(struct repository *r,\n \t\tadd_children_by_path(r, tree, &map);\n \t}\n \n-\thashmap_for_each_entry(&map, &map_iter, entry,\n-\t\t\t\tstruct path_and_oids_entry,\n-\t\t\t\tent /* member name */)\n+\thashmap_for_each_entry(&map, &map_iter, entry, ent /* member name */)\n \t\tmark_trees_uninteresting_sparse(r, &entry->trees);\n \n \tpaths_and_oids_clear(&map);\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 5462acc8ec..c22855cd38 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -100,7 +100,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)\n \t * their .gitmodules blob sha1 and submodule name.\n \t */\n \thashmap_for_each_entry(&cache->for_name, &iter, entry,\n-\t\t\t\tstruct submodule_entry, ent /* member name */)\n+\t\t\t\tent /* member name */)\n \t\tfree_one_config(entry);\n \n \thashmap_free_entries(&cache->for_path, struct submodule_entry, ent);\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex 6f2530dcc8..f89d1194ef 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -205,10 +205,8 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\t/* print result */\n \t\t\tif (!entry)\n \t\t\t\tputs(\"NULL\");\n-\t\t\thashmap_for_each_entry_from(&map, entry,\n-\t\t\t\t\t\tstruct test_entry, ent) {\n+\t\t\thashmap_for_each_entry_from(&map, entry, ent)\n \t\t\t\tputs(get_value(entry));\n-\t\t\t}\n \n \t\t} else if (!strcmp(\"remove\", cmd) && p1) {\n \n@@ -230,7 +228,6 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tstruct hashmap_iter iter;\n \n \t\t\thashmap_for_each_entry(&map, &iter, entry,\n-\t\t\t\t\t\tstruct test_entry,\n \t\t\t\t\t\tent /* member name */)\n \t\t\t\tprintf(\"%s %s\\n\", entry->key, get_value(entry));\n \ndiff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c\nindex 9d4664d6a4..cd1b4c9736 100644\n--- a/t/helper/test-lazy-init-name-hash.c\n+++ b/t/helper/test-lazy-init-name-hash.c\n@@ -42,11 +42,11 @@ static void dump_run(void)\n \t}\n \n \thashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,\n-\t\t\t\tstruct dir_entry, ent /* member name */)\n+\t\t\t\tent /* member name */)\n \t\tprintf(\"dir %08x %7d %s\\n\", dir->ent.hash, dir->nr, dir->name);\n \n \thashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,\n-\t\t\t\tstruct cache_entry, ent /* member name */)\n+\t\t\t\tent /* member name */)\n \t\tprintf(\"name %08x %s\\n\", ce->ent.hash, ce->name);\n \n \tdiscard_cache();\n"},{"id":"383549","messageId":"20191006233043.3516-20-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 19/20] hashmap: remove type arg from hashmap_{get,put,remove}_entry","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:42Z","receivedAt":"2019-10-06T23:31:42Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Since these macros already take a `keyvar' pointer of a known type,\nwe can rely on OFFSETOF_VAR to get the correct offset without\nrelying on non-portable `__typeof__' and `offsetof'.\n\nArgument order is also rearranged, so `keyvar' and `member' are\nsequential as they are used as: `keyvar->member'\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReviewed-by: Derrick Stolee <stolee@gmail.com>\n---\n attr.c                  |  2 +-\n blame.c                 | 10 ++++-----\n builtin/difftool.c      |  2 +-\n builtin/fast-export.c   |  2 +-\n config.c                |  3 +--\n diff.c                  |  6 ++----\n hashmap.c               |  2 +-\n hashmap.h               | 45 ++++++++++++++++++++++++++++++-----------\n merge-recursive.c       |  6 ++----\n name-hash.c             |  3 +--\n patch-ids.c             |  3 +--\n range-diff.c            |  4 +---\n remote.c                |  3 +--\n revision.c              |  3 +--\n sub-process.c           |  3 +--\n submodule-config.c      | 10 +++------\n t/helper/test-hashmap.c |  4 +---\n 17 files changed, 56 insertions(+), 55 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 9849106627..15f0efdf60 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -103,7 +103,7 @@ static void *attr_hashmap_get(struct attr_hashmap *map,\n \thashmap_entry_init(&k.ent, memhash(key, keylen));\n \tk.key = key;\n \tk.keylen = keylen;\n-\te = hashmap_get_entry(&map->map, &k, NULL, struct attr_hash_entry, ent);\n+\te = hashmap_get_entry(&map->map, &k, ent, NULL);\n \n \treturn e ? e->value : NULL;\n }\ndiff --git a/blame.c b/blame.c\nindex 90b247abf9..6384f48133 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -419,8 +419,8 @@ static void get_fingerprint(struct fingerprint *result,\n \t\t\tcontinue;\n \t\thashmap_entry_init(&entry->entry, hash);\n \n-\t\tfound_entry = hashmap_get_entry(&result->map, entry, NULL,\n-\t\t\t\t\tstruct fingerprint_entry, entry);\n+\t\tfound_entry = hashmap_get_entry(&result->map, entry,\n+\t\t\t\t\t\t/* member name */ entry, NULL);\n \t\tif (found_entry) {\n \t\t\tfound_entry->count += 1;\n \t\t} else {\n@@ -452,8 +452,7 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)\n \n \thashmap_for_each_entry(&b->map, &iter, entry_b,\n \t\t\t\tentry /* member name */) {\n-\t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n-\t\t\t\t\tstruct fingerprint_entry, entry);\n+\t\tentry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);\n \t\tif (entry_a) {\n \t\t\tintersection += entry_a->count < entry_b->count ?\n \t\t\t\t\tentry_a->count : entry_b->count;\n@@ -474,8 +473,7 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)\n \n \thashmap_for_each_entry(&b->map, &iter, entry_b,\n \t\t\t\tentry /* member name */) {\n-\t\tentry_a = hashmap_get_entry(&a->map, entry_b, NULL,\n-\t\t\t\t\tstruct fingerprint_entry, entry);\n+\t\tentry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);\n \t\tif (entry_a) {\n \t\t\tif (entry_a->count <= entry_b->count)\n \t\t\t\thashmap_remove(&a->map, &entry_b->entry, NULL);\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex f2d4d1e0f8..c280e682b2 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -167,7 +167,7 @@ static void add_left_or_right(struct hashmap *map, const char *path,\n \n \tFLEX_ALLOC_STR(e, path, path);\n \thashmap_entry_init(&e->entry, strhash(path));\n-\texisting = hashmap_get_entry(map, e, NULL, struct pair_entry, entry);\n+\texisting = hashmap_get_entry(map, e, entry, NULL);\n \tif (existing) {\n \t\tfree(e);\n \t\te = existing;\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex ef0578bf90..e3de403efd 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -156,7 +156,7 @@ static const void *anonymize_mem(struct hashmap *map,\n \thashmap_entry_init(&key.hash, memhash(orig, *len));\n \tkey.orig = orig;\n \tkey.orig_len = *len;\n-\tret = hashmap_get_entry(map, &key, NULL, struct anonymized_entry, hash);\n+\tret = hashmap_get_entry(map, &key, hash, NULL);\n \n \tif (!ret) {\n \t\tret = xmalloc(sizeof(*ret));\ndiff --git a/config.c b/config.c\nindex 77ed00bfbf..a4fa464ed2 100644\n--- a/config.c\n+++ b/config.c\n@@ -1863,8 +1863,7 @@ static struct config_set_element *configset_find_element(struct config_set *cs,\n \n \thashmap_entry_init(&k.ent, strhash(normalized_key));\n \tk.key = normalized_key;\n-\tfound_entry = hashmap_get_entry(&cs->config_hash, &k, NULL,\n-\t\t\t\tstruct config_set_element, ent);\n+\tfound_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);\n \tfree(normalized_key);\n \treturn found_entry;\n }\ndiff --git a/diff.c b/diff.c\nindex 051de9832d..a9ecb93af3 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1146,15 +1146,13 @@ static void mark_color_as_moved(struct diff_options *o,\n \t\tcase DIFF_SYMBOL_PLUS:\n \t\t\thm = del_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get_entry(hm, key, NULL,\n-\t\t\t\t\t\tstruct moved_entry, ent);\n+\t\t\tmatch = hashmap_get_entry(hm, key, ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tcase DIFF_SYMBOL_MINUS:\n \t\t\thm = add_lines;\n \t\t\tkey = prepare_entry(o, n);\n-\t\t\tmatch = hashmap_get_entry(hm, key, NULL,\n-\t\t\t\t\t\tstruct moved_entry, ent);\n+\t\t\tmatch = hashmap_get_entry(hm, key, ent, NULL);\n \t\t\tfree(key);\n \t\t\tbreak;\n \t\tdefault:\ndiff --git a/hashmap.c b/hashmap.c\nindex 65b447f6cd..39c13110bc 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -311,7 +311,7 @@ const void *memintern(const void *data, size_t len)\n \t/* lookup interned string in pool */\n \thashmap_entry_init(&key.ent, memhash(data, len));\n \tkey.len = len;\n-\te = hashmap_get_entry(&map, &key, data, struct pool_entry, ent);\n+\te = hashmap_get_entry(&map, &key, ent, data);\n \tif (!e) {\n \t\t/* not found: create it */\n \t\tFLEX_ALLOC_MEM(e, data, data, len);\ndiff --git a/hashmap.h b/hashmap.h\nindex 96786c724a..bf4a05937d 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -63,7 +63,7 @@\n  *             k.key = key;\n  *\n  *             flags &= ~COMPARE_VALUE;\n- *             e = hashmap_get_entry(&map, &k, NULL, struct long2string, ent);\n+ *             e = hashmap_get_entry(&map, &k, ent, NULL);\n  *             if (e) {\n  *                 printf(\"first: %ld %s\\n\", e->key, e->value);\n  *                 while ((e = hashmap_get_next_entry(&map, e,\n@@ -359,8 +359,17 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);\n struct hashmap_entry *hashmap_put(struct hashmap *map,\n \t\t\t\tstruct hashmap_entry *entry);\n \n-#define hashmap_put_entry(map, keyvar, type, member) \\\n-\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n+/*\n+ * Adds or replaces a hashmap entry contained within @keyvar,\n+ * where @keyvar is a pointer to a struct containing a\n+ * \"struct hashmap_entry\" @member.\n+ *\n+ * Returns the replaced pointer which is of the same type as @keyvar,\n+ * or NULL if not found.\n+ */\n+#define hashmap_put_entry(map, keyvar, member) \\\n+\tcontainer_of_or_null_offset(hashmap_put(map, &(keyvar)->member), \\\n+\t\t\t\tOFFSETOF_VAR(keyvar, member))\n \n /*\n  * Removes a hashmap entry matching the specified key. If the hashmap contains\n@@ -373,9 +382,20 @@ struct hashmap_entry *hashmap_remove(struct hashmap *map,\n \t\t\t\t\tconst struct hashmap_entry *key,\n \t\t\t\t\tconst void *keydata);\n \n-#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n-\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n-\t\t\t\ttype, member)\n+/*\n+ * Removes a hashmap entry contained within @keyvar,\n+ * where @keyvar is a pointer to a struct containing a\n+ * \"struct hashmap_entry\" @member.\n+ *\n+ * See `hashmap_get` for an explanation of @keydata\n+ *\n+ * Returns the replaced pointer which is of the same type as @keyvar,\n+ * or NULL if not found.\n+ */\n+#define hashmap_remove_entry(map, keyvar, member, keydata) \\\n+\tcontainer_of_or_null_offset( \\\n+\t\t\thashmap_remove(map, &(keyvar)->member, keydata), \\\n+\t\t\tOFFSETOF_VAR(keyvar, member))\n \n /*\n  * Returns the `bucket` an entry is stored in.\n@@ -436,13 +456,14 @@ static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,\n \t\t\t\t\t\tOFFSETOF_VAR(var, member)))\n \n /*\n- * returns a @pointer of @type matching @keyvar, or NULL if nothing found.\n- * @keyvar is a pointer of @type\n- * @member is the name of the \"struct hashmap_entry\" field in @type\n+ * returns a pointer of type matching @keyvar, or NULL if nothing found.\n+ * @keyvar is a pointer to a struct containing a\n+ * \"struct hashmap_entry\" @member.\n  */\n-#define hashmap_get_entry(map, keyvar, keydata, type, member) \\\n-\tcontainer_of_or_null(hashmap_get(map, &(keyvar)->member, keydata), \\\n-\t\t\t\ttype, member)\n+#define hashmap_get_entry(map, keyvar, member, keydata) \\\n+\tcontainer_of_or_null_offset( \\\n+\t\t\t\thashmap_get(map, &(keyvar)->member, keydata), \\\n+\t\t\t\tOFFSETOF_VAR(keyvar, member))\n \n #define hashmap_get_entry_from_hash(map, hash, keydata, type, member) \\\n \tcontainer_of_or_null(hashmap_get_from_hash(map, hash, keydata), \\\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3abba3a618..8787a40b0c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -65,8 +65,7 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,\n \t\treturn NULL;\n \thashmap_entry_init(&key.ent, strhash(dir));\n \tkey.dir = dir;\n-\treturn hashmap_get_entry(hashmap, &key, NULL,\n-\t\t\t\tstruct dir_rename_entry, ent);\n+\treturn hashmap_get_entry(hashmap, &key, ent, NULL);\n }\n \n static int dir_rename_cmp(const void *unused_cmp_data,\n@@ -104,8 +103,7 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,\n \n \thashmap_entry_init(&key.ent, strhash(target_file));\n \tkey.target_file = target_file;\n-\treturn hashmap_get_entry(hashmap, &key, NULL,\n-\t\t\t\tstruct collision_entry, ent);\n+\treturn hashmap_get_entry(hashmap, &key, ent, NULL);\n }\n \n static int collision_cmp(const void *unused_cmp_data,\ndiff --git a/name-hash.c b/name-hash.c\nindex 3cda22b657..ceb1d7bd6f 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -37,8 +37,7 @@ static struct dir_entry *find_dir_entry__hash(struct index_state *istate,\n \tstruct dir_entry key;\n \thashmap_entry_init(&key.ent, hash);\n \tkey.namelen = namelen;\n-\treturn hashmap_get_entry(&istate->dir_hash, &key, name,\n-\t\t\t\t\tstruct dir_entry, ent);\n+\treturn hashmap_get_entry(&istate->dir_hash, &key, ent, name);\n }\n \n static struct dir_entry *find_dir_entry(struct index_state *istate,\ndiff --git a/patch-ids.c b/patch-ids.c\nindex af17828e33..12aa6d494b 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -101,8 +101,7 @@ struct patch_id *has_commit_patch_id(struct commit *commit,\n \tif (init_patch_id_entry(&patch, commit, ids))\n \t\treturn NULL;\n \n-\treturn hashmap_get_entry(&ids->patches, &patch, NULL,\n-\t\t\t\t\tstruct patch_id, ent);\n+\treturn hashmap_get_entry(&ids->patches, &patch, ent, NULL);\n }\n \n struct patch_id *add_commit_patch_id(struct commit *commit,\ndiff --git a/range-diff.c b/range-diff.c\nindex 9df53569bb..22ad959cee 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -229,9 +229,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)\n \t\tutil->patch = b->items[i].string;\n \t\tutil->diff = util->patch + util->diff_offset;\n \t\thashmap_entry_init(&util->e, strhash(util->diff));\n-\t\tother = hashmap_remove_entry(&map, util, NULL,\n-\t\t\t\t\tstruct patch_util,\n-\t\t\t\t\te /* member name */);\n+\t\tother = hashmap_remove_entry(&map, util, e, NULL);\n \t\tif (other) {\n \t\t\tif (other->matching >= 0)\n \t\t\t\tBUG(\"already assigned!\");\ndiff --git a/remote.c b/remote.c\nindex 5fcddcd88d..5c4666b53a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -162,8 +162,7 @@ static struct remote *make_remote(const char *name, int len)\n \tremotes[remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\treplaced = hashmap_put_entry(&remotes_hash, ret, struct remote,\n-\t\t\t\t\tent /* member name */);\n+\treplaced = hashmap_put_entry(&remotes_hash, ret, ent);\n \tassert(replaced == NULL);  /* no previous entry overwritten */\n \treturn ret;\n }\ndiff --git a/revision.c b/revision.c\nindex 5abd4a1fe7..6688f89d0d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -151,8 +151,7 @@ static void paths_and_oids_insert(struct hashmap *map,\n \tkey.path = (char *)path;\n \toidset_init(&key.trees, 0);\n \n-\tentry = hashmap_get_entry(map, &key, NULL,\n-\t\t\t\tstruct path_and_oids_entry, ent);\n+\tentry = hashmap_get_entry(map, &key, ent, NULL);\n \tif (!entry) {\n \t\tentry = xcalloc(1, sizeof(struct path_and_oids_entry));\n \t\thashmap_entry_init(&entry->ent, hash);\ndiff --git a/sub-process.c b/sub-process.c\nindex ad94f72665..1b1af9dcbd 100644\n--- a/sub-process.c\n+++ b/sub-process.c\n@@ -24,8 +24,7 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch\n \n \thashmap_entry_init(&key.ent, strhash(cmd));\n \tkey.cmd = cmd;\n-\treturn hashmap_get_entry(hashmap, &key, NULL,\n-\t\t\t\tstruct subprocess_entry, ent);\n+\treturn hashmap_get_entry(hashmap, &key, ent, NULL);\n }\n \n int subprocess_read_status(int fd, struct strbuf *status)\ndiff --git a/submodule-config.c b/submodule-config.c\nindex c22855cd38..401a9b2382 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -141,9 +141,7 @@ static void cache_remove_path(struct submodule_cache *cache,\n \tstruct submodule_entry *removed;\n \thashmap_entry_init(&e.ent, hash);\n \te.config = submodule;\n-\tremoved = hashmap_remove_entry(&cache->for_path, &e, NULL,\n-\t\t\t\t\tstruct submodule_entry,\n-\t\t\t\t\tent /* member name */);\n+\tremoved = hashmap_remove_entry(&cache->for_path, &e, ent, NULL);\n \tfree(removed);\n }\n \n@@ -172,8 +170,7 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get_entry(&cache->for_path, &key, NULL,\n-\t\t\t\tstruct submodule_entry, ent);\n+\tentry = hashmap_get_entry(&cache->for_path, &key, ent, NULL);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\n@@ -193,8 +190,7 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,\n \thashmap_entry_init(&key.ent, hash);\n \tkey.config = &key_config;\n \n-\tentry = hashmap_get_entry(&cache->for_name, &key, NULL,\n-\t\t\t\tstruct submodule_entry, ent);\n+\tentry = hashmap_get_entry(&cache->for_name, &key, ent, NULL);\n \tif (entry)\n \t\treturn entry->config;\n \treturn NULL;\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex f89d1194ef..cc577c8956 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -189,9 +189,7 @@ int cmd__hashmap(int argc, const char **argv)\n \t\t\tentry = alloc_test_entry(hash, p1, p2);\n \n \t\t\t/* add / replace entry */\n-\t\t\tentry = hashmap_put_entry(&map, entry,\n-\t\t\t\t\t\tstruct test_entry,\n-\t\t\t\t\t\tent /* member name */);\n+\t\t\tentry = hashmap_put_entry(&map, entry, ent);\n \n \t\t\t/* print and free replaced entry, if any */\n \t\t\tputs(entry ? get_value(entry) : \"NULL\");\n"},{"id":"383550","messageId":"20191006233043.3516-21-e@80x24.org","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"[PATCH v3 20/20] hashmap_entry: remove first member requirement from docs","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-06T23:30:43Z","receivedAt":"2019-10-06T23:31:45Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Comments stating that \"struct hashmap_entry\" must be the first\nmember in a struct are no longer valid.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Eric Wong <e@80x24.org>\n---\n attr.c                  | 2 +-\n builtin/fetch.c         | 2 +-\n hashmap.h               | 4 ++--\n merge-recursive.h       | 4 ++--\n ref-filter.c            | 2 +-\n refs.c                  | 2 +-\n remote.h                | 2 +-\n sub-process.h           | 2 +-\n t/helper/test-hashmap.c | 1 +\n 9 files changed, 11 insertions(+), 10 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 15f0efdf60..e5c951db69 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -62,7 +62,7 @@ static struct attr_hashmap g_attr_hashmap;\n \n /* The container for objects stored in \"struct attr_hashmap\" */\n struct attr_hash_entry {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \tconst char *key; /* the key; memory should be owned by value */\n \tsize_t keylen; /* length of the key */\n \tvoid *value; /* the stored value */\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 09f7170616..a3154a4edb 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -251,7 +251,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)\n }\n \n struct refname_hash_entry {\n-\tstruct hashmap_entry ent; /* must be the first member */\n+\tstruct hashmap_entry ent;\n \tstruct object_id oid;\n \tint ignore;\n \tchar refname[FLEX_ARRAY];\ndiff --git a/hashmap.h b/hashmap.h\nindex bf4a05937d..bd2701549f 100644\n--- a/hashmap.h\n+++ b/hashmap.h\n@@ -13,7 +13,7 @@\n  *\n  * struct hashmap map;\n  * struct long2string {\n- *     struct hashmap_entry ent; // must be the first member!\n+ *     struct hashmap_entry ent;\n  *     long key;\n  *     char value[FLEX_ARRAY];   // be careful with allocating on stack!\n  * };\n@@ -141,7 +141,7 @@ static inline unsigned int oidhash(const struct object_id *oid)\n \n /*\n  * struct hashmap_entry is an opaque structure representing an entry in the\n- * hash table, which must be used as first member of user data structures.\n+ * hash table.\n  * Ideally it should be followed by an int-sized member to prevent unused\n  * memory on 64-bit systems due to alignment.\n  */\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex c2b7bb65c6..daa742568e 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -50,7 +50,7 @@ struct merge_options {\n  *                       in get_directory_renames() for details\n  */\n struct dir_rename_entry {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \tchar *dir;\n \tunsigned non_unique_new_dir:1;\n \tstruct strbuf new_dir;\n@@ -58,7 +58,7 @@ struct dir_rename_entry {\n };\n \n struct collision_entry {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \tchar *target_file;\n \tstruct string_list source_files;\n \tunsigned reported_already:1;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 0950b789e3..5c10a343c6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -79,7 +79,7 @@ static struct expand_data {\n } oi, oi_deref;\n \n struct ref_to_worktree_entry {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \tstruct worktree *wt; /* key is wt->head_ref */\n };\n \ndiff --git a/refs.c b/refs.c\nindex 2d3eb40f39..1ab0bb54d3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1772,7 +1772,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \n struct ref_store_hash_entry\n {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \n \tstruct ref_store *refs;\n \ndiff --git a/remote.h b/remote.h\nindex 83e885672b..0e1d2b245b 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -14,7 +14,7 @@ enum {\n };\n \n struct remote {\n-\tstruct hashmap_entry ent;  /* must be first */\n+\tstruct hashmap_entry ent;\n \n \tconst char *name;\n \tint origin, configured_in_repo;\ndiff --git a/sub-process.h b/sub-process.h\nindex 0d12708b8c..e85f21fa1a 100644\n--- a/sub-process.h\n+++ b/sub-process.h\n@@ -24,7 +24,7 @@\n \n /* Members should not be accessed directly. */\n struct subprocess_entry {\n-\tstruct hashmap_entry ent; /* must be the first member! */\n+\tstruct hashmap_entry ent;\n \tconst char *cmd;\n \tstruct child_process process;\n };\ndiff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c\nindex cc577c8956..f38706216f 100644\n--- a/t/helper/test-hashmap.c\n+++ b/t/helper/test-hashmap.c\n@@ -5,6 +5,7 @@\n \n struct test_entry\n {\n+\tint padding; /* hashmap entry no longer needs to be the first member */\n \tstruct hashmap_entry ent;\n \t/* key and value as two \\0-terminated strings */\n \tchar key[FLEX_ARRAY];\n"},{"id":"383555","messageId":"xmqqo8yt4aak.fsf@gitster-ct.c.googlers.com","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"Re: [PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-07T01:24:51Z","receivedAt":"2019-10-07T01:24:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <e@80x24.org> writes:\n\n> v3 changes:\n> - use __typeof__ to avoid invalid clang warning on uninitialized var\n> - formatting fixes recommended by Stolee\n> - add Reviewed-by for Stolee\n> - add patch 20 to update docs to drop first member requirement\n\nI just did\n\n\tgit checkout ew/hashmap\n\tgit checkout master...\n\tgit am -s <mbox-with-these-patches\n\tgit diff @{-1}\n\nand all the differences between them I agree with (and needless to\nsay, I looked at all the changes in the previous round).\n\nLooks much nicer.\n\nThanks.\n"},{"id":"383557","messageId":"xmqqeezp490w.fsf@gitster-ct.c.googlers.com","threadId":"51981","inReplyTo":"20191006233043.3516-21-e@80x24.org","subject":"Re: [PATCH v3 20/20] hashmap_entry: remove first member requirement from docs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-07T01:52:15Z","receivedAt":"2019-10-07T01:52:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <e@80x24.org> writes:\n\n> Comments stating that \"struct hashmap_entry\" must be the first\n> member in a struct are no longer valid.\n>\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Signed-off-by: Eric Wong <e@80x24.org>\n> ---\n>  attr.c                  | 2 +-\n>  builtin/fetch.c         | 2 +-\n>  hashmap.h               | 4 ++--\n>  merge-recursive.h       | 4 ++--\n>  ref-filter.c            | 2 +-\n>  refs.c                  | 2 +-\n>  remote.h                | 2 +-\n>  sub-process.h           | 2 +-\n>  t/helper/test-hashmap.c | 1 +\n>  9 files changed, 11 insertions(+), 10 deletions(-)\n\nAfter this patch, there is one \"/* must be the first member! */\"\ncomment left in convert.c, which is both misleading and unfortunate;\na structure 'subprocess_entry' wants to be the first field of any\nenclosing structure, where 'subprocess_entry' has a hashmap_entry as\nits first field.\n\n"},{"id":"383562","messageId":"20191007084339.GA7808@dcvr","threadId":"51981","inReplyTo":"xmqqeezp490w.fsf@gitster-ct.c.googlers.com","subject":"[PATCH v3 21/20] convert: drop invalid comment for subprocess_entry","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-07T08:43:39Z","receivedAt":"2019-10-07T08:43:41Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> Eric Wong <e@80x24.org> writes:\n> \n> > Comments stating that \"struct hashmap_entry\" must be the first\n> > member in a struct are no longer valid.\n> \n> After this patch, there is one \"/* must be the first member! */\"\n> comment left in convert.c, which is both misleading and unfortunate;\n> a structure 'subprocess_entry' wants to be the first field of any\n> enclosing structure, where 'subprocess_entry' has a hashmap_entry as\n> its first field.\n\nOops, I was only grepping for hashmap_entry :x\nThanks for the sharp eyes :>\n\nAlso, the first member requirement for the oidmap stuff could be\nlifted sometime in the future...\n\n----8<-----\nSubject: [PATCH] convert: drop invalid comment for subprocess_entry\n\n\"struct hashmap_entry\" inside \"struct subprocess_entry\"\nno longer needs to be the first member of any struct,\nso the old comment is no longer true.\n\nSigned-off-by: Eric Wong <e@80x24.org>\nReported-by: Junio C Hamano <gitster@pobox.com>\n---\n convert.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/convert.c b/convert.c\nindex 94ff837649..93c1e1eae9 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -753,7 +753,7 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le\n #define CAP_DELAY    (1u<<2)\n \n struct cmd2process {\n-\tstruct subprocess_entry subprocess; /* must be the first member! */\n+\tstruct subprocess_entry subprocess;\n \tunsigned int supported_capabilities;\n };\n \n"},{"id":"383640","messageId":"xmqq4l0koucn.fsf@gitster-ct.c.googlers.com","threadId":"51981","inReplyTo":"20191007084339.GA7808@dcvr","subject":"Re: [PATCH v3 21/20] convert: drop invalid comment for subprocess_entry","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-08T02:15:52Z","receivedAt":"2019-10-08T02:15:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <e@80x24.org> writes:\n\n> \"struct hashmap_entry\" inside \"struct subprocess_entry\"\n> no longer needs to be the first member of any struct,\n> so the old comment is no longer true.\n\nHmm, is that true?\n\nstruct cmd2process {\n\tstruct subprocess_entry subprocess;\n\tunsigned int supported_capabilities;\n};\n\nstatic int start_multi_file_filter_fn(struct subprocess_entry *subprocess)\n{\n\tstatic int versions[] = {2, 0};\n\tstatic struct subprocess_capability capabilities[] = {\n\t\t{ \"clean\",  CAP_CLEAN  },\n\t\t{ \"smudge\", CAP_SMUDGE },\n\t\t{ \"delay\",  CAP_DELAY  },\n\t\t{ NULL, 0 }\n\t};\n\tstruct cmd2process *entry = (struct cmd2process *)subprocess;\n\treturn subprocess_handshake(subprocess, \"git-filter\", versions, NULL,\n\t\t\t\t    capabilities,\n\t\t\t\t    &entry->supported_capabilities);\n}\n\nThe cast \"struct subprocess_entry *\" to \"struct cmd2process *\" we\nsee here does require that the address of the subprocess field must\nbe the same as the address of the structure itself.  So I'd have to\nsay that the comment still is true, but not for the reasons of what\nis in \"struct subprocess_entry\".\n"},{"id":"383645","messageId":"20191008025641.zinxoqsrncgzyjcq@dcvr","threadId":"51981","inReplyTo":"xmqq4l0koucn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 21/20] convert: drop invalid comment for subprocess_entry","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2019-10-08T02:56:41Z","receivedAt":"2019-10-08T02:56:43Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> Eric Wong <e@80x24.org> writes:\n> \n> > \"struct hashmap_entry\" inside \"struct subprocess_entry\"\n> > no longer needs to be the first member of any struct,\n> > so the old comment is no longer true.\n> \n> Hmm, is that true?\n> \n> struct cmd2process {\n> \tstruct subprocess_entry subprocess;\n> \tunsigned int supported_capabilities;\n> };\n> \n> static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)\n> {\n> \tstatic int versions[] = {2, 0};\n> \tstatic struct subprocess_capability capabilities[] = {\n> \t\t{ \"clean\",  CAP_CLEAN  },\n> \t\t{ \"smudge\", CAP_SMUDGE },\n> \t\t{ \"delay\",  CAP_DELAY  },\n> \t\t{ NULL, 0 }\n> \t};\n> \tstruct cmd2process *entry = (struct cmd2process *)subprocess;\n> \treturn subprocess_handshake(subprocess, \"git-filter\", versions, NULL,\n> \t\t\t\t    capabilities,\n> \t\t\t\t    &entry->supported_capabilities);\n> }\n> \n> The cast \"struct subprocess_entry *\" to \"struct cmd2process *\" we\n> see here does require that the address of the subprocess field must\n> be the same as the address of the structure itself.  So I'd have to\n> say that the comment still is true, but not for the reasons of what\n> is in \"struct subprocess_entry\".\n\nOops, right.   Let's just leave it at 20 patches for this series\nand skip this one, for now.\n\nI seem to recall there's a bunch of places where we do casts\nlike that which could be rewritten more flexibly with container_of.\n"},{"id":"383664","messageId":"nycvar.QRO.7.76.6.1910081055210.46@tvgsbejvaqbjf.bet","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"Re: [PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-08T08:58:25Z","receivedAt":"2019-10-08T08:58:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric & Junio,\n\nOn Sun, 6 Oct 2019, Eric Wong wrote:\n\n> v3 changes:\n> - use __typeof__ to avoid invalid clang warning on uninitialized var\n> - formatting fixes recommended by Stolee\n> - add Reviewed-by for Stolee\n> - add patch 20 to update docs to drop first member requirement\n\nThis has quite a bit of fallout, even more than previous rounds, it\nseems. I need at least these fixup???s to fix the build of `pu`:\n\nhttps://github.com/git-for-windows/git/commit/f74259754971b427a14e6290681e18950824b99d\nhttps://github.com/git-for-windows/git/commit/124c8bc08e974e76ca7d956dc07eb288e71d639e\nhttps://github.com/git-for-windows/git/commit/45948433d1b48ff513fbd37f134c0f1491c78192\n\nJunio, feel free to fetch and squash those into the appropriate merge\ncommit(s) (I feel that 4432e8b5f36 (Merge branch 'ds/sparse-cone' into\npu, 2019-10-08) may be the right spot).\n\nThanks,\nDscho\n\n>\n> v2 here:\n>   https://public-inbox.org/git/20190924010324.22619-1-e@80x24.org/\n>\n> The following changes since commit 745f6812895b31c02b29bdfe4ae8e5498f776c26:\n>\n>   First batch after Git 2.23 (2019-08-22 12:41:04 -0700)\n>\n> are available in the Git repository at:\n>\n>   https://80x24.org/git-svn.git hashmap-wip-v3\n>\n> for you to fetch changes up to 4ec5200646d6b9363e64e2002eb374aa888a6c88:\n>\n>   hashmap_entry: remove first member requirement from docs (2019-10-06 23:05:22 +0000)\n>\n> ----------------------------------------------------------------\n> Eric Wong (20):\n>       diff: use hashmap_entry_init on moved_entry.ent\n>       coccicheck: detect hashmap_entry.hash assignment\n>       packfile: use hashmap_entry in delta_base_cache_entry\n>       hashmap_entry_init takes \"struct hashmap_entry *\"\n>       hashmap_get_next takes \"const struct hashmap_entry *\"\n>       hashmap_add takes \"struct hashmap_entry *\"\n>       hashmap_get takes \"const struct hashmap_entry *\"\n>       hashmap_remove takes \"const struct hashmap_entry *\"\n>       hashmap_put takes \"struct hashmap_entry *\"\n>       introduce container_of macro\n>       hashmap_get_next returns \"struct hashmap_entry *\"\n>       hashmap: use *_entry APIs to wrap container_of\n>       hashmap_get{,_from_hash} return \"struct hashmap_entry *\"\n>       hashmap_cmp_fn takes hashmap_entry params\n>       hashmap: use *_entry APIs for iteration\n>       hashmap: hashmap_{put,remove} return hashmap_entry *\n>       hashmap: introduce hashmap_free_entries\n>       OFFSETOF_VAR macro to simplify hashmap iterators\n>       hashmap: remove type arg from hashmap_{get,put,remove}_entry\n>       hashmap_entry: remove first member requirement from docs\n>\n>  attr.c                              |  24 ++---\n>  blame.c                             |  25 ++---\n>  builtin/describe.c                  |  21 +++--\n>  builtin/difftool.c                  |  56 +++++++-----\n>  builtin/fast-export.c               |  15 ++-\n>  builtin/fetch.c                     |  32 ++++---\n>  config.c                            |  24 ++---\n>  contrib/coccinelle/hashmap.cocci    |  16 ++++\n>  diff.c                              |  31 ++++---\n>  diffcore-rename.c                   |  15 ++-\n>  git-compat-util.h                   |  38 ++++++++\n>  hashmap.c                           |  58 +++++++-----\n>  hashmap.h                           | 176 +++++++++++++++++++++++++++++-------\n>  merge-recursive.c                   |  87 ++++++++++--------\n>  merge-recursive.h                   |   4 +-\n>  name-hash.c                         |  57 ++++++------\n>  oidmap.c                            |  20 ++--\n>  oidmap.h                            |   6 +-\n>  packfile.c                          |  22 +++--\n>  patch-ids.c                         |  18 ++--\n>  range-diff.c                        |  10 +-\n>  ref-filter.c                        |  33 ++++---\n>  refs.c                              |  25 +++--\n>  remote.c                            |  21 +++--\n>  remote.h                            |   2 +-\n>  revision.c                          |  28 +++---\n>  sequencer.c                         |  44 ++++++---\n>  sub-process.c                       |  20 ++--\n>  sub-process.h                       |   6 +-\n>  submodule-config.c                  |  52 ++++++-----\n>  t/helper/test-hashmap.c             |  50 +++++-----\n>  t/helper/test-lazy-init-name-hash.c |  12 +--\n>  32 files changed, 667 insertions(+), 381 deletions(-)\n>  create mode 100644 contrib/coccinelle/hashmap.cocci\n>\n> Range-diff against v2:\n>  1:  51ba91d2f9 !  1:  0f530552de diff: use hashmap_entry_init on moved_entry.ent\n>     @@ -10,6 +10,7 @@\n>          hashmap_add callers to take \"struct hashmap_entry *\".\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/diff.c b/diff.c\n>       --- a/diff.c\n>  2:  be4071d33d !  2:  d12ddb4327 coccicheck: detect hashmap_entry.hash assignment\n>     @@ -8,6 +8,7 @@\n>          hashmap_entry_init, instead.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/contrib/coccinelle/hashmap.cocci b/contrib/coccinelle/hashmap.cocci\n>       new file mode 100644\n>  3:  ff40a39bad !  3:  624b89fbb4 packfile: use hashmap_entry in delta_base_cache_entry\n>     @@ -15,6 +15,7 @@\n>          systems.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/packfile.c b/packfile.c\n>       --- a/packfile.c\n>  4:  d1db0f6472 !  4:  7512f2f2f7 hashmap_entry_init takes \"struct hashmap_entry *\"\n>     @@ -8,6 +8,7 @@\n>          safety and readability.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>     @@ -212,8 +213,8 @@\n>        * and if it is on stack, you can just let it go out of scope).\n>        */\n>      -static inline void hashmap_entry_init(void *entry, unsigned int hash)\n>     -+static inline void\n>     -+hashmap_entry_init(struct hashmap_entry *e, unsigned int hash)\n>     ++static inline void hashmap_entry_init(struct hashmap_entry *e,\n>     ++\t\t\t\t\tunsigned int hash)\n>       {\n>      -\tstruct hashmap_entry *e = entry;\n>       \te->hash = hash;\n>  5:  a293445e97 !  5:  dd167a1113 hashmap_get_next takes \"const struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          now detects invalid types being passed.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/diff.c b/diff.c\n>       --- a/diff.c\n>  6:  beabdab6fe !  6:  53f429ef6f hashmap_add takes \"struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          detects invalid types being passed.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>  7:  99970934c7 !  7:  87739268dc hashmap_get takes \"const struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          now detects invalid types being passed.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>  8:  8148ed70eb !  8:  22226b3bb4 hashmap_remove takes \"const struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          now detects invalid types being passed.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/blame.c b/blame.c\n>       --- a/blame.c\n>  9:  3756dcc34e !  9:  0b64282a1f hashmap_put takes \"struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          detects invalid types being passed.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/builtin/fast-export.c b/builtin/fast-export.c\n>       --- a/builtin/fast-export.c\n> 10:  ff586aa43c ! 10:  c2e9201838 introduce container_of macro\n>     @@ -7,15 +7,16 @@\n>          and chained hash tables while allowing the compiler to do\n>          type checking.\n>\n>     -    I intend to use this to remove the limitation of \"hashmap_entry\"\n>     -    being location-dependent and to allow more compile-time type\n>     -    checking.\n>     +    Later patches will use container_of() to remove the limitation\n>     +    of \"hashmap_entry\" being location-dependent. This will complete\n>     +    the transition to compile-time type checking for the hashmap API.\n>\n>          This macro already exists in our source as \"list_entry\" in\n>          list.h and making \"list_entry\" an alias to \"container_of\"\n>          as the Linux kernel has done is a possibility.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/git-compat-util.h b/git-compat-util.h\n>       --- a/git-compat-util.h\n> 11:  931356f061 ! 11:  c709e607b1 hashmap_get_next returns \"struct hashmap_entry *\"\n>     @@ -6,6 +6,7 @@\n>          hashmap_entry being the first field of a struct.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/diff.c b/diff.c\n>       --- a/diff.c\n>     @@ -14,10 +15,10 @@\n>       {\n>       \tint i;\n>       \tchar *got_match = xcalloc(1, pmb_nr);\n>     -+\tstruct hashmap_entry *ent = &match->ent;\n>     ++\tstruct hashmap_entry *ent;\n>\n>      -\tfor (; match; match = hashmap_get_next(hm, &match->ent)) {\n>     -+\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n>     ++\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n>      +\t\tmatch = container_of(ent, struct moved_entry, ent);\n>       \t\tfor (i = 0; i < pmb_nr; i++) {\n>       \t\t\tstruct moved_entry *prev = pmb[i].match;\n> 12:  a748b9b0c4 ! 12:  27ba68a7b8 hashmap: use *_entry APIs to wrap container_of\n>     @@ -11,6 +11,7 @@\n>          extra parameter to specify the type.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/diff.c b/diff.c\n>       --- a/diff.c\n>     @@ -19,9 +20,9 @@\n>       {\n>       \tint i;\n>       \tchar *got_match = xcalloc(1, pmb_nr);\n>     --\tstruct hashmap_entry *ent = &match->ent;\n>     +-\tstruct hashmap_entry *ent;\n>\n>     --\tfor (; ent; ent = hashmap_get_next(hm, ent)) {\n>     +-\tfor (ent = &match->ent; ent; ent = hashmap_get_next(hm, ent)) {\n>      -\t\tmatch = container_of(ent, struct moved_entry, ent);\n>      +\thashmap_for_each_entry_from(hm, match, struct moved_entry, ent) {\n>       \t\tfor (i = 0; i < pmb_nr; i++) {\n>     @@ -125,7 +126,6 @@\n>       #define container_of(ptr, type, member) \\\n>       \t((type *) ((char *)(ptr) - offsetof(type, member)))\n>\n>     -+\n>      +/*\n>      + * helper function for `container_of_or_null' to avoid multiple\n>      + * evaluation of @ptr\n> 13:  e8faa5e12e ! 13:  222b1f07f3 hashmap_get{,_from_hash} return \"struct hashmap_entry *\"\n>     @@ -9,6 +9,7 @@\n>          hashmap_entry being the first field in a struct.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>     @@ -152,11 +153,13 @@\n>       }\n>\n>      -void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n>     -+struct hashmap_entry *\n>     -+hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n>     - \t\tconst void *keydata)\n>     +-\t\tconst void *keydata)\n>     ++struct hashmap_entry *hashmap_get(const struct hashmap *map,\n>     ++\t\t\t\tconst struct hashmap_entry *key,\n>     ++\t\t\t\tconst void *keydata)\n>       {\n>       \treturn *find_entry_ptr(map, key, keydata);\n>     + }\n>      @@\n>       \t/* lookup interned string in pool */\n>       \thashmap_entry_init(&key.ent, memhash(data, len));\n>     @@ -175,21 +178,27 @@\n>        * to `hashmap_cmp_fn` to decide whether the entry matches the key.\n>        */\n>      -void *hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n>     -+struct hashmap_entry *\n>     -+hashmap_get(const struct hashmap *map, const struct hashmap_entry *key,\n>     - \t\t\t const void *keydata);\n>     +-\t\t\t const void *keydata);\n>     ++struct hashmap_entry *hashmap_get(const struct hashmap *map,\n>     ++\t\t\t\tconst struct hashmap_entry *key,\n>     ++\t\t\t\tconst void *keydata);\n>\n>       /*\n>     +  * Returns the hashmap entry for the specified hash code and key data,\n>      @@\n>        * `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry\n>        * structure that should not be used in the comparison.\n>        */\n>      -static inline void *hashmap_get_from_hash(const struct hashmap *map,\n>     -+static inline struct hashmap_entry *\n>     -+hashmap_get_from_hash(const struct hashmap *map,\n>     - \t\t\t\t\t  unsigned int hash,\n>     - \t\t\t\t\t  const void *keydata)\n>     +-\t\t\t\t\t  unsigned int hash,\n>     +-\t\t\t\t\t  const void *keydata)\n>     ++static inline struct hashmap_entry *hashmap_get_from_hash(\n>     ++\t\t\t\t\tconst struct hashmap *map,\n>     ++\t\t\t\t\tunsigned int hash,\n>     ++\t\t\t\t\tconst void *keydata)\n>       {\n>     + \tstruct hashmap_entry key;\n>     + \thashmap_entry_init(&key, hash);\n>\n>       diff --git a/merge-recursive.c b/merge-recursive.c\n>       --- a/merge-recursive.c\n> 14:  f84bd96b8e ! 14:  c789f98c81 hashmap_cmp_fn takes hashmap_entry params\n>     @@ -6,6 +6,7 @@\n>          being the first member of a struct.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n> 15:  23fbd888d9 ! 15:  57ae5b5142 hashmap: use *_entry APIs for iteration\n>     @@ -7,6 +7,7 @@\n>          by compilers lacking __typeof__ support.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n> 16:  6908364381 ! 16:  f1e35ba094 hashmap: hashmap_{put,remove} return hashmap_entry *\n>     @@ -6,6 +6,7 @@\n>          to simplify most callers.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/hashmap.c b/hashmap.c\n>       --- a/hashmap.c\n>     @@ -15,18 +16,20 @@\n>       }\n>\n>      -void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n>     -+struct hashmap_entry *\n>     -+hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n>     - \t\tconst void *keydata)\n>     +-\t\tconst void *keydata)\n>     ++struct hashmap_entry *hashmap_remove(struct hashmap *map,\n>     ++\t\t\t\t\tconst struct hashmap_entry *key,\n>     ++\t\t\t\t\tconst void *keydata)\n>       {\n>       \tstruct hashmap_entry *old;\n>     + \tstruct hashmap_entry **e = find_entry_ptr(map, key, keydata);\n>      @@\n>       \treturn old;\n>       }\n>\n>      -void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n>     -+struct hashmap_entry *\n>     -+hashmap_put(struct hashmap *map, struct hashmap_entry *entry)\n>     ++struct hashmap_entry *hashmap_put(struct hashmap *map,\n>     ++\t\t\t\tstruct hashmap_entry *entry)\n>       {\n>       \tstruct hashmap_entry *old = hashmap_remove(map, entry, NULL);\n>       \thashmap_add(map, entry);\n>     @@ -39,8 +42,8 @@\n>        * Returns the replaced entry, or NULL if not found (i.e. the entry was added).\n>        */\n>      -void *hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n>     -+struct hashmap_entry *\n>     -+hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n>     ++struct hashmap_entry *hashmap_put(struct hashmap *map,\n>     ++\t\t\t\tstruct hashmap_entry *entry);\n>      +\n>      +#define hashmap_put_entry(map, keyvar, type, member) \\\n>      +\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n>     @@ -52,17 +55,17 @@\n>        * Argument explanation is the same as in `hashmap_get`.\n>        */\n>      -void *hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n>     -+struct hashmap_entry *\n>     -+hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n>     - \t\tconst void *keydata);\n>     -\n>     +-\t\tconst void *keydata);\n>     ++struct hashmap_entry *hashmap_remove(struct hashmap *map,\n>     ++\t\t\t\t\tconst struct hashmap_entry *key,\n>     ++\t\t\t\t\tconst void *keydata);\n>     ++\n>      +#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n>      +\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n>      +\t\t\t\ttype, member)\n>     -+\n>     +\n>       /*\n>        * Returns the `bucket` an entry is stored in.\n>     -  * Useful for multithreaded read access.\n>\n>       diff --git a/range-diff.c b/range-diff.c\n>       --- a/range-diff.c\n> 17:  150944128b ! 17:  adc7cb5516 hashmap: introduce hashmap_free_entries\n>     @@ -12,6 +12,7 @@\n>          the hashmap itself.\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/blame.c b/blame.c\n>       --- a/blame.c\n> 18:  de212aa6d6 ! 18:  1a68e1645d OFFSETOF_VAR macro to simplify hashmap iterators\n>     @@ -5,7 +5,7 @@\n>          While we cannot rely on a `__typeof__' operator being portable\n>          to use with `offsetof'; we can calculate the pointer offset\n>          using an existing pointer and the address of a member using\n>     -    pointer arithmetic.\n>     +    pointer arithmetic for compilers without `__typeof__'.\n>\n>          This allows us to simplify usage of hashmap iterator macros\n>          by not having to specify a type when a pointer of that type\n>     @@ -16,7 +16,10 @@\n>          trouble of using container_of/list_entry macros and without\n>          relying on non-portable `__typeof__'.\n>\n>     +    v3: use `__typeof__' to avoid clang warnings\n>     +\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>     @@ -141,11 +144,17 @@\n>       \t(type *)container_of_or_null_offset(ptr, offsetof(type, member))\n>\n>      +/*\n>     -+ * like offsetof(), but takes a pointer to type instead of the type\n>     -+ * @ptr is subject to multiple evaluation since we can't rely on TYPEOF()\n>     ++ * like offsetof(), but takes a pointer to a a variable of type which\n>     ++ * contains @member, instead of a specified type.\n>     ++ * @ptr is subject to multiple evaluation since we can't rely on __typeof__\n>     ++ * everywhere.\n>      + */\n>     ++#if defined(__GNUC__) /* clang sets this, too */\n>     ++#define OFFSETOF_VAR(ptr, member) offsetof(__typeof__(*ptr), member)\n>     ++#else /* !__GNUC__ */\n>      +#define OFFSETOF_VAR(ptr, member) \\\n>      +\t((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))\n>     ++#endif /* !__GNUC__ */\n>      +\n>       #endif\n>\n> 19:  212a596edd ! 19:  fe02b9f839 hashmap: remove type arg from hashmap_{get,put,remove}_entry\n>     @@ -10,6 +10,7 @@\n>          sequential as they are used as: `keyvar->member'\n>\n>          Signed-off-by: Eric Wong <e@80x24.org>\n>     +    Reviewed-by: Derrick Stolee <stolee@gmail.com>\n>\n>       diff --git a/attr.c b/attr.c\n>       --- a/attr.c\n>     @@ -147,8 +148,8 @@\n>        *                 printf(\"first: %ld %s\\n\", e->key, e->value);\n>        *                 while ((e = hashmap_get_next_entry(&map, e,\n>      @@\n>     - struct hashmap_entry *\n>     - hashmap_put(struct hashmap *map, struct hashmap_entry *entry);\n>     + struct hashmap_entry *hashmap_put(struct hashmap *map,\n>     + \t\t\t\tstruct hashmap_entry *entry);\n>\n>      -#define hashmap_put_entry(map, keyvar, type, member) \\\n>      -\tcontainer_of_or_null(hashmap_put(map, &(keyvar)->member), type, member)\n>     @@ -167,8 +168,8 @@\n>       /*\n>        * Removes a hashmap entry matching the specified key. If the hashmap contains\n>      @@\n>     - hashmap_remove(struct hashmap *map, const struct hashmap_entry *key,\n>     - \t\tconst void *keydata);\n>     + \t\t\t\t\tconst struct hashmap_entry *key,\n>     + \t\t\t\t\tconst void *keydata);\n>\n>      -#define hashmap_remove_entry(map, keyvar, keydata, type, member) \\\n>      -\tcontainer_of_or_null(hashmap_remove(map, &(keyvar)->member, keydata), \\\n>  -:  ---------- > 20:  4ec5200646 hashmap_entry: remove first member requirement from docs\n>\n>\n"},{"id":"383683","messageId":"111295eb-8bac-7074-158d-a65903de9db7@gmail.com","threadId":"51981","inReplyTo":"nycvar.QRO.7.76.6.1910081055210.46@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-10-08T13:56:26Z","receivedAt":"2019-10-08T13:56:30Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 10/8/2019 4:58 AM, Johannes Schindelin wrote:\n> Hi Eric & Junio,\n> \n> On Sun, 6 Oct 2019, Eric Wong wrote:\n> \n>> v3 changes:\n>> - use __typeof__ to avoid invalid clang warning on uninitialized var\n>> - formatting fixes recommended by Stolee\n>> - add Reviewed-by for Stolee\n>> - add patch 20 to update docs to drop first member requirement\n> \n> This has quite a bit of fallout, even more than previous rounds, it\n> seems. I need at least these fixup???s to fix the build of `pu`:\n> \n> https://github.com/git-for-windows/git/commit/f74259754971b427a14e6290681e18950824b99d\n> https://github.com/git-for-windows/git/commit/124c8bc08e974e76ca7d956dc07eb288e71d639e\n> https://github.com/git-for-windows/git/commit/45948433d1b48ff513fbd37f134c0f1491c78192\n> \n> Junio, feel free to fetch and squash those into the appropriate merge\n> commit(s) (I feel that 4432e8b5f36 (Merge branch 'ds/sparse-cone' into\n> pu, 2019-10-08) may be the right spot).\n\nI expected that my series would have semantic conflicts with this series [1].\nI could re-work my code to take these changes into account, based on an\nappropriate merge of ds/include-exclude and ew/hashmap. ew/hashmap is likely\nmuch closer to merging than ds/sparse-cone.\n\nThanks for doing most of the heavy lifting, Johannes! I'll probably start by\ntaking your fixups into the appropriate commits of my series.\n\n-Stolee\n\n[1] https://public-inbox.org/git/d16b22c9-c437-0947-fb5a-9421f987a1f1@gmail.com/\n"},{"id":"383684","messageId":"b4e667d3-89ee-7f0f-55a5-10926d615ace@gmail.com","threadId":"51981","inReplyTo":"20191006233043.3516-1-e@80x24.org","subject":"Re: [PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-10-08T13:59:37Z","receivedAt":"2019-10-08T13:59:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 10/6/2019 7:30 PM, Eric Wong wrote:\n> v3 changes:\n> - use __typeof__ to avoid invalid clang warning on uninitialized var\n> - formatting fixes recommended by Stolee\n> - add Reviewed-by for Stolee\n> - add patch 20 to update docs to drop first member requirement\n> \n> Range-diff against v2:\n\nI scanned the range-diff (and patch 20) and think this series is in good shape.\nMy Reviewed-by stands.\n\nThanks,\n-Stolee\n\n"},{"id":"383718","messageId":"xmqq1rvmn285.fsf@gitster-ct.c.googlers.com","threadId":"51981","inReplyTo":"b4e667d3-89ee-7f0f-55a5-10926d615ace@gmail.com","subject":"Re: [PATCH v3 00/20] hashmap bug/safety/ease-of-use fixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-09T01:20:58Z","receivedAt":"2019-10-09T01:21:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 10/6/2019 7:30 PM, Eric Wong wrote:\n>> v3 changes:\n>> - use __typeof__ to avoid invalid clang warning on uninitialized var\n>> - formatting fixes recommended by Stolee\n>> - add Reviewed-by for Stolee\n>> - add patch 20 to update docs to drop first member requirement\n>> \n>> Range-diff against v2:\n>\n> I scanned the range-diff (and patch 20) and think this series is in good shape.\n> My Reviewed-by stands.\n\nThanks.\n"}]}