From ffd638a0b4268df8c6046d37d218da9e6ec9c406 Mon Sep 17 00:00:00 2001 From: Russell McConnachie Date: Mon, 28 Mar 2022 21:30:49 +0000 Subject: [PATCH 1/3] Add hashmap_set_by_hash, and hashmap_get_by_hash. --- hashmap.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/hashmap.c b/hashmap.c index fa21374..e4b6eef 100644 --- a/hashmap.c +++ b/hashmap.c @@ -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 @@ -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. From e31ac31bd394d6c372aa2e8e768537db8504d5a2 Mon Sep 17 00:00:00 2001 From: Russell McConnachie Date: Mon, 28 Mar 2022 21:30:52 +0000 Subject: [PATCH 2/3] Add hashmap_set_by_hash, and hashmap_get_by_hash. --- hashmap.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hashmap.h b/hashmap.h index fa4a129..b5c146e 100644 --- a/hashmap.h +++ b/hashmap.h @@ -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, From 9f066d937e7b3922e0c633b649e873475e6f3b5a Mon Sep 17 00:00:00 2001 From: Russell McConnachie Date: Mon, 28 Mar 2022 21:49:00 +0000 Subject: [PATCH 3/3] Update README.md --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c61403e..4b164c2 100644 --- a/README.md +++ b/README.md @@ -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" }); @@ -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