Wed, 27 Nov 2024 22:33:30 +0100
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);