#include #include #include bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal, bh_hash_cb_t hash) { bh_hashmap_t *result; result = malloc(sizeof(*result)); if (result) bh_hashmap_init(result, equal, hash); return result; } void bh_hashmap_free(bh_hashmap_t *hashmap) { bh_hashmap_destroy(hashmap); free(hashmap); } void bh_hashmap_init(bh_hashmap_t *hashmap, bh_equal_cb_t equal, bh_hash_cb_t hash) { memset(hashmap, 0, sizeof(*hashmap)); hashmap->factor = 0.75f; hashmap->equal = equal; hashmap->hash = hash; } void bh_hashmap_destroy(bh_hashmap_t *hashmap) { if (hashmap->capacity) { free(hashmap->data); free(hashmap->psls); } } void bh_hashmap_clear(bh_hashmap_t *hashmap) { if (hashmap->capacity) memset(hashmap->psls, 0, hashmap->capacity * sizeof(size_t)); hashmap->size = 0; } int bh_hashmap_reserve(bh_hashmap_t *hashmap, size_t size) { bh_hashmap_t other; size_t capacity, max_capacity, threshold; /* Calculate hashmap max capacity and capacity threshold */ capacity = hashmap->capacity; threshold = hashmap->capacity * hashmap->factor; max_capacity = ((size_t)-1) / sizeof(bh_hashmap_node_t); /* New capacity can't be smaller then current hashmap size */ if (size < hashmap->size) size = hashmap->size; /* Find new hashmap capacity */ if (!size) { /* No capacity needed */ capacity = 0; threshold = 0; } else if (size > threshold) { /* Bigger capacity needed - grow the hashmap */ while (size > threshold) { capacity = (capacity) ? (capacity * 2) : (16); threshold = capacity * hashmap->factor; /* Capacity can't be bigger than max capacity and overflow */ if (capacity > max_capacity || capacity < 16) return -1; } } else { /* Smaller capacity needed - shrink the hashmap */ while (size <= threshold / 2 && capacity > 16) { capacity /= 2; threshold = capacity * hashmap->factor; } } /* Prevent same size reallocation */ if (capacity == hashmap->capacity) return 0; /* Initialize new hashmap */ bh_hashmap_init(&other, hashmap->equal, hashmap->hash); other.factor = hashmap->factor; if (capacity) { void *iter; /* Allocate new memory for the hashmap */ other.data = malloc(sizeof(*other.data) * capacity); other.psls = malloc(sizeof(size_t) * capacity); other.threshold = threshold; other.capacity = capacity; /* Check for allocations failure */ if (!other.data || !other.psls) { if (other.data) free(other.data); if (other.psls) free(other.psls); return -1; } /* Reset probe sequence lengths */ memset(other.psls, 0, sizeof(size_t) * other.capacity); /* Iterate and insert data into hashmap */ iter = bh_hashmap_iter_next(hashmap, NULL); while (iter) { void *key, *value; key = bh_hashmap_iter_key(iter); value = bh_hashmap_iter_value(iter); bh_hashmap_insert(&other, key, value); iter = bh_hashmap_iter_next(hashmap, iter); } } /* Swap hashmaps */ bh_hashmap_destroy(hashmap); *hashmap = other; return 0; } int bh_hashmap_insert(bh_hashmap_t *hashmap, void *key, void *value) { size_t bucket, psl, tmp_psl; bh_hashmap_node_t item, tmp; /* Try to stay below hashmap threshold */ if (hashmap->size + 1 > hashmap->threshold) if (bh_hashmap_reserve(hashmap, hashmap->size + 1)) if (hashmap->size >= hashmap->capacity) return -1; /* Prepare inserted data and set PSL to 1 */ item.key = key; item.value = value; psl = 1; /* Calculate prefered bucket index */ bucket = hashmap->hash(key) & (hashmap->capacity - 1); /* Find empty bucket */ while (hashmap->psls[bucket]) { /* Current bucket is richer then us - swap elements */ if (psl > hashmap->psls[bucket]) { tmp = hashmap->data[bucket]; tmp_psl = hashmap->psls[bucket]; hashmap->data[bucket] = item; hashmap->psls[bucket] = psl; item = tmp; psl = tmp_psl; } bucket = (bucket + 1) & (hashmap->capacity - 1); psl++; } /* Found empty bucket - place item here */ hashmap->data[bucket] = item; hashmap->psls[bucket] = psl; hashmap->size++; return 0; } void bh_hashmap_remove(bh_hashmap_t *hashmap, void *key) { void *iter; iter = bh_hashmap_iter_at(hashmap, key); if (iter) bh_hashmap_iter_remove(hashmap, iter); } void *bh_hashmap_at(bh_hashmap_t *hashmap, void *key, int *exists) { void *iter; iter = bh_hashmap_iter_at(hashmap, key); if (iter) { /* If exists flag passed - set to 1 */ if (exists) *exists = 1; return bh_hashmap_iter_value(iter); } /* If exists flag passed - set to 0 */ if (exists) *exists = 0; return NULL; } int bh_hashmap_empty(bh_hashmap_t *hashmap) { return !hashmap->size; } size_t bh_hashmap_size(bh_hashmap_t *hashmap) { return hashmap->size; } size_t bh_hashmap_capacity(bh_hashmap_t *hashmap) { return hashmap->capacity; } float bh_hashmap_factor(bh_hashmap_t *hashmap) { return hashmap->factor; } void bh_hashmap_set_factor(bh_hashmap_t *hashmap, float factor) { factor = (factor > 1.0f) ? (1.0f) : (factor); factor = (factor < 0.15f) ? (0.15f) : (factor); hashmap->factor = factor; hashmap->threshold = hashmap->capacity * factor; } void *bh_hashmap_iter_at(bh_hashmap_t *hashmap, void *key) { size_t bucket, psl; /* Nothing can be in empty map */ if (!hashmap->size) return NULL; /* Calculate prefered bucket index and set PSL to 1 */ bucket = hashmap->hash(key) & (hashmap->capacity - 1); psl = 1; /* Iterate hashmap until we find element or find richer bucket */ while (hashmap->psls[bucket] >= psl && hashmap->equal(hashmap->data[bucket].key, key)) { bucket = (bucket + 1) & (hashmap->capacity - 1); psl++; } /* If bucket is poorer or equal to us - we found our element */ if (hashmap->psls[bucket] >= psl) return hashmap->data + bucket; return NULL; } void *bh_hashmap_iter_next(bh_hashmap_t *hashmap, void *iter) { bh_hashmap_node_t *item; item = (bh_hashmap_node_t *)iter; while (1) { /* Advance or set iterator to the first element */ if (item) item++; else item = hashmap->data; /* Check iterator for validity */ if (item >= hashmap->data + hashmap->capacity) return NULL; /* Check iterator's item PSL */ if (hashmap->psls[item - hashmap->data]) return item; } } void bh_hashmap_iter_remove(bh_hashmap_t *hashmap, void *iter) { size_t bucket, next_bucket; /* Check if hashmap is empty or we are given NULL iterator */ if (!iter || !hashmap->size) return; /* Adjust hashmap size, calculate current and next bucket index */ hashmap->size--; bucket = (bh_hashmap_node_t *)iter - hashmap->data; next_bucket = (bucket + 1) & (hashmap->capacity - 1); /* Shift all elements toward their preffered place */ while (hashmap->psls[next_bucket] > 1) { /* Copy item and adjust PSL */ hashmap->data[bucket] = hashmap->data[next_bucket]; hashmap->psls[bucket] = hashmap->psls[next_bucket] - 1; /* Calculate next bucket index */ bucket = next_bucket; next_bucket = (bucket + 1) & (hashmap->capacity - 1); } /* Reset bucket's PSL (mark empty) */ hashmap->psls[bucket] = 0; } void *bh_hashmap_iter_key(void *iter) { return ((bh_hashmap_node_t *)iter)->key; } void *bh_hashmap_iter_value(void *iter) { return ((bh_hashmap_node_t *)iter)->value; }