From 89bdf7bbdfc76f1a21e9679e43ee322bfd881ddb Mon Sep 17 00:00:00 2001 From: Cristian Gora Date: Wed, 8 Apr 2026 23:28:11 +0200 Subject: [PATCH] v1.1.2 Optimized Hm__Bucket creation --- hm.h | 71 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/hm.h b/hm.h index 2d59a67..6243a3e 100644 --- a/hm.h +++ b/hm.h @@ -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. // // [License and changelog] @@ -77,6 +77,10 @@ // key already exists, it's value will be overridden. Returns false on error // 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) // // This function returns the value associated with the specified key. Returns @@ -254,8 +258,6 @@ void hm_free(HashMap *hm) while (cur != NULL) { Hm__Bucket *next = cur->next; - HM_FREE(cur->key); - HM_FREE(cur->value); HM_FREE(cur); cur = next; } @@ -310,25 +312,31 @@ bool hm_put(HashMap *hm, const void *key, const void *value) return true; } - // Temporary buffer in case of overlapping pointers - uint8_t *tmp = (uint8_t*)HM_REALLOC(NULL, value_size); - if (tmp == NULL) { + // Reallocate the entire bucket + size_t size = sizeof(Hm__Bucket) + key_size + value_size; + Hm__Bucket *new_bucket = (Hm__Bucket*)HM_REALLOC(cur, size); + if (new_bucket == NULL) { HM_ASSERT(false && "Reallocation failed"); return false; } - memcpy(tmp, value, value_size); - // Realloc and copy - 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; + // Recalculate key-value pointers + new_bucket->key = (uint8_t*)new_bucket + sizeof(*new_bucket); + new_bucket->value = (uint8_t*)new_bucket->key + key_size; + new_bucket->value_size = value_size; + + // 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; } @@ -406,8 +414,6 @@ bool hm_remove(HashMap *hm, const void *key) cur->next->prev = cur->prev; } - HM_FREE(cur->key); - HM_FREE(cur->value); HM_FREE(cur); --hm->count; return true; @@ -631,34 +637,23 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu } // 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) { HM_ASSERT(false && "Reallocation failed"); return NULL; } memset(bucket, 0, sizeof(*bucket)); - // Allocate key - bucket->key = HM_REALLOC(NULL, key_size); - if (bucket->key == NULL) { - HM_ASSERT(false && "Reallocation failed"); - HM_FREE(bucket); - return NULL; - } + // Prepare key-value pointers offset + bucket->key = (uint8_t*)bucket + sizeof(*bucket); + bucket->value = (uint8_t*)bucket->key + key_size; + bucket->value_size = value_size; - // Allocate value - 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 + // Copy key-value data memcpy(bucket->key, key, key_size); memcpy(bucket->value, value, value_size); - bucket->value_size = value_size; return bucket; } @@ -672,6 +667,8 @@ Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *valu /* * 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.0 (2026-01-10) Added new hm_count() function; * Fix value_size comparison in hm_put();