Skip to content

Support hashmap_get_by_hash, and hashmap_set_by_hash #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,36 @@ uint64_t user_hash(const void *item, uint64_t seed0, uint64_t seed1) {

int main() {
// create a new hash map where each item is a `struct user`. The second
// argument is the initial capacity. The third and fourth arguments are
// argument is the initial capacity. The third and fourth arguments are
// optional seeds that are passed to the following hash function.
struct hashmap *map = hashmap_new(sizeof(struct user), 0, 0, 0,
struct hashmap *map = hashmap_new(sizeof(struct user), 0, 0, 0,
user_hash, user_compare, NULL, NULL);

// Here we'll load some users into the hash map. Each set operation
// performs a copy of the data that is pointed to in the second argument.
hashmap_set(map, &(struct user){ .name="Dale", .age=44 });
hashmap_set(map, &(struct user){ .name="Roger", .age=68 });
hashmap_set(map, &(struct user){ .name="Jane", .age=47 });
const struct user dale = { .name="Dale", .age=44 };
const struct user roger = { .name="Roger", .age=68 };
const struct user jane = { .name="Jane", .age=47 };
const struct user russ = { .name="Russell", .age=40 };

hashmap_set(map, &dale);
hashmap_set(map, &roger);
hashmap_set(map, &jane);
hashmap_set_by_hash(map, &russ, &russ);

struct user *user;

struct user *user;

printf("\n-- get some users --\n");
user = hashmap_get(map, &(struct user){ .name="Jane" });
user = hashmap_get(map, &jane);
printf("%s age=%d\n", user->name, user->age);

user = hashmap_get(map, &roger);
printf("%s age=%d\n", user->name, user->age);

user = hashmap_get(map, &(struct user){ .name="Roger" });
user = hashmap_get(map, &dale);
printf("%s age=%d\n", user->name, user->age);

user = hashmap_get(map, &(struct user){ .name="Dale" });
user = hashmap_get_by_hash(map, &russ);
printf("%s age=%d\n", user->name, user->age);

user = hashmap_get(map, &(struct user){ .name="Tom" });
Expand All @@ -83,22 +92,25 @@ int main() {
}

// output:
//
// -- get some users --
// Jane age=47
// Roger age=68
// Dale age=44
// Russell age=40
// not exists
//
//
// -- iterate over all users (hashmap_scan) --
// Dale (age=44)
// Roger (age=68)
// Jane (age=47)
// Russell (age=40)
//
// -- iterate over all users (hashmap_iter) --
// Dale (age=44)
// Roger (age=68)
// Jane (age=47)

// Russell (age=40)
```

## Functions
Expand Down
70 changes: 70 additions & 0 deletions hashmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,54 @@ static bool resize(struct hashmap *map, size_t new_cap) {
return true;
}

// hashmap_set_by_hash inserts or replaces an item in the hash map. If an item is
// replaced then it is returned otherwise NULL is returned. This operation
// may allocate memory. If the system is unable to allocate additional
// memory then NULL is returned and hashmap_oom() returns true.
void *hashmap_set_by_hash(struct hashmap *map, const void *key, const void *item) {
if (!item) {
panic("item is null");
}
map->oom = false;
if (map->count == map->growat) {
if (!resize(map, map->nbuckets*2)) {
map->oom = true;
return NULL;
}
}


struct bucket *entry = map->edata;
entry->hash = (uint64_t) key;
entry->dib = 1;
memcpy(bucket_item(entry), item, map->elsize);

size_t i = entry->hash & map->mask;
for (;;) {
struct bucket *bucket = bucket_at(map, i);
if (bucket->dib == 0) {
memcpy(bucket, entry, map->bucketsz);
map->count++;
return NULL;
}
if (entry->hash == bucket->hash &&
map->compare(bucket_item(entry), bucket_item(bucket),
map->udata) == 0)
{
memcpy(map->spare, bucket_item(bucket), map->elsize);
memcpy(bucket_item(bucket), bucket_item(entry), map->elsize);
return map->spare;
}
if (bucket->dib < entry->dib) {
memcpy(map->spare, bucket, map->bucketsz);
memcpy(bucket, entry, map->bucketsz);
memcpy(entry, map->spare, map->bucketsz);
}
i = (i + 1) & map->mask;
entry->dib += 1;
}
}

// hashmap_set inserts or replaces an item in the hash map. If an item is
// replaced then it is returned otherwise NULL is returned. This operation
// may allocate memory. If the system is unable to allocate additional
Expand Down Expand Up @@ -314,6 +362,28 @@ void *hashmap_get(struct hashmap *map, const void *key) {
}
}

// hashmap_get_by_hash returns the item based on the provided key. If the item is not
// found then NULL is returned.
void *hashmap_get_by_hash(struct hashmap *map, const void *key) {
if (!key) {
panic("key is null");
}
uint64_t hash = (uint64_t) key;
size_t i = hash & map->mask;
for (;;) {
struct bucket *bucket = bucket_at(map, i);
if (!bucket->dib) {
return NULL;
}
if (bucket->hash == hash &&
map->compare(key, bucket_item(bucket), map->udata) == 0)
{
return bucket_item(bucket);
}
i = (i + 1) & map->mask;
}
}

// hashmap_probe returns the item in the bucket at position or NULL if an item
// is not set for that bucket. The position is 'moduloed' by the number of
// buckets in the hashmap.
Expand Down
2 changes: 2 additions & 0 deletions hashmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ void hashmap_clear(struct hashmap *map, bool update_cap);
size_t hashmap_count(struct hashmap *map);
bool hashmap_oom(struct hashmap *map);
void *hashmap_get(struct hashmap *map, const void *item);
void *hashmap_get_by_hash(struct hashmap *map, const void *item);
void *hashmap_set(struct hashmap *map, const void *item);
void *hashmap_set_by_hash(struct hashmap *map, const void *key, const void *item);
void *hashmap_delete(struct hashmap *map, void *item);
void *hashmap_probe(struct hashmap *map, uint64_t position);
bool hashmap_scan(struct hashmap *map,
Expand Down