/// MentOS, The Mentoring Operating system project /// @file hashmap.c /// @brief /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "hashmap.h" #include "assert.h" #include "string.h" #include "slab.h" /// @brief Stores information of an entry of the hashmap. struct hashmap_entry_t { /// Key of the entry. char *key; /// Value of the entry. void *value; /// Pointer to the next entry. struct hashmap_entry_t *next; }; /// @brief Stores information of a hashmap. struct hashmap_t { /// Hashing function, used to generate hash keys. hashmap_hash_t hash_func; /// Comparison function, used to compare hash keys. hashmap_comp_t hash_comp; /// Key duplication function, used to duplicate hash keys. hashmap_dupe_t hash_key_dup; /// Key deallocation function, used to free the memory occupied by hash keys. hashmap_free_t hash_key_free; /// Size of the hashmap. unsigned int size; /// List of entries. hashmap_entry_t **entries; }; static inline hashmap_t *__alloc_hashmap() { hashmap_t *hashmap = kmalloc(sizeof(hashmap_t)); memset(hashmap, 0, sizeof(hashmap_t)); return hashmap; } static inline hashmap_entry_t *__alloc_entry() { hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t)); memset(entry, 0, sizeof(hashmap_entry_t)); return entry; } static inline void __dealloc_entry(hashmap_entry_t *entry) { assert(entry && "Invalid pointer to an entry."); kfree(entry); } static inline hashmap_entry_t **__alloc_entries(unsigned int size) { hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size); memset(entries, 0, sizeof(hashmap_entry_t *) * size); return entries; } static inline void __dealloc_entries(hashmap_entry_t **entries) { assert(entries && "Invalid pointer to entries."); kfree(entries); } unsigned int hashmap_int_hash(const void *key) { return (unsigned int)key; } int hashmap_int_comp(const void *a, const void *b) { return (int)a == (int)b; } unsigned int hashmap_str_hash(const void *_key) { unsigned int hash = 0; const char *key = (const char *)_key; char c; // This is the so-called "sdbm" hash. It comes from a piece of public // domain code from a clone of ndbm. while ((c = *key++)) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } int hashmap_str_comp(const void *a, const void *b) { return !strcmp(a, b); } void *hashmap_do_not_duplicate(const void *value) { return (void *)value; } void hashmap_do_not_free(void *value) { (void)value; } hashmap_t *hashmap_create( unsigned int size, hashmap_hash_t hash_fun, hashmap_comp_t comp_fun, hashmap_dupe_t dupe_fun, hashmap_free_t key_free_fun) { // Allocate the map. hashmap_t *map = __alloc_hashmap(); // Initialize the entries. map->size = size; map->entries = __alloc_entries(size); // Initialize its functions. map->hash_func = hash_fun; map->hash_comp = comp_fun; map->hash_key_dup = dupe_fun; map->hash_key_free = key_free_fun; return map; } void hashmap_free(hashmap_t *map) { for (unsigned int i = 0; i < map->size; ++i) { hashmap_entry_t *x = map->entries[i], *p; while (x) { p = x; x = x->next; map->hash_key_free(p->key); __dealloc_entry(p); } } __dealloc_entries(map->entries); } void *hashmap_set(hashmap_t *map, const void *key, void *value) { unsigned int hash = map->hash_func(key) % map->size; hashmap_entry_t *x = map->entries[hash]; if (x == NULL) { hashmap_entry_t *e = __alloc_entry(); e->key = map->hash_key_dup(key); e->value = value; e->next = NULL; map->entries[hash] = e; return NULL; } hashmap_entry_t *p = NULL; do { if (map->hash_comp(x->key, key)) { void *out = x->value; x->value = value; return out; } p = x; x = x->next; } while (x); hashmap_entry_t *e = __alloc_entry(); e->key = map->hash_key_dup(key); e->value = value; e->next = NULL; p->next = e; return NULL; } void *hashmap_get(hashmap_t *map, const void *key) { unsigned int hash = map->hash_func(key) % map->size; for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) if (map->hash_comp(x->key, key)) return x->value; return NULL; } void *hashmap_remove(hashmap_t *map, const void *key) { unsigned int hash = map->hash_func(key) % map->size; hashmap_entry_t *x = map->entries[hash]; if (x == NULL) { return NULL; } if (map->hash_comp(x->key, key)) { void *out = x->value; map->entries[hash] = x->next; map->hash_key_free(x->key); __dealloc_entry(x); return out; } hashmap_entry_t *p = x; x = x->next; do { if (map->hash_comp(x->key, key)) { void *out = x->value; p->next = x->next; map->hash_key_free(x->key); __dealloc_entry(x); return out; } p = x; x = x->next; } while (x); return NULL; } int hashmap_is_empty(hashmap_t *map) { for (unsigned int i = 0; i < map->size; ++i) if (map->entries[i]) return 0; return 1; } int hashmap_has(hashmap_t *map, const void *key) { unsigned int hash = map->hash_func(key) % map->size; for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) if (map->hash_comp(x->key, key)) return 1; return 0; } list_t *hashmap_keys(hashmap_t *map) { list_t *l = list_create(); for (unsigned int i = 0; i < map->size; ++i) for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) list_insert_back(l, x->key); return l; } list_t *hashmap_values(hashmap_t *map) { list_t *l = list_create(); for (unsigned int i = 0; i < map->size; ++i) for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) list_insert_back(l, x->value); return l; }