v1.1.0
This commit is contained in:
105
hm.h
105
hm.h
@@ -1,4 +1,4 @@
|
|||||||
// hm.h - v1.0.1 - MIT License
|
// hm.h - v1.1.0 - MIT License
|
||||||
// chained hash table implementation as a single header library.
|
// chained hash table implementation as a single header library.
|
||||||
//
|
//
|
||||||
// [License and changelog]
|
// [License and changelog]
|
||||||
@@ -74,8 +74,8 @@
|
|||||||
// bool hm_put(HashMap *hm, const void *key, const void *value)
|
// bool hm_put(HashMap *hm, const void *key, const void *value)
|
||||||
//
|
//
|
||||||
// This function inserts a new value associated with the specified key. If the
|
// This function inserts a new value associated with the specified key. If the
|
||||||
// key already exists, it's value will be overridden. Return false on error
|
// key already exists, it's value will be overridden. Returns false on error
|
||||||
// only if HM_NO_ASSERT is not defined.
|
// only if HM_NO_ASSERT is defined.
|
||||||
//
|
//
|
||||||
// void *hm_get(const HashMap *hm, const void *key)
|
// void *hm_get(const HashMap *hm, const void *key)
|
||||||
//
|
//
|
||||||
@@ -87,10 +87,16 @@
|
|||||||
// This function removes the entry associated with the specified key. Returns
|
// This function removes the entry associated with the specified key. Returns
|
||||||
// whether the key was found and removed or not.
|
// whether the key was found and removed or not.
|
||||||
//
|
//
|
||||||
|
// size_t hm_count(const HashMap *hm)
|
||||||
|
//
|
||||||
|
// Returns the number of elements stored in the specified HashMap. Returns 0
|
||||||
|
// if the parameter is NULL.
|
||||||
|
//
|
||||||
// HashMapIterator hm_iterate(const HashMap *hm)
|
// HashMapIterator hm_iterate(const HashMap *hm)
|
||||||
//
|
//
|
||||||
// This function creates a HashMapIterator used to iterate the specfied
|
// This function creates a HashMapIterator used to iterate the specfied
|
||||||
// HashMap.
|
// HashMap. Note that the returned iterator does not start from the first
|
||||||
|
// element of the HashMap. You'll need to call hm_next() at least once.
|
||||||
//
|
//
|
||||||
// const void *hm_key(const HashMapIterator *it)
|
// const void *hm_key(const HashMapIterator *it)
|
||||||
//
|
//
|
||||||
@@ -106,6 +112,10 @@
|
|||||||
//
|
//
|
||||||
// This function iterates through the next entry of the HashMap and returns
|
// This function iterates through the next entry of the HashMap and returns
|
||||||
// it's value. Returns NULL if the iterator reached the end of the HashMap.
|
// it's value. Returns NULL if the iterator reached the end of the HashMap.
|
||||||
|
//
|
||||||
|
// [Notes]
|
||||||
|
//
|
||||||
|
// This library is not thread-safe.
|
||||||
|
|
||||||
#ifndef HM_H_
|
#ifndef HM_H_
|
||||||
#define HM_H_
|
#define HM_H_
|
||||||
@@ -181,6 +191,7 @@ void hm_free(HashMap *hm);
|
|||||||
bool hm_put(HashMap *hm, const void *key, const void *value);
|
bool hm_put(HashMap *hm, const void *key, const void *value);
|
||||||
void *hm_get(const HashMap *hm, const void *key);
|
void *hm_get(const HashMap *hm, const void *key);
|
||||||
bool hm_remove(HashMap *hm, const void *key);
|
bool hm_remove(HashMap *hm, const void *key);
|
||||||
|
size_t hm_count(const HashMap *hm);
|
||||||
|
|
||||||
// Iterator functions
|
// Iterator functions
|
||||||
HashMapIterator hm_iterate(const HashMap *hm);
|
HashMapIterator hm_iterate(const HashMap *hm);
|
||||||
@@ -220,7 +231,11 @@ HashMap hm_create(size_t key_size, size_t value_size)
|
|||||||
|
|
||||||
void hm_free(HashMap *hm)
|
void hm_free(HashMap *hm)
|
||||||
{
|
{
|
||||||
if (hm == NULL || hm->map == NULL) {
|
if (hm == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hm->map == NULL) {
|
||||||
memset(hm, 0, sizeof(*hm));
|
memset(hm, 0, sizeof(*hm));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -253,12 +268,12 @@ bool hm_put(HashMap *hm, const void *key, const void *value)
|
|||||||
|
|
||||||
// Ensure that HashMap is initialised
|
// Ensure that HashMap is initialised
|
||||||
if (hm->map == NULL) {
|
if (hm->map == NULL) {
|
||||||
hm->map = (Hm__Bucket**)HM_REALLOC(NULL, sizeof(*hm->map) * HM_INITIAL_CAPACITY);
|
hm->capacity = HM_INITIAL_CAPACITY;
|
||||||
|
hm->map = (Hm__Bucket**)HM_REALLOC(NULL, sizeof(*hm->map) * hm->capacity);
|
||||||
if (hm->map == NULL) {
|
if (hm->map == NULL) {
|
||||||
HM_ASSERT(false && "Reallocation failed");
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hm->capacity = HM_INITIAL_CAPACITY;
|
|
||||||
memset(hm->map, 0, sizeof(*hm->map) * hm->capacity);
|
memset(hm->map, 0, sizeof(*hm->map) * hm->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +299,7 @@ bool hm_put(HashMap *hm, const void *key, const void *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fixed or equal value_size
|
// Fixed or equal value_size
|
||||||
if (hm->value_size != 0 || value_size == cur->value_size) {
|
if (value_size == cur->value_size) {
|
||||||
memcpy(cur->value, value, value_size);
|
memcpy(cur->value, value, value_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -301,6 +316,7 @@ bool hm_put(HashMap *hm, const void *key, const void *value)
|
|||||||
uint8_t *new_value = (uint8_t*)HM_REALLOC(cur->value, value_size);
|
uint8_t *new_value = (uint8_t*)HM_REALLOC(cur->value, value_size);
|
||||||
if (new_value == NULL) {
|
if (new_value == NULL) {
|
||||||
HM_ASSERT(false && "Reallocation failed");
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
HM_FREE(tmp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cur->value = new_value;
|
cur->value = new_value;
|
||||||
@@ -394,6 +410,15 @@ bool hm_remove(HashMap *hm, const void *key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t hm_count(const HashMap *hm)
|
||||||
|
{
|
||||||
|
if (hm == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hm->count;
|
||||||
|
}
|
||||||
|
|
||||||
HashMapIterator hm_iterate(const HashMap *hm)
|
HashMapIterator hm_iterate(const HashMap *hm)
|
||||||
{
|
{
|
||||||
HashMapIterator it;
|
HashMapIterator it;
|
||||||
@@ -415,8 +440,8 @@ const void *hm_key(const HashMapIterator *it)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reached the end
|
// Error or reached the end
|
||||||
if (it->end) {
|
if (it->end || it->bucket == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,8 +455,8 @@ void *hm_value(const HashMapIterator *it)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reached the end
|
// Error or reached the end
|
||||||
if (it->end) {
|
if (it->end || it->bucket == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,29 +475,48 @@ void *hm_next(HashMapIterator *it)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it->bucket == NULL) {
|
const HashMap *hm = it->hm;
|
||||||
// Find first list of buckets
|
|
||||||
while ((it->bucket = it->hm->map[it->index++]) == NULL);
|
// Empty HashMap
|
||||||
return it->bucket->value;
|
if (hm->count == 0) {
|
||||||
|
it->end = true;
|
||||||
|
it->bucket = NULL;
|
||||||
|
it->index = hm->capacity;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next bucket
|
// Find first list of buckets when iterator hasn't started yet
|
||||||
|
if (it->bucket == NULL) {
|
||||||
|
while (it->index < hm->capacity) {
|
||||||
|
it->bucket = hm->map[it->index++];
|
||||||
|
if (it->bucket != NULL) {
|
||||||
|
return it->bucket->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it->end = true;
|
||||||
|
it->bucket = NULL;
|
||||||
|
it->index = hm->capacity;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance within current chain
|
||||||
if (it->bucket->next != NULL) {
|
if (it->bucket->next != NULL) {
|
||||||
it->bucket = it->bucket->next;
|
it->bucket = it->bucket->next;
|
||||||
return it->bucket->value;
|
return it->bucket->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find next list of buckets
|
// Move to next non-empty bucket array slot
|
||||||
while ((it->bucket = it->hm->map[++it->index]) == NULL);
|
while (it->index < hm->capacity) {
|
||||||
|
it->bucket = hm->map[it->index++];
|
||||||
// Reached the end
|
if (it->bucket != NULL) {
|
||||||
if (it->index >= it->hm->capacity) {
|
return it->bucket->value;
|
||||||
it->end = true;
|
}
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it->bucket = it->hm->map[it->index];
|
it->end = true;
|
||||||
return it->bucket->value;
|
it->bucket = NULL;
|
||||||
|
it->index = hm->capacity;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t hm__fnv1a(const void *buffer, size_t size)
|
uint64_t hm__fnv1a(const void *buffer, size_t size)
|
||||||
@@ -482,6 +526,8 @@ uint64_t hm__fnv1a(const void *buffer, size_t size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: When buffer is NULL terminated, the NULL is included in the hash
|
||||||
|
|
||||||
const uint8_t *b = (const uint8_t*)buffer;
|
const uint8_t *b = (const uint8_t*)buffer;
|
||||||
uint64_t hash = 14695981039346656037ULL;
|
uint64_t hash = 14695981039346656037ULL;
|
||||||
|
|
||||||
@@ -593,7 +639,6 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu
|
|||||||
HM_FREE(bucket);
|
HM_FREE(bucket);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(bucket->key, 0, key_size);
|
|
||||||
|
|
||||||
// Allocate value
|
// Allocate value
|
||||||
bucket->value = HM_REALLOC(NULL, value_size);
|
bucket->value = HM_REALLOC(NULL, value_size);
|
||||||
@@ -603,7 +648,6 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu
|
|||||||
HM_FREE(bucket);
|
HM_FREE(bucket);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(bucket->value, 0, value_size);
|
|
||||||
|
|
||||||
// Copy Key-value data
|
// Copy Key-value data
|
||||||
memcpy(bucket->key, key, key_size);
|
memcpy(bucket->key, key, key_size);
|
||||||
@@ -622,6 +666,11 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu
|
|||||||
/*
|
/*
|
||||||
* Revision history:
|
* Revision history:
|
||||||
*
|
*
|
||||||
|
* 1.1.0 (2026-01-10) Added new hm_count() function;
|
||||||
|
* Fix value_size comparison in hm_put();
|
||||||
|
* Fix memory leak on failed reallocation in hm_put();
|
||||||
|
* Fix out-of-bounds access in hm_next();
|
||||||
|
* Improved documentation
|
||||||
* 1.0.1 (2025-12-30) Added documentation
|
* 1.0.1 (2025-12-30) Added documentation
|
||||||
* 1.0.0 (2025-11-18) Initial release
|
* 1.0.0 (2025-11-18) Initial release
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user