mirror of
https://github.com/git/git.git
synced 2024-05-04 11:16:11 +02:00
Merge branch 'en/strmap'
A specialization of hashmap that uses a string as key has been introduced. Hopefully it will see wider use over time. * en/strmap: shortlog: use strset from strmap.h Use new HASHMAP_INIT macro to simplify hashmap initialization strmap: take advantage of FLEXPTR_ALLOC_STR when relevant strmap: enable allocations to come from a mem_pool strmap: add a strset sub-type strmap: split create_entry() out of strmap_put() strmap: add functions facilitating use as a string->int map strmap: enable faster clearing and reusing of strmaps strmap: add more utility functions strmap: new utility functions hashmap: provide deallocation function names hashmap: introduce a new hashmap_partial_clear() hashmap: allow re-use after hashmap_free() hashmap: adjust spacing to fix argument alignment hashmap: add usage documentation explaining hashmap_free[_entries]()
This commit is contained in:
commit
bf0a430f70
1
Makefile
1
Makefile
|
@ -1004,6 +1004,7 @@ LIB_OBJS += stable-qsort.o
|
||||||
LIB_OBJS += strbuf.o
|
LIB_OBJS += strbuf.o
|
||||||
LIB_OBJS += streaming.o
|
LIB_OBJS += streaming.o
|
||||||
LIB_OBJS += string-list.o
|
LIB_OBJS += string-list.o
|
||||||
|
LIB_OBJS += strmap.o
|
||||||
LIB_OBJS += strvec.o
|
LIB_OBJS += strvec.o
|
||||||
LIB_OBJS += sub-process.o
|
LIB_OBJS += sub-process.o
|
||||||
LIB_OBJS += submodule-config.o
|
LIB_OBJS += submodule-config.o
|
||||||
|
|
|
@ -557,7 +557,7 @@ static int get_modified_files(struct repository *r,
|
||||||
if (ps)
|
if (ps)
|
||||||
clear_pathspec(&rev.prune_data);
|
clear_pathspec(&rev.prune_data);
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
|
hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
|
||||||
if (unmerged_count)
|
if (unmerged_count)
|
||||||
*unmerged_count = s.unmerged_count;
|
*unmerged_count = s.unmerged_count;
|
||||||
if (binary_count)
|
if (binary_count)
|
||||||
|
|
26
attr.c
26
attr.c
|
@ -52,13 +52,6 @@ static inline void hashmap_unlock(struct attr_hashmap *map)
|
||||||
pthread_mutex_unlock(&map->mutex);
|
pthread_mutex_unlock(&map->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The global dictionary of all interned attributes. This
|
|
||||||
* is a singleton object which is shared between threads.
|
|
||||||
* Access to this dictionary must be surrounded with a mutex.
|
|
||||||
*/
|
|
||||||
static struct attr_hashmap g_attr_hashmap;
|
|
||||||
|
|
||||||
/* The container for objects stored in "struct attr_hashmap" */
|
/* The container for objects stored in "struct attr_hashmap" */
|
||||||
struct attr_hash_entry {
|
struct attr_hash_entry {
|
||||||
struct hashmap_entry ent;
|
struct hashmap_entry ent;
|
||||||
|
@ -80,11 +73,14 @@ static int attr_hash_entry_cmp(const void *unused_cmp_data,
|
||||||
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
|
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize an 'attr_hashmap' object */
|
/*
|
||||||
static void attr_hashmap_init(struct attr_hashmap *map)
|
* The global dictionary of all interned attributes. This
|
||||||
{
|
* is a singleton object which is shared between threads.
|
||||||
hashmap_init(&map->map, attr_hash_entry_cmp, NULL, 0);
|
* Access to this dictionary must be surrounded with a mutex.
|
||||||
}
|
*/
|
||||||
|
static struct attr_hashmap g_attr_hashmap = {
|
||||||
|
HASHMAP_INIT(attr_hash_entry_cmp, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieve the 'value' stored in a hashmap given the provided 'key'.
|
* Retrieve the 'value' stored in a hashmap given the provided 'key'.
|
||||||
|
@ -96,9 +92,6 @@ static void *attr_hashmap_get(struct attr_hashmap *map,
|
||||||
struct attr_hash_entry k;
|
struct attr_hash_entry k;
|
||||||
struct attr_hash_entry *e;
|
struct attr_hash_entry *e;
|
||||||
|
|
||||||
if (!map->map.tablesize)
|
|
||||||
attr_hashmap_init(map);
|
|
||||||
|
|
||||||
hashmap_entry_init(&k.ent, memhash(key, keylen));
|
hashmap_entry_init(&k.ent, memhash(key, keylen));
|
||||||
k.key = key;
|
k.key = key;
|
||||||
k.keylen = keylen;
|
k.keylen = keylen;
|
||||||
|
@ -114,9 +107,6 @@ static void attr_hashmap_add(struct attr_hashmap *map,
|
||||||
{
|
{
|
||||||
struct attr_hash_entry *e;
|
struct attr_hash_entry *e;
|
||||||
|
|
||||||
if (!map->map.tablesize)
|
|
||||||
attr_hashmap_init(map);
|
|
||||||
|
|
||||||
e = xmalloc(sizeof(struct attr_hash_entry));
|
e = xmalloc(sizeof(struct attr_hash_entry));
|
||||||
hashmap_entry_init(&e->ent, memhash(key, keylen));
|
hashmap_entry_init(&e->ent, memhash(key, keylen));
|
||||||
e->key = key;
|
e->key = key;
|
||||||
|
|
2
blame.c
2
blame.c
|
@ -435,7 +435,7 @@ static void get_fingerprint(struct fingerprint *result,
|
||||||
|
|
||||||
static void free_fingerprint(struct fingerprint *f)
|
static void free_fingerprint(struct fingerprint *f)
|
||||||
{
|
{
|
||||||
hashmap_free(&f->map);
|
hashmap_clear(&f->map);
|
||||||
free(f->entries);
|
free(f->entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
bloom.c
5
bloom.c
|
@ -229,10 +229,9 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
|
||||||
diffcore_std(&diffopt);
|
diffcore_std(&diffopt);
|
||||||
|
|
||||||
if (diff_queued_diff.nr <= settings->max_changed_paths) {
|
if (diff_queued_diff.nr <= settings->max_changed_paths) {
|
||||||
struct hashmap pathmap;
|
struct hashmap pathmap = HASHMAP_INIT(pathmap_cmp, NULL);
|
||||||
struct pathmap_hash_entry *e;
|
struct pathmap_hash_entry *e;
|
||||||
struct hashmap_iter iter;
|
struct hashmap_iter iter;
|
||||||
hashmap_init(&pathmap, pathmap_cmp, NULL, 0);
|
|
||||||
|
|
||||||
for (i = 0; i < diff_queued_diff.nr; i++) {
|
for (i = 0; i < diff_queued_diff.nr; i++) {
|
||||||
const char *path = diff_queued_diff.queue[i]->two->path;
|
const char *path = diff_queued_diff.queue[i]->two->path;
|
||||||
|
@ -287,7 +286,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry);
|
hashmap_clear_and_free(&pathmap, struct pathmap_hash_entry, entry);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < diff_queued_diff.nr; i++)
|
for (i = 0; i < diff_queued_diff.nr; i++)
|
||||||
diff_free_filepair(diff_queued_diff.queue[i]);
|
diff_free_filepair(diff_queued_diff.queue[i]);
|
||||||
|
|
|
@ -342,7 +342,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
|
||||||
const char *workdir, *tmp;
|
const char *workdir, *tmp;
|
||||||
int ret = 0, i;
|
int ret = 0, i;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct hashmap working_tree_dups, submodules, symlinks2;
|
struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp,
|
||||||
|
NULL);
|
||||||
|
struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL);
|
||||||
|
struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
|
||||||
struct hashmap_iter iter;
|
struct hashmap_iter iter;
|
||||||
struct pair_entry *entry;
|
struct pair_entry *entry;
|
||||||
struct index_state wtindex;
|
struct index_state wtindex;
|
||||||
|
@ -383,10 +386,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
|
||||||
rdir_len = rdir.len;
|
rdir_len = rdir.len;
|
||||||
wtdir_len = wtdir.len;
|
wtdir_len = wtdir.len;
|
||||||
|
|
||||||
hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0);
|
|
||||||
hashmap_init(&submodules, pair_cmp, NULL, 0);
|
|
||||||
hashmap_init(&symlinks2, pair_cmp, NULL, 0);
|
|
||||||
|
|
||||||
child.no_stdin = 1;
|
child.no_stdin = 1;
|
||||||
child.git_cmd = 1;
|
child.git_cmd = 1;
|
||||||
child.use_shell = 0;
|
child.use_shell = 0;
|
||||||
|
|
|
@ -393,7 +393,7 @@ static void find_non_local_tags(const struct ref *refs,
|
||||||
item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
|
item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
|
||||||
string_list_insert(&remote_refs_list, ref->name);
|
string_list_insert(&remote_refs_list, ref->name);
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
|
hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We may have a final lightweight tag that needs to be
|
* We may have a final lightweight tag that needs to be
|
||||||
|
@ -428,7 +428,7 @@ static void find_non_local_tags(const struct ref *refs,
|
||||||
**tail = rm;
|
**tail = rm;
|
||||||
*tail = &rm->next;
|
*tail = &rm->next;
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
|
hashmap_clear_and_free(&remote_refs, struct refname_hash_entry, ent);
|
||||||
string_list_clear(&remote_refs_list, 0);
|
string_list_clear(&remote_refs_list, 0);
|
||||||
oidset_clear(&fetch_oids);
|
oidset_clear(&fetch_oids);
|
||||||
}
|
}
|
||||||
|
@ -573,7 +573,7 @@ static struct ref *get_ref_map(struct remote *remote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (existing_refs_populated)
|
if (existing_refs_populated)
|
||||||
hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
|
hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent);
|
||||||
|
|
||||||
return ref_map;
|
return ref_map;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "shortlog.h"
|
#include "shortlog.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
#include "trailer.h"
|
#include "trailer.h"
|
||||||
|
#include "strmap.h"
|
||||||
|
|
||||||
static char const * const shortlog_usage[] = {
|
static char const * const shortlog_usage[] = {
|
||||||
N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
|
N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
|
||||||
|
@ -169,60 +170,6 @@ static void read_from_stdin(struct shortlog *log)
|
||||||
strbuf_release(&oneline);
|
strbuf_release(&oneline);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct strset_item {
|
|
||||||
struct hashmap_entry ent;
|
|
||||||
char value[FLEX_ARRAY];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct strset {
|
|
||||||
struct hashmap map;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define STRSET_INIT { { NULL } }
|
|
||||||
|
|
||||||
static int strset_item_hashcmp(const void *hash_data,
|
|
||||||
const struct hashmap_entry *entry,
|
|
||||||
const struct hashmap_entry *entry_or_key,
|
|
||||||
const void *keydata)
|
|
||||||
{
|
|
||||||
const struct strset_item *a, *b;
|
|
||||||
|
|
||||||
a = container_of(entry, const struct strset_item, ent);
|
|
||||||
if (keydata)
|
|
||||||
return strcmp(a->value, keydata);
|
|
||||||
|
|
||||||
b = container_of(entry_or_key, const struct strset_item, ent);
|
|
||||||
return strcmp(a->value, b->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adds "str" to the set if it was not already present; returns true if it was
|
|
||||||
* already there.
|
|
||||||
*/
|
|
||||||
static int strset_check_and_add(struct strset *ss, const char *str)
|
|
||||||
{
|
|
||||||
unsigned int hash = strhash(str);
|
|
||||||
struct strset_item *item;
|
|
||||||
|
|
||||||
if (!ss->map.table)
|
|
||||||
hashmap_init(&ss->map, strset_item_hashcmp, NULL, 0);
|
|
||||||
|
|
||||||
if (hashmap_get_from_hash(&ss->map, hash, str))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
FLEX_ALLOC_STR(item, value, str);
|
|
||||||
hashmap_entry_init(&item->ent, hash);
|
|
||||||
hashmap_add(&ss->map, &item->ent);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void strset_clear(struct strset *ss)
|
|
||||||
{
|
|
||||||
if (!ss->map.table)
|
|
||||||
return;
|
|
||||||
hashmap_free_entries(&ss->map, struct strset_item, ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void insert_records_from_trailers(struct shortlog *log,
|
static void insert_records_from_trailers(struct shortlog *log,
|
||||||
struct strset *dups,
|
struct strset *dups,
|
||||||
struct commit *commit,
|
struct commit *commit,
|
||||||
|
@ -253,7 +200,7 @@ static void insert_records_from_trailers(struct shortlog *log,
|
||||||
if (!parse_ident(log, &ident, value))
|
if (!parse_ident(log, &ident, value))
|
||||||
value = ident.buf;
|
value = ident.buf;
|
||||||
|
|
||||||
if (strset_check_and_add(dups, value))
|
if (!strset_add(dups, value))
|
||||||
continue;
|
continue;
|
||||||
insert_one_record(log, value, oneline);
|
insert_one_record(log, value, oneline);
|
||||||
}
|
}
|
||||||
|
@ -291,7 +238,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||||
log->email ? "%aN <%aE>" : "%aN",
|
log->email ? "%aN <%aE>" : "%aN",
|
||||||
&ident, &ctx);
|
&ident, &ctx);
|
||||||
if (!HAS_MULTI_BITS(log->groups) ||
|
if (!HAS_MULTI_BITS(log->groups) ||
|
||||||
!strset_check_and_add(&dups, ident.buf))
|
strset_add(&dups, ident.buf))
|
||||||
insert_one_record(log, ident.buf, oneline_str);
|
insert_one_record(log, ident.buf, oneline_str);
|
||||||
}
|
}
|
||||||
if (log->groups & SHORTLOG_GROUP_COMMITTER) {
|
if (log->groups & SHORTLOG_GROUP_COMMITTER) {
|
||||||
|
@ -300,7 +247,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||||
log->email ? "%cN <%cE>" : "%cN",
|
log->email ? "%cN <%cE>" : "%cN",
|
||||||
&ident, &ctx);
|
&ident, &ctx);
|
||||||
if (!HAS_MULTI_BITS(log->groups) ||
|
if (!HAS_MULTI_BITS(log->groups) ||
|
||||||
!strset_check_and_add(&dups, ident.buf))
|
strset_add(&dups, ident.buf))
|
||||||
insert_one_record(log, ident.buf, oneline_str);
|
insert_one_record(log, ident.buf, oneline_str);
|
||||||
}
|
}
|
||||||
if (log->groups & SHORTLOG_GROUP_TRAILER) {
|
if (log->groups & SHORTLOG_GROUP_TRAILER) {
|
||||||
|
|
2
config.c
2
config.c
|
@ -1963,7 +1963,7 @@ void git_configset_clear(struct config_set *cs)
|
||||||
free(entry->key);
|
free(entry->key);
|
||||||
string_list_clear(&entry->value_list, 1);
|
string_list_clear(&entry->value_list, 1);
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
|
hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
|
||||||
cs->hash_initialized = 0;
|
cs->hash_initialized = 0;
|
||||||
free(cs->list.items);
|
free(cs->list.items);
|
||||||
cs->list.nr = 0;
|
cs->list.nr = 0;
|
||||||
|
|
4
diff.c
4
diff.c
|
@ -6315,9 +6315,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
|
||||||
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
|
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
|
||||||
dim_moved_lines(o);
|
dim_moved_lines(o);
|
||||||
|
|
||||||
hashmap_free_entries(&add_lines, struct moved_entry,
|
hashmap_clear_and_free(&add_lines, struct moved_entry,
|
||||||
ent);
|
ent);
|
||||||
hashmap_free_entries(&del_lines, struct moved_entry,
|
hashmap_clear_and_free(&del_lines, struct moved_entry,
|
||||||
ent);
|
ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -407,7 +407,7 @@ static int find_exact_renames(struct diff_options *options)
|
||||||
renames += find_identical_files(&file_table, i, options);
|
renames += find_identical_files(&file_table, i, options);
|
||||||
|
|
||||||
/* Free the hash data structure and entries */
|
/* Free the hash data structure and entries */
|
||||||
hashmap_free_entries(&file_table, struct file_similarity, entry);
|
hashmap_clear_and_free(&file_table, struct file_similarity, entry);
|
||||||
|
|
||||||
return renames;
|
return renames;
|
||||||
}
|
}
|
||||||
|
|
8
dir.c
8
dir.c
|
@ -817,8 +817,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
|
||||||
|
|
||||||
clear_hashmaps:
|
clear_hashmaps:
|
||||||
warning(_("disabling cone pattern matching"));
|
warning(_("disabling cone pattern matching"));
|
||||||
hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
|
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
|
||||||
hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
|
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
|
||||||
pl->use_cone_patterns = 0;
|
pl->use_cone_patterns = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,8 +921,8 @@ void clear_pattern_list(struct pattern_list *pl)
|
||||||
free(pl->patterns[i]);
|
free(pl->patterns[i]);
|
||||||
free(pl->patterns);
|
free(pl->patterns);
|
||||||
free(pl->filebuf);
|
free(pl->filebuf);
|
||||||
hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
|
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
|
||||||
hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
|
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
|
||||||
|
|
||||||
memset(pl, 0, sizeof(*pl));
|
memset(pl, 0, sizeof(*pl));
|
||||||
}
|
}
|
||||||
|
|
72
hashmap.c
72
hashmap.c
|
@ -92,8 +92,9 @@ static void alloc_table(struct hashmap *map, unsigned int size)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int entry_equals(const struct hashmap *map,
|
static inline int entry_equals(const struct hashmap *map,
|
||||||
const struct hashmap_entry *e1, const struct hashmap_entry *e2,
|
const struct hashmap_entry *e1,
|
||||||
const void *keydata)
|
const struct hashmap_entry *e2,
|
||||||
|
const void *keydata)
|
||||||
{
|
{
|
||||||
return (e1 == e2) ||
|
return (e1 == e2) ||
|
||||||
(e1->hash == e2->hash &&
|
(e1->hash == e2->hash &&
|
||||||
|
@ -101,7 +102,7 @@ static inline int entry_equals(const struct hashmap *map,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int bucket(const struct hashmap *map,
|
static inline unsigned int bucket(const struct hashmap *map,
|
||||||
const struct hashmap_entry *key)
|
const struct hashmap_entry *key)
|
||||||
{
|
{
|
||||||
return key->hash & (map->tablesize - 1);
|
return key->hash & (map->tablesize - 1);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +114,7 @@ int hashmap_bucket(const struct hashmap *map, unsigned int hash)
|
||||||
|
|
||||||
static void rehash(struct hashmap *map, unsigned int newsize)
|
static void rehash(struct hashmap *map, unsigned int newsize)
|
||||||
{
|
{
|
||||||
|
/* map->table MUST NOT be NULL when this function is called */
|
||||||
unsigned int i, oldsize = map->tablesize;
|
unsigned int i, oldsize = map->tablesize;
|
||||||
struct hashmap_entry **oldtable = map->table;
|
struct hashmap_entry **oldtable = map->table;
|
||||||
|
|
||||||
|
@ -133,6 +135,7 @@ static void rehash(struct hashmap *map, unsigned int newsize)
|
||||||
static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
|
static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
|
||||||
const struct hashmap_entry *key, const void *keydata)
|
const struct hashmap_entry *key, const void *keydata)
|
||||||
{
|
{
|
||||||
|
/* map->table MUST NOT be NULL when this function is called */
|
||||||
struct hashmap_entry **e = &map->table[bucket(map, key)];
|
struct hashmap_entry **e = &map->table[bucket(map, key)];
|
||||||
while (*e && !entry_equals(map, *e, key, keydata))
|
while (*e && !entry_equals(map, *e, key, keydata))
|
||||||
e = &(*e)->next;
|
e = &(*e)->next;
|
||||||
|
@ -148,7 +151,7 @@ static int always_equal(const void *unused_cmp_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
|
void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
|
||||||
const void *cmpfn_data, size_t initial_size)
|
const void *cmpfn_data, size_t initial_size)
|
||||||
{
|
{
|
||||||
unsigned int size = HASHMAP_INITIAL_SIZE;
|
unsigned int size = HASHMAP_INITIAL_SIZE;
|
||||||
|
|
||||||
|
@ -171,22 +174,37 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
|
||||||
map->do_count_items = 1;
|
map->do_count_items = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
|
static void free_individual_entries(struct hashmap *map, ssize_t entry_offset)
|
||||||
|
{
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct hashmap_entry *e;
|
||||||
|
|
||||||
|
hashmap_iter_init(map, &iter);
|
||||||
|
while ((e = hashmap_iter_next(&iter)))
|
||||||
|
/*
|
||||||
|
* like container_of, but using caller-calculated
|
||||||
|
* offset (caller being hashmap_clear_and_free)
|
||||||
|
*/
|
||||||
|
free((char *)e - entry_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset)
|
||||||
{
|
{
|
||||||
if (!map || !map->table)
|
if (!map || !map->table)
|
||||||
return;
|
return;
|
||||||
if (entry_offset >= 0) { /* called by hashmap_free_entries */
|
if (entry_offset >= 0) /* called by hashmap_clear_entries */
|
||||||
struct hashmap_iter iter;
|
free_individual_entries(map, entry_offset);
|
||||||
struct hashmap_entry *e;
|
memset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *));
|
||||||
|
map->shrink_at = 0;
|
||||||
|
map->private_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
hashmap_iter_init(map, &iter);
|
void hashmap_clear_(struct hashmap *map, ssize_t entry_offset)
|
||||||
while ((e = hashmap_iter_next(&iter)))
|
{
|
||||||
/*
|
if (!map || !map->table)
|
||||||
* like container_of, but using caller-calculated
|
return;
|
||||||
* offset (caller being hashmap_free_entries)
|
if (entry_offset >= 0) /* called by hashmap_clear_and_free */
|
||||||
*/
|
free_individual_entries(map, entry_offset);
|
||||||
free((char *)e - entry_offset);
|
|
||||||
}
|
|
||||||
free(map->table);
|
free(map->table);
|
||||||
memset(map, 0, sizeof(*map));
|
memset(map, 0, sizeof(*map));
|
||||||
}
|
}
|
||||||
|
@ -195,11 +213,13 @@ struct hashmap_entry *hashmap_get(const struct hashmap *map,
|
||||||
const struct hashmap_entry *key,
|
const struct hashmap_entry *key,
|
||||||
const void *keydata)
|
const void *keydata)
|
||||||
{
|
{
|
||||||
|
if (!map->table)
|
||||||
|
return NULL;
|
||||||
return *find_entry_ptr(map, key, keydata);
|
return *find_entry_ptr(map, key, keydata);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
|
struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
|
||||||
const struct hashmap_entry *entry)
|
const struct hashmap_entry *entry)
|
||||||
{
|
{
|
||||||
struct hashmap_entry *e = entry->next;
|
struct hashmap_entry *e = entry->next;
|
||||||
for (; e; e = e->next)
|
for (; e; e = e->next)
|
||||||
|
@ -210,8 +230,12 @@ struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
|
||||||
|
|
||||||
void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
|
void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
|
||||||
{
|
{
|
||||||
unsigned int b = bucket(map, entry);
|
unsigned int b;
|
||||||
|
|
||||||
|
if (!map->table)
|
||||||
|
alloc_table(map, HASHMAP_INITIAL_SIZE);
|
||||||
|
|
||||||
|
b = bucket(map, entry);
|
||||||
/* add entry */
|
/* add entry */
|
||||||
entry->next = map->table[b];
|
entry->next = map->table[b];
|
||||||
map->table[b] = entry;
|
map->table[b] = entry;
|
||||||
|
@ -225,11 +249,15 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hashmap_entry *hashmap_remove(struct hashmap *map,
|
struct hashmap_entry *hashmap_remove(struct hashmap *map,
|
||||||
const struct hashmap_entry *key,
|
const struct hashmap_entry *key,
|
||||||
const void *keydata)
|
const void *keydata)
|
||||||
{
|
{
|
||||||
struct hashmap_entry *old;
|
struct hashmap_entry *old;
|
||||||
struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
|
struct hashmap_entry **e;
|
||||||
|
|
||||||
|
if (!map->table)
|
||||||
|
return NULL;
|
||||||
|
e = find_entry_ptr(map, key, keydata);
|
||||||
if (!*e)
|
if (!*e)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -249,7 +277,7 @@ struct hashmap_entry *hashmap_remove(struct hashmap *map,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hashmap_entry *hashmap_put(struct hashmap *map,
|
struct hashmap_entry *hashmap_put(struct hashmap *map,
|
||||||
struct hashmap_entry *entry)
|
struct hashmap_entry *entry)
|
||||||
{
|
{
|
||||||
struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
|
struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
|
||||||
hashmap_add(map, entry);
|
hashmap_add(map, entry);
|
||||||
|
|
91
hashmap.h
91
hashmap.h
|
@ -96,7 +96,7 @@
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* if (!strcmp("end", action)) {
|
* if (!strcmp("end", action)) {
|
||||||
* hashmap_free_entries(&map, struct long2string, ent);
|
* hashmap_clear_and_free(&map, struct long2string, ent);
|
||||||
* break;
|
* break;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
|
@ -210,6 +210,9 @@ struct hashmap {
|
||||||
|
|
||||||
/* hashmap functions */
|
/* hashmap functions */
|
||||||
|
|
||||||
|
#define HASHMAP_INIT(fn, data) { .cmpfn = fn, .cmpfn_data = data, \
|
||||||
|
.do_count_items = 1 }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes a hashmap structure.
|
* Initializes a hashmap structure.
|
||||||
*
|
*
|
||||||
|
@ -228,24 +231,72 @@ struct hashmap {
|
||||||
* prevent expensive resizing. If 0, the table is dynamically resized.
|
* prevent expensive resizing. If 0, the table is dynamically resized.
|
||||||
*/
|
*/
|
||||||
void hashmap_init(struct hashmap *map,
|
void hashmap_init(struct hashmap *map,
|
||||||
hashmap_cmp_fn equals_function,
|
hashmap_cmp_fn equals_function,
|
||||||
const void *equals_function_data,
|
const void *equals_function_data,
|
||||||
size_t initial_size);
|
size_t initial_size);
|
||||||
|
|
||||||
/* internal function for freeing hashmap */
|
/* internal functions for clearing or freeing hashmap */
|
||||||
void hashmap_free_(struct hashmap *map, ssize_t offset);
|
void hashmap_partial_clear_(struct hashmap *map, ssize_t offset);
|
||||||
|
void hashmap_clear_(struct hashmap *map, ssize_t offset);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frees a hashmap structure and allocated memory, leaves entries undisturbed
|
* Frees a hashmap structure and allocated memory for the table, but does not
|
||||||
|
* free the entries nor anything they point to.
|
||||||
|
*
|
||||||
|
* Usage note:
|
||||||
|
*
|
||||||
|
* Many callers will need to iterate over all entries and free the data each
|
||||||
|
* entry points to; in such a case, they can free the entry itself while at it.
|
||||||
|
* Thus, you might see:
|
||||||
|
*
|
||||||
|
* hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) {
|
||||||
|
* free(e->somefield);
|
||||||
|
* free(e);
|
||||||
|
* }
|
||||||
|
* hashmap_clear(map);
|
||||||
|
*
|
||||||
|
* instead of
|
||||||
|
*
|
||||||
|
* hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) {
|
||||||
|
* free(e->somefield);
|
||||||
|
* }
|
||||||
|
* hashmap_clear_and_free(map, struct my_entry_struct, hashmap_entry_name);
|
||||||
|
*
|
||||||
|
* to avoid the implicit extra loop over the entries. However, if there are
|
||||||
|
* no special fields in your entry that need to be freed beyond the entry
|
||||||
|
* itself, it is probably simpler to avoid the explicit loop and just call
|
||||||
|
* hashmap_clear_and_free().
|
||||||
*/
|
*/
|
||||||
#define hashmap_free(map) hashmap_free_(map, -1)
|
#define hashmap_clear(map) hashmap_clear_(map, -1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frees @map and all entries. @type is the struct type of the entry
|
* Similar to hashmap_clear(), except that the table is no deallocated; it
|
||||||
* where @member is the hashmap_entry struct used to associate with @map
|
* is merely zeroed out but left the same size as before. If the hashmap
|
||||||
|
* will be reused, this avoids the overhead of deallocating and
|
||||||
|
* reallocating map->table. As with hashmap_clear(), you may need to free
|
||||||
|
* the entries yourself before calling this function.
|
||||||
*/
|
*/
|
||||||
#define hashmap_free_entries(map, type, member) \
|
#define hashmap_partial_clear(map) hashmap_partial_clear_(map, -1)
|
||||||
hashmap_free_(map, offsetof(type, member));
|
|
||||||
|
/*
|
||||||
|
* Similar to hashmap_clear() but also frees all entries. @type is the
|
||||||
|
* struct type of the entry where @member is the hashmap_entry struct used
|
||||||
|
* to associate with @map.
|
||||||
|
*
|
||||||
|
* See usage note above hashmap_clear().
|
||||||
|
*/
|
||||||
|
#define hashmap_clear_and_free(map, type, member) \
|
||||||
|
hashmap_clear_(map, offsetof(type, member))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Similar to hashmap_partial_clear() but also frees all entries. @type is
|
||||||
|
* the struct type of the entry where @member is the hashmap_entry struct
|
||||||
|
* used to associate with @map.
|
||||||
|
*
|
||||||
|
* See usage note above hashmap_clear().
|
||||||
|
*/
|
||||||
|
#define hashmap_partial_clear_and_free(map, type, member) \
|
||||||
|
hashmap_partial_clear_(map, offsetof(type, member))
|
||||||
|
|
||||||
/* hashmap_entry functions */
|
/* hashmap_entry functions */
|
||||||
|
|
||||||
|
@ -261,7 +312,7 @@ void hashmap_free_(struct hashmap *map, ssize_t offset);
|
||||||
* and if it is on stack, you can just let it go out of scope).
|
* and if it is on stack, you can just let it go out of scope).
|
||||||
*/
|
*/
|
||||||
static inline void hashmap_entry_init(struct hashmap_entry *e,
|
static inline void hashmap_entry_init(struct hashmap_entry *e,
|
||||||
unsigned int hash)
|
unsigned int hash)
|
||||||
{
|
{
|
||||||
e->hash = hash;
|
e->hash = hash;
|
||||||
e->next = NULL;
|
e->next = NULL;
|
||||||
|
@ -303,8 +354,8 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)
|
||||||
* to `hashmap_cmp_fn` to decide whether the entry matches the key.
|
* to `hashmap_cmp_fn` to decide whether the entry matches the key.
|
||||||
*/
|
*/
|
||||||
struct hashmap_entry *hashmap_get(const struct hashmap *map,
|
struct hashmap_entry *hashmap_get(const struct hashmap *map,
|
||||||
const struct hashmap_entry *key,
|
const struct hashmap_entry *key,
|
||||||
const void *keydata);
|
const void *keydata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the hashmap entry for the specified hash code and key data,
|
* Returns the hashmap entry for the specified hash code and key data,
|
||||||
|
@ -337,7 +388,7 @@ static inline struct hashmap_entry *hashmap_get_from_hash(
|
||||||
* call to `hashmap_get` or `hashmap_get_next`.
|
* call to `hashmap_get` or `hashmap_get_next`.
|
||||||
*/
|
*/
|
||||||
struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
|
struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
|
||||||
const struct hashmap_entry *entry);
|
const struct hashmap_entry *entry);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adds a hashmap entry. This allows to add duplicate entries (i.e.
|
* Adds a hashmap entry. This allows to add duplicate entries (i.e.
|
||||||
|
@ -357,7 +408,7 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);
|
||||||
* Returns the replaced entry, or NULL if not found (i.e. the entry was added).
|
* Returns the replaced entry, or NULL if not found (i.e. the entry was added).
|
||||||
*/
|
*/
|
||||||
struct hashmap_entry *hashmap_put(struct hashmap *map,
|
struct hashmap_entry *hashmap_put(struct hashmap *map,
|
||||||
struct hashmap_entry *entry);
|
struct hashmap_entry *entry);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adds or replaces a hashmap entry contained within @keyvar,
|
* Adds or replaces a hashmap entry contained within @keyvar,
|
||||||
|
@ -379,8 +430,8 @@ struct hashmap_entry *hashmap_put(struct hashmap *map,
|
||||||
* Argument explanation is the same as in `hashmap_get`.
|
* Argument explanation is the same as in `hashmap_get`.
|
||||||
*/
|
*/
|
||||||
struct hashmap_entry *hashmap_remove(struct hashmap *map,
|
struct hashmap_entry *hashmap_remove(struct hashmap *map,
|
||||||
const struct hashmap_entry *key,
|
const struct hashmap_entry *key,
|
||||||
const void *keydata);
|
const void *keydata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Removes a hashmap entry contained within @keyvar,
|
* Removes a hashmap entry contained within @keyvar,
|
||||||
|
@ -422,7 +473,7 @@ struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter);
|
||||||
|
|
||||||
/* Initializes the iterator and returns the first entry, if any. */
|
/* Initializes the iterator and returns the first entry, if any. */
|
||||||
static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
|
static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
|
||||||
struct hashmap_iter *iter)
|
struct hashmap_iter *iter)
|
||||||
{
|
{
|
||||||
hashmap_iter_init(map, iter);
|
hashmap_iter_init(map, iter);
|
||||||
return hashmap_iter_next(iter);
|
return hashmap_iter_next(iter);
|
||||||
|
|
|
@ -2651,7 +2651,7 @@ static struct string_list *get_renames(struct merge_options *opt,
|
||||||
free(e->target_file);
|
free(e->target_file);
|
||||||
string_list_clear(&e->source_files, 0);
|
string_list_clear(&e->source_files, 0);
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&collisions, struct collision_entry, ent);
|
hashmap_clear_and_free(&collisions, struct collision_entry, ent);
|
||||||
return renames;
|
return renames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2870,7 +2870,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
|
||||||
strbuf_release(&e->new_dir);
|
strbuf_release(&e->new_dir);
|
||||||
/* possible_new_dirs already cleared in get_directory_renames */
|
/* possible_new_dirs already cleared in get_directory_renames */
|
||||||
}
|
}
|
||||||
hashmap_free_entries(dir_renames, struct dir_rename_entry, ent);
|
hashmap_clear_and_free(dir_renames, struct dir_rename_entry, ent);
|
||||||
free(dir_renames);
|
free(dir_renames);
|
||||||
|
|
||||||
free(pairs->queue);
|
free(pairs->queue);
|
||||||
|
@ -3497,7 +3497,7 @@ static int merge_trees_internal(struct merge_options *opt,
|
||||||
string_list_clear(entries, 1);
|
string_list_clear(entries, 1);
|
||||||
free(entries);
|
free(entries);
|
||||||
|
|
||||||
hashmap_free_entries(&opt->priv->current_file_dir_set,
|
hashmap_clear_and_free(&opt->priv->current_file_dir_set,
|
||||||
struct path_hashmap_entry, e);
|
struct path_hashmap_entry, e);
|
||||||
|
|
||||||
if (clean < 0) {
|
if (clean < 0) {
|
||||||
|
|
|
@ -726,6 +726,6 @@ void free_name_hash(struct index_state *istate)
|
||||||
return;
|
return;
|
||||||
istate->name_hash_initialized = 0;
|
istate->name_hash_initialized = 0;
|
||||||
|
|
||||||
hashmap_free(&istate->name_hash);
|
hashmap_clear(&istate->name_hash);
|
||||||
hashmap_free_entries(&istate->dir_hash, struct dir_entry, ent);
|
hashmap_clear_and_free(&istate->dir_hash, struct dir_entry, ent);
|
||||||
}
|
}
|
||||||
|
|
2
object.c
2
object.c
|
@ -532,7 +532,7 @@ void raw_object_store_clear(struct raw_object_store *o)
|
||||||
close_object_store(o);
|
close_object_store(o);
|
||||||
o->packed_git = NULL;
|
o->packed_git = NULL;
|
||||||
|
|
||||||
hashmap_free(&o->pack_map);
|
hashmap_clear(&o->pack_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parsed_object_pool_clear(struct parsed_object_pool *o)
|
void parsed_object_pool_clear(struct parsed_object_pool *o)
|
||||||
|
|
2
oidmap.c
2
oidmap.c
|
@ -27,7 +27,7 @@ void oidmap_free(struct oidmap *map, int free_entries)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* TODO: make oidmap itself not depend on struct layouts */
|
/* TODO: make oidmap itself not depend on struct layouts */
|
||||||
hashmap_free_(&map->map, free_entries ? 0 : -1);
|
hashmap_clear_(&map->map, free_entries ? 0 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *oidmap_get(const struct oidmap *map, const struct object_id *key)
|
void *oidmap_get(const struct oidmap *map, const struct object_id *key)
|
||||||
|
|
|
@ -71,7 +71,7 @@ int init_patch_ids(struct repository *r, struct patch_ids *ids)
|
||||||
|
|
||||||
int free_patch_ids(struct patch_ids *ids)
|
int free_patch_ids(struct patch_ids *ids)
|
||||||
{
|
{
|
||||||
hashmap_free_entries(&ids->patches, struct patch_id, ent);
|
hashmap_clear_and_free(&ids->patches, struct patch_id, ent);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,11 +232,9 @@ static int patch_util_cmp(const void *dummy, const struct patch_util *a,
|
||||||
|
|
||||||
static void find_exact_matches(struct string_list *a, struct string_list *b)
|
static void find_exact_matches(struct string_list *a, struct string_list *b)
|
||||||
{
|
{
|
||||||
struct hashmap map;
|
struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
hashmap_init(&map, (hashmap_cmp_fn)patch_util_cmp, NULL, 0);
|
|
||||||
|
|
||||||
/* First, add the patches of a to a hash map */
|
/* First, add the patches of a to a hash map */
|
||||||
for (i = 0; i < a->nr; i++) {
|
for (i = 0; i < a->nr; i++) {
|
||||||
struct patch_util *util = a->items[i].util;
|
struct patch_util *util = a->items[i].util;
|
||||||
|
@ -266,7 +264,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmap_free(&map);
|
hashmap_clear(&map);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void diffsize_consume(void *data, char *line, unsigned long len)
|
static void diffsize_consume(void *data, char *line, unsigned long len)
|
||||||
|
|
|
@ -2230,7 +2230,7 @@ void ref_array_clear(struct ref_array *array)
|
||||||
used_atom_cnt = 0;
|
used_atom_cnt = 0;
|
||||||
|
|
||||||
if (ref_to_worktree_map.worktrees) {
|
if (ref_to_worktree_map.worktrees) {
|
||||||
hashmap_free_entries(&(ref_to_worktree_map.map),
|
hashmap_clear_and_free(&(ref_to_worktree_map.map),
|
||||||
struct ref_to_worktree_entry, ent);
|
struct ref_to_worktree_entry, ent);
|
||||||
free_worktrees(ref_to_worktree_map.worktrees);
|
free_worktrees(ref_to_worktree_map.worktrees);
|
||||||
ref_to_worktree_map.worktrees = NULL;
|
ref_to_worktree_map.worktrees = NULL;
|
||||||
|
|
11
revision.c
11
revision.c
|
@ -124,11 +124,6 @@ static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
|
||||||
return strcmp(e1->path, e2->path);
|
return strcmp(e1->path, e2->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paths_and_oids_init(struct hashmap *map)
|
|
||||||
{
|
|
||||||
hashmap_init(map, path_and_oids_cmp, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void paths_and_oids_clear(struct hashmap *map)
|
static void paths_and_oids_clear(struct hashmap *map)
|
||||||
{
|
{
|
||||||
struct hashmap_iter iter;
|
struct hashmap_iter iter;
|
||||||
|
@ -139,7 +134,7 @@ static void paths_and_oids_clear(struct hashmap *map)
|
||||||
free(entry->path);
|
free(entry->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmap_free_entries(map, struct path_and_oids_entry, ent);
|
hashmap_clear_and_free(map, struct path_and_oids_entry, ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paths_and_oids_insert(struct hashmap *map,
|
static void paths_and_oids_insert(struct hashmap *map,
|
||||||
|
@ -213,7 +208,7 @@ void mark_trees_uninteresting_sparse(struct repository *r,
|
||||||
struct oidset *trees)
|
struct oidset *trees)
|
||||||
{
|
{
|
||||||
unsigned has_interesting = 0, has_uninteresting = 0;
|
unsigned has_interesting = 0, has_uninteresting = 0;
|
||||||
struct hashmap map;
|
struct hashmap map = HASHMAP_INIT(path_and_oids_cmp, NULL);
|
||||||
struct hashmap_iter map_iter;
|
struct hashmap_iter map_iter;
|
||||||
struct path_and_oids_entry *entry;
|
struct path_and_oids_entry *entry;
|
||||||
struct object_id *oid;
|
struct object_id *oid;
|
||||||
|
@ -237,8 +232,6 @@ void mark_trees_uninteresting_sparse(struct repository *r,
|
||||||
if (!has_uninteresting || !has_interesting)
|
if (!has_uninteresting || !has_interesting)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
paths_and_oids_init(&map);
|
|
||||||
|
|
||||||
oidset_iter_init(trees, &iter);
|
oidset_iter_init(trees, &iter);
|
||||||
while ((oid = oidset_iter_next(&iter))) {
|
while ((oid = oidset_iter_next(&iter))) {
|
||||||
struct tree *tree = lookup_tree(r, oid);
|
struct tree *tree = lookup_tree(r, oid);
|
||||||
|
|
|
@ -5085,7 +5085,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
|
||||||
|
|
||||||
oidmap_free(&commit2todo, 1);
|
oidmap_free(&commit2todo, 1);
|
||||||
oidmap_free(&state.commit2label, 1);
|
oidmap_free(&state.commit2label, 1);
|
||||||
hashmap_free_entries(&state.labels, struct labels_entry, entry);
|
hashmap_clear_and_free(&state.labels, struct labels_entry, entry);
|
||||||
strbuf_release(&state.buf);
|
strbuf_release(&state.buf);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -5601,7 +5601,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
|
||||||
for (i = 0; i < todo_list->nr; i++)
|
for (i = 0; i < todo_list->nr; i++)
|
||||||
free(subjects[i]);
|
free(subjects[i]);
|
||||||
free(subjects);
|
free(subjects);
|
||||||
hashmap_free_entries(&subject2item, struct subject2item_entry, entry);
|
hashmap_clear_and_free(&subject2item, struct subject2item_entry, entry);
|
||||||
|
|
||||||
clear_commit_todo_item(&commit_todo);
|
clear_commit_todo_item(&commit_todo);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
#include "git-compat-util.h"
|
||||||
|
#include "strmap.h"
|
||||||
|
#include "mem-pool.h"
|
||||||
|
|
||||||
|
int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
|
||||||
|
const struct hashmap_entry *entry1,
|
||||||
|
const struct hashmap_entry *entry2,
|
||||||
|
const void *keydata)
|
||||||
|
{
|
||||||
|
const struct strmap_entry *e1, *e2;
|
||||||
|
|
||||||
|
e1 = container_of(entry1, const struct strmap_entry, ent);
|
||||||
|
e2 = container_of(entry2, const struct strmap_entry, ent);
|
||||||
|
return strcmp(e1->key, e2->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct strmap_entry *find_strmap_entry(struct strmap *map,
|
||||||
|
const char *str)
|
||||||
|
{
|
||||||
|
struct strmap_entry entry;
|
||||||
|
hashmap_entry_init(&entry.ent, strhash(str));
|
||||||
|
entry.key = str;
|
||||||
|
return hashmap_get_entry(&map->map, &entry, ent, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strmap_init(struct strmap *map)
|
||||||
|
{
|
||||||
|
strmap_init_with_options(map, NULL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strmap_init_with_options(struct strmap *map,
|
||||||
|
struct mem_pool *pool,
|
||||||
|
int strdup_strings)
|
||||||
|
{
|
||||||
|
hashmap_init(&map->map, cmp_strmap_entry, NULL, 0);
|
||||||
|
map->pool = pool;
|
||||||
|
map->strdup_strings = strdup_strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strmap_free_entries_(struct strmap *map, int free_values)
|
||||||
|
{
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct strmap_entry *e;
|
||||||
|
|
||||||
|
if (!map)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!free_values && map->pool)
|
||||||
|
/* Memory other than util is owned by and freed with the pool */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to iterate over the hashmap entries and free
|
||||||
|
* e->key and e->value ourselves; hashmap has no API to
|
||||||
|
* take care of that for us. Since we're already iterating over
|
||||||
|
* the hashmap, though, might as well free e too and avoid the need
|
||||||
|
* to make some call into the hashmap API to do that.
|
||||||
|
*/
|
||||||
|
hashmap_for_each_entry(&map->map, &iter, e, ent) {
|
||||||
|
if (free_values)
|
||||||
|
free(e->value);
|
||||||
|
if (!map->pool)
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void strmap_clear(struct strmap *map, int free_values)
|
||||||
|
{
|
||||||
|
strmap_free_entries_(map, free_values);
|
||||||
|
hashmap_clear(&map->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strmap_partial_clear(struct strmap *map, int free_values)
|
||||||
|
{
|
||||||
|
strmap_free_entries_(map, free_values);
|
||||||
|
hashmap_partial_clear(&map->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct strmap_entry *create_entry(struct strmap *map,
|
||||||
|
const char *str,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct strmap_entry *entry;
|
||||||
|
|
||||||
|
if (map->strdup_strings) {
|
||||||
|
if (!map->pool) {
|
||||||
|
FLEXPTR_ALLOC_STR(entry, key, str);
|
||||||
|
} else {
|
||||||
|
size_t len = st_add(strlen(str), 1); /* include NUL */
|
||||||
|
entry = mem_pool_alloc(map->pool,
|
||||||
|
st_add(sizeof(*entry), len));
|
||||||
|
memcpy(entry + 1, str, len);
|
||||||
|
entry->key = (void *)(entry + 1);
|
||||||
|
}
|
||||||
|
} else if (!map->pool) {
|
||||||
|
entry = xmalloc(sizeof(*entry));
|
||||||
|
} else {
|
||||||
|
entry = mem_pool_alloc(map->pool, sizeof(*entry));
|
||||||
|
}
|
||||||
|
hashmap_entry_init(&entry->ent, strhash(str));
|
||||||
|
if (!map->strdup_strings)
|
||||||
|
entry->key = str;
|
||||||
|
entry->value = data;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *strmap_put(struct strmap *map, const char *str, void *data)
|
||||||
|
{
|
||||||
|
struct strmap_entry *entry = find_strmap_entry(map, str);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
void *old = entry->value;
|
||||||
|
entry->value = data;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = create_entry(map, str, data);
|
||||||
|
hashmap_add(&map->map, &entry->ent);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str)
|
||||||
|
{
|
||||||
|
return find_strmap_entry(map, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *strmap_get(struct strmap *map, const char *str)
|
||||||
|
{
|
||||||
|
struct strmap_entry *entry = find_strmap_entry(map, str);
|
||||||
|
return entry ? entry->value : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strmap_contains(struct strmap *map, const char *str)
|
||||||
|
{
|
||||||
|
return find_strmap_entry(map, str) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strmap_remove(struct strmap *map, const char *str, int free_value)
|
||||||
|
{
|
||||||
|
struct strmap_entry entry, *ret;
|
||||||
|
hashmap_entry_init(&entry.ent, strhash(str));
|
||||||
|
entry.key = str;
|
||||||
|
ret = hashmap_remove_entry(&map->map, &entry, ent, NULL);
|
||||||
|
if (!ret)
|
||||||
|
return;
|
||||||
|
if (free_value)
|
||||||
|
free(ret->value);
|
||||||
|
if (!map->pool)
|
||||||
|
free(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt)
|
||||||
|
{
|
||||||
|
struct strmap_entry *entry = find_strmap_entry(&map->map, str);
|
||||||
|
if (entry) {
|
||||||
|
intptr_t *whence = (intptr_t*)&entry->value;
|
||||||
|
*whence += amt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
strintmap_set(map, str, map->default_value + amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int strset_add(struct strset *set, const char *str)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Cannot use strmap_put() because it'll return NULL in both cases:
|
||||||
|
* - cannot find str: NULL means "not found"
|
||||||
|
* - does find str: NULL is the value associated with str
|
||||||
|
*/
|
||||||
|
struct strmap_entry *entry = find_strmap_entry(&set->map, str);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
entry = create_entry(&set->map, str, NULL);
|
||||||
|
hashmap_add(&set->map.map, &entry->ent);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,268 @@
|
||||||
|
#ifndef STRMAP_H
|
||||||
|
#define STRMAP_H
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
|
||||||
|
struct mem_pool;
|
||||||
|
struct strmap {
|
||||||
|
struct hashmap map;
|
||||||
|
struct mem_pool *pool;
|
||||||
|
unsigned int strdup_strings:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct strmap_entry {
|
||||||
|
struct hashmap_entry ent;
|
||||||
|
const char *key;
|
||||||
|
void *value;
|
||||||
|
/* strmap_entry may be allocated extra space to store the key at end */
|
||||||
|
};
|
||||||
|
|
||||||
|
int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
|
||||||
|
const struct hashmap_entry *entry1,
|
||||||
|
const struct hashmap_entry *entry2,
|
||||||
|
const void *keydata);
|
||||||
|
|
||||||
|
#define STRMAP_INIT { \
|
||||||
|
.map = HASHMAP_INIT(cmp_strmap_entry, NULL), \
|
||||||
|
.strdup_strings = 1, \
|
||||||
|
}
|
||||||
|
#define STRINTMAP_INIT { \
|
||||||
|
.map = STRMAP_INIT, \
|
||||||
|
.default_value = 0, \
|
||||||
|
}
|
||||||
|
#define STRSET_INIT { .map = STRMAP_INIT }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the members of the strmap. Any keys added to the strmap will
|
||||||
|
* be strdup'ed with their memory managed by the strmap.
|
||||||
|
*/
|
||||||
|
void strmap_init(struct strmap *map);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as strmap_init, but for those who want to control the memory management
|
||||||
|
* carefully instead of using the default of strdup_strings=1 and pool=NULL.
|
||||||
|
*/
|
||||||
|
void strmap_init_with_options(struct strmap *map,
|
||||||
|
struct mem_pool *pool,
|
||||||
|
int strdup_strings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove all entries from the map, releasing any allocated resources.
|
||||||
|
*/
|
||||||
|
void strmap_clear(struct strmap *map, int free_values);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Similar to strmap_clear() but leaves map->map->table allocated and
|
||||||
|
* pre-sized so that subsequent uses won't need as many rehashings.
|
||||||
|
*/
|
||||||
|
void strmap_partial_clear(struct strmap *map, int free_values);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert "str" into the map, pointing to "data".
|
||||||
|
*
|
||||||
|
* If an entry for "str" already exists, its data pointer is overwritten, and
|
||||||
|
* the original data pointer returned. Otherwise, returns NULL.
|
||||||
|
*/
|
||||||
|
void *strmap_put(struct strmap *map, const char *str, void *data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the strmap_entry mapped by "str", or NULL if there is not such
|
||||||
|
* an item in map.
|
||||||
|
*/
|
||||||
|
struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the data pointer mapped by "str", or NULL if the entry does not
|
||||||
|
* exist.
|
||||||
|
*/
|
||||||
|
void *strmap_get(struct strmap *map, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return non-zero iff "str" is present in the map. This differs from
|
||||||
|
* strmap_get() in that it can distinguish entries with a NULL data pointer.
|
||||||
|
*/
|
||||||
|
int strmap_contains(struct strmap *map, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the given entry from the strmap. If the string isn't in the
|
||||||
|
* strmap, the map is not altered.
|
||||||
|
*/
|
||||||
|
void strmap_remove(struct strmap *map, const char *str, int free_value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return how many entries the strmap has.
|
||||||
|
*/
|
||||||
|
static inline unsigned int strmap_get_size(struct strmap *map)
|
||||||
|
{
|
||||||
|
return hashmap_get_size(&map->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return whether the strmap is empty.
|
||||||
|
*/
|
||||||
|
static inline int strmap_empty(struct strmap *map)
|
||||||
|
{
|
||||||
|
return strmap_get_size(map) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iterate through @map using @iter, @var is a pointer to a type strmap_entry
|
||||||
|
*/
|
||||||
|
#define strmap_for_each_entry(mystrmap, iter, var) \
|
||||||
|
hashmap_for_each_entry(&(mystrmap)->map, iter, var, ent)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strintmap:
|
||||||
|
* A map of string -> int, typecasting the void* of strmap to an int.
|
||||||
|
*
|
||||||
|
* Primary differences:
|
||||||
|
* 1) Since the void* value is just an int in disguise, there is no value
|
||||||
|
* to free. (Thus one fewer argument to strintmap_clear)
|
||||||
|
* 2) strintmap_get() returns an int, or returns the default_value if the
|
||||||
|
* key is not found in the strintmap.
|
||||||
|
* 3) No strmap_put() equivalent; strintmap_set() and strintmap_incr()
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct strintmap {
|
||||||
|
struct strmap map;
|
||||||
|
int default_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define strintmap_for_each_entry(mystrmap, iter, var) \
|
||||||
|
strmap_for_each_entry(&(mystrmap)->map, iter, var)
|
||||||
|
|
||||||
|
static inline void strintmap_init(struct strintmap *map, int default_value)
|
||||||
|
{
|
||||||
|
strmap_init(&map->map);
|
||||||
|
map->default_value = default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strintmap_init_with_options(struct strintmap *map,
|
||||||
|
int default_value,
|
||||||
|
struct mem_pool *pool,
|
||||||
|
int strdup_strings)
|
||||||
|
{
|
||||||
|
strmap_init_with_options(&map->map, pool, strdup_strings);
|
||||||
|
map->default_value = default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strintmap_clear(struct strintmap *map)
|
||||||
|
{
|
||||||
|
strmap_clear(&map->map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strintmap_partial_clear(struct strintmap *map)
|
||||||
|
{
|
||||||
|
strmap_partial_clear(&map->map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strintmap_contains(struct strintmap *map, const char *str)
|
||||||
|
{
|
||||||
|
return strmap_contains(&map->map, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strintmap_remove(struct strintmap *map, const char *str)
|
||||||
|
{
|
||||||
|
return strmap_remove(&map->map, str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strintmap_empty(struct strintmap *map)
|
||||||
|
{
|
||||||
|
return strmap_empty(&map->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int strintmap_get_size(struct strintmap *map)
|
||||||
|
{
|
||||||
|
return strmap_get_size(&map->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the value for str in the map. If str isn't found in the map,
|
||||||
|
* the map's default_value is returned.
|
||||||
|
*/
|
||||||
|
static inline int strintmap_get(struct strintmap *map, const char *str)
|
||||||
|
{
|
||||||
|
struct strmap_entry *result = strmap_get_entry(&map->map, str);
|
||||||
|
if (!result)
|
||||||
|
return map->default_value;
|
||||||
|
return (intptr_t)result->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strintmap_set(struct strintmap *map, const char *str,
|
||||||
|
intptr_t v)
|
||||||
|
{
|
||||||
|
strmap_put(&map->map, str, (void *)v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Increment the value for str by amt. If str isn't in the map, add it and
|
||||||
|
* set its value to default_value + amt.
|
||||||
|
*/
|
||||||
|
void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strset:
|
||||||
|
* A set of strings.
|
||||||
|
*
|
||||||
|
* Primary differences with strmap:
|
||||||
|
* 1) The value is always NULL, and ignored. As there is no value to free,
|
||||||
|
* there is one fewer argument to strset_clear
|
||||||
|
* 2) No strset_get() because there is no value.
|
||||||
|
* 3) No strset_put(); use strset_add() instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct strset {
|
||||||
|
struct strmap map;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define strset_for_each_entry(mystrset, iter, var) \
|
||||||
|
strmap_for_each_entry(&(mystrset)->map, iter, var)
|
||||||
|
|
||||||
|
static inline void strset_init(struct strset *set)
|
||||||
|
{
|
||||||
|
strmap_init(&set->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strset_init_with_options(struct strset *set,
|
||||||
|
struct mem_pool *pool,
|
||||||
|
int strdup_strings)
|
||||||
|
{
|
||||||
|
strmap_init_with_options(&set->map, pool, strdup_strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strset_clear(struct strset *set)
|
||||||
|
{
|
||||||
|
strmap_clear(&set->map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strset_partial_clear(struct strset *set)
|
||||||
|
{
|
||||||
|
strmap_partial_clear(&set->map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strset_contains(struct strset *set, const char *str)
|
||||||
|
{
|
||||||
|
return strmap_contains(&set->map, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strset_remove(struct strset *set, const char *str)
|
||||||
|
{
|
||||||
|
return strmap_remove(&set->map, str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strset_empty(struct strset *set)
|
||||||
|
{
|
||||||
|
return strmap_empty(&set->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int strset_get_size(struct strset *set)
|
||||||
|
{
|
||||||
|
return strmap_get_size(&set->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if str is added to the set; returns 0 if str was already in set */
|
||||||
|
int strset_add(struct strset *set, const char *str);
|
||||||
|
|
||||||
|
#endif /* STRMAP_H */
|
|
@ -103,8 +103,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)
|
||||||
ent /* member name */)
|
ent /* member name */)
|
||||||
free_one_config(entry);
|
free_one_config(entry);
|
||||||
|
|
||||||
hashmap_free_entries(&cache->for_path, struct submodule_entry, ent);
|
hashmap_clear_and_free(&cache->for_path, struct submodule_entry, ent);
|
||||||
hashmap_free_entries(&cache->for_name, struct submodule_entry, ent);
|
hashmap_clear_and_free(&cache->for_name, struct submodule_entry, ent);
|
||||||
cache->initialized = 0;
|
cache->initialized = 0;
|
||||||
cache->gitmodules_read = 0;
|
cache->gitmodules_read = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
|
||||||
hashmap_add(&map, &entries[i]->ent);
|
hashmap_add(&map, &entries[i]->ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmap_free(&map);
|
hashmap_clear(&map);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* test map lookups */
|
/* test map lookups */
|
||||||
|
@ -130,7 +130,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmap_free(&map);
|
hashmap_clear(&map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,12 +151,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
|
||||||
int cmd__hashmap(int argc, const char **argv)
|
int cmd__hashmap(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
struct strbuf line = STRBUF_INIT;
|
struct strbuf line = STRBUF_INIT;
|
||||||
struct hashmap map;
|
|
||||||
int icase;
|
int icase;
|
||||||
|
struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
|
||||||
|
|
||||||
/* init hash map */
|
/* init hash map */
|
||||||
icase = argc > 1 && !strcmp("ignorecase", argv[1]);
|
icase = argc > 1 && !strcmp("ignorecase", argv[1]);
|
||||||
hashmap_init(&map, test_entry_cmp, &icase, 0);
|
|
||||||
|
|
||||||
/* process commands from stdin */
|
/* process commands from stdin */
|
||||||
while (strbuf_getline(&line, stdin) != EOF) {
|
while (strbuf_getline(&line, stdin) != EOF) {
|
||||||
|
@ -262,6 +261,6 @@ int cmd__hashmap(int argc, const char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_release(&line);
|
strbuf_release(&line);
|
||||||
hashmap_free_entries(&map, struct test_entry, ent);
|
hashmap_clear_and_free(&map, struct test_entry, ent);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue