From a8937f1a3e964ba23df454c9ad6f38997f9c2e97 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 25 Feb 2024 14:00:46 +0100 Subject: [PATCH] gh-674: Add copy callback support for array list Also remove STRING_REF element type. --- .../src/ArrayListErrorInjectionTestSuite.cc | 20 +- libs/utils/gtest/src/ArrayListTestSuite.cc | 54 ++-- libs/utils/include/celix_array_list.h | 268 +++++++----------- libs/utils/src/array_list.c | 83 ++++-- 4 files changed, 205 insertions(+), 220 deletions(-) diff --git a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc index 0d6131a1f..177adba9d 100644 --- a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc @@ -95,21 +95,31 @@ TEST_F(ArrayListErrorInjectionTestSuite, AddStringAndAddVersionFailureTest) { } TEST_F(ArrayListErrorInjectionTestSuite, CopyArrayListFailureTest) { - // Given a string array list with 10 elements (whitebox knowledge) + // Given a string array list with 11 elements (more than max initial capacity, whitebox knowledge) celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); - celix_arrayList_addString(stringList, "test1"); + for (int i = 0; i < 11; ++i) { + std::string str = "test" + std::to_string(i); + celix_arrayList_addString(stringList, str.c_str()); + } - // When an error is injected for calloc + // When an error is injected for calloc (create array list) celix_ei_expect_calloc((void*)celix_arrayList_copy, 1, nullptr); // Then copying an array list should fail EXPECT_EQ(nullptr, celix_arrayList_copy(stringList)); // And a celix_err is expected EXPECT_EQ(1, celix_err_getErrorCount()); - // When an error is injected for celix_utils_strdup - celix_ei_expect_celix_utils_strdup((void*)celix_arrayList_addString, 0, nullptr); + // And an error is injected for realloc (ensureCapacity) + celix_ei_expect_realloc((void*)celix_arrayList_copy, 1, nullptr); // Then copying an array list should fail EXPECT_EQ(nullptr, celix_arrayList_copy(stringList)); // And a celix_err is expected EXPECT_EQ(2, celix_err_getErrorCount()); + + // When an error is injected for celix_utils_strdup (used in string array list copy callback (whitebox knowledge)) + celix_ei_expect_celix_utils_strdup((void*)celix_arrayList_copy, 1, nullptr); + // Then copying an array list should fail + EXPECT_EQ(nullptr, celix_arrayList_copy(stringList)); + // And a celix_err is expected + EXPECT_EQ(3, celix_err_getErrorCount()); } diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index 78e77b6f0..24c864a22 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -20,7 +20,9 @@ #include #include "celix_array_list.h" +#include "celix_version.h" #include "celix_utils.h" +#include "celix_filter.h" class ArrayListTestSuite : public ::testing::Test { public: @@ -154,19 +156,6 @@ TEST_F(ArrayListTestSuite, StringArrayList) { celix_arrayList_removeString(stringList, "2"); EXPECT_EQ(2, celix_arrayList_size(stringList)); - - celix_autoptr(celix_array_list_t) stringRefList = celix_arrayList_createStringRefArray(); - celix_arrayList_addString(stringRefList, str1); - celix_arrayList_addString(stringRefList, "2"); - celix_arrayList_addString(stringRefList, "3"); - - EXPECT_EQ(3, celix_arrayList_size(stringRefList)); - EXPECT_STREQ("1", celix_arrayList_getString(stringRefList, 0)); - EXPECT_EQ((void*)str1, (void*)celix_arrayList_getString(stringRefList, 0)); //string is added as reference - EXPECT_STREQ("2", celix_arrayList_getString(stringRefList, 1)); - - celix_arrayList_removeString(stringRefList, "2"); - EXPECT_EQ(2, celix_arrayList_size(stringRefList)); } TEST_F(ArrayListTestSuite, VersionArrayList) { @@ -315,27 +304,50 @@ TEST_F(ArrayListTestSuite, CopyArrayTest) { celix_arrayList_addString(stringList, "2"); celix_arrayList_addString(stringList, "3"); - celix_autoptr(celix_array_list_t) stringRefList = celix_arrayList_createStringRefArray(); - celix_arrayList_addString(stringRefList, "1"); - celix_arrayList_addString(stringRefList, "2"); - celix_arrayList_addString(stringRefList, "3"); - celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); celix_arrayList_assignVersion(versionList, celix_version_create(1, 0, 0, "")); celix_arrayList_assignVersion(versionList, celix_version_create(2, 0, 0, "")); celix_arrayList_assignVersion(versionList, celix_version_create(3, 0, 0, "")); + // And a custom pointer list configured for handling celix_filter_t + celix_array_list_create_options_t opts{}; + opts.elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER; + opts.simpleRemovedCallback = [](void* d) { celix_filter_destroy((celix_filter_t*)d); }; + opts.copyCallback = [](celix_array_list_entry_t src, celix_array_list_entry_t* dst) -> celix_status_t { + auto* filter = (celix_filter_t*)src.voidPtrVal; + dst->voidPtrVal = celix_filter_create(celix_filter_getFilterString(filter)); + return (dst->voidPtrVal) ? CELIX_SUCCESS : CELIX_ENOMEM; + }; + opts.equalsCallback = [](celix_array_list_entry_t a, celix_array_list_entry_t b) -> bool { + auto* fa = (celix_filter_t*)a.voidPtrVal; + auto* fb = (celix_filter_t*)b.voidPtrVal; + return celix_utils_stringEquals(celix_filter_getFilterString(fa), celix_filter_getFilterString(fb)); + }; + celix_autoptr(celix_array_list_t) ptrList = celix_arrayList_createWithOptions(&opts); + celix_filter_t* f1 = celix_filter_create("(a=1)"); + celix_filter_t* f2 = celix_filter_create("(a=2)"); + celix_filter_t* f3 = celix_filter_create("(a=3)"); + celix_arrayList_add(ptrList, f1); + celix_arrayList_add(ptrList, f2); + celix_arrayList_add(ptrList, f3); + // When copying the lists celix_autoptr(celix_array_list_t) longListCopy = celix_arrayList_copy(longList); celix_autoptr(celix_array_list_t) stringListCopy = celix_arrayList_copy(stringList); - celix_autoptr(celix_array_list_t) stringRefListCopy = celix_arrayList_copy(stringRefList); celix_autoptr(celix_array_list_t) versionListCopy = celix_arrayList_copy(versionList); + celix_autoptr(celix_array_list_t) ptrListCopy = celix_arrayList_copy(ptrList); + + // Then the size of the copied array list is 3 + EXPECT_EQ(3, celix_arrayList_size(longListCopy)); + EXPECT_EQ(3, celix_arrayList_size(stringListCopy)); + EXPECT_EQ(3, celix_arrayList_size(versionListCopy)); + EXPECT_EQ(3, celix_arrayList_size(ptrListCopy)); - // Then the copied lists are equal to the original lists + // And the copied lists are equal to the original lists EXPECT_TRUE(celix_arrayList_equals(longList, longListCopy)); EXPECT_TRUE(celix_arrayList_equals(stringList, stringListCopy)); - EXPECT_TRUE(celix_arrayList_equals(stringRefList, stringRefListCopy)); EXPECT_TRUE(celix_arrayList_equals(versionList, versionListCopy)); + EXPECT_TRUE(celix_arrayList_equals(ptrList, ptrListCopy)); } TEST_F(ArrayListTestSuite, SimpleRemovedCallbacksForArrayListTest) { diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index a5de204e2..b53c70e7e 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -19,10 +19,10 @@ #include +#include "celix_utils_export.h" #include "celix_cleanup.h" #include "celix_errno.h" -#include "celix_utils_export.h" -#include "celix_version.h" +#include "celix_version_type.h" #ifndef CELIX_ARRAY_LIST_H_ #define CELIX_ARRAY_LIST_H_ @@ -49,12 +49,10 @@ typedef enum celix_array_list_element_type { CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED = 0, /**< Represents an undefined element type. */ CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER = 1, /**< Represents a pointer element type. */ CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING = 2, /**< Represents a string element type where the array list is the owner */ - CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF = - 3, /**< Represents a string element type where the array list is not the owner */ - CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG = 5, /**< Represents a long integer element type. */ - CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE = 9, /**< Represents a double element type. */ - CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL = 10, /**< Represents a boolean element type. */ - CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION = 12 /**< Represents a celix_version_t* element type. */ + CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG = 3, /**< Represents a long integer element type. */ + CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE = 4, /**< Represents a double element type. */ + CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL = 5, /**< Represents a boolean element type. */ + CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION = 6, /**< Represents a celix_version_t* element type. */ } celix_array_list_element_type_t; /** @@ -95,70 +93,14 @@ typedef bool (*celix_arrayList_equals_fp)(celix_array_list_entry_t, celix_array_ typedef int (*celix_array_list_compare_entries_fp)(celix_array_list_entry_t a, celix_array_list_entry_t b); /** - * Additional create options when creating a array list. - */ -typedef struct celix_array_list_create_options { - /** - * The element type of the array list. Default is CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - */ - celix_array_list_element_type_t elementType CELIX_OPTS_INIT; - - /** - * A simple removed callback, which if provided will be called if a entry is removed - * from the array list. The removed entry is provided as pointer. - * - * @note Assumes that the array list entries are pointer values. - * - * @note only simpleRemovedCallback or removedCallback should be configured, if both are configured, - * only the simpledRemoveCallback will be used. - * - * Default is NULL. - */ - void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT; - - /** - * Optional callback data, which will be provided to the removedCallback callback. - * - * Default is NULL. - */ - void* removedCallbackData CELIX_OPTS_INIT; - - /** - * A removed callback, which if provided will be called if a entry is removed from the array list. - * The callback data pointer will be provided as first argument to the removed callback. - * - * @note only simpleRemovedCallback or removedCallback should be configured, if both are configured, - * only the simpledRemoveCallback will be used. - * - * Default is NULL. - */ - void (*removedCallback)(void* data, celix_array_list_entry_t entry) CELIX_OPTS_INIT; - - /** - * Equals callback used when trying to find a array list entry. - */ - celix_arrayList_equals_fp equalsCallback CELIX_OPTS_INIT; - - /** - * Compare callback used when sorting the array list. - */ - celix_array_list_compare_entries_fp compareCallback CELIX_OPTS_INIT; - -} celix_array_list_create_options_t; - -#ifndef __cplusplus -/** - * @brief C Macro to create a empty celix_array_list_create_options_t type. + * @brief Copy function for array list entries, which can be used to copy a array list entry. + * @return CELIX_SUCCESS if the entry is copied, CELIX_ENOMEM if the entry could not be copied. */ -#define CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS \ - { \ - .elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED, .simpleRemovedCallback = NULL, \ - .removedCallbackData = NULL, .removedCallback = NULL, .equalsCallback = NULL, .compareCallback = NULL \ - } -#endif +typedef celix_status_t (*celix_array_list_copy_entry_fp)(celix_array_list_entry_t src, celix_array_list_entry_t* dst); /** * @brief Creates a new empty array list with an undefined element type. + * @deprecated Use celix_arrayList_createWithOptions or celix_arrayList_createArray instead. * * The remove, equals and compare callback will be NULL. * @@ -172,7 +114,7 @@ celix_array_list_t* celix_arrayList_create(); * @brief Creates a new empty array list with a pointer element type where the array list is not the owner of the * pointers. * - * The remove, equals and compare callback will be NULL. + * The remove, equals, compare and copy callback will be NULL. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -183,8 +125,8 @@ celix_array_list_t* celix_arrayList_createPointerArray(); /** * @brief Creates a new empty array list with a string element type where the array list is the owner of the strings. * - * The remove callback will be configured to free the string, and equals and compare callback will be configured for - * string comparison. + * The remove callback will be configured to free the string, the equals and compare callback will be configured for + * string comparison and the copy callback will be a callback to uses celix_utils_strdup. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -192,24 +134,11 @@ celix_array_list_t* celix_arrayList_createPointerArray(); CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createStringArray(); -/** - * @brief Creates a new empty array list with a string element type where the array list is **not** the owner of the - * strings. - * - * The remove callback will be configured to NULL, and equals and compare callback will be configured for - * string comparison. - * - * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message - * is logged to celix_err. - */ -CELIX_UTILS_EXPORT -celix_array_list_t* celix_arrayList_createStringRefArray(); - /** * @brief Creates a new empty array list with a long integer element type. * - * The remove callback will be configured to NULL, and equals and compare callback will be configured for - * integer comparison. + * The remove callback will be configured to NULL, the equals and compare callback will be configured for + * integer comparison and the copy callback will be configured to NULL. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -220,8 +149,8 @@ celix_array_list_t* celix_arrayList_createLongArray(); /** * @brief Creates a new empty array list with a double element type. * - * The remove callback will be configured to NULL, and equals and compare callback will be configured for - * double comparison. + * The remove callback will be configured to NULL, the equals and compare callback will be configured for + * double comparison and the copy callback will be configured to NULL. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -232,8 +161,8 @@ celix_array_list_t* celix_arrayList_createDoubleArray(); /** * @brief Creates a new empty array list with a boolean element type. * - * The remove callback will be configured to NULL, and equals and compare callback will be configured for - * boolean comparison. + * The remove callback will be configured to NULL, the equals and compare callback will be configured for + * boolean comparison and the copy callback will be configured to NULL. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -244,8 +173,8 @@ celix_array_list_t* celix_arrayList_createBoolArray(); /** * @brief Creates a new empty array list with a celix_version_t* element type. * - * The remove callback will be configured to free a celix version, and equals and compare callback will be configured - * for celix version comparison. + * The remove callback will be configured to free a celix version, the equals and compare callback will be configured + * for celix version comparison and the copy callback will a callback that uses celix_version_copy. * * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message * is logged to celix_err. @@ -253,12 +182,84 @@ celix_array_list_t* celix_arrayList_createBoolArray(); CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createVersionArray(); +/** + * Additional create options when creating a array list. + */ +typedef struct celix_array_list_create_options { + /** + * The element type of the array list. Default is CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. + */ + celix_array_list_element_type_t elementType CELIX_OPTS_INIT; + + /** + * A simple removed callback, which if provided will be called if a entry is removed + * from the array list. The removed entry is provided as pointer. + * + * @note Assumes that the array list entries are pointer values. + * + * @note only simpleRemovedCallback or removedCallback should be configured, if both are configured, + * only the simpledRemoveCallback will be used. + * + * Default is NULL. + */ + void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT; + + /** + * Optional callback data, which will be provided to the removedCallback callback. + * + * Default is NULL. + */ + void* removedCallbackData CELIX_OPTS_INIT; + + /** + * A removed callback, which if provided will be called if a entry is removed from the array list. + * The callback data pointer will be provided as first argument to the removed callback. + * + * @note only simpleRemovedCallback or removedCallback should be configured, if both are configured, + * only the simpledRemoveCallback will be used. + * + * Default is NULL. + */ + void (*removedCallback)(void* data, celix_array_list_entry_t entry) CELIX_OPTS_INIT; + + /** + * Equals callback used when trying to find a array list entry. + */ + celix_arrayList_equals_fp equalsCallback CELIX_OPTS_INIT; + + /** + * Compare callback used when sorting the array list. + */ + celix_array_list_compare_entries_fp compareCallback CELIX_OPTS_INIT; + + /** + * The copy callback used to copy a array list entry. + * If callback is NULL, a celix_arrayList_copy will result in a shallow copy. + */ + celix_array_list_copy_entry_fp copyCallback CELIX_OPTS_INIT; + +} celix_array_list_create_options_t; + +#ifndef __cplusplus +/** + * @brief C Macro to create a empty celix_array_list_create_options_t type. + */ +#define CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS \ + { \ + .elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED, .simpleRemovedCallback = NULL, \ + .removedCallbackData = NULL, .removedCallback = NULL, .equalsCallback = NULL, \ + .compareCallback = NULL, .copyCallback = NULL, \ + } +#endif + /** * @brief Creates a new empty array list using using the provided array list create options. * - * If the element type is set to something other than CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED, the remove, equals, - * and compare callbacks will be set according to the corresponding celix_arrayList_create*Array function. - * The provided callbacks in the options will override the default callbacks. + * The callbacks in the options will override the default callbacks based on the provided element type. + * The default callbacks (e.g. the remove, equals, compare and copy callback) correspond to the + * celix_arrayList_createArray function. + * For example if the elementType is CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, the default callbacks will be set to + * handle string values. * * @param opts The create options, only used during the creation of the array list. * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message @@ -305,8 +306,8 @@ void* celix_arrayList_get(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, - * CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF or CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. + * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING + * or CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. * * @param list The array list. * @param index The entry index to return. @@ -383,12 +384,12 @@ celix_status_t celix_arrayList_add(celix_array_list_t* list, void* value); /** * @brief Add a string entry to the back of the array list. * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, - * CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF or CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. + * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING + * or CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. * * If the array list element type is CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, the string will be copied, but - * if the array list element type is CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF or - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED, the string will be added as reference (as-is). + * if the array list element type is CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED, + * the string will be added as reference (as-is). * * @param list The array list. * @param value The string value to add to the array list. Cannot be NULL. @@ -550,18 +551,6 @@ void celix_arrayList_remove(celix_array_list_t* list, void* value); CELIX_UTILS_EXPORT void celix_arrayList_removeString(celix_array_list_t* list, const char* value); -/** - * @brief Remove the first int entry from array list which matches the provided value. - * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_INT and - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeInt(celix_array_list_t* list, int value); - /** * @brief Remove the first long entry from array list which matches the provided value. * @@ -574,42 +563,6 @@ void celix_arrayList_removeInt(celix_array_list_t* list, int value); CELIX_UTILS_EXPORT void celix_arrayList_removeLong(celix_array_list_t* list, long value); -/** - * @brief Remove the first unsigned int entry from array list which matches the provided value. - * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_UINT and - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeUInt(celix_array_list_t* list, unsigned int value); - -/** - * @brief Remove the first unsigned long entry from array list which matches the provided value. - * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_ULONG and - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeULong(celix_array_list_t* list, unsigned long value); - -/** - * @brief Remove the first float entry from array list which matches the provided value. - * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_FLOAT and - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeFloat(celix_array_list_t* list, float value); - /** * @brief Remove the first double entry from array list which matches the provided value. * @@ -634,18 +587,6 @@ void celix_arrayList_removeDouble(celix_array_list_t* list, double value); CELIX_UTILS_EXPORT void celix_arrayList_removeBool(celix_array_list_t* list, bool value); -/** - * @brief Remove the first size entry from array list which matches the provided value. - * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_SIZE or - * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeSize(celix_array_list_t* list, size_t value); - /** * @brief Remove the first version entry from array list which matches the provided value. * @@ -695,9 +636,8 @@ bool celix_arrayList_equals(const celix_array_list_t* listA, const celix_array_l * @Brief Copy the array list to a new array list. * * The new array list will have the same element type and the same callbacks as the original array list. - * If the element type is CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, the strings will be copied and - * if the element type is CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION, the versions will be copied. - * For all other element types the values will be copied. + * For copying the array list entries the copy callback will be used, if the capy callback is NULL a shallow copy will + * be done. * * If a NULL is returned and the original array list is not NULL, a error message is logged to celix_err. * diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index d70af3677..3fbb7f4ff 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -18,16 +18,15 @@ */ #include -#include -#include + #include #include #include "celix_array_list.h" -#include "celix_build_assert.h" #include "celix_err.h" #include "celix_stdlib_cleanup.h" #include "celix_utils.h" +#include "celix_version.h" struct celix_array_list { celix_array_list_element_type_t elementType; @@ -36,6 +35,7 @@ struct celix_array_list { size_t capacity; celix_arrayList_equals_fp equalsCallback; celix_array_list_compare_entries_fp compareCallback; + celix_array_list_copy_entry_fp copyCallback; void (*simpleRemovedCallback)(void* value); void* removedCallbackData; void (*removedCallback)(void* data, celix_array_list_entry_t entry); @@ -100,6 +100,24 @@ static bool celix_arrayList_equalsForElement(celix_array_list_t *list, celix_arr return false; } +static celix_status_t celix_arrayList_copyStringEntry(celix_array_list_entry_t src, celix_array_list_entry_t* dst) { + assert(dst); + dst->stringVal = celix_utils_strdup(src.stringVal); + if (!dst->stringVal) { + return ENOMEM; + } + return CELIX_SUCCESS; +} + +static celix_status_t celix_arrayList_copyVersionEntry(celix_array_list_entry_t src, celix_array_list_entry_t* dst) { + assert(dst); + dst->versionVal = celix_version_copy(src.versionVal); + if (!dst->versionVal) { + return ENOMEM; + } + return CELIX_SUCCESS; +} + static void celix_arrayList_destroyVersion(void* v) { celix_version_t* version = v; celix_version_destroy(version); @@ -114,7 +132,7 @@ static void celix_arrayList_callRemovedCallback(celix_array_list_t *list, int in } } -static celix_status_t celix_arrayList_ensureCapacity(celix_array_list_t* list, int capacity) { +static celix_status_t celix_arrayList_ensureCapacity(celix_array_list_t* list, size_t capacity) { celix_array_list_entry_t *newList; size_t oldCapacity = list->capacity; if (capacity > oldCapacity) { @@ -122,7 +140,7 @@ static celix_status_t celix_arrayList_ensureCapacity(celix_array_list_t* list, i newList = realloc(list->elementData, sizeof(celix_array_list_entry_t) * newCapacity); if (!newList) { celix_err_push("Failed to reallocate memory for elementData"); - return CELIX_ENOMEM; + return ENOMEM; } list->capacity = newCapacity; list->elementData = newList; @@ -140,10 +158,7 @@ static void celix_arrayList_setTypeSpecificCallbacks(celix_array_list_t* list) { list->simpleRemovedCallback = free; list->equalsCallback = celix_arrayList_stringEquals; list->compareCallback = celix_arrayList_compareStringEntries; - break; - case CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF: - list->equalsCallback = celix_arrayList_stringEquals; - list->compareCallback = celix_arrayList_compareStringEntries; + list->copyCallback = celix_arrayList_copyStringEntry; break; case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: list->equalsCallback = celix_arrayList_longEquals; @@ -161,6 +176,7 @@ static void celix_arrayList_setTypeSpecificCallbacks(celix_array_list_t* list) { list->simpleRemovedCallback = celix_arrayList_destroyVersion; list->equalsCallback = celix_arrayList_versionEquals; list->compareCallback = celix_arrayList_compareVersionEntries; + list->copyCallback = celix_arrayList_copyVersionEntry; break; default: assert(list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); @@ -200,6 +216,9 @@ celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_cre if (opts->compareCallback) { list->compareCallback = opts->compareCallback; } + if (opts->copyCallback) { + list->copyCallback = opts->copyCallback; + } return celix_steal_ptr(list); } @@ -221,10 +240,6 @@ celix_array_list_t* celix_arrayList_createStringArray() { return celix_arrayList_createTypedArray(CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING); } -celix_array_list_t* celix_arrayList_createStringRefArray() { - return celix_arrayList_createTypedArray(CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF); -} - celix_array_list_t* celix_arrayList_createLongArray() { return celix_arrayList_createTypedArray(CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG); } @@ -274,7 +289,6 @@ void* celix_arrayList_get(const celix_array_list_t* list, int index) { const char* celix_arrayList_getString(const celix_array_list_t* list, int index) { assert(list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING || - list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF || list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); return arrayList_getEntry(list, index).stringVal; } @@ -303,7 +317,7 @@ const celix_version_t* celix_arrayList_getVersion(const celix_array_list_t* list } static celix_status_t celix_arrayList_addEntry(celix_array_list_t* list, celix_array_list_entry_t entry) { - celix_status_t status = celix_arrayList_ensureCapacity(list, (int)list->size + 1); + celix_status_t status = celix_arrayList_ensureCapacity(list, list->size + 1); if (status == CELIX_SUCCESS) { list->elementData[list->size++] = entry; } else { @@ -329,14 +343,13 @@ celix_status_t celix_arrayList_add(celix_array_list_t* list, void* element) { celix_status_t celix_arrayList_addString(celix_array_list_t* list, const char* val) { assert(val); assert(list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING || - list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF || list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING) { entry.stringVal = celix_utils_strdup(val); if (entry.stringVal == NULL) { - return CELIX_ENOMEM; + return ENOMEM; } } else { entry.stringVal = val; @@ -389,7 +402,7 @@ celix_status_t celix_arrayList_addVersion(celix_array_list_t* list, const celix_ if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION) { entry.versionVal = celix_version_copy(value); if (entry.versionVal == NULL) { - return CELIX_ENOMEM; + return ENOMEM; } } else { entry.versionVal = value; @@ -444,7 +457,6 @@ void celix_arrayList_remove(celix_array_list_t* list, void* ptr) { void celix_arrayList_removeString(celix_array_list_t* list, const char* val) { assert(list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING || - list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF || list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); @@ -535,6 +547,10 @@ bool celix_arrayList_equals(const celix_array_list_t* listA, const celix_array_l } celix_array_list_t* celix_arrayList_copy(const celix_array_list_t* list) { + if (!list) { + return NULL; + } + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; opts.elementType = list->elementType; opts.equalsCallback = list->equalsCallback; @@ -542,25 +558,32 @@ celix_array_list_t* celix_arrayList_copy(const celix_array_list_t* list) { opts.removedCallback = list->removedCallback; opts.removedCallbackData = list->removedCallbackData; opts.simpleRemovedCallback = list->simpleRemovedCallback; + opts.copyCallback = list->copyCallback; celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); if (!copy) { return NULL; } + celix_status_t status = celix_arrayList_ensureCapacity(copy, list->capacity); + if (status != CELIX_SUCCESS) { + return NULL; + } + for (int i = 0; i < celix_arrayList_size(list); ++i) { - celix_array_list_entry_t entry = list->elementData[i]; - celix_status_t status; - if (copy->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING) { - status = celix_arrayList_addString(copy, entry.stringVal); - } else if (copy->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION) { - status = celix_arrayList_addVersion(copy, entry.versionVal); + celix_array_list_entry_t entry; + if (list->copyCallback) { + memset(&entry, 0, sizeof(entry)); + status = list->copyCallback(list->elementData[i], &entry); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to copy entry"); + return NULL; + } } else { - status = celix_arrayList_addEntry(copy, entry); - } - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add entry to copy list."); - return NULL; + entry = list->elementData[i]; //shallow copy } + + (void)celix_arrayList_addEntry(copy, entry); // Cannot fail, because ensureCapacity is already called to + // ensure enough space } return celix_steal_ptr(copy);