/* * hashmap.c * Copyright (c) 2009 Vedant Kumar * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "hashmap_public.h" struct key_val_map { key k; val v; }; struct hashmap { key_val_map *map; uint32_t size; uint32_t capacity; uint32_t (*hash_fn)(key); bool (*eq_fn)(key, key); #ifdef HMAP_DESTRUCTORS void (*del_fn)(val); #endif #ifdef HMAP_THREAD_SAFE sem_t lock; #endif }; // hashmaps need a hash function, an equality function, and a destructor hashmap* mk_hmap(uint32_t (*hash_fn)(key), bool (*eq_fn)(key, key) #ifdef HMAP_DESTRUCTORS , void (*del_fn)(val) #endif ) { hashmap* hmap = (hashmap*) malloc(sizeof(hashmap)); hmap->map = (key_val_map*) malloc(sizeof(key_val_map) * HMAP_PRESET_SIZE); bzero(hmap->map, sizeof(key_val_map) * HMAP_PRESET_SIZE); hmap->size = 0; hmap->capacity = HMAP_PRESET_SIZE; hmap->hash_fn = hash_fn; hmap->eq_fn = eq_fn; #ifdef HMAP_DESTRUCTORS hmap->del_fn = del_fn; #endif #ifdef HMAP_THREAD_SAFE sem_init(&hmap->lock, 0, 1); #endif return hmap; } void free_hmap(hashmap* hmap) { #ifdef HMAP_THREAD_SAFE sem_wait(&hmap->lock); #endif #ifdef HMAP_DESTRUCTORS static uint32_t it; for (it=0; it < hmap->size; ++it) { if (hmap->map[it].v != NULL) { hmap->del_fn(hmap->map[it].v); } } #endif free(hmap->map); #ifdef HMAP_THREAD_SAFE sem_post(&hmap->lock); #endif free(hmap); } static void __oa_hmap_add(key_val_map* map, uint32_t size, uint32_t (*hash_fn)(key), key in, val out) { static uint32_t hash; hash = hash_fn(in) % size; uint32_t count = 0; while (map[hash].k != NULL) { hash = (hash + 1) % size; count++; if (count > size) { fprintf(stdout, "Hashmap has no free entries \n"); exit(0); } } map[hash].k = in; map[hash].v = out; } bool __hmap_add(hashmap* hmap, key in, val out) { #ifdef HMAP_THREAD_SAFE sem_wait(&hmap->lock); #endif // performace degrades after a certain load if (((float) hmap->size) / hmap->capacity > 0.70) { key_val_map* temp = (key_val_map*) malloc(sizeof(key_val_map) * hmap->capacity * HMAP_GROWTH_RATE); if (temp != NULL) { //hmap->capacity *= HMAP_GROWTH_RATE; } else { #ifdef HMAP_THREAD_SAFE sem_post(&hmap->lock); #endif // we're out of memory return false; } // init new hashmap bzero(temp, sizeof(key_val_map) * hmap->capacity * HMAP_GROWTH_RATE); // re-posn all elements static uint32_t it; for (it=0; it < hmap->capacity; ++it) { if (hmap->map[it].k != NULL) { __oa_hmap_add(temp, hmap->capacity * HMAP_GROWTH_RATE, hmap->hash_fn, hmap->map[it].k, hmap->map[it].v); } } // swap out the old map with the new one free(hmap->map); hmap->map = temp; hmap->capacity *= HMAP_GROWTH_RATE; } __oa_hmap_add(hmap->map, hmap->capacity, hmap->hash_fn, in, out); hmap->size += 1; #ifdef HMAP_THREAD_SAFE sem_post(&hmap->lock); #endif return true; } val __hmap_get(hashmap* hmap, key in) { #ifdef HMAP_THREAD_SAFE sem_wait(&hmap->lock); #endif static uint32_t hash; hash = hmap->hash_fn(in) % hmap->capacity; while (hmap->map[hash].k != NULL) { if (hmap->eq_fn(in, hmap->map[hash].k)) { #ifdef HMAP_THREAD_SAFE sem_post(&hmap->lock); #endif return hmap->map[hash].v; } hash = (hash + 1) % hmap->capacity; } #ifdef HMAP_THREAD_SAFE sem_post(&hmap->lock); #endif return NULL; } #ifdef HMAP_MAKE_HASHFN // Robert Jenkins' 32 bit integer hash function uint32_t int_hash_fn(key in) { static uint32_t a; a = *((uint32_t*) in); a = (a+0x7ed55d16) + (a<<12); a = (a^0xc761c23c) ^ (a>>19); a = (a+0x165667b1) + (a<<5); a = (a+0xd3a2646c) ^ (a<<9); a = (a+0xfd7046c5) + (a<<3); a = (a^0xb55a4f09) ^ (a>>16); return a; } bool int_eq_fn(key a, key b) { return *((uint32_t*) a) == *((uint32_t*) b) ? true : false; } #endif