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.
|
||||
//
|
||||
// [License and changelog]
|
||||
@@ -74,8 +74,8 @@
|
||||
// bool hm_put(HashMap *hm, const void *key, const void *value)
|
||||
//
|
||||
// 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
|
||||
// only if HM_NO_ASSERT is not defined.
|
||||
// key already exists, it's value will be overridden. Returns false on error
|
||||
// only if HM_NO_ASSERT is defined.
|
||||
//
|
||||
// void *hm_get(const HashMap *hm, const void *key)
|
||||
//
|
||||
@@ -87,10 +87,16 @@
|
||||
// This function removes the entry associated with the specified key. Returns
|
||||
// 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)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
@@ -106,6 +112,10 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// [Notes]
|
||||
//
|
||||
// This library is not thread-safe.
|
||||
|
||||
#ifndef 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);
|
||||
void *hm_get(const HashMap *hm, const void *key);
|
||||
bool hm_remove(HashMap *hm, const void *key);
|
||||
size_t hm_count(const HashMap *hm);
|
||||
|
||||
// Iterator functions
|
||||
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)
|
||||
{
|
||||
if (hm == NULL || hm->map == NULL) {
|
||||
if (hm == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hm->map == NULL) {
|
||||
memset(hm, 0, sizeof(*hm));
|
||||
return;
|
||||
}
|
||||
@@ -253,12 +268,12 @@ bool hm_put(HashMap *hm, const void *key, const void *value)
|
||||
|
||||
// Ensure that HashMap is initialised
|
||||
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) {
|
||||
HM_ASSERT(false && "Reallocation failed");
|
||||
return false;
|
||||
}
|
||||
hm->capacity = HM_INITIAL_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
|
||||
if (hm->value_size != 0 || value_size == cur->value_size) {
|
||||
if (value_size == cur->value_size) {
|
||||
memcpy(cur->value, value, value_size);
|
||||
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);
|
||||
if (new_value == NULL) {
|
||||
HM_ASSERT(false && "Reallocation failed");
|
||||
HM_FREE(tmp);
|
||||
return false;
|
||||
}
|
||||
cur->value = new_value;
|
||||
@@ -394,6 +410,15 @@ bool hm_remove(HashMap *hm, const void *key)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t hm_count(const HashMap *hm)
|
||||
{
|
||||
if (hm == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hm->count;
|
||||
}
|
||||
|
||||
HashMapIterator hm_iterate(const HashMap *hm)
|
||||
{
|
||||
HashMapIterator it;
|
||||
@@ -415,8 +440,8 @@ const void *hm_key(const HashMapIterator *it)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Reached the end
|
||||
if (it->end) {
|
||||
// Error or reached the end
|
||||
if (it->end || it->bucket == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -430,8 +455,8 @@ void *hm_value(const HashMapIterator *it)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Reached the end
|
||||
if (it->end) {
|
||||
// Error or reached the end
|
||||
if (it->end || it->bucket == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -450,29 +475,48 @@ void *hm_next(HashMapIterator *it)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (it->bucket == NULL) {
|
||||
// Find first list of buckets
|
||||
while ((it->bucket = it->hm->map[it->index++]) == NULL);
|
||||
return it->bucket->value;
|
||||
const HashMap *hm = it->hm;
|
||||
|
||||
// Empty HashMap
|
||||
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) {
|
||||
it->bucket = it->bucket->next;
|
||||
return it->bucket->value;
|
||||
}
|
||||
|
||||
// Find next list of buckets
|
||||
while ((it->bucket = it->hm->map[++it->index]) == NULL);
|
||||
|
||||
// Reached the end
|
||||
if (it->index >= it->hm->capacity) {
|
||||
it->end = true;
|
||||
return NULL;
|
||||
// Move to next non-empty bucket array slot
|
||||
while (it->index < hm->capacity) {
|
||||
it->bucket = hm->map[it->index++];
|
||||
if (it->bucket != NULL) {
|
||||
return it->bucket->value;
|
||||
}
|
||||
}
|
||||
|
||||
it->bucket = it->hm->map[it->index];
|
||||
return it->bucket->value;
|
||||
it->end = true;
|
||||
it->bucket = NULL;
|
||||
it->index = hm->capacity;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: When buffer is NULL terminated, the NULL is included in the hash
|
||||
|
||||
const uint8_t *b = (const uint8_t*)buffer;
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
memset(bucket->key, 0, key_size);
|
||||
|
||||
// Allocate value
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
memset(bucket->value, 0, value_size);
|
||||
|
||||
// Copy Key-value data
|
||||
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:
|
||||
*
|
||||
* 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.0 (2025-11-18) Initial release
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user