Skip to content

Stemt/hm.h

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hm.h

Makefile CI

This single header library seeks to provide a very simple, quick and easy to use hashmap implementation. This library does not aim to be the fastest nor most memory efficient hashmap implementation. It rather aims to be as easy to use and minimalist as possible, while ramaining useful to more advanced users.

Usage

Raw API Example

Note

By default hm.h panics if memory allocation has failed and causes an abort if that is the case. See Disable Panic on Allocation Failure if you want to change that behavior.

#define HM_IMPLEMENTATION
#include "hm.h"

int main(void){
    HM hm = {0};
    
    HM_init(&hm, sizeof(int), 10); 
    
    // inserting a key value pair
    int value = 2;
    HM_set(&hm, "key", &value);

    // retrieving value by key
    int* res = (int*)HM_get(&hm, "test");
    if(res != NULL){
      printf("res: %d\n", *res);
    }else{
      printf("res: not found\n");
    }
    
    // removing a key value pair
    HM_remove(&hm, "test");
    assert(HM_get(&hm, "test") == NULL);

    HM_deinit(&hm);
    return 0;
}

Type Wrapped API

To provide better type information, hm.h comes with 2 macros to generate api functions for specific data types. HM_GEN_WRAPPER_PROTOYPE(type) generates function prototypes/declarations which should ideally be placed inside a header file (.h). HM_GEN_WRAPPER_IMPLEMENTATION(type) generates function implementations (which just call their equivalent in the raw api) and should ideally be placed inside a source file (.c).

Note

Due to the function names that the wrapper generates, it's not possible to generate a wrapper for a pointer using the normal syntax (e.g. ❌ HM_GEN_WRAPPER_PROTOTYPE(int*) ❌). Instead, you'll have to typedef your pointer type:

typedef int* int_ptr_t;
HM_GEN_WRAPPER_PROTOTYPE(int_ptr_t);
HM_GEN_WRAPPER_IMPLEMENTATION(int_ptr_t);

For example for an int the macros will generate the following:

// HM_GEN_WRAPPER_PROTOTYPE(int);
bool HM_int_init(HM* self, size_t capacity);\
bool HM_int_set(HM* self, const char* key, int value);\
int* HM_int_value_at(HM* self, HM_Iterator it);\
int* HM_int_get(HM* self, const char* key);\

// HM_GEN_WRAPPER_IMPLEMENTATION(int);
bool HM_int_init(HM* self, size_t capacity){ 
    return HM_init(self, sizeof(int), capacity); 
}

bool HM_int_set(HM* self, const char* key, int value){
    return HM_set(self, key, &value); 
}

int* HM_int_value_at(HM* self, HM_Iterator it){ 
    return HM_value_at(self, it); 
}

int* HM_int_get(HM* self, const char* key){ 
    return HM_get(self, key); 
}

Full example:

#define HM_IMPLEMENTATION
#include "hm.h"

HM_GEN_WRAPPER_PROTOTYPE(int);
HM_GEN_WRAPPER_IMPLEMENTATION(int);

int main(void){
    HM hm = {0};

    HM_int_init(&hm, 10); 
    
    // inserting a key value pair
    HM_int_set(&hm, "key", 2);

    // retrieving value by key
    int* res = HM_int_get(&hm, "test");
    if(res != NULL){
      printf("res: %d\n", *res);
    }else{
      printf("res: not found\n");
    }

    // removing a key value pair
    HM_remove(&hm, "test");
    assert(HM_get(&hm, "test") == NULL);

    HM_deinit(&hm);
    return 0;
}

Using Keys with Custom Length (kwl)

If you want to use a custom type as a key or simply a non null terminated string there are function variants which allow you to specify a length for the given key value. These variants are indicated with kwl, short for "key with length".

bool HM_kwl_set(HM* self, const void* key, size_t key_len, void* value);
void* HM_kwl_get(HM* self, const void* key, size_t key_len);
void HM_kwl_remove(HM* self, const void* key, size_t key_len);

The type wrapped API also provides the same variants, e.g. for int:

bool HM_int_kwl_set(HM* self, const void* key, size_t key_len, int value);
int* HM_int_kwl_get(HM* self, const void* key, size_t key_len);
void HM_int_kwl_remove(HM* self, const void* key, size_t key_len);

Specifically for non null terminated strings there is also a function for requesting the key length.

const size_t* HM_key_len_at(HM* self, HM_Iterator it);

Iterating over keys and values

Note

Results from the _at family of function (e.g. HM_key_at() and HM_value_at()) only return valid results with an HM_Iterator retrieved using HM_iterate() performed on the same hashmap. Otherwise the return value is undefined.

#define HM_IMPLEMENTATION
#include "hm.h"

HM_GEN_WRAPPER_PROTOTYPE(int);
HM_GEN_WRAPPER_IMPLEMENTATION(int);

int main(void){
    HM_int_init(&hm, 0);
    
    for(int i = 0; i < 10; ++i){
        char key[1024] = {0};
        snprintf(key, sizeof(key)-1, "key-%d", i);
        HM_int_set(&hm, key, i);
    }

    for(HM_Iterator i = HM_iterate(&hm, NULL); i != NULL; i = HM_iterate(&hm, i)){
        printf("key: '%s', value '%d'\n", HM_key_at(&hm, i), *HM_int_value_at(&hm, i));
    }

    HM_deinit(&hm);
    return 0;
}

Disable Panic on Allocation Failure

For convenience hm.h will crash your program so that you don't have to check the results of the HM_init() and HM_set() functions. If however, if you do want to handle these failures you can use the following define:

#define HM_DISABLE_ALLOC_PANIC

And most importantly remember to actually handle allocation failures:

#define HM_DISABLE_ALLOC_PANIC
#define HM_IMPLEMENTATION
#include "hm.h"

HM_GEN_WRAPPER_PROTOTYPE(int);
HM_GEN_WRAPPER_IMPLEMENTATION(int);

int main(void){
    HM hm = {0};
    
    if(!HM_int_init(&hm, 10)){
        // handle alloc failure (hm is not initialized)
    }
    
    if(!HM_int_set(&hm, "key", 2)){
        // handle alloc failure (new value was not added to hm)
    }

    int* res = HM_int_get(&hm, "test");
    if(res != NULL){
      printf("res: %d\n", *res);
    }else{
      printf("res: not found\n");
    }

    HM_deinit(&hm);
    return 0;
}

Tests

Tests make use of utest.h by sheredom. To run them simply run:

make test

About

A single header hashmap library

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages