Maps - osvaldoandrade/ova-lib GitHub Wiki
Read this page when the problem is key to value lookup.
The constructor is:
map *create_map(map_type type, int capacity, int (*hash_func)(void *, int), comparator compare);The 2 variants are:
| Variant | Meaning |
|---|---|
HASH_MAP |
hash map without internal locking |
HASH_TABLE |
hash map with one internal mutex around put, get, and remove
|
Both variants use separate chaining with linked map_entry nodes.
| Field | Meaning |
|---|---|
put(self, key, data) |
inserts or updates one key-value pair |
get(self, key) |
returns the stored value for a key or NULL
|
remove(self, key) |
removes a key and returns its value or NULL
|
size(self) |
returns the current number of stored entries |
capacity(self) |
returns the current bucket count |
free(self) |
frees buckets, entries, and the map object |
The method-table field put_bulk(self, keys, values, count) inserts multiple pairs. It is called as map->put_bulk(map, keys, values, count). If the map is NULL, either pointer array is NULL, or count <= 0, it does nothing.
The map enforces a minimum starting capacity of 20.
If hash_func is NULL, the constructor falls back to bernstein_hash.
The public header also exports:
| Hash function | Name |
|---|---|
| Bernstein | bernstein_hash |
| FNV-1a | fnv1a_hash |
| XOR | xor_hash |
| rotational | rotational_hash |
| additive | additive_hash |
When the load factor exceeds 0.75, the table doubles and rehashes existing entries.
The comparator defines key equality during collision traversal. The hash function defines bucket placement.
Those 2 callbacks must agree. Equal keys must land in the same equality class and the same hash class, or lookup and removal become inconsistent.
If put sees an existing key, the map updates the stored value. The shipped tests verify that the last inserted value is the value later returned by get.
The same rule applies to put_bulk: if the bulk input repeats a key, the final value for that key is the last one in the bulk input.
The shipped tests store and retrieve a NULL key successfully with the default hash path and a comparator that can compare NULL.
The shipped tests also store a NULL value successfully.
That means one important caveat exists: get returning NULL does not always mean the key is absent. It can also mean the key exists and its stored value is NULL.
HASH_TABLE wraps the core operations with one mutex and is the only module in the library with internal synchronization.
That lock does not change ownership rules. It only changes internal access safety for the map object itself.
The map owns its buckets, entries, and mutex when present. It does not own user keys or user values.
Destroy the map with map->free(map). Free keys and values in your own code when your program owns them.
#include "map.h"
#include <string.h>
static int string_compare(const void *a, const void *b) {
const char *lhs = (const char *)a;
const char *rhs = (const char *)b;
if (!lhs && !rhs) return 0;
if (!lhs) return -1;
if (!rhs) return 1;
return strcmp(lhs, rhs);
}
int main(void) {
map *users = create_map(HASH_MAP, 10, NULL, string_compare);
char key[] = "alice";
int id = 7;
users->put(users, key, &id);
int *result = (int *)users->get(users, key);
users->free(users);
return result && *result == 7 ? 0 : 1;
}If the value is not separate from the key and uniqueness is the point, move to Sets. If order by key matters, move to Trees.