v1.1.2 Optimized Hm__Bucket creation

This commit is contained in:
2026-04-08 23:28:11 +02:00
parent 2be677c945
commit 89bdf7bbdf

71
hm.h
View File

@@ -1,4 +1,4 @@
// hm.h - v1.1.1 - MIT License // hm.h - v1.1.2 - 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]
@@ -77,6 +77,10 @@
// key already exists, it's value will be overridden. Returns false on error // key already exists, it's value will be overridden. Returns false on error
// only if HM_NO_ASSERT is defined. // only if HM_NO_ASSERT is defined.
// //
// WARNING: Passing a 'value' pointer that references memory currently stored
// within the HashMap (self-referencing update) results in undefined
// behavior. Always pass a pointer to an external memory buffer.
//
// void *hm_get(const HashMap *hm, const void *key) // void *hm_get(const HashMap *hm, const void *key)
// //
// This function returns the value associated with the specified key. Returns // This function returns the value associated with the specified key. Returns
@@ -254,8 +258,6 @@ void hm_free(HashMap *hm)
while (cur != NULL) { while (cur != NULL) {
Hm__Bucket *next = cur->next; Hm__Bucket *next = cur->next;
HM_FREE(cur->key);
HM_FREE(cur->value);
HM_FREE(cur); HM_FREE(cur);
cur = next; cur = next;
} }
@@ -310,25 +312,31 @@ bool hm_put(HashMap *hm, const void *key, const void *value)
return true; return true;
} }
// Temporary buffer in case of overlapping pointers // Reallocate the entire bucket
uint8_t *tmp = (uint8_t*)HM_REALLOC(NULL, value_size); size_t size = sizeof(Hm__Bucket) + key_size + value_size;
if (tmp == NULL) { Hm__Bucket *new_bucket = (Hm__Bucket*)HM_REALLOC(cur, size);
if (new_bucket == NULL) {
HM_ASSERT(false && "Reallocation failed"); HM_ASSERT(false && "Reallocation failed");
return false; return false;
} }
memcpy(tmp, value, value_size);
// Realloc and copy // Recalculate key-value pointers
uint8_t *new_value = (uint8_t*)HM_REALLOC(cur->value, value_size); new_bucket->key = (uint8_t*)new_bucket + sizeof(*new_bucket);
if (new_value == NULL) { new_bucket->value = (uint8_t*)new_bucket->key + key_size;
HM_ASSERT(false && "Reallocation failed"); new_bucket->value_size = value_size;
HM_FREE(tmp);
return false; // Update linked list pointers of neighbors
if (new_bucket != cur) {
if (new_bucket->prev != NULL) {
new_bucket->prev->next = new_bucket;
} else {
size_t idx = hm__fnv1a(key, key_size) % hm->capacity;
hm->map[idx] = new_bucket;
}
} }
cur->value = new_value;
memcpy(cur->value, tmp, value_size);
HM_FREE(tmp); // Copy the new value
memcpy(new_bucket->value, value, value_size);
return true; return true;
} }
@@ -406,8 +414,6 @@ bool hm_remove(HashMap *hm, const void *key)
cur->next->prev = cur->prev; cur->next->prev = cur->prev;
} }
HM_FREE(cur->key);
HM_FREE(cur->value);
HM_FREE(cur); HM_FREE(cur);
--hm->count; --hm->count;
return true; return true;
@@ -631,34 +637,23 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu
} }
// Allocate bucket // Allocate bucket
Hm__Bucket *bucket = (Hm__Bucket*)HM_REALLOC(NULL, sizeof(*bucket)); // [ Hm__Bucket ][ key ][ value ]
size_t size = sizeof(Hm__Bucket) + key_size + value_size;
Hm__Bucket *bucket = (Hm__Bucket*)HM_REALLOC(NULL, size);
if (bucket == NULL) { if (bucket == NULL) {
HM_ASSERT(false && "Reallocation failed"); HM_ASSERT(false && "Reallocation failed");
return NULL; return NULL;
} }
memset(bucket, 0, sizeof(*bucket)); memset(bucket, 0, sizeof(*bucket));
// Allocate key // Prepare key-value pointers offset
bucket->key = HM_REALLOC(NULL, key_size); bucket->key = (uint8_t*)bucket + sizeof(*bucket);
if (bucket->key == NULL) { bucket->value = (uint8_t*)bucket->key + key_size;
HM_ASSERT(false && "Reallocation failed"); bucket->value_size = value_size;
HM_FREE(bucket);
return NULL;
}
// Allocate value // Copy key-value data
bucket->value = HM_REALLOC(NULL, value_size);
if (bucket->value == NULL) {
HM_ASSERT(false && "Reallocation failed");
HM_FREE(bucket->key);
HM_FREE(bucket);
return NULL;
}
// Copy Key-value data
memcpy(bucket->key, key, key_size); memcpy(bucket->key, key, key_size);
memcpy(bucket->value, value, value_size); memcpy(bucket->value, value, value_size);
bucket->value_size = value_size;
return bucket; return bucket;
} }
@@ -672,6 +667,8 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu
/* /*
* Revision history: * Revision history:
* *
* 1.1.2 (2026-04-08) Optimized Hm__Bucket creation by only allocating one
* buffer instead of three per bucket
* 1.1.1 (2026-02-13) Added new hm_of() helper macro * 1.1.1 (2026-02-13) Added new hm_of() helper macro
* 1.1.0 (2026-01-10) Added new hm_count() function; * 1.1.0 (2026-01-10) Added new hm_count() function;
* Fix value_size comparison in hm_put(); * Fix value_size comparison in hm_put();