remove map detach function - fixes #487 default tip

Wed, 27 Nov 2024 22:33:30 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 27 Nov 2024 22:33:30 +0100
changeset 994
3603bdf4a78b
parent 993
b642eca4b956

remove map detach function - fixes #487

CHANGELOG file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/hash_map.c file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Tue Nov 26 22:16:27 2024 +0100
+++ b/CHANGELOG	Wed Nov 27 22:33:30 2024 +0100
@@ -1,5 +1,6 @@
 Version 3.1 - tbd.
 ------------------------
+
  * adds properties.h
  * adds tree.h
  * adds reallocarray() like functions to allocator.h
@@ -19,7 +20,8 @@
  * moves cx_szmul() to common.h
  * moves stream copy functions to new streams.h
  * removes utils.h
- * removes flag_removal function from iterator (unfortunately breaks binary compatibility)
+ * removes flag_removal function from iterator
+ * removes cxMapDetach() and makes cxMapRemoveAndGet() compatible with both map variants
  * removes CMake
  * removes GTest dependency
  * removes flags to disable SBO in tests
@@ -28,6 +30,7 @@
 
 Version 3.0 - 2023-07-09
 ------------------------
+
  * complete redesign from scratch
  * collections can now store copies of objects and not just pointers
  * collections are now dynamically implemented
--- a/src/cx/map.h	Tue Nov 26 22:16:27 2024 +0100
+++ b/src/cx/map.h	Wed Nov 27 22:33:30 2024 +0100
@@ -120,12 +120,20 @@
 
     /**
      * Removes an element.
+     *
+     * Implementations SHALL check if \p targetbuf is set and copy the elements
+     * to the buffer without invoking any destructor.
+     * When \p targetbuf is not set, the destructors SHALL be invoked.
+     *
+     * The function SHALL return zero when the \p key was found and
+     * non-zero, otherwise. 
      */
-    cx_attr_nonnull
-    void *(*remove)(
+    cx_attr_nonnull_arg(1)
+    cx_attr_access_w(3)
+    int (*remove)(
             CxMap *map,
             CxHashKey key,
-            bool destroy
+            void *targetbuf
     );
 
     /**
@@ -474,292 +482,191 @@
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructor function, if any, on the removed element.
- * If this map is storing pointers and you just want to retrieve the pointer
- * without invoking the destructor, use cxMapRemoveAndGet().
- * If you just want to detach the element from the map without invoking the
- * destructor or returning the element, use cxMapDetach().
+ * Always invokes the destructors functions, if any, on the removed element.
  *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapRemoveAndGet()
- * @see cxMapDetach()
  */
 cx_attr_nonnull
-static inline void cxMapRemove(
+static inline int cxMapRemove(
         CxMap *map,
         CxHashKey const &key
 ) {
-    (void) map->cl->remove(map, key, true);
+    return map->cl->remove(map, key, nullptr);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructor function, if any, on the removed element.
- * If this map is storing pointers and you just want to retrieve the pointer
- * without invoking the destructor, use cxMapRemoveAndGet().
- * If you just want to detach the element from the map without invoking the
- * destructor or returning the element, use cxMapDetach().
+ * Always invokes the destructors functions, if any, on the removed element.
  *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapRemoveAndGet()
- * @see cxMapDetach()
  */
 cx_attr_nonnull
-static inline void cxMapRemove(
+static inline int cxMapRemove(
         CxMap *map,
         cxstring const &key
 ) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructor function, if any, on the removed element.
- * If this map is storing pointers and you just want to retrieve the pointer
- * without invoking the destructor, use cxMapRemoveAndGet().
- * If you just want to detach the element from the map without invoking the
- * destructor or returning the element, use cxMapDetach().
+ * Always invokes the destructors functions, if any, on the removed element.
  *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapRemoveAndGet()
- * @see cxMapDetach()
  */
 cx_attr_nonnull
-static inline void cxMapRemove(
+static inline int cxMapRemove(
         CxMap *map,
         cxmutstr const &key
 ) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructors functions, if any, on the removed element.
+ *
+ * @param map the map
+ * @param key the key
+ * @return zero on success, non-zero if the key was not found
+ * 
+ * @see cxMapRemoveAndGet()
+ */
+cx_attr_nonnull
+cx_attr_cstr_arg(2)
+static inline int cxMapRemove(
+        CxMap *map,
+        const char *key
+) {
+    return map->cl->remove(map, cx_hash_key_str(key), nullptr);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructor function, if any, on the removed element.
- * If this map is storing pointers and you just want to retrieve the pointer
- * without invoking the destructor, use cxMapRemoveAndGet().
- * If you just want to detach the element from the map without invoking the
- * destructor or returning the element, use cxMapDetach().
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemoveAndGet()
- * @see cxMapDetach()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline void cxMapRemove(
-        CxMap *map,
-        const char *key
-) {
-    (void) map->cl->remove(map, cx_hash_key_str(key), true);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * In general, you should only use this function if the map does not own
- * the data and there is a valid reference to the data somewhere else
- * in the program. In all other cases it is preferable to use
- * cxMapRemove() or cxMapRemoveAndGet().
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemove()
- * @see cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-static inline void cxMapDetach(
-        CxMap *map,
-        CxHashKey const &key
-) {
-    (void) map->cl->remove(map, key, false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
- *
- * In general, you should only use this function if the map does not own
- * the data and there is a valid reference to the data somewhere else
- * in the program. In all other cases it is preferable to use
- * cxMapRemove() or cxMapRemoveAndGet().
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
+ * 
+ * @see cxMapStorePointers()
  * @see cxMapRemove()
- * @see cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-static inline void cxMapDetach(
-        CxMap *map,
-        cxstring const &key
-) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
- *
- * In general, you should only use this function if the map does not own
- * the data and there is a valid reference to the data somewhere else
- * in the program. In all other cases it is preferable to use
- * cxMapRemove() or cxMapRemoveAndGet().
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemove()
- * @see cxMapRemoveAndGet()
  */
 cx_attr_nonnull
-static inline void cxMapDetach(
+cx_attr_access_w(3)
+static inline int cxMapRemoveAndGet(
         CxMap *map,
-        cxmutstr const &key
+        CxHashKey key,
+        void *targetbuf
 ) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
- *
- * In general, you should only use this function if the map does not own
- * the data and there is a valid reference to the data somewhere else
- * in the program. In all other cases it is preferable to use
- * cxMapRemove() or cxMapRemoveAndGet().
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemove()
- * @see cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline void cxMapDetach(
-        CxMap *map,
-        const char *key
-) {
-    (void) map->cl->remove(map, cx_hash_key_str(key), false);
+    return map->cl->remove(map, key, targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * This function can be used when the map is storing pointers,
- * in order to retrieve the pointer from the map without invoking
- * any destructor function. Sometimes you do not want the pointer
- * to be returned - in that case (instead of suppressing the "unused
- * result" warning) you can use cxMapDetach().
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * If this map is not storing pointers, this function behaves like
- * cxMapRemove() and returns \c NULL.
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapStorePointers()
- * @see cxMapDetach()
+ * @see cxMapRemove()
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapRemoveAndGet(
+cx_attr_access_w(3)
+static inline int cxMapRemoveAndGet(
         CxMap *map,
-        CxHashKey key
+        cxstring key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, key, !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * This function can be used when the map is storing pointers,
- * in order to retrieve the pointer from the map without invoking
- * any destructor function. Sometimes you do not want the pointer
- * to be returned - in that case (instead of suppressing the "unused
- * result" warning) you can use cxMapDetach().
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * If this map is not storing pointers, this function behaves like
- * cxMapRemove() and returns \c NULL.
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapStorePointers()
- * @see cxMapDetach()
+ * @see cxMapRemove()
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapRemoveAndGet(
+cx_attr_access_w(3)
+static inline int cxMapRemoveAndGet(
         CxMap *map,
-        cxstring key
+        cxmutstr key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * This function can be used when the map is storing pointers,
- * in order to retrieve the pointer from the map without invoking
- * any destructor function. Sometimes you do not want the pointer
- * to be returned - in that case (instead of suppressing the "unused
- * result" warning) you can use cxMapDetach().
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * If this map is not storing pointers, this function behaves like
- * cxMapRemove() and returns \c NULL.
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapStorePointers()
- * @see cxMapDetach()
+ * @see cxMapRemove()
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapRemoveAndGet(
+cx_attr_access_w(3)
+cx_attr_cstr_arg(2)
+static inline int cxMapRemoveAndGet(
         CxMap *map,
-        cxmutstr key
+        const char *key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer);
-}
-
-/**
- * Removes a key/value-pair from the map by using the key.
- *
- * This function can be used when the map is storing pointers,
- * in order to retrieve the pointer from the map without invoking
- * any destructor function. Sometimes you do not want the pointer
- * to be returned - in that case (instead of suppressing the "unused
- * result" warning) you can use cxMapDetach().
- *
- * If this map is not storing pointers, this function behaves like
- * cxMapRemove() and returns \c NULL.
- *
- * @param map the map
- * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
- * @see cxMapStorePointers()
- * @see cxMapDetach()
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(2)
-static inline void *cxMapRemoveAndGet(
-        CxMap *map,
-        const char *key
-) {
-    return map->cl->remove(map, cx_hash_key_str(key), !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
 }
 
 #else // __cplusplus
@@ -932,73 +839,82 @@
 /**
  * Removes a key/value-pair from the map by using the key.
  *
+ * Always invokes the destructors functions, if any, on the removed element.
+ *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-static inline void cx_map_remove(
+static inline int cx_map_remove(
         CxMap *map,
         CxHashKey key
 ) {
-    (void) map->cl->remove(map, key, true);
+    return map->cl->remove(map, key, NULL);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
+ * Always invokes the destructors functions, if any, on the removed element.
+ *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-static inline void cx_map_remove_cxstr(
+static inline int cx_map_remove_cxstr(
         CxMap *map,
         cxstring key
 ) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
+ * Always invokes the destructors functions, if any, on the removed element.
+ *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-static inline void cx_map_remove_mustr(
+static inline int cx_map_remove_mustr(
         CxMap *map,
         cxmutstr key
 ) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
+ * Always invokes the destructors functions, if any, on the removed element.
+ *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(2)
-static inline void cx_map_remove_str(
+static inline int cx_map_remove_str(
         CxMap *map,
         const char *key
 ) {
-    (void) map->cl->remove(map, cx_hash_key_str(key), true);
+    return map->cl->remove(map, cx_hash_key_str(key), NULL);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructor function, if any, on the removed element.
- * If this map is storing pointers and you just want to retrieve the pointer
- * without invoking the destructor, use cxMapRemoveAndGet().
- * If you just want to detach the element from the map without invoking the
- * destructor or returning the element, use cxMapDetach().
+ * Always invokes the destructors functions, if any, on the removed element.
  *
  * @param map the map
  * @param key the key
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapRemoveAndGet()
- * @see cxMapDetach()
  */
 #define cxMapRemove(map, key) _Generic((key), \
     CxHashKey: cx_map_remove,                 \
@@ -1009,183 +925,131 @@
     (map, key)
 
 /**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
+ * Removes a key/value-pair from the map by using the key.
  *
- * @param map the map
- * @param key the key
- */
-cx_attr_nonnull
-static inline void cx_map_detach(
-        CxMap *map,
-        CxHashKey key
-) {
-    (void) map->cl->remove(map, key, false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * @param map the map
- * @param key the key
- */
-cx_attr_nonnull
-static inline void cx_map_detach_cxstr(
-        CxMap *map,
-        cxstring key
-) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- */
-cx_attr_nonnull
-static inline void cx_map_detach_mustr(
-        CxMap *map,
-        cxmutstr key
-) {
-    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
-}
-
-/**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
- *
- * @param map the map
- * @param key the key
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline void cx_map_detach_str(
+cx_attr_access_w(3)
+static inline int cx_map_remove_and_get(
         CxMap *map,
-        const char *key
+        CxHashKey key,
+        void *targetbuf
 ) {
-    (void) map->cl->remove(map, cx_hash_key_str(key), false);
+    return map->cl->remove(map, key, targetbuf);
 }
 
 /**
- * Detaches a key/value-pair from the map by using the key
- * without invoking the destructor.
- *
- * In general, you should only use this function if the map does not own
- * the data and there is a valid reference to the data somewhere else
- * in the program. In all other cases it is preferable to use
- * cxMapRemove() or cxMapRemoveAndGet().
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemove()
- * @see cxMapRemoveAndGet()
- */
-#define cxMapDetach(map, key) _Generic((key), \
-    CxHashKey: cx_map_detach,                 \
-    cxstring: cx_map_detach_cxstr,            \
-    cxmutstr: cx_map_detach_mustr,            \
-    char*: cx_map_detach_str,                 \
-    const char*: cx_map_detach_str)           \
-    (map, key)
-
-/**
  * Removes a key/value-pair from the map by using the key.
  *
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
+ *
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
+ *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_remove_and_get(
+cx_attr_access_w(3)
+static inline int cx_map_remove_and_get_cxstr(
         CxMap *map,
-        CxHashKey key
+        cxstring key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, key, !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * @param map the map
- * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_remove_and_get_cxstr(
-        CxMap *map,
-        cxstring key
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer);
-}
-
-/**
- * Removes a key/value-pair from the map by using the key.
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
+ *
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_remove_and_get_mustr(
+cx_attr_access_w(3)
+static inline int cx_map_remove_and_get_mustr(
         CxMap *map,
-        cxmutstr key
+        cxmutstr key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
+ *
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
+ *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
  */
 cx_attr_nonnull
-cx_attr_nodiscard
+cx_attr_access_w(3)
 cx_attr_cstr_arg(2)
-static inline void *cx_map_remove_and_get_str(
+static inline int cx_map_remove_and_get_str(
         CxMap *map,
-        const char *key
+        const char *key,
+        void *targetbuf
 ) {
-    return map->cl->remove(map, cx_hash_key_str(key), !map->collection.store_pointer);
+    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
 }
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * This function can be used when the map is storing pointers,
- * in order to retrieve the pointer from the map without invoking
- * any destructor function. Sometimes you do not want the pointer
- * to be returned - in that case (instead of suppressing the "unused
- * result" warning) you can use cxMapDetach().
+ * This function will copy the contents to the target buffer
+ * which must be guaranteed to be large enough to hold the element.
+ * The destructor functions, if any, will \em not be called.
  *
- * If this map is not storing pointers, this function behaves like
- * cxMapRemove() and returns \c NULL.
+ * If this map is storing pointers, the element is the pointer itself
+ * and not the object it points to.
  *
  * @param map the map
  * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
+ * @param targetbuf the buffer where the element shall be copied to
+ * @return zero on success, non-zero if the key was not found
+ * 
  * @see cxMapStorePointers()
- * @see cxMapDetach()
+ * @see cxMapRemove()
  */
-#define cxMapRemoveAndGet(map, key) _Generic((key), \
+#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
     CxHashKey: cx_map_remove_and_get,               \
     cxstring: cx_map_remove_and_get_cxstr,          \
     cxmutstr: cx_map_remove_and_get_mustr,          \
     char*: cx_map_remove_and_get_str,               \
     const char*: cx_map_remove_and_get_str)         \
-    (map, key)
+    (map, key, targetbuf)
 
 #endif // __cplusplus
 
--- 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) {
--- a/tests/test_hash_map.c	Tue Nov 26 22:16:27 2024 +0100
+++ b/tests/test_hash_map.c	Wed Nov 27 22:33:30 2024 +0100
@@ -207,8 +207,10 @@
         CX_TEST_ASSERT(s3p !=  &s3);
 
         // remove a string
-        cxMapRemove(map, "s2");
+        cxstring ret = {0};
+        CX_TEST_ASSERT(0 == cxMapRemoveAndGet(map, "s2", &ret));
         CX_TEST_ASSERT(map->collection.size == 3);
+        CX_TEST_ASSERT(0 == cx_strcmp(ret, cx_str("is")));
 
         // iterate
         bool s3found = false, s4found = false, s5found = false;
@@ -289,9 +291,9 @@
     cxMapPut(map, k3, v3);
     cxMapPut(map, k4, v4);
 
-    cxMapRemove(map, k2);
-    char *r = cxMapRemoveAndGet(map, k3);
-    cxMapDetach(map, k1);
+    CX_TEST_ASSERT(0 == cxMapRemove(map, k2));
+    char *r;
+    CX_TEST_ASSERT(0 == cxMapRemoveAndGet(map, k3, &r));
 
     CX_TEST_ASSERT(0 == strcmp(v1, "val 1"));
     CX_TEST_ASSERT(0 == strcmp(v2, "OK"));
@@ -426,7 +428,7 @@
     cx_testing_allocator_init(&talloc);
     CxAllocator *allocator = &talloc.base;
     CX_TEST_DO {
-        CxMap *map = cxHashMapCreate(allocator, sizeof(cxstring), 0);
+        CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
         cxMapPut(map, "test", "test");
         cxMapPut(map, cx_mutstr("foo"), "bar");
         cxMapPut(map, cx_str("hallo"), "welt");
@@ -436,20 +438,23 @@
         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "hallo"), "welt"));
 
-        // note: we don't have a destructor here, so remove and detach are the same
-        cxMapRemove(map, cx_str("test"));
+        CX_TEST_ASSERT(0 == cxMapRemove(map, cx_str("test")));
         const char *hallo = "hallo";
-        cxMapDetach(map, hallo);
+        CX_TEST_ASSERT(0 == cxMapRemove(map, hallo));
         cxMapPut(map, cx_hash_key_str("key"), "value");
 
         CX_TEST_ASSERT(map->collection.size == 2);
         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key"), "value"));
         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
 
-        void *r;
-        r = cxMapRemoveAndGet(map, "key");
-        r = cxMapRemoveAndGet(map, cx_str("foo"));
-        if (r != NULL) map->collection.size = 47;
+        const char *r1, *r2;
+        CX_TEST_ASSERT(0 == cxMapRemoveAndGet(map, "key", &r1));
+        CX_TEST_ASSERT(0 == strcmp(r1, "value"));
+        CX_TEST_ASSERT(0 == cxMapRemoveAndGet(map, cx_str("foo"), &r2));
+        CX_TEST_ASSERT(0 == strcmp(r2, "bar"));
+        r2 = "nope";
+        CX_TEST_ASSERT(0 != cxMapRemoveAndGet(map, cx_hash_key("notfound",9), &r2));
+        CX_TEST_ASSERT(0 == strcmp(r2, "nope"));
 
         CX_TEST_ASSERT(map->collection.size == 0);
 
@@ -641,8 +646,16 @@
             } else {
                 // execute a remove and verify that the removed element was returned (or NULL)
                 const char *found = test_map_reference_remove(kv.key);
-                void *removed = cxMapRemoveAndGet(map, key);
-                CX_TEST_ASSERT(found == removed);
+                void *removed = (void*) 0x1337;
+                int result = cxMapRemoveAndGet(map, key, &removed);
+                if (found == NULL) {
+                    CX_TEST_ASSERT(0 != result);
+                    CX_TEST_ASSERT(removed == (void*) 0x1337);
+                } else {
+                    CX_TEST_ASSERT(0 == result);
+                    CX_TEST_ASSERT(removed == found);
+                }
+
             }
             // compare the current map state with the reference map
             CX_TEST_CALL_SUBROUTINE(verify_map_contents, map);

mercurial