freewtp/lib/hash.c

556 lines
12 KiB
C
Raw Permalink Normal View History

2016-02-05 17:38:49 +01:00
#include "capwap.h"
2016-08-22 16:59:55 +02:00
#include "hash.h"
2016-02-05 17:38:49 +01:00
/* */
static void capwap_hash_free_item(struct capwap_hash* hash, struct capwap_hash_item* item) {
ASSERT(hash != NULL);
ASSERT(item != NULL);
if (item->data && hash->item_free) {
hash->item_free(item->data);
}
capwap_free(item);
}
/* */
static void capwap_hash_free_items(struct capwap_hash* hash, struct capwap_hash_item* item) {
ASSERT(hash != NULL);
ASSERT(item != NULL);
/* Free child */
if (item->left) {
capwap_hash_free_items(hash, item->left);
}
if (item->right) {
capwap_hash_free_items(hash, item->right);
}
/* */
capwap_hash_free_item(hash, item);
}
/* */
static struct capwap_hash_item* capwap_hash_search_items(struct capwap_hash* hash, struct capwap_hash_item* item, const void* key) {
int result;
struct capwap_hash_item* search;
ASSERT(hash != NULL);
ASSERT(key != NULL);
search = item;
while (search) {
result = hash->item_cmp(key, hash->item_getkey(search->data));
if (!result) {
return search;
} else if (result < 0) {
search = item->left;
} else if (result > 0) {
search = item->right;
}
}
return NULL;
}
/* */
static int capwap_hash_foreach_items(struct capwap_hash* hash, struct capwap_hash_item* item, capwap_hash_item_foreach item_foreach, void* param) {
int result;
ASSERT(hash != NULL);
ASSERT(item_foreach != NULL);
ASSERT(item != NULL);
/* */
if (item->left) {
result = capwap_hash_foreach_items(hash, item->left, item_foreach, param);
if (result == HASH_BREAK) {
return HASH_BREAK;
}
}
/* */
item->removenext = NULL;
result = item_foreach(item->data, param);
/* Delete item */
if ((result == HASH_DELETE_AND_BREAK) || (result == HASH_DELETE_AND_CONTINUE)) {
item->removenext = hash->removeitems;
hash->removeitems = item;
}
/* Break */
if ((result == HASH_BREAK) || (result == HASH_DELETE_AND_BREAK)) {
return HASH_BREAK;
}
/* */
if (item->right) {
result = capwap_hash_foreach_items(hash, item->right, item_foreach, param);
if (result == HASH_BREAK) {
return HASH_BREAK;
}
}
return HASH_CONTINUE;
}
/* */
static struct capwap_hash_item* capwap_hash_create_item(struct capwap_hash* hash, void* data) {
struct capwap_hash_item* item;
ASSERT(hash != NULL);
ASSERT(data != NULL);
/* */
item = (struct capwap_hash_item*)capwap_alloc(sizeof(struct capwap_hash_item));
memset(item, 0, sizeof(struct capwap_hash_item));
item->data = data;
return item;
}
/* */
static void capwap_hash_update_height(struct capwap_hash_item* item) {
ASSERT(item != NULL);
if (item->left && item->right) {
item->height = ((item->left->height > item->right->height) ? item->left->height + 1 : item->right->height + 1);
} else if (item->left) {
item->height = item->left->height + 1;
} else if (item->right) {
item->height = item->right->height + 1;
} else {
item->height = 0;
}
}
/* */
static void capwap_hash_set_left_item(struct capwap_hash_item* item, struct capwap_hash_item* child) {
ASSERT(item != NULL);
if (child) {
child->parent = item;
}
item->left = child;
capwap_hash_update_height(item);
}
/* */
static void capwap_hash_set_right_item(struct capwap_hash_item* item, struct capwap_hash_item* child) {
ASSERT(item != NULL);
if (child) {
child->parent = item;
}
item->right = child;
capwap_hash_update_height(item);
}
/* */
static void capwap_hash_rotate_left(struct capwap_hash_item* item, struct capwap_hash_item** root) {
int parentside;
struct capwap_hash_item* right;
struct capwap_hash_item* parent;
ASSERT(item != NULL);
/* Check parent */
parent = item->parent;
if (parent) {
parentside = ((parent->left == item) ? 1 : 0);
}
/* Rotate */
right = item->right;
capwap_hash_set_right_item(item, right->left);
capwap_hash_set_left_item(right, item);
/* Update parent */
if (parent) {
if (parentside) {
capwap_hash_set_left_item(parent, right);
} else {
capwap_hash_set_right_item(parent, right);
}
} else {
right->parent = NULL;
*root = right;
}
}
/* */
static void capwap_hash_rotate_right(struct capwap_hash_item* item, struct capwap_hash_item** root) {
int parentside;
struct capwap_hash_item* left;
struct capwap_hash_item* parent;
ASSERT(item != NULL);
/* Check parent */
parent = item->parent;
if (parent) {
parentside = ((parent->left == item) ? 1 : 0);
}
/* Rotate */
left = item->left;
capwap_hash_set_left_item(item, left->right);
capwap_hash_set_right_item(left, item);
/* Update parent */
if (parent) {
if (parentside) {
capwap_hash_set_left_item(parent, left);
} else {
capwap_hash_set_right_item(parent, left);
}
} else {
left->parent = NULL;
*root = left;
}
}
/* */
static int capwap_hash_get_balance_item(struct capwap_hash_item* item) {
ASSERT(item != NULL);
if (item->left && item->right) {
return item->left->height - item->right->height;
} else if (item->left) {
return item->left->height + 1;
} else if (item->right) {
return -(item->right->height + 1);
}
return 0;
}
/* */
static void capwap_hash_balance_tree(struct capwap_hash_item* item, struct capwap_hash_item** root) {
int result;
ASSERT(item != NULL);
result = capwap_hash_get_balance_item(item);
if (result > 1) {
if (capwap_hash_get_balance_item(item->left) < 0) {
capwap_hash_rotate_left(item->left, root);
}
capwap_hash_rotate_right(item, root);
} else if (result < -1) {
if (capwap_hash_get_balance_item(item->right) > 0) {
capwap_hash_rotate_right(item->right, root);
}
capwap_hash_rotate_left(item, root);
}
}
/* */
static void capwap_hash_deleteitem(struct capwap_hash* hash, const void* key, struct capwap_hash_item* search, unsigned long hashvalue) {
struct capwap_hash_item* parent;
ASSERT(hash != NULL);
ASSERT(key != NULL);
ASSERT(search != NULL);
ASSERT(hashvalue < hash->hashsize);
/* Rebalancing tree */
parent = search->parent;
if (!search->left && !search->right) {
if (parent) {
if (parent->left == search) {
capwap_hash_set_left_item(parent, NULL);
} else {
capwap_hash_set_right_item(parent, NULL);
}
/* */
capwap_hash_balance_tree(parent, &hash->items[hashvalue]);
} else {
hash->items[hashvalue] = NULL;
}
} else if (!search->right) {
if (parent) {
if (parent->left == search) {
capwap_hash_set_left_item(parent, search->left);
} else {
capwap_hash_set_right_item(parent, search->left);
}
/* */
capwap_hash_balance_tree(parent, &hash->items[hashvalue]);
} else {
search->left->parent = NULL;
hash->items[hashvalue] = search->left;
}
} else if (!search->left) {
if (parent) {
if (parent->left == search) {
capwap_hash_set_left_item(parent, search->right);
} else {
capwap_hash_set_right_item(parent, search->right);
}
/* */
capwap_hash_balance_tree(parent, &hash->items[hashvalue]);
} else {
search->right->parent = NULL;
hash->items[hashvalue] = search->right;
}
} else {
struct capwap_hash_item* replacement = NULL;
if (capwap_hash_get_balance_item(search) > 0) {
if (!search->left->right) {
replacement = search->left;
capwap_hash_set_right_item(replacement, search->right);
} else {
replacement = search->left->right;
while (replacement->right) {
replacement = replacement->right;
}
capwap_hash_set_right_item(replacement->parent, replacement->left);
capwap_hash_set_left_item(replacement, search->left);
capwap_hash_set_right_item(replacement, search->right);
}
} else {
if (!search->right->left) {
replacement = search->right;
capwap_hash_set_left_item(replacement, search->left);
} else {
replacement = search->right->left;
while (replacement->left) {
replacement = replacement->left;
}
capwap_hash_set_left_item(replacement->parent, replacement->right);
capwap_hash_set_left_item(replacement, search->left);
capwap_hash_set_right_item(replacement, search->right);
}
}
if (parent) {
if (parent->left == search) {
capwap_hash_set_left_item(parent, replacement);
} else {
capwap_hash_set_right_item(parent, replacement);
}
} else {
replacement->parent = NULL;
hash->items[hashvalue] = replacement;
}
capwap_hash_balance_tree(replacement, &hash->items[hashvalue]);
}
/* Free node */
hash->count--;
capwap_hash_free_item(hash, search);
}
/* */
struct capwap_hash* capwap_hash_create(unsigned long hashsize) {
unsigned long size;
struct capwap_hash* hash;
ASSERT(hashsize > 0);
/* */
hash = (struct capwap_hash*)capwap_alloc(sizeof(struct capwap_hash));
hash->hashsize = hashsize;
hash->count = 0;
size = sizeof(struct capwap_hash_item*) * hashsize;
hash->items = (struct capwap_hash_item**)capwap_alloc(size);
memset(hash->items, 0, size);
return hash;
}
/* */
void capwap_hash_free(struct capwap_hash* hash) {
ASSERT(hash != NULL);
/* Delete all items */
capwap_hash_deleteall(hash);
/* Free */
capwap_free(hash->items);
capwap_free(hash);
}
/* */
void capwap_hash_add(struct capwap_hash* hash, void* data) {
int result;
const void* key;
unsigned long hashvalue;
struct capwap_hash_item* search;
struct capwap_hash_item* item = NULL;
ASSERT(data != NULL);
ASSERT(hash != NULL);
ASSERT(hash->item_gethash != NULL);
ASSERT(hash->item_getkey != NULL);
ASSERT(hash->item_cmp != NULL);
/* */
key = hash->item_getkey(data);
hashvalue = hash->item_gethash(key, hash->hashsize);
ASSERT(hashvalue < hash->hashsize);
/* Search position where insert item */
search = hash->items[hashvalue];
if (!search) {
hash->count++;
hash->items[hashvalue] = capwap_hash_create_item(hash, data);
} else {
while (search) {
result = hash->item_cmp(key, hash->item_getkey(search->data));
if (!result) {
/* Free old element and update data value without create new item */
if (search->data && hash->item_free) {
hash->item_free(search->data);
}
search->data = data;
break;
} else if (result < 0) {
if (search->left) {
search = search->left;
} else {
hash->count++;
item = capwap_hash_create_item(hash, data);
capwap_hash_set_left_item(search, item);
break;
}
} else if (result > 0) {
if (search->right) {
search = search->right;
} else {
hash->count++;
item = capwap_hash_create_item(hash, data);
capwap_hash_set_right_item(search, item);
break;
}
}
}
/* Rebalancing tree */
while (item) {
capwap_hash_update_height(item);
capwap_hash_balance_tree(item, &hash->items[hashvalue]);
/* Rebalancing parent */
item = item->parent;
}
}
}
/* */
void capwap_hash_delete(struct capwap_hash* hash, const void* key) {
unsigned long hashvalue;
struct capwap_hash_item* search;
ASSERT(hash != NULL);
ASSERT(key != NULL);
/* */
hashvalue = hash->item_gethash(key, hash->hashsize);
ASSERT(hashvalue < hash->hashsize);
if (!hash->items[hashvalue]) {
return;
}
/* */
search = capwap_hash_search_items(hash, hash->items[hashvalue], key);
if (!search) {
return;
}
/* */
capwap_hash_deleteitem(hash, key, search, hashvalue);
}
/* */
void capwap_hash_deleteall(struct capwap_hash* hash) {
unsigned long i;
ASSERT(hash != NULL);
for (i = 0; i < hash->hashsize; i++) {
if (hash->items[i]) {
capwap_hash_free_items(hash, hash->items[i]);
hash->items[i] = NULL;
}
}
/* */
hash->count = 0;
}
/* */
void* capwap_hash_search(struct capwap_hash* hash, const void* key) {
unsigned long hashvalue;
struct capwap_hash_item* items;
struct capwap_hash_item* result;
ASSERT(hash != NULL);
ASSERT(key != NULL);
/* Search item */
hashvalue = hash->item_gethash(key, hash->hashsize);
items = hash->items[hashvalue];
if (!items) {
return NULL;
}
/* */
result = capwap_hash_search_items(hash, items, key);
if (!result) {
return NULL;
}
return result->data;
}
/* */
void capwap_hash_foreach(struct capwap_hash* hash, capwap_hash_item_foreach item_foreach, void* param) {
int result;
unsigned long i;
ASSERT(hash != NULL);
ASSERT(item_foreach != NULL);
/* */
hash->removeitems = NULL;
/* */
for (i = 0; i < hash->hashsize; i++) {
if (hash->items[i]) {
result = capwap_hash_foreach_items(hash, hash->items[i], item_foreach, param);
if (result == HASH_BREAK) {
break;
}
}
}
/* Delete marked items */
while (hash->removeitems) {
struct capwap_hash_item* item = hash->removeitems;
const void* key = hash->item_getkey(item->data);
/* */
hash->removeitems = item->removenext;
capwap_hash_deleteitem(hash, key, item, hash->item_gethash(key, hash->hashsize));
}
}