The Eina_Hash
provides a way to store values in association with a key.
For example, if you want to store some tuples into a table, you can do it
using the Eina_Hash
.
The Eina_Hash
is implemented using an array of ābucketsā where each bucket
is a pointer to a structure that is the head of a red-black tree. This
implementation makes it very robust against week keys as in the worst case
scenario, you can still depend on an efficient binary tree implementation.
To create the hash table, use the eina_hash_new()
function:
NULL
can be passed as the callback.
When you create an Eina_Hash
instance, you have to create 4 potentially
long callback functions. To make the functions shorter, Eina_Hash
offers
some predefined functions to create the following kinds of hash tables:
eina_hash_string_djb2_new()
creates a new hash table using the djb2 algorithm for strings.eina_hash_string_superfast_new()
creates a new hash table for use with strings (better with long strings).eina_hash_string_small_new()
creates a new hash table for use with strings with a small bucket size.eina_hash_int32_new()
and eina_hash_int64_new()
create a new hash table for use with 32-bit and 64-bit integers.eina_hash_pointer_new()
creates a new hash table for use with pointers.eina_hash_stringshared_new()
creates a new hash table for use with shared strings.All these predefined functions require only one parameter, which is the function to free the data you store in the hash table.
The following example shows how to manage a small phone book using the
eina_hash_string_superfast_new()
function to create the hash table.
1. Create the phone book structure and some static data:
struct _Phone_Entry { const char *name; // Full name const char *number; // Phone number }; typedef struct _Phone_Entry Phone_Entry; static Phone_Entry _start_entries[] = { { "Wolfgang Amadeus Mozart", "+01 23 456-78910" }, { "Ludwig van Beethoven", "+12 34 567-89101" }, { "Richard Georg Strauss", "+23 45 678-91012" }, { "Heitor Villa-Lobos", "+34 56 789-10123" }, { NULL, NULL } };
2. Create the callback to free the data:
static void _phone_entry_free_cb(void *data) { free(data); }
The free callback can be changed later using the eina_hash_free_cb_set()
function. You need to pass the hash and the new callback function.
3. Create and destroy the hash table.
The eina_hash_free_buckets()
function frees all hash table buckets. It
empties the hash but does not destroy it, and you can still use it for another
purpose. When eina_hash_free()
is called, the space allocated for the hash
is freed.
int free_data() { Eina_Hash *phone_book = NULL; phone_book = eina_hash_string_superfast_new(_phone_entry_free_cb); // Empty the phone book without destroying it eina_hash_free_buckets(phone_book); eina_hash_free(phone_book); }
Use the eina_hash_add()
function. This function takes the hash, the key to
access the data, and the data as its parameters.
The following example shows how to add the initial data declared earlier to the hash:
for (i = 0; _start_entries[i].name != NULL; i++) { eina_hash_add(phone_book, _start_entries[i].name, strdup(_start_entries[i].number)); }
The Eina_Hash
offers various ways to add elements to a hash, such as the
eina_hash_direct_add()
function, which adds the entry without duplicating
the string key. The key is stored in the struct, so this function can be used
with eina_stringshare
to avoid key data duplication.
for (i = 0; _start_entries[i].name != NULL; i++) { // Allocating memory for the phone entry Phone_Entry *e = malloc(sizeof(Phone_Entry)); // Creating an eina_stringshare for the name and the phone number e->name = eina_stringshare_add(_start_entries[i].name); e->number = eina_stringshare_add(_start_entries[i].number); // Adding the entry to the hash eina_hash_direct_add(phone_book, e->name, e); }
Use eina_hash_modify()
function passing the hash, the key of the data to
change, and the new data. The function returns the old data on success.
The eina_hash_set()
function does the same work as eina_hash_modify()
,
but if the entry does not exist, the function creates a new one.
char *old_phone = NULL; char *phone = NULL; // Replace the phone number of Richard Strauss old_phone = eina_hash_modify(phone_book, "Richard Georg Strauss", strdup("+23 45 111-11111")); phone = eina_hash_set(phone_book, "Philippe de Magalhães", strdup("+33 6 111-11111")); eina_hash_set(phone_book, "Richard Georg Strauss", strdup("+23 45 111-117711"));
Use the eina_hash_move()
function. You only have to pass the hash, the old
key, and the new key. If the operation succeeds, the function returns
EINA_TRUE
, if not, it returns EINA_FALSE
.
Eina_Bool res; res = eina_hash_move(phone_book, "Philippe de Magalhães", "Filipe de Magalhães");
Use the eina_hash_del()
function to remove the entry identified by a key
or data from the given hash table:
Eina_Bool r; const char *entry_name = "Heitor Villa-Lobos"; r = eina_hash_del(phone_book, entry_name, NULL);
Use the eina_hash_del_by_key()
function to remove an entry based on the
key:
r = eina_hash_del_by_key(phone_book, "Richard Georg Strauss");
Use the eina_hash_del_by_data()
function to remove an entry based on the
data:
r = eina_hash_del_by_data(phone_book, "+12 34 567-89101");
To find hash table elements and get data based on the key name:
Use the eina_hash_find()
function by passing the hash and the key you are
looking for:
char *phone = NULL; const char *entry_name = "Heitor Villa-Lobos"; // Look for a specific entry and get its phone number phone = eina_hash_find(phone_book, entry_name);
Use the eina_hash_population()
function. Pass the hash as the only
argument.
unsigned int nb_elm; nb_elm = eina_hash_population(phone_book);
You can use various methods:
eina_hash_foreach()
function:Eina_Bool
, EINA_TRUE
if the iteration has to continue and EINA_FALSE
if the iteration has to stop.The following example prints the key and the data of the hash entry (the name and the phone number):
static Eina_Bool pb_foreach_cb(const Eina_Hash *phone_book, const void *key, void *data, void *fdata) { const char *name = key; const char *number = data; printf("%s: %s\n", name, number); // Return EINA_FALSE to stop this callback from being called return EINA_TRUE; } printf("List of phones:\n"); // Running the callback on the hash called phone_book eina_hash_foreach(phone_book, pb_foreach_cb, NULL); printf("\n");
Use the eina_hash_iterator_key_new()
function:
// Declaration of the Eina_Iterator Eina_Iterator *it; // Variable to handle the current iteration "data" void *data; // Iterate over the keys (names) printf("List of names in the phone book:\n"); it = eina_hash_iterator_key_new(phone_book); // Use the generic eina_iterator_next() while (eina_iterator_next(it, &data)) { const char *name = data; printf("%s\n", name); } // Free the iterator eina_iterator_free(it);
Use the eina_hash_iterator_data_new()
function the same way as
eina_hash_iterator_key_new()
:
// Declaration of the Eina_Iterator Eina_Iterator *it; // Variable to handle the current iteration "data" void *data; // Iterate over hash data printf("List of numbers in the phone book:\n"); it = eina_hash_iterator_data_new(phone_book); while (eina_iterator_next(it, &data)) { const char *number = data; printf("%s\n", number); } // Free the iterator eina_iterator_free(it);
Use the eina_hash_iterator_tuple_new()
function:
// Declaration of the Eina_Iterator Eina_Iterator *tit; // Variable to handle the current iteration "data" void *tuple; printf("List of phones:\n"); tit = eina_hash_iterator_tuple_new(phone_book); while (eina_iterator_next(tit, &tuple)) { Eina_Hash_Tuple *t = tuple; const char *name = t->key; const char *number = t->data; printf("%s: %s\n", name, number); } // Always free the iterator after its use eina_iterator_free(tit);