src/hash_map.c

changeset 994
3603bdf4a78b
parent 989
8aa57a7fecc4
child 1040
1ecf4dbbc60c
--- a/src/hash_map.c	Tue Nov 26 22:16:27 2024 +0100
+++ b/src/hash_map.c	Wed Nov 27 22:33:30 2024 +0100
@@ -172,17 +172,28 @@
 /**
  * Helper function to avoid code duplication.
  *
+ * If \p remove is true, and \p targetbuf is \c NULL, the element
+ * will be destroyed when found.
+ *
+ * If \p remove is true, and \p targetbuf is set, the element will
+ * be copied to that buffer and no destructor function is called.
+ *
+ * If \p remove is false, \p targetbuf must not be non-null and
+ * either the pointer, when the map is storing pointers, is copied
+ * to the target buffer, or a pointer to the stored object will
+ * be copied to the target buffer.
+ *
  * @param map the map
  * @param key the key to look up
+ * @param targetbuf see description
  * @param remove flag indicating whether the looked up entry shall be removed
- * @param destroy flag indicating whether the destructor shall be invoked
- * @return a pointer to the value corresponding to the key or \c NULL
+ * @return zero, if the key was found, non-zero otherwise
  */
-static void *cx_hash_map_get_remove(
+static int cx_hash_map_get_remove(
         CxMap *map,
         CxHashKey key,
-        bool remove,
-        bool destroy
+        void *targetbuf,
+        bool remove
 ) {
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
 
@@ -198,27 +209,31 @@
     while (elm && elm->key.hash <= hash) {
         if (elm->key.hash == hash && elm->key.len == key.len) {
             if (memcmp(elm->key.data, key.data, key.len) == 0) {
-                void *data = NULL;
-                if (destroy) {
-                    cx_invoke_destructor(map, elm->data);
+                if (remove) {
+                    if (targetbuf == NULL) {
+                        cx_invoke_destructor(map, elm->data);
+                    } else {
+                        memcpy(targetbuf, elm->data, map->collection.elem_size);
+                    }
+                    cx_hash_map_unlink(hash_map, slot, prev, elm);
                 } else {
+                    assert(targetbuf != NULL);
+                    void *data = NULL;
                     if (map->collection.store_pointer) {
                         data = *(void **) elm->data;
                     } else {
                         data = elm->data;
                     }
+                    memcpy(targetbuf, &data, sizeof(void *));
                 }
-                if (remove) {
-                    cx_hash_map_unlink(hash_map, slot, prev, elm);
-                }
-                return data;
+                return 0;
             }
         }
         prev = elm;
         elm = prev->next;
     }
 
-    return NULL;
+    return 1;
 }
 
 static void *cx_hash_map_get(
@@ -226,15 +241,17 @@
         CxHashKey key
 ) {
     // we can safely cast, because we know the map stays untouched
-    return cx_hash_map_get_remove((CxMap *) map, key, false, false);
+    void *ptr = NULL;
+    int found = cx_hash_map_get_remove((CxMap *) map, key, &ptr, false);
+    return found == 0 ? ptr : NULL;
 }
 
-static void *cx_hash_map_remove(
+static int cx_hash_map_remove(
         CxMap *map,
         CxHashKey key,
-        bool destroy
+        void *targetbuf
 ) {
-    return cx_hash_map_get_remove(map, key, true, destroy);
+    return cx_hash_map_get_remove(map, key, targetbuf, true);
 }
 
 static void *cx_hash_map_iter_current_entry(const void *it) {

mercurial