Maps - osvaldoandrade/ova-lib GitHub Wiki

Maps

Read this page when the problem is key to value lookup.

Constructor and Variants

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.

Core API

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.

Capacity, Growth, and Hashing

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.

Comparator and Equality Rule

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.

Update and Duplicate-Key Semantics

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.

NULL Keys and NULL Values

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.

Thread-Safe Variant

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.

Ownership and Cleanup

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.

Example

#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;
}

Read Next

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.

⚠️ **GitHub.com Fallback** ⚠️