forked from enlightenment/efl
Ecore hash was originally written to teach the concept of a hash, so it used
high level lists. Since it's getting more extensive use, I've switched it to use it's own list structure for collision chaining. This is more memory efficient and overall faster. SVN revision: 18503
This commit is contained in:
parent
ebbe8f1267
commit
c28f6b8d68
|
@ -266,6 +266,7 @@ extern "C" {
|
||||||
# define ECORE_HASH_NODE(hash) ((Ecore_Hash_Node *)hash)
|
# define ECORE_HASH_NODE(hash) ((Ecore_Hash_Node *)hash)
|
||||||
|
|
||||||
struct _ecore_hash_node {
|
struct _ecore_hash_node {
|
||||||
|
Ecore_Hash_Node *next; /* Pointer to the next node in the bucket list */
|
||||||
void *key; /* The key for the data node */
|
void *key; /* The key for the data node */
|
||||||
void *value; /* The value associated with this node */
|
void *value; /* The value associated with this node */
|
||||||
|
|
||||||
|
@ -276,7 +277,7 @@ extern "C" {
|
||||||
# define ECORE_HASH(hash) ((Ecore_Hash *)hash)
|
# define ECORE_HASH(hash) ((Ecore_Hash *)hash)
|
||||||
|
|
||||||
struct _ecore_hash {
|
struct _ecore_hash {
|
||||||
Ecore_List **buckets;
|
Ecore_Hash_Node **buckets;
|
||||||
int size; /* An index into the table of primes to
|
int size; /* An index into the table of primes to
|
||||||
determine size */
|
determine size */
|
||||||
int nodes; /* The number of nodes currently in the hash */
|
int nodes; /* The number of nodes currently in the hash */
|
||||||
|
|
|
@ -24,11 +24,11 @@ static int _ecore_hash_add_node(Ecore_Hash *hash, Ecore_Hash_Node *node);
|
||||||
static Ecore_Hash_Node * _ecore_hash_get_node(Ecore_Hash *hash, void *key);
|
static Ecore_Hash_Node * _ecore_hash_get_node(Ecore_Hash *hash, void *key);
|
||||||
static int _ecore_hash_increase(Ecore_Hash *hash);
|
static int _ecore_hash_increase(Ecore_Hash *hash);
|
||||||
static int _ecore_hash_decrease(Ecore_Hash *hash);
|
static int _ecore_hash_decrease(Ecore_Hash *hash);
|
||||||
inline int _ecore_hash_rehash(Ecore_Hash *hash, Ecore_List **old_table, int old_size);
|
inline int _ecore_hash_rehash(Ecore_Hash *hash, Ecore_Hash_Node **old_table, int old_size);
|
||||||
static int _ecore_hash_bucket_destroy(Ecore_List *list, Ecore_Free_Cb keyd,
|
static int _ecore_hash_bucket_destroy(Ecore_Hash_Node *list, Ecore_Free_Cb keyd,
|
||||||
Ecore_Free_Cb valued);
|
Ecore_Free_Cb valued);
|
||||||
inline Ecore_Hash_Node * _ecore_hash_get_bucket(Ecore_Hash *hash, Ecore_List *bucket,
|
inline Ecore_Hash_Node * _ecore_hash_get_bucket(Ecore_Hash *hash,
|
||||||
void *key);
|
Ecore_Hash_Node *bucket, void *key);
|
||||||
|
|
||||||
static Ecore_Hash_Node *_ecore_hash_node_new(void *key, void *value);
|
static Ecore_Hash_Node *_ecore_hash_node_new(void *key, void *value);
|
||||||
static int _ecore_hash_node_init(Ecore_Hash_Node *node, void *key, void *value);
|
static int _ecore_hash_node_init(Ecore_Hash_Node *node, void *key, void *value);
|
||||||
|
@ -79,9 +79,8 @@ int ecore_hash_init(Ecore_Hash *hash, Ecore_Hash_Cb hash_func, Ecore_Compare_Cb
|
||||||
hash->hash_func = hash_func;
|
hash->hash_func = hash_func;
|
||||||
hash->compare = compare;
|
hash->compare = compare;
|
||||||
|
|
||||||
hash->buckets = (Ecore_List **)malloc(ecore_prime_table[0] *
|
hash->buckets = (Ecore_Hash_Node **)calloc(ecore_prime_table[0],
|
||||||
sizeof(Ecore_List *));
|
sizeof(Ecore_Hash_Node *));
|
||||||
memset(hash->buckets, 0, ecore_prime_table[0] * sizeof(Ecore_List *));
|
|
||||||
|
|
||||||
ECORE_INIT_LOCKS(hash);
|
ECORE_INIT_LOCKS(hash);
|
||||||
|
|
||||||
|
@ -182,9 +181,11 @@ void ecore_hash_destroy(Ecore_Hash *hash)
|
||||||
ECORE_WRITE_LOCK(hash);
|
ECORE_WRITE_LOCK(hash);
|
||||||
|
|
||||||
while (i < ecore_prime_table[hash->size]) {
|
while (i < ecore_prime_table[hash->size]) {
|
||||||
if (hash->buckets[i])
|
if (hash->buckets[i]) {
|
||||||
_ecore_hash_bucket_destroy(hash->buckets[i],
|
_ecore_hash_bucket_destroy(hash->buckets[i],
|
||||||
hash->free_key, hash->free_value);
|
hash->free_key, hash->free_value);
|
||||||
|
hash->buckets[i] = NULL;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,8 +227,7 @@ int ecore_hash_for_each_node(Ecore_Hash *hash, Ecore_For_Each for_each_func,
|
||||||
if (hash->buckets[i]) {
|
if (hash->buckets[i]) {
|
||||||
Ecore_Hash_Node *node;
|
Ecore_Hash_Node *node;
|
||||||
|
|
||||||
ecore_list_goto_first(hash->buckets[i]);
|
for (node = hash->buckets[i]; node; node = node->next) {
|
||||||
while ((node = ecore_list_next(hash->buckets[i]))) {
|
|
||||||
for_each_func(node, user_data);
|
for_each_func(node, user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,8 +260,7 @@ Ecore_List *ecore_hash_keys(Ecore_Hash *hash)
|
||||||
if (hash->buckets[i]) {
|
if (hash->buckets[i]) {
|
||||||
Ecore_Hash_Node *node;
|
Ecore_Hash_Node *node;
|
||||||
|
|
||||||
ecore_list_goto_first(hash->buckets[i]);
|
for (node = hash->buckets[i]; node; node = node->next) {
|
||||||
while ((node = ecore_list_next(hash->buckets[i]))) {
|
|
||||||
ecore_list_append(keys, node->key);
|
ecore_list_append(keys, node->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,23 +284,28 @@ ecore_hash_dump_graph(Ecore_Hash *hash)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ecore_prime_table[hash->size]; i++)
|
for (i = 0; i < ecore_prime_table[hash->size]; i++)
|
||||||
if (hash->buckets[i])
|
if (hash->buckets[i]) {
|
||||||
printf("%d\t%u\n", i, ecore_list_nodes(hash->buckets[i]));
|
int n = 0;
|
||||||
|
Ecore_Hash_Node *node;
|
||||||
|
for (node = hash->buckets[i]; node; node = node->next)
|
||||||
|
n++;
|
||||||
|
printf("%d\t%u\n", i, n);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
printf("%d\t0\n", i);
|
printf("%d\t0\n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
_ecore_hash_bucket_destroy(Ecore_List *list, Ecore_Free_Cb keyd, Ecore_Free_Cb valued)
|
_ecore_hash_bucket_destroy(Ecore_Hash_Node *list, Ecore_Free_Cb keyd, Ecore_Free_Cb valued)
|
||||||
{
|
{
|
||||||
Ecore_Hash_Node *node;
|
Ecore_Hash_Node *node;
|
||||||
|
|
||||||
CHECK_PARAM_POINTER_RETURN("list", list, FALSE);
|
CHECK_PARAM_POINTER_RETURN("list", list, FALSE);
|
||||||
|
|
||||||
while ((node = ecore_list_remove_first(list)) != NULL)
|
for (node = list; node; node = list) {
|
||||||
|
list = list->next;
|
||||||
_ecore_hash_node_destroy(node, keyd, valued);
|
_ecore_hash_node_destroy(node, keyd, valued);
|
||||||
|
}
|
||||||
ecore_list_destroy(list);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -330,13 +334,9 @@ _ecore_hash_add_node(Ecore_Hash *hash, Ecore_Hash_Node *node)
|
||||||
else
|
else
|
||||||
hash_val = ECORE_COMPUTE_HASH(hash, node->key);
|
hash_val = ECORE_COMPUTE_HASH(hash, node->key);
|
||||||
|
|
||||||
/* Create the list if it's not already present */
|
/* Prepend the node to the list at the index position */
|
||||||
if (!hash->buckets[hash_val])
|
node->next = hash->buckets[hash_val];
|
||||||
hash->buckets[hash_val] = ecore_list_new();
|
hash->buckets[hash_val] = node;
|
||||||
|
|
||||||
/* Append the node to the list at the index position */
|
|
||||||
if (!ecore_list_prepend(hash->buckets[hash_val], node))
|
|
||||||
return FALSE;
|
|
||||||
hash->nodes++;
|
hash->nodes++;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -381,7 +381,7 @@ void *ecore_hash_get(Ecore_Hash *hash, void *key)
|
||||||
void *ecore_hash_remove(Ecore_Hash *hash, void *key)
|
void *ecore_hash_remove(Ecore_Hash *hash, void *key)
|
||||||
{
|
{
|
||||||
Ecore_Hash_Node *node = NULL;
|
Ecore_Hash_Node *node = NULL;
|
||||||
Ecore_List *list;
|
Ecore_Hash_Node *list;
|
||||||
unsigned int hash_val;
|
unsigned int hash_val;
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
|
|
||||||
|
@ -401,24 +401,32 @@ void *ecore_hash_remove(Ecore_Hash *hash, void *key)
|
||||||
*/
|
*/
|
||||||
if (hash->buckets[hash_val]) {
|
if (hash->buckets[hash_val]) {
|
||||||
list = hash->buckets[hash_val];
|
list = hash->buckets[hash_val];
|
||||||
ecore_list_goto_first(list);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Traverse the list to find the specified key
|
* Traverse the list to find the specified key
|
||||||
*/
|
*/
|
||||||
|
node = list;
|
||||||
if (hash->compare) {
|
if (hash->compare) {
|
||||||
while ((node = ecore_list_current(list)) &&
|
while ((node) && (hash->compare(node->key, key) != 0)) {
|
||||||
hash->compare(node->key, key) != 0)
|
list = node;
|
||||||
ecore_list_next(list);
|
node = node->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while ((node = ecore_list_current(list)) &&
|
while ((node) && (node->key != key)) {
|
||||||
node->key != key)
|
list = node;
|
||||||
ecore_list_next(list);
|
node = node->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the node with the matching key and free it's memory
|
||||||
|
*/
|
||||||
if (node) {
|
if (node) {
|
||||||
ecore_list_remove(list);
|
if (list == node)
|
||||||
|
hash->buckets[hash_val] = node->next;
|
||||||
|
else
|
||||||
|
list->next = node->next;
|
||||||
ret = node->value;
|
ret = node->value;
|
||||||
node->value = NULL;
|
node->value = NULL;
|
||||||
_ecore_hash_node_destroy(node, hash->free_key,
|
_ecore_hash_node_destroy(node, hash->free_key,
|
||||||
|
@ -450,6 +458,11 @@ _ecore_hash_get_node(Ecore_Hash *hash, void *key)
|
||||||
|
|
||||||
ECORE_READ_LOCK(hash);
|
ECORE_READ_LOCK(hash);
|
||||||
|
|
||||||
|
if (!hash->buckets) {
|
||||||
|
ECORE_READ_UNLOCK(hash);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute the position in the table */
|
/* Compute the position in the table */
|
||||||
if (!hash->hash_func)
|
if (!hash->hash_func)
|
||||||
hash_val = (unsigned int )key % ecore_prime_table[hash->size];
|
hash_val = (unsigned int )key % ecore_prime_table[hash->size];
|
||||||
|
@ -457,8 +470,17 @@ _ecore_hash_get_node(Ecore_Hash *hash, void *key)
|
||||||
hash_val = ECORE_COMPUTE_HASH(hash, key);
|
hash_val = ECORE_COMPUTE_HASH(hash, key);
|
||||||
|
|
||||||
/* Grab the bucket at the specified position */
|
/* Grab the bucket at the specified position */
|
||||||
if (hash->buckets[hash_val])
|
if (hash->buckets[hash_val]) {
|
||||||
node = _ecore_hash_get_bucket(hash, hash->buckets[hash_val], key);
|
node = _ecore_hash_get_bucket(hash, hash->buckets[hash_val], key);
|
||||||
|
/*
|
||||||
|
* Move matched node to the front of the list as it's likely
|
||||||
|
* to be searched for again soon.
|
||||||
|
*/
|
||||||
|
if (node && node != hash->buckets[hash_val]) {
|
||||||
|
node->next = hash->buckets[hash_val];
|
||||||
|
hash->buckets[hash_val] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ECORE_READ_UNLOCK(hash);
|
ECORE_READ_UNLOCK(hash);
|
||||||
|
|
||||||
|
@ -473,42 +495,52 @@ _ecore_hash_get_node(Ecore_Hash *hash, void *key)
|
||||||
* @return Returns NULL on error or not found, the found node on success
|
* @return Returns NULL on error or not found, the found node on success
|
||||||
*/
|
*/
|
||||||
inline Ecore_Hash_Node *
|
inline Ecore_Hash_Node *
|
||||||
_ecore_hash_get_bucket(Ecore_Hash *hash, Ecore_List *bucket, void *key)
|
_ecore_hash_get_bucket(Ecore_Hash *hash, Ecore_Hash_Node *bucket, void *key)
|
||||||
{
|
{
|
||||||
|
Ecore_Hash_Node *prev = NULL;
|
||||||
Ecore_Hash_Node *node = NULL;
|
Ecore_Hash_Node *node = NULL;
|
||||||
|
|
||||||
ECORE_READ_LOCK(hash);
|
ECORE_READ_LOCK(hash);
|
||||||
ecore_list_goto_first(bucket);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Traverse the list to find the desired node, if the node is in the
|
* Traverse the list to find the desired node, if the node is in the
|
||||||
* list, then return the node.
|
* list, then return the node.
|
||||||
*/
|
*/
|
||||||
if (hash->compare) {
|
if (hash->compare) {
|
||||||
while ((node = ecore_list_next(bucket)) != NULL) {
|
for (node = bucket; node; node = node->next) {
|
||||||
ECORE_READ_LOCK(node);
|
ECORE_READ_LOCK(node);
|
||||||
if (hash->compare(node->key, key) == 0) {
|
if (hash->compare(node->key, key) == 0)
|
||||||
ECORE_READ_UNLOCK(node);
|
break;
|
||||||
ECORE_READ_UNLOCK(hash);
|
prev = node;
|
||||||
return node;
|
|
||||||
}
|
|
||||||
ECORE_READ_UNLOCK(node);
|
ECORE_READ_UNLOCK(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while ((node = ecore_list_next(bucket)) != NULL) {
|
for (node = bucket; node; node = node->next) {
|
||||||
ECORE_READ_LOCK(node);
|
ECORE_READ_LOCK(node);
|
||||||
if (node->key == key) {
|
if (node->key == key)
|
||||||
ECORE_READ_UNLOCK(node);
|
break;
|
||||||
ECORE_READ_UNLOCK(hash);
|
prev = node;
|
||||||
return node;
|
|
||||||
}
|
|
||||||
ECORE_READ_UNLOCK(node);
|
ECORE_READ_UNLOCK(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove node from the list to replace it at the beginning.
|
||||||
|
*/
|
||||||
|
if (node && prev) {
|
||||||
|
ECORE_WRITE_LOCK(prev);
|
||||||
|
prev->next = node->next;
|
||||||
|
ECORE_WRITE_UNLOCK(prev);
|
||||||
|
|
||||||
|
ECORE_WRITE_LOCK(node);
|
||||||
|
node->next = NULL;
|
||||||
|
ECORE_WRITE_UNLOCK(node);
|
||||||
|
}
|
||||||
|
|
||||||
ECORE_READ_UNLOCK(hash);
|
ECORE_READ_UNLOCK(hash);
|
||||||
|
|
||||||
return NULL;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -536,8 +568,8 @@ _ecore_hash_increase(Ecore_Hash *hash)
|
||||||
/*
|
/*
|
||||||
* Allocate a new bucket area, of the new larger size
|
* Allocate a new bucket area, of the new larger size
|
||||||
*/
|
*/
|
||||||
hash->buckets = (Ecore_List **)calloc(ecore_prime_table[hash->size],
|
hash->buckets = calloc(ecore_prime_table[hash->size],
|
||||||
sizeof(Ecore_List *));
|
sizeof(Ecore_Hash_Node *));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the allocation succeeded, if not replace the old data and
|
* Make sure the allocation succeeded, if not replace the old data and
|
||||||
|
@ -574,7 +606,7 @@ _ecore_hash_increase(Ecore_Hash *hash)
|
||||||
static int
|
static int
|
||||||
_ecore_hash_decrease(Ecore_Hash *hash)
|
_ecore_hash_decrease(Ecore_Hash *hash)
|
||||||
{
|
{
|
||||||
Ecore_List **old;
|
Ecore_Hash_Node **old;
|
||||||
|
|
||||||
CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE);
|
CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE);
|
||||||
|
|
||||||
|
@ -590,8 +622,8 @@ _ecore_hash_decrease(Ecore_Hash *hash)
|
||||||
/*
|
/*
|
||||||
* Allocate a new area to store the data
|
* Allocate a new area to store the data
|
||||||
*/
|
*/
|
||||||
hash->buckets = (Ecore_List **)malloc(ecore_prime_table[hash->size] *
|
hash->buckets = (Ecore_Hash_Node **)calloc(ecore_prime_table[hash->size],
|
||||||
sizeof(Ecore_List *));
|
sizeof(Ecore_Hash_Node *));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure allocation succeeded otherwise rreturn to the previous
|
* Make sure allocation succeeded otherwise rreturn to the previous
|
||||||
|
@ -603,11 +635,6 @@ _ecore_hash_decrease(Ecore_Hash *hash)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Zero out the new area
|
|
||||||
*/
|
|
||||||
memset(hash->buckets, 0, ecore_prime_table[hash->size]
|
|
||||||
* sizeof(Ecore_List *));
|
|
||||||
hash->nodes = 0;
|
hash->nodes = 0;
|
||||||
|
|
||||||
if (_ecore_hash_rehash(hash, old, hash->size - 1)) {
|
if (_ecore_hash_rehash(hash, old, hash->size - 1)) {
|
||||||
|
@ -625,11 +652,10 @@ _ecore_hash_decrease(Ecore_Hash *hash)
|
||||||
* @return Returns TRUE on success, FALSE on success
|
* @return Returns TRUE on success, FALSE on success
|
||||||
*/
|
*/
|
||||||
inline int
|
inline int
|
||||||
_ecore_hash_rehash(Ecore_Hash *hash, Ecore_List **old_table, int old_size)
|
_ecore_hash_rehash(Ecore_Hash *hash, Ecore_Hash_Node **old_table, int old_size)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
Ecore_Hash_Node *node;
|
Ecore_Hash_Node *old;
|
||||||
Ecore_List *old;
|
|
||||||
|
|
||||||
CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE);
|
CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE);
|
||||||
CHECK_PARAM_POINTER_RETURN("old_table", old_table, FALSE);
|
CHECK_PARAM_POINTER_RETURN("old_table", old_table, FALSE);
|
||||||
|
@ -641,13 +667,10 @@ _ecore_hash_rehash(Ecore_Hash *hash, Ecore_List **old_table, int old_size)
|
||||||
old_table[i] = NULL;
|
old_table[i] = NULL;
|
||||||
|
|
||||||
/* Loop through re-adding each node to the hash table */
|
/* Loop through re-adding each node to the hash table */
|
||||||
while (old && (node = ecore_list_remove_last(old))) {
|
while ((old = old_table[i])) {
|
||||||
_ecore_hash_add_node(hash, node);
|
_ecore_hash_add_node(hash, old);
|
||||||
|
old_table[i] = old->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now free up the old list space */
|
|
||||||
if (old)
|
|
||||||
ecore_list_destroy(old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
Loading…
Reference in New Issue