HashMap API
This API provides a straightforward HashMap implementation.
Creating a HashMap
There are two methods to create a hashmap. Either use le_hashmap_Create()
to create a hashmap on the heap, or use LE_HASHMAP_DEFINE_STATIC to define space for a hashmap, then use le_hashmap_InitStatic()
to initialize the hashmap. It's the responsibility of the caller to maintain type integrity using this function's parameters. It's important to supply hash and equality functions that operate on the type of key that you intend to store. It's unwise to mix types in a single table because implementation of the table has no way to detect this behaviour.
Choose the initial size should carefully as the index size remains fixed. The best choice for the initial size is a prime number slightly larger than the maximum expected capacity. If a too small size is chosen, there will be an increase in collisions that degrade performance over time.
All hashmaps have names for diagnostic purposes.
Adding key-value pairs
Key-value pairs are added using le_hashmap_Put(). For example:
static void StoreStuff(const char* keyStr, const char* valueStr){myTable = le_hashmap_Create("My Table",31,);le_hashmap_Put(myTable, keyStr, valueStr);....}
The table does not take control of the keys or values. The map only stores the pointers to these values, not the values themselves. It's the responsibility of the caller to manage the actual data storage.
Tip
The code sample shows some pre-defined functions for certain key types. The key types supported are uint32_t, uint64_t and strings. The strings must be NULL terminated.
Tables can also have their own hash and equality functions, but ensure the functions work on the type of key you're storing. The hash function should provide a good distribution of values. It is not required that they be unique.
Iterating over a map
This API allows the user of the map to iterate over the entire map, acting on each key-value pair. You supply a callback function conforming to the prototype:
bool (*callback)(void* key, void* value, void* context)
This can then be used to process every value in the map. The return value from the callback function determines if iteration should continue or stop. If the function returns false then iteration will cease. For example:
bool ProcessTableData(void* keyPtr, // Pointer to the map entry's keyvoid* valuePtr, // Pointer to the map entry's valuevoid* contextPtr // Pointer to an abritrary context for the callback function){int keyCheck = *((int*)context);int currentKey = *((int*)key);if (keyCheck == currentKey) return false;// Do some stuff, maybe print out data or do a calculationreturn true;}{// ... somewhere else in the code ...int lastKey = 10;le_hashmap_ForEach (myTable, processTableData, &lastKey);}
This code sample shows the callback function must also be aware of the types stored in the table.
However, keep in mind that it is unsafe and undefined to modify the map during this style of iteration.
Alternatively, the calling function can control the iteration by first calling le_hashmap_GetIterator()
. This returns an iterator that is ready to return each key/value pair in the map in the order in which they are stored. The iterator is controlled by calling le_hashmap_NextNode()
, and must be called before accessing any elements. You can then retrieve pointers to the key and value by using le_hashmap_GetKey() and le_hashmap_GetValue().
- Note
- There is only one iterator per hashtable. Calling le_hashmap_GetIterator() will simply re-initialize the current iterator
It is possible to add and remove items during this style of iteration. When adding items during an iteration it is not guaranteed that the newly added item will be iterated over. It's very possible that the newly added item is added in an earlier location than the iterator is curently pointed at.
When removing items during an iteration you also have to keep in mind that the iterator's current item may be the one removed. If this is the case, le_hashmap_GetKey, and le_hashmap_GetValue will return NULL until either, le_hashmap_NextNode, or le_hashmap_PrevNode are called.
For example (assuming a table of string/string):
void ProcessTable(le_hashmap_Ref_t myTable){char* nextKey;char* nextVal;le_hashmap_It_Ref_t myIter = le_hashmap_GetIterator(myTable);{// Do something with the stringsnextKey = le_hashmap_GetKey(myIter);nextVal = le_hashmap_GetValue(myIter);}}
If you need to control access to the hashmap, then a mutex can be used.
Tracing a map
Hashmaps can be traced using the logging system.
If le_hashmap_MakeTraceable()
is called for a specified hashmap object, the name of that hashmap (the name passed into le_hashmap_Create() ) becomes a trace keyword to enable and disable tracing of that particular hashmap.
If le_hashmap_EnableTrace()
is called for a hashmap object, tracing is immediately activated for that hashmap.
See Log Controls for more information on how to enable and disable tracing using configuration and/or the log control tool.
Copyright (C) Sierra Wireless Inc.