static struct dir_entry *hash_dir_entry(struct index_state *istate, struct cache_entry *ce, int namelen) { /* * Throw each directory component in the hash for quick lookup * during a git status. Directory components are stored without their * closing slash. Despite submodules being a directory, they never * reach this point, because they are stored * in index_state.name_hash (as ordinary cache_entries). */ struct dir_entry *dir; /* get length of parent directory */ while (namelen > 0 && !is_dir_sep(ce->name[namelen - 1])) namelen--; if (namelen <= 0) return NULL; namelen--; /* lookup existing entry for that directory */ dir = find_dir_entry(istate, ce->name, namelen); if (!dir) { /* not found, create it and add to hash table */ FLEX_ALLOC_MEM(dir, name, ce->name, namelen); hashmap_entry_init(dir, memihash(ce->name, namelen)); dir->namelen = namelen; hashmap_add(&istate->dir_hash, dir); /* recursively add missing parent directories */ dir->parent = hash_dir_entry(istate, ce, namelen); } return dir; }
static struct dir_entry *find_dir_entry(struct index_state *istate, const char *name, unsigned int namelen) { struct dir_entry key; hashmap_entry_init(&key, memihash(name, namelen)); key.namelen = namelen; return hashmap_get(&istate->dir_hash, &key, name); }
static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) { if (ce->ce_flags & CE_HASHED) return; ce->ce_flags |= CE_HASHED; hashmap_entry_init(ce, memihash(ce->name, ce_namelen(ce))); hashmap_add(&istate->name_hash, ce); if (ignore_case) add_dir_entry(istate, ce); }
struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase) { struct cache_entry *ce; lazy_init_name_hash(istate); ce = hashmap_get_from_hash(&istate->name_hash, memihash(name, namelen), NULL); while (ce) { if (same_name(ce, name, namelen, icase)) return ce; ce = hashmap_get_next(&istate->name_hash, ce); } return NULL; }
static struct dir_entry *hash_dir_entry_with_parent_and_prefix( struct index_state *istate, struct dir_entry *parent, struct strbuf *prefix) { struct dir_entry *dir; unsigned int hash; int lock_nr; /* * Either we have a parent directory and path with slash(es) * or the directory is an immediate child of the root directory. */ assert((parent != NULL) ^ (strchr(prefix->buf, '/') == NULL)); if (parent) hash = memihash_cont(parent->ent.hash, prefix->buf + parent->namelen, prefix->len - parent->namelen); else hash = memihash(prefix->buf, prefix->len); lock_nr = compute_dir_lock_nr(&istate->dir_hash, hash); lock_dir_mutex(lock_nr); dir = find_dir_entry__hash(istate, prefix->buf, prefix->len, hash); if (!dir) { FLEX_ALLOC_MEM(dir, name, prefix->buf, prefix->len); hashmap_entry_init(dir, hash); dir->namelen = prefix->len; dir->parent = parent; hashmap_add(&istate->dir_hash, dir); if (parent) { unlock_dir_mutex(lock_nr); /* All I really need here is an InterlockedIncrement(&(parent->nr)) */ lock_nr = compute_dir_lock_nr(&istate->dir_hash, parent->ent.hash); lock_dir_mutex(lock_nr); parent->nr++; } } unlock_dir_mutex(lock_nr); return dir; }
/* * Read stdin line by line and print result of commands to stdout: * * hash key -> strhash(key) memhash(key) strihash(key) memihash(key) * put key value -> NULL / old value * get key -> NULL / value * remove key -> NULL / old value * iterate -> key1 value1\nkey2 value2\n... * size -> tablesize numentries * * perfhashmap method rounds -> test hashmap.[ch] performance */ int main(int argc, char *argv[]) { char line[1024]; struct hashmap map; int icase; /* init hash map */ icase = argc > 1 && !strcmp("ignorecase", argv[1]); hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase : test_entry_cmp), 0); /* process commands from stdin */ while (fgets(line, sizeof(line), stdin)) { char *cmd, *p1 = NULL, *p2 = NULL; int l1 = 0, l2 = 0, hash = 0; struct test_entry *entry; /* break line into command and up to two parameters */ cmd = strtok(line, DELIM); /* ignore empty lines */ if (!cmd || *cmd == '#') continue; p1 = strtok(NULL, DELIM); if (p1) { l1 = strlen(p1); hash = icase ? strihash(p1) : strhash(p1); p2 = strtok(NULL, DELIM); if (p2) l2 = strlen(p2); } if (!strcmp("hash", cmd) && l1) { /* print results of different hash functions */ printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1), strihash(p1), memihash(p1, l1)); } else if (!strcmp("add", cmd) && l1 && l2) { /* create entry with key = p1, value = p2 */ entry = alloc_test_entry(hash, p1, l1, p2, l2); /* add to hashmap */ hashmap_add(&map, entry); } else if (!strcmp("put", cmd) && l1 && l2) { /* create entry with key = p1, value = p2 */ entry = alloc_test_entry(hash, p1, l1, p2, l2); /* add / replace entry */ entry = hashmap_put(&map, entry); /* print and free replaced entry, if any */ puts(entry ? get_value(entry) : "NULL"); free(entry); } else if (!strcmp("get", cmd) && l1) { /* lookup entry in hashmap */ entry = hashmap_get_from_hash(&map, hash, p1); /* print result */ if (!entry) puts("NULL"); while (entry) { puts(get_value(entry)); entry = hashmap_get_next(&map, entry); } } else if (!strcmp("remove", cmd) && l1) { /* setup static key */ struct hashmap_entry key; hashmap_entry_init(&key, hash); /* remove entry from hashmap */ entry = hashmap_remove(&map, &key, p1); /* print result and free entry*/ puts(entry ? get_value(entry) : "NULL"); free(entry); } else if (!strcmp("iterate", cmd)) { struct hashmap_iter iter; hashmap_iter_init(&map, &iter); while ((entry = hashmap_iter_next(&iter))) printf("%s %s\n", entry->key, get_value(entry)); } else if (!strcmp("size", cmd)) { /* print table sizes */ printf("%u %u\n", map.tablesize, map.size); } else if (!strcmp("intern", cmd) && l1) { /* test that strintern works */ const char *i1 = strintern(p1); const char *i2 = strintern(p1); if (strcmp(i1, p1)) printf("strintern(%s) returns %s\n", p1, i1); else if (i1 == p1) printf("strintern(%s) returns input pointer\n", p1); else if (i1 != i2) printf("strintern(%s) != strintern(%s)", i1, i2); else printf("%s\n", i1); } else if (!strcmp("perfhashmap", cmd) && l1 && l2) { perf_hashmap(atoi(p1), atoi(p2)); } else { printf("Unknown command %s\n", cmd); } } hashmap_free(&map, 1); return 0; }
/* * Calculates the hash code of an fsentry structure's path. */ static unsigned int fsentry_hash(const struct fsentry *fse) { unsigned int hash = fse->list ? fse->list->ent.hash : 0; return hash ^ memihash(fse->name, fse->len); }
static int handle_range_1( struct index_state *istate, int k_start, int k_end, struct dir_entry *parent, struct strbuf *prefix, struct lazy_entry *lazy_entries) { int input_prefix_len = prefix->len; int k = k_start; while (k < k_end) { struct cache_entry *ce_k = istate->cache[k]; const char *name, *slash; if (prefix->len && strncmp(ce_k->name, prefix->buf, prefix->len)) break; name = ce_k->name + prefix->len; slash = strchr(name, '/'); if (slash) { int len = slash - name; int processed; struct dir_entry *dir_new; strbuf_add(prefix, name, len); processed = handle_range_dir(istate, k, k_end, parent, prefix, lazy_entries, &dir_new); if (processed) { k += processed; strbuf_setlen(prefix, input_prefix_len); continue; } strbuf_addch(prefix, '/'); processed = handle_range_1(istate, k, k_end, dir_new, prefix, lazy_entries); k += processed; strbuf_setlen(prefix, input_prefix_len); continue; } /* * It is too expensive to take a lock to insert "ce_k" * into "istate->name_hash" and increment the ref-count * on the "parent" dir. So we defer actually updating * permanent data structures until phase 2 (where we * can change the locking requirements) and simply * accumulate our current results into the lazy_entries * data array). * * We do not need to lock the lazy_entries array because * we have exclusive access to the cells in the range * [k_start,k_end) that this thread was given. */ lazy_entries[k].dir = parent; if (parent) { lazy_entries[k].hash_name = memihash_cont( parent->ent.hash, ce_k->name + parent->namelen, ce_namelen(ce_k) - parent->namelen); lazy_entries[k].hash_dir = parent->ent.hash; } else { lazy_entries[k].hash_name = memihash(ce_k->name, ce_namelen(ce_k)); } k++; } return k - k_start; }
static struct dir_entry *find_dir_entry(struct index_state *istate, const char *name, unsigned int namelen) { return find_dir_entry__hash(istate, name, namelen, memihash(name, namelen)); }