From c28810e96780c9a82b6b4be728f731b8a1907a72 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 31 Dec 2022 16:58:58 +0100 Subject: [PATCH 01/21] Rename current properties imp to properties_v1.c --- libs/utils/CMakeLists.txt | 2 +- libs/utils/src/{properties.c => properties_v1.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename libs/utils/src/{properties.c => properties_v1.c} (100%) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 51fa47ca1..e8f51369a 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -36,7 +36,7 @@ add_library(utils SHARED src/celix_threads.c src/version.c src/version_range.c - src/properties.c + src/properties_v1.c src/utils.c src/ip_utils.c src/filter.c diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties_v1.c similarity index 100% rename from libs/utils/src/properties.c rename to libs/utils/src/properties_v1.c From 1ff603ba0702464c928bd9c22475bf834628f85b Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 1 Jan 2023 20:13:06 +0100 Subject: [PATCH 02/21] Refactor celix properties to be based on celix_string_hash_map. --- .../src/pubsub_discovery_impl.c | 2 +- .../src/endpoint_descriptor_writer.c | 13 +- .../src/remote_service_admin_dfi.c | 15 +- .../rsa_shm/src/rsa_shm_impl.c | 5 +- .../topology_manager/src/topology_manager.c | 3 +- .../topology_manager/tms_tst/tms_tests.cpp | 9 +- libs/framework/src/service_reference.c | 11 +- libs/utils/CMakeLists.txt | 10 +- libs/utils/gtest/CMakeLists.txt | 5 + .../resources}/properties.txt | 0 .../utils/gtest/src/CxxPropertiesTestSuite.cc | 16 + .../src/PropertiesTestSuite.cc} | 171 +++--- libs/utils/include/celix/Properties.h | 19 +- libs/utils/include/celix_long_hash_map.h | 4 + libs/utils/include/celix_properties.h | 243 +++++++- libs/utils/include/celix_string_hash_map.h | 5 + libs/utils/include_deprecated/properties.h | 7 +- libs/utils/src/properties.c | 523 ++++++++++++++++++ libs/utils/src/utils.c | 2 +- 19 files changed, 884 insertions(+), 179 deletions(-) rename libs/utils/{private/resources-test => gtest/resources}/properties.txt (100%) rename libs/utils/{private/test/properties_test.cpp => gtest/src/PropertiesTestSuite.cc} (62%) create mode 100644 libs/utils/src/properties.c diff --git a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c index c6d1aa954..4815bd920 100644 --- a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c +++ b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c @@ -558,7 +558,7 @@ static char* pubsub_discovery_createJsonEndpoint(const celix_properties_t *props json_t *jsEndpoint = json_object(); const char* propKey = NULL; - PROPERTIES_FOR_EACH((celix_properties_t*)props, propKey) { + CELIX_PROPERTIES_FOR_EACH(props, propKey) { const char* val = celix_properties_get(props, propKey, NULL); json_object_set_new(jsEndpoint, propKey, json_string(val)); } diff --git a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c index d2e2421fa..66580b5a6 100644 --- a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c +++ b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c @@ -139,15 +139,11 @@ static celix_status_t endpointDescriptorWriter_writeEndpoint(endpoint_descriptor } else { xmlTextWriterStartElement(writer->writer, ENDPOINT_DESCRIPTION); - hash_map_iterator_pt iter = hashMapIterator_create(endpoint->properties); - while (hashMapIterator_hasNext(iter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(iter); - - void* propertyName = hashMapEntry_getKey(entry); - const xmlChar* propertyValue = (const xmlChar*) hashMapEntry_getValue(entry); - + const char* propertyName; + CELIX_PROPERTIES_FOR_EACH(endpoint->properties, propertyName) { + const xmlChar* propertyValue = (const xmlChar*) celix_properties_get(endpoint->properties, propertyName, ""); xmlTextWriterStartElement(writer->writer, PROPERTY); - xmlTextWriterWriteAttribute(writer->writer, NAME, propertyName); + xmlTextWriterWriteAttribute(writer->writer, NAME, (const xmlChar*)propertyName); if (strcmp(OSGI_FRAMEWORK_OBJECTCLASS, (char*) propertyName) == 0) { // objectClass *must* be represented as array of string values... @@ -162,7 +158,6 @@ static celix_status_t endpointDescriptorWriter_writeEndpoint(endpoint_descriptor xmlTextWriterEndElement(writer->writer); } - hashMapIterator_destroy(iter); xmlTextWriterEndElement(writer->writer); } diff --git a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c index 31bddb24c..656b37734 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c @@ -723,10 +723,7 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic } } - hash_map_entry_pt entry = hashMap_getEntry(endpointProperties, (void *) OSGI_FRAMEWORK_SERVICE_ID); - - char* key = hashMapEntry_getKey(entry); - char *serviceId = (char *) hashMap_remove(endpointProperties, (void *) OSGI_FRAMEWORK_SERVICE_ID); + const char* serviceId = celix_properties_get(endpointProperties, CELIX_FRAMEWORK_SERVICE_ID, "-1"); const char *uuid = NULL; char buf[512]; @@ -750,12 +747,10 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic celix_properties_set(endpointProperties, RSA_DFI_ENDPOINT_URL, url); if (props != NULL) { - hash_map_iterator_pt propIter = hashMapIterator_create(props); - while (hashMapIterator_hasNext(propIter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(propIter); - celix_properties_set(endpointProperties, (char*)hashMapEntry_getKey(entry), (char*)hashMapEntry_getValue(entry)); + const char* key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + celix_properties_set(endpointProperties, key, celix_properties_get(props, key, "")); } - hashMapIterator_destroy(propIter); } *endpoint = calloc(1, sizeof(**endpoint)); @@ -769,8 +764,6 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic (*endpoint)->properties = endpointProperties; } - free(key); - free(serviceId); free(keys); return status; diff --git a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c index b72cb95db..2d8c2bc93 100755 --- a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c +++ b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c @@ -230,12 +230,12 @@ static void rsaShm_overlayProperties(celix_properties_t *additionalProperties, c * A property key in the additional properties map must therefore override any case variant property key in the properties of the specified Service Reference.*/ const char *additionalPropKey = NULL; const char *servicePropKey = NULL; - PROPERTIES_FOR_EACH(additionalProperties, additionalPropKey) { + CELIX_PROPERTIES_FOR_EACH(additionalProperties, additionalPropKey) { if (strcmp(additionalPropKey,(char*) OSGI_FRAMEWORK_OBJECTCLASS) != 0 && strcmp(additionalPropKey,(char*) OSGI_FRAMEWORK_SERVICE_ID) != 0) { bool propKeyCaseEqual = false; - PROPERTIES_FOR_EACH(serviceProperties, servicePropKey) { + CELIX_PROPERTIES_FOR_EACH(serviceProperties, servicePropKey) { if (strcasecmp(additionalPropKey,servicePropKey) == 0) { const char* val = celix_properties_get(additionalProperties,additionalPropKey,NULL); celix_properties_set(serviceProperties,servicePropKey,val); @@ -250,7 +250,6 @@ static void rsaShm_overlayProperties(celix_properties_t *additionalProperties, c } } } - return; } static bool rsaShm_isConfigTypeMatched(celix_properties_t *properties) { diff --git a/bundles/remote_services/topology_manager/src/topology_manager.c b/bundles/remote_services/topology_manager/src/topology_manager.c index f834ac525..691e72afc 100644 --- a/bundles/remote_services/topology_manager/src/topology_manager.c +++ b/bundles/remote_services/topology_manager/src/topology_manager.c @@ -402,8 +402,7 @@ celix_status_t topologyManager_importScopeChanged(void *handle, char *service_na hash_map_entry_pt entry = hashMapIterator_nextEntry(importedServicesIterator); endpoint = hashMapEntry_getKey(entry); - entry = hashMap_getEntry(endpoint->properties, (void *) OSGI_FRAMEWORK_OBJECTCLASS); - char* name = (char *) hashMapEntry_getValue(entry); + const char* name = celix_properties_get(endpoint->properties, OSGI_FRAMEWORK_OBJECTCLASS, ""); // Test if a service with the same name is imported if (strcmp(name, service_name) == 0) { found = true; diff --git a/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp b/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp index 92bc86b6e..72bfbd57f 100644 --- a/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp +++ b/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp @@ -397,8 +397,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void*)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); /* printf("Service: %s ", ep->service); @@ -433,8 +432,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void*)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); } printf("End: %s\n", __func__); @@ -458,8 +456,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void *)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); } printf("End: %s\n", __func__); diff --git a/libs/framework/src/service_reference.c b/libs/framework/src/service_reference.c index 183023514..a7faebd77 100644 --- a/libs/framework/src/service_reference.c +++ b/libs/framework/src/service_reference.c @@ -200,17 +200,14 @@ FRAMEWORK_EXPORT celix_status_t serviceReference_getPropertyKeys(service_referen properties_pt props = NULL; serviceRegistration_getProperties(ref->registration, &props); - hash_map_iterator_pt it; int i = 0; - int vsize = hashMap_size(props); + int vsize = celix_properties_size(props); *size = (unsigned int)vsize; *keys = malloc(vsize * sizeof(**keys)); - it = hashMapIterator_create(props); - while (hashMapIterator_hasNext(it)) { - (*keys)[i] = hashMapIterator_nextKey(it); - i++; + const char* key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + (*keys)[i++] = (char*)key; } - hashMapIterator_destroy(it); return status; } diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index e8f51369a..6de893b6d 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -36,7 +36,7 @@ add_library(utils SHARED src/celix_threads.c src/version.c src/version_range.c - src/properties_v1.c + src/properties.c src/utils.c src/ip_utils.c src/filter.c @@ -114,10 +114,6 @@ if (ENABLE_TESTING) target_include_directories(linked_list_test PRIVATE include_deprecated) target_link_libraries(linked_list_test Celix::utils CppUTest::CppUTest pthread) - add_executable(properties_test private/test/properties_test.cpp) - target_include_directories(properties_test PRIVATE include_deprecated) - target_link_libraries(properties_test CppUTest::CppUTest CppUTest::CppUTestExt Celix::utils pthread) - add_executable(utils_test private/test/utils_test.cpp) target_include_directories(utils_test PRIVATE include_deprecated) target_link_libraries(utils_test CppUTest::CppUTest Celix::utils pthread) @@ -134,13 +130,10 @@ if (ENABLE_TESTING) target_include_directories(version_test PRIVATE include_deprecated) target_link_libraries(version_test CppUTest::CppUTest Celix::utils pthread) - configure_file(private/resources-test/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) - add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) add_test(NAME run_celix_threads_test COMMAND celix_threads_test) add_test(NAME run_linked_list_test COMMAND linked_list_test) - add_test(NAME run_properties_test COMMAND properties_test) add_test(NAME run_utils_test COMMAND utils_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) add_test(NAME filter_test COMMAND filter_test) @@ -150,7 +143,6 @@ if (ENABLE_TESTING) setup_target_for_coverage(hash_map_test) setup_target_for_coverage(celix_threads_test) setup_target_for_coverage(linked_list_test) - setup_target_for_coverage(properties_test) setup_target_for_coverage(utils_test) setup_target_for_coverage(ip_utils_test) setup_target_for_coverage(filter_test) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 7440357cf..dee276f77 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(test_utils src/HashMapTestSuite.cc src/ArrayListTestSuite.cc src/FileUtilsTestSuite.cc + src/PropertiesTestSuite.cc ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) @@ -37,6 +38,10 @@ target_link_libraries(test_utils PRIVATE Celix::utils GTest::gtest GTest::gtest_ target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) celix_deprecated_utils_headers(test_utils) +####### Configure properties file for testing ######################################################################### +configure_file(resources/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) + + ####### generating zip file used for testing ########################################################################## file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/top.properties" CONTENT "level=1\n") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/subdir/sub.properties" CONTENT "level=2\n") diff --git a/libs/utils/private/resources-test/properties.txt b/libs/utils/gtest/resources/properties.txt similarity index 100% rename from libs/utils/private/resources-test/properties.txt rename to libs/utils/gtest/resources/properties.txt diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 01241b99a..d5acac775 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -59,6 +59,22 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) { EXPECT_EQ(5, count); } +TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) { + celix::Properties props0{}; + for (const auto& pair : props0) { + FAIL() << "Should not get an loop entry with a properties size of 0. got key: " << pair.first; + } + + celix::Properties props1{}; + props1.set("key1", "value1"); + int count = 0; + for (const auto& pair : props1) { + EXPECT_EQ(pair.first, "key1"); + count++; + } + EXPECT_EQ(1, count); +} + TEST_F(CxxPropertiesTestSuite, testCopy) { celix::Properties props{}; diff --git a/libs/utils/private/test/properties_test.cpp b/libs/utils/gtest/src/PropertiesTestSuite.cc similarity index 62% rename from libs/utils/private/test/properties_test.cpp rename to libs/utils/gtest/src/PropertiesTestSuite.cc index 613f6ef24..62bfa8dd1 100644 --- a/libs/utils/private/test/properties_test.cpp +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -16,74 +16,46 @@ * specific language governing permissions and limitations * under the License. */ -/** - * properties_test.cpp - * - * \date Feb 11, 2013 - * \author Apache Celix Project Team - * \copyright Apache License, Version 2.0 - */ - -#include -#include -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" -#include "CppUTestExt/MockSupport.h" +#include -extern "C" { -#include - -#include "properties.h" #include "celix_properties.h" -} -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} +using ::testing::MatchesRegex; -TEST_GROUP(properties) { - celix_properties_t *properties; - - void setup(void) { - } - - void teardown() { - mock().checkExpectations(); - mock().clear(); - } +class PropertiesTestSuite : public ::testing::Test { +public: }; -TEST(properties, create) { - properties = celix_properties_create(); - CHECK(properties); + +TEST_F(PropertiesTestSuite, create) { + auto* properties = celix_properties_create(); + EXPECT_TRUE(properties); celix_properties_destroy(properties); } -TEST(properties, load) { +TEST_F(PropertiesTestSuite, load) { char propertiesFile[] = "resources-test/properties.txt"; - properties = celix_properties_load(propertiesFile); - LONGS_EQUAL(4, hashMap_size(properties)); + auto* properties = celix_properties_load(propertiesFile); + EXPECT_EQ(4, celix_properties_size(properties)); const char keyA[] = "a"; - const char *valueA = celix_properties_get(properties, keyA, NULL); - STRCMP_EQUAL("b", valueA); + const char *valueA = celix_properties_get(properties, keyA, nullptr); + EXPECT_STREQ("b", valueA); const char keyNiceA[] = "nice_a"; - const char *valueNiceA = celix_properties_get(properties, keyNiceA, NULL); - STRCMP_EQUAL("nice_b", valueNiceA); + const char *valueNiceA = celix_properties_get(properties, keyNiceA, nullptr); + EXPECT_STREQ("nice_b", valueNiceA); const char keyB[] = "b"; - const char *valueB = celix_properties_get(properties, keyB, NULL); - STRCMP_EQUAL("c \t d", valueB); + const char *valueB = celix_properties_get(properties, keyB, nullptr); + EXPECT_STREQ("c \t d", valueB); celix_properties_destroy(properties); } -TEST(properties, asLong) { +TEST_F(PropertiesTestSuite, asLong) { celix_properties_t *props = celix_properties_create(); celix_properties_set(props, "t1", "42"); celix_properties_set(props, "t2", "-42"); @@ -92,59 +64,59 @@ TEST(properties, asLong) { celix_properties_set(props, "t5", "bla"); long v = celix_properties_getAsLong(props, "t1", -1); - LONGS_EQUAL(42, v); + EXPECT_EQ(42, v); v = celix_properties_getAsLong(props, "t2", -1); - LONGS_EQUAL(-42, v); + EXPECT_EQ(-42, v); v = celix_properties_getAsLong(props, "t3", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); v = celix_properties_getAsLong(props, "t4", -1); - LONGS_EQUAL(42, v); + EXPECT_EQ(42, v); v = celix_properties_getAsLong(props, "t5", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); v = celix_properties_getAsLong(props, "non-existing", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); celix_properties_destroy(props); } -TEST(properties, store) { +TEST_F(PropertiesTestSuite, store) { char propertiesFile[] = "resources-test/properties_out.txt"; - properties = celix_properties_create(); + auto* properties = celix_properties_create(); char keyA[] = "x"; char keyB[] = "y"; char valueA[] = "1"; char valueB[] = "2"; celix_properties_set(properties, keyA, valueA); celix_properties_set(properties, keyB, valueB); - celix_properties_store(properties, propertiesFile, NULL); + celix_properties_store(properties, propertiesFile, nullptr); celix_properties_destroy(properties); } -TEST(properties, copy) { +TEST_F(PropertiesTestSuite, copy) { char propertiesFile[] = "resources-test/properties.txt"; - properties = celix_properties_load(propertiesFile); - LONGS_EQUAL(4, hashMap_size(properties)); + auto* properties = celix_properties_load(propertiesFile); + EXPECT_EQ(4, celix_properties_size(properties)); celix_properties_t *copy = celix_properties_copy(properties); char keyA[] = "a"; - const char *valueA = celix_properties_get(copy, keyA, NULL); - STRCMP_EQUAL("b", valueA); + const char *valueA = celix_properties_get(copy, keyA, nullptr); + EXPECT_STREQ("b", valueA); const char keyB[] = "b"; - STRCMP_EQUAL("c \t d", celix_properties_get(copy, keyB, NULL)); + EXPECT_STREQ("c \t d", celix_properties_get(copy, keyB, nullptr)); celix_properties_destroy(properties); celix_properties_destroy(copy); } -TEST(properties, getSet) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, getSet) { + auto* properties = celix_properties_create(); char keyA[] = "x"; char keyB[] = "y"; char keyC[] = "z"; @@ -157,34 +129,34 @@ TEST(properties, getSet) { celix_properties_set(properties, keyB, valueB); celix_properties_setWithoutCopy(properties, keyD, valueD); - STRCMP_EQUAL(valueA, celix_properties_get(properties, keyA, NULL)); - STRCMP_EQUAL(valueB, celix_properties_get(properties, keyB, NULL)); - STRCMP_EQUAL(valueC, celix_properties_get(properties, keyC, valueC)); - STRCMP_EQUAL(valueD, celix_properties_get(properties, keyD, NULL)); + EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); + EXPECT_STREQ(valueB, celix_properties_get(properties, keyB, nullptr)); + EXPECT_STREQ(valueC, celix_properties_get(properties, keyC, valueC)); + EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); celix_properties_destroy(properties); } -TEST(properties, setUnset) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, setUnset) { + auto* properties = celix_properties_create(); char keyA[] = "x"; char *keyD = strndup("a", 1); char valueA[] = "1"; char *valueD = strndup("4", 1); celix_properties_set(properties, keyA, valueA); celix_properties_setWithoutCopy(properties, keyD, valueD); - STRCMP_EQUAL(valueA, celix_properties_get(properties, keyA, NULL)); - STRCMP_EQUAL(valueD, celix_properties_get(properties, keyD, NULL)); + EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); + EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); celix_properties_unset(properties, keyA); celix_properties_unset(properties, keyD); - POINTERS_EQUAL(NULL, celix_properties_get(properties, keyA, NULL)); - POINTERS_EQUAL(NULL, celix_properties_get(properties, "a", NULL)); + EXPECT_EQ(nullptr, celix_properties_get(properties, keyA, nullptr)); + EXPECT_EQ(nullptr, celix_properties_get(properties, "a", nullptr)); celix_properties_destroy(properties); } -TEST(properties, longTest) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, longTest) { + auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "2"); celix_properties_set(properties, "b", "-10032L"); @@ -197,24 +169,24 @@ TEST(properties, longTest) { long d = celix_properties_getAsLong(properties, "d", -1L); long e = celix_properties_getAsLong(properties, "e", -1L); - CHECK_EQUAL(2, a); - CHECK_EQUAL(-10032L, b); - CHECK_EQUAL(-1L, c); - CHECK_EQUAL(-1L, d); - CHECK_EQUAL(-1L, e); + EXPECT_EQ(2, a); + EXPECT_EQ(-10032L, b); + EXPECT_EQ(-1L, c); + EXPECT_EQ(-1L, d); + EXPECT_EQ(-1L, e); celix_properties_setLong(properties, "a", 3L); celix_properties_setLong(properties, "b", -4L); a = celix_properties_getAsLong(properties, "a", -1L); b = celix_properties_getAsLong(properties, "b", -1L); - CHECK_EQUAL(3L, a); - CHECK_EQUAL(-4L, b); + EXPECT_EQ(3L, a); + EXPECT_EQ(-4L, b); celix_properties_destroy(properties); } -TEST(properties, boolTest) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, boolTest) { + auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "true"); celix_properties_set(properties, "b", "false"); @@ -228,39 +200,40 @@ TEST(properties, boolTest) { bool e = celix_properties_getAsBool(properties, "e", false); bool f = celix_properties_getAsBool(properties, "f", true); - CHECK_EQUAL(true, a); - CHECK_EQUAL(false, b); - CHECK_EQUAL(true, c); - CHECK_EQUAL(true, d); - CHECK_EQUAL(false, e); - CHECK_EQUAL(true, f); + EXPECT_EQ(true, a); + EXPECT_EQ(false, b); + EXPECT_EQ(true, c); + EXPECT_EQ(true, d); + EXPECT_EQ(false, e); + EXPECT_EQ(true, f); celix_properties_setBool(properties, "a", true); celix_properties_setBool(properties, "b", false); a = celix_properties_getAsBool(properties, "a", false); b = celix_properties_getAsBool(properties, "b", true); - CHECK_EQUAL(true, a); - CHECK_EQUAL(false, b); + EXPECT_EQ(true, a); + EXPECT_EQ(false, b); celix_properties_destroy(properties); } -TEST(properties, sizeAndIteratorTest) { +TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_t *props = celix_properties_create(); - CHECK_EQUAL(0, celix_properties_size(props)); + EXPECT_EQ(0, celix_properties_size(props)); celix_properties_set(props, "a", "1"); celix_properties_set(props, "b", "2"); - CHECK_EQUAL(2, celix_properties_size(props)); + EXPECT_EQ(2, celix_properties_size(props)); celix_properties_set(props, "c", " 3 "); celix_properties_set(props, "d", "4"); - CHECK_EQUAL(4, celix_properties_size(props)); + EXPECT_EQ(4, celix_properties_size(props)); int count = 0; - const char *_key = NULL; - CELIX_PROPERTIES_FOR_EACH(props, _key) { + const char *key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + EXPECT_NE(key, nullptr); count++; } - CHECK_EQUAL(4, count); + EXPECT_EQ(4, count); celix_properties_destroy(props); } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 4e56860ce..3796b004a 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -54,14 +54,10 @@ namespace celix { } bool operator==(const celix::PropertiesIterator& rhs) const { - bool sameMap = iter._data1 == rhs.iter._data1; //map - bool sameIndex = iter._data5 == rhs.iter._data5; //index - bool oneIsEnd = end || rhs.end; - if (oneIsEnd) { - return sameMap && end && rhs.end; - } else { - return sameMap && sameIndex; + if (end || rhs.end) { + return end && rhs.end; } + return celix_propertiesIterator_equals(&iter, &rhs.iter); } bool operator!=(const celix::PropertiesIterator& rhs) const { @@ -81,15 +77,16 @@ namespace celix { } void moveToEnd() { - end = true; first = {}; second = {}; + end = true; } + //TODO for C++17 try to update first and second to stringview std::string first{}; std::string second{}; private: - celix_properties_iterator_t iter{nullptr, nullptr, nullptr, 0, 0}; + celix_properties_iterator_t iter{._data = {}}; bool end{false}; }; @@ -414,6 +411,10 @@ namespace celix { return convertToMap(); } #endif + + + //TODO save + //TODO load private: explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {} diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index 3ff1e906e..c704ab602 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -290,7 +290,11 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); * printf("Visiting hash map entry with key %li\n", inter.key); * } * @endcode + * + * @param map The (const celix_long_hash_map_t*) map to iterate over. + * @param iterName A iterName which will be of type celix_long_hash_map_iterator_t to hold the iterator. */ +//TODO test if the macro can be used nested #define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \ for (celix_long_hash_map_iterator_t iterName = celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); celix_longHashMapIterator_next(&(iterName))) diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 762fcff35..edd1b3cd4 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -17,6 +17,17 @@ * under the License. */ +/** + * @file celix_properties.h + * @brief Header file for the Celix Properties API. + * + * The Celix Properties API provides a means for storing and manipulating key-value pairs, called properties, + * which can be used to store configuration data or metadata for a service, component, or framework configuration. + * Functions are provided for creating and destroying property sets, loading and storing properties from/to a file + * or stream, and setting, getting, and unsetting individual properties. There are also functions for converting + * property values to various types (e.g. long, bool, double) and for iterating over the properties in a set. + */ + #include #include @@ -29,68 +40,262 @@ extern "C" { #endif -typedef struct hashMap celix_properties_t; //opaque struct, TODO try to make this a celix_properties struct +/** + * @brief celix_properties_t is a type that represents a set of key-value pairs called properties, + * which can be used to store configuration data or metadata for a services, components or framework configuration. + */ +typedef struct celix_properties celix_properties_t; + +//TODO rethink the iter. Maybe this can be more closely coupled to celix_string_hash_map? +//typedef struct celix_properties_iterator { +// //private data +// void* _data1; +// void* _data2; +// void* _data3; +// int _data4; +// int _data5; +//} celix_properties_iterator_t; +/** + * @brief Type representing an iterator for iterating over the properties in a property set. + */ typedef struct celix_properties_iterator { - //private data - void* _data1; - void* _data2; - void* _data3; - int _data4; - int _data5; + //private opaque data + char _data[56]; } celix_properties_iterator_t; -/********************************************************************************************************************** - ********************************************************************************************************************** - * Updated API - ********************************************************************************************************************** - **********************************************************************************************************************/ - +/** + * @brief Creates a new empty property set. + * @return A new empty property set. + */ celix_properties_t* celix_properties_create(void); +/** + * @brief Destroys a property set, freeing all associated resources. + * + * @param properties The property set to destroy. If properties is NULL, this function will do nothing. + */ void celix_properties_destroy(celix_properties_t *properties); +/** + * @brief Loads properties from a file. + * + * @param filename The name of the file to load properties from. + * @return A property set containing the properties from the file. + * @retval NULL If an error occurred (e.g. file not found). + */ celix_properties_t* celix_properties_load(const char *filename); + +/** + * @brief Loads properties from a stream. + * + * @param stream The stream to load properties from. + * @return A property set containing the properties from the stream. + * @retval NULL If an error occurred (e.g. invalid format). + */ celix_properties_t* celix_properties_loadWithStream(FILE *stream); +/** + * @brief Loads properties from a string. + * + * @param input The string to load properties from. + * @return A property set containing the properties from the string. + * @retval NULL If an error occurred (e.g. invalid format). + */ celix_properties_t* celix_properties_loadFromString(const char *input); -void celix_properties_store(celix_properties_t *properties, const char *file, const char *header); +/** + * @brief Stores properties to a file. + * + * @param properties The property set to store. + * @param file The name of the file to store the properties to. + * @param header An optional header to write to the file before the properties. + */ +void celix_properties_store(celix_properties_t *properties, const char *file, const char *header); //TODO add return status +/** + * @brief Gets the value of a property. + * + * @param properties The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set. + * @return The value of the property, or the default value if the property is not set. + */ const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue); +/** + * @brief Sets the value of a property. + * + * + * @param properties The property set to modify. + * @param key The key of the property to set. + * @param value The value to set the property to. + */ void celix_properties_set(celix_properties_t *properties, const char *key, const char *value); +/** + * @brief Sets the value of a property without copying the key and value strings. + * + * @param properties The property set to modify. + * @param key The key of the property to set. This string will be used directly, so it must not be freed or modified + * after calling this function. + * @param value The value to set the property to. This string will be used directly, so it must not be freed or + * modified after calling this function. + */ void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value); +/** + * @brief Unsets a property, removing it from the property set. + * @param properties The property set to modify. + * @param key The key of the property to unset. + */ void celix_properties_unset(celix_properties_t *properties, const char *key); +/** + * @brief Makes a copy of a property set. + * + * @param properties The property set to copy. + * @return A copy of the given property set. + */ celix_properties_t* celix_properties_copy(const celix_properties_t *properties); +/** + * @brief Gets the value of a property as a long integer. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a long. + * @return The value of the property as a long, or the default value if the property is not set or cannot be converted + * to a long. + */ long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue); + +/** + * @brief Sets the value of a property to a long integer. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param value The long value to set the property to. + */ void celix_properties_setLong(celix_properties_t *props, const char *key, long value); +/** + * @brief Gets the value of a property as a boolean. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a boolean. + * @return The value of the property as a boolean, or the default value if the property is not set or cannot be + * converted to a boolean. + */ bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue); -void celix_properties_setBool(celix_properties_t *props, const char *key, bool val); +/** + * @brief Sets the value of a property to a boolean. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param val The boolean value to set the property to. + */ +void celix_properties_setBool(celix_properties_t *props, const char *key, bool val); +/** + * @brief Sets the value of a property to a double. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param val The double value to set the property to. + */ void celix_properties_setDouble(celix_properties_t *props, const char *key, double val); + +/** + * @brief Gets the value of a property as a double. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a double. + * @return The value of the property as a double, or the default value if the property is not set or cannot be converted to a double. + */ double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue); +/** + * @brief Gets the number of properties in a property set. + * + * @param properties The property set to get the size of. + * @return The number of properties in the property set. + */ int celix_properties_size(const celix_properties_t *properties); +/** + * @brief Constructs a new iterator for iterating over the properties in a property set. + * + * @param properties The property set to iterate over. + * @return A new iterator for the given property set. + */ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties); + +/** + * @brief Determines whether the iterator has a next property. + * + * @param iter The iterator to check. + * @return true if the iterator has a next property, false otherwise. + */ bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); + +/** + * @brief Gets the next key in the iterator. + * @param iter The iterator to get the next key from. + * @return The next key in the iterator. + */ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter); -celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter); -#define CELIX_PROPERTIES_FOR_EACH(props, key) \ - for(celix_properties_iterator_t iter = celix_propertiesIterator_construct(props); \ - celix_propertiesIterator_hasNext(&iter), (key) = celix_propertiesIterator_nextKey(&iter);) +/** + * @brief Gets the property set being iterated over. + * + * @param iter The iterator to get the property set from. + * @return The property set being iterated over. + */ +celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter); +/** + * @brief Determines whether two iterators are equal. + * + * @param a The first iterator to compare. + * @param b The second iterator to compare. + * @return true if the iterators are equal, false otherwise. + */ +bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); +/** + * @brief Macro for iterating over the properties in a property set. + * + * @param props The property set to iterate over. + * @param key The variable to use for the current key in the loop. + * + * Example usage: + * @code{.c} + * celix_properties_t *props = celix_properties_create(); + * celix_properties_set(props, "key1", "value1"); + * celix_properties_set(props, "key2", "value2"); + * celix_properties_set(props, "key3", "value3"); + * + * const char* key; + * CELIX_PROPERTIES_FOR_EACH(props, key) { + * printf("%s = %s\n", key, celix_properties_get(props, key, "")); + * } + * @endcode + * Output: + * @code{.c} + * key1 = value1 + * key2 = value2 + * key3 = value3 + * @endcode +*/ +#define CELIX_PROPERTIES_FOR_EACH(props, key) \ + for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(props); \ + celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) #ifdef __cplusplus } diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index c402be4c5..97c1735b6 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -291,6 +291,7 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); /** * @brief Marco to loop over all the entries of a string hash map. * + * * Small example of how to use the iterate macro: * @code * celix_string_hash_map_t* map = ... @@ -298,7 +299,11 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); * printf("Visiting hash map entry with key %s\n", inter.key); * } * @endcode + * + * @param map The (const celix_string_hash_map_t*) map to iterate over. + * @param iterName A iterName which will be of type celix_string_hash_map_iterator_t to hold the iterator. */ +//TODO test if the macro can be used nested #define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \ for (celix_string_hash_map_iterator_t iterName = celix_stringHashMap_begin(map); !celix_stringHashMapIterator_isEnd(&(iterName)); celix_stringHashMapIterator_next(&(iterName))) diff --git a/libs/utils/include_deprecated/properties.h b/libs/utils/include_deprecated/properties.h index 00e9c785a..8bcbc3db8 100644 --- a/libs/utils/include_deprecated/properties.h +++ b/libs/utils/include_deprecated/properties.h @@ -37,8 +37,8 @@ extern "C" { #endif -typedef hash_map_pt properties_pt __attribute__((deprecated("properties is deprecated use celix_properties instead"))); -typedef hash_map_t properties_t __attribute__((deprecated("properties is deprecated use celix_properties instead"))); +typedef struct celix_properties* properties_pt __attribute__((deprecated("properties is deprecated use celix_properties instead"))); +typedef struct celix_properties properties_t __attribute__((deprecated("properties is deprecated use celix_properties instead"))); UTILS_EXPORT celix_properties_t* properties_create(void); @@ -62,10 +62,11 @@ UTILS_EXPORT void properties_unset(celix_properties_t *properties, const char *k UTILS_EXPORT celix_status_t properties_copy(celix_properties_t *properties, celix_properties_t **copy); +/** TODO refactor #define PROPERTIES_FOR_EACH(props, key) \ for(hash_map_iterator_t iter = hashMapIterator_construct(props); \ hashMapIterator_hasNext(&iter), (key) = (const char*)hashMapIterator_nextKey(&iter);) - +*/ #ifdef __cplusplus } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c new file mode 100644 index 000000000..a83211248 --- /dev/null +++ b/libs/utils/src/properties.c @@ -0,0 +1,523 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "properties.h" +#include "celix_properties.h" + +#include +#include +#include +#include +#include +#include + +#include "celix_build_assert.h" +#include "utils.h" //TODO try to remove +#include "celix_utils.h" +#include "celix_string_hash_map.h" + + +#define PROPERTIES_INITIAL_HASHMAP_CAPACITY 10 +#define SHORT_PROPERTIES_OPTIMIZATION_SIZE 1024 + +struct celix_properties { + celix_string_hash_map_t* map; + + /** + * buffer used to store the first key/value entries so that no additional memory allocation is needed. + */ + char buffer[SHORT_PROPERTIES_OPTIMIZATION_SIZE]; +}; + + +#define MALLOC_BLOCK_SIZE 5 + +static void parseLine(const char* line, celix_properties_t *props); + +properties_pt properties_create(void) { + return celix_properties_create(); +} + +void properties_destroy(properties_pt properties) { + celix_properties_destroy(properties); +} + +properties_pt properties_load(const char* filename) { + return celix_properties_load(filename); +} + +properties_pt properties_loadWithStream(FILE *file) { + return celix_properties_loadWithStream(file); +} + +properties_pt properties_loadFromString(const char *input){ + return celix_properties_loadFromString(input); +} + + +/** + * Header is ignored for now, cannot handle comments yet + */ +void properties_store(properties_pt properties, const char* filename, const char* header) { + return celix_properties_store(properties, filename, header); +} + +celix_status_t properties_copy(properties_pt properties, properties_pt *out) { + celix_properties_t *copy = celix_properties_copy(properties); + *out = copy; + return copy == NULL ? CELIX_BUNDLE_EXCEPTION : CELIX_SUCCESS; +} + +const char* properties_get(properties_pt properties, const char* key) { + return celix_properties_get(properties, key, NULL); +} + +const char* properties_getWithDefault(properties_pt properties, const char* key, const char* defaultValue) { + return celix_properties_get(properties, key, defaultValue); +} + +void properties_set(properties_pt properties, const char* key, const char* value) { + celix_properties_set(properties, key, value); +} + +void properties_unset(properties_pt properties, const char* key) { + celix_properties_unset(properties, key); +} + +static void updateBuffers(char **key, char ** value, char **output, int outputPos, int *key_len, int *value_len) { + if (*output == *key) { + if (outputPos == (*key_len) - 1) { + (*key_len) += MALLOC_BLOCK_SIZE; + *key = realloc(*key, *key_len); + *output = *key; + } + } + else { + if (outputPos == (*value_len) - 1) { + (*value_len) += MALLOC_BLOCK_SIZE; + *value = realloc(*value, *value_len); + *output = *value; + } + } +} + +static void parseLine(const char* line, celix_properties_t *props) { + int linePos = 0; + bool precedingCharIsBackslash = false; + bool isComment = false; + int outputPos = 0; + char *output = NULL; + int key_len = MALLOC_BLOCK_SIZE; + int value_len = MALLOC_BLOCK_SIZE; + linePos = 0; + precedingCharIsBackslash = false; + isComment = false; + output = NULL; + outputPos = 0; + + //Ignore empty lines + if (line[0] == '\n' && line[1] == '\0') { + return; + } + + char *key = calloc(1, key_len); + char *value = calloc(1, value_len); + key[0] = '\0'; + value[0] = '\0'; + + while (line[linePos] != '\0') { + if (line[linePos] == ' ' || line[linePos] == '\t') { + if (output == NULL) { + //ignore + linePos += 1; + continue; + } + } + else { + if (output == NULL) { + output = key; + } + } + if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') { + if (precedingCharIsBackslash) { + //escaped special character + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + precedingCharIsBackslash = false; + } + else { + if (line[linePos] == '#' || line[linePos] == '!') { + if (outputPos == 0) { + isComment = true; + break; + } + else { + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + } + else { // = or : + if (output == value) { //already have a seperator + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + else { + output[outputPos++] = '\0'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + output = value; + outputPos = 0; + } + } + } + } + else if (line[linePos] == '\\') { + if (precedingCharIsBackslash) { //double backslash -> backslash + output[outputPos++] = '\\'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + precedingCharIsBackslash = true; + } + else { //normal character + precedingCharIsBackslash = false; + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + linePos += 1; + } + if (output != NULL) { + output[outputPos] = '\0'; + } + + if (!isComment) { + //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); + celix_properties_set(props, utils_stringTrim(key), utils_stringTrim(value)); + } + if(key) { + free(key); + } + if(value) { + free(value); + } + +} + + + +/********************************************************************************************************************** + ********************************************************************************************************************** + * Updated API + ********************************************************************************************************************** + **********************************************************************************************************************/ + +static void celix_properties_removeEntryCallback(void* handle, const char* key, celix_hash_map_value_t val) { + //celix_properties_t* props = handle; + //TODO add check of short properties optimization buffer + free((char*)key); + free(val.ptrValue); +} + + +celix_properties_t* celix_properties_create(void) { + celix_properties_t* props = malloc(sizeof(*props)); + if (props != NULL) { + celix_string_hash_map_create_options_t opts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS; + opts.storeKeysWeakly = true; + opts.initialCapacity = PROPERTIES_INITIAL_HASHMAP_CAPACITY; + opts.removedCallbackData = props; + opts.removedCallback = celix_properties_removeEntryCallback; + props->map = celix_stringHashMap_createWithOptions(&opts); + } + return props; +} + +void celix_properties_destroy(celix_properties_t *props) { + if (props != NULL) { + celix_stringHashMap_destroy(props->map); + free(props); + } +} + +celix_properties_t* celix_properties_load(const char *filename) { + FILE *file = fopen(filename, "r"); + if (file == NULL) { + return NULL; + } + celix_properties_t *props = celix_properties_loadWithStream(file); + fclose(file); + return props; +} + +celix_properties_t* celix_properties_loadWithStream(FILE *file) { + celix_properties_t *props = NULL; + + if (file != NULL ) { + char *saveptr; + char *filebuffer = NULL; + char *line = NULL; + size_t file_size = 0; + + props = celix_properties_create(); + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size > 0) { + filebuffer = calloc(file_size + 1, sizeof(char)); + if (filebuffer) { + size_t rs = fread(filebuffer, sizeof(char), file_size, file); + if (rs != file_size) { + fprintf(stderr,"fread read only %lu bytes out of %lu\n", (long unsigned int) rs, (long unsigned int) file_size); + } + filebuffer[file_size]='\0'; + line = strtok_r(filebuffer, "\n", &saveptr); + while (line != NULL) { + parseLine(line, props); + line = strtok_r(NULL, "\n", &saveptr); + } + free(filebuffer); + } + } + } + + return props; +} + +celix_properties_t* celix_properties_loadFromString(const char *input) { + celix_properties_t *props = celix_properties_create(); + + char *in = strdup(input); + char *line = NULL; + char *saveLinePointer = NULL; + + bool firstTime = true; + do { + if (firstTime){ + line = strtok_r(in, "\n", &saveLinePointer); + firstTime = false; + }else { + line = strtok_r(NULL, "\n", &saveLinePointer); + } + + if (line == NULL){ + break; + } + + parseLine(line, props); + } while(line != NULL); + + free(in); + + return props; +} + +/** + * @brief Store properties string to file and escape the characters '#', '!', '=' and ':' if encountered. + */ +static void celix_properties_storeEscapedString(FILE* file, const char* str) { + for (int i = 0; i < strlen(str); i += 1) { + if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') { + fputc('\\', file); + } + fputc(str[i], file); + } +} + +void celix_properties_store(celix_properties_t *properties, const char *filename, const char *header) { + FILE *file = fopen (filename, "w+" ); + + if (file == NULL) { + perror("File is null"); + return; + } + + CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { + const char* val = iter.value.ptrValue; + celix_properties_storeEscapedString(file, iter.key); + fputc('=', file); + celix_properties_storeEscapedString(file, val); + fputc('\n', file); + + } + fclose(file); +} + +celix_properties_t* celix_properties_copy(const celix_properties_t *properties) { + celix_properties_t *copy = celix_properties_create(); + if (properties == NULL) { + return copy; + } + + CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { + celix_properties_set(copy, iter.key, iter.value.ptrValue); + } + return copy; +} + +const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue) { + const char* value = NULL; + if (properties != NULL) { + value = celix_stringHashMap_get(properties->map, key); + } + return value == NULL ? defaultValue : value; +} + +void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { + if (properties != NULL) { + celix_stringHashMap_put(properties->map, celix_utils_strdup(key), celix_utils_strdup(value)); + } +} + +void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) { + if (properties != NULL) { + if (properties != NULL) { + celix_stringHashMap_put(properties->map, key, value); + } + } +} + +void celix_properties_unset(celix_properties_t *properties, const char *key) { + if (properties != NULL) { + celix_stringHashMap_remove(properties->map, key); + } +} + +long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue) { + long result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char *enptr = NULL; + errno = 0; + long r = strtol(val, &enptr, 10); + if (enptr != val && errno == 0) { + result = r; + } + } + return result; +} + +void celix_properties_setLong(celix_properties_t *props, const char *key, long value) { + char buf[32]; //should be enough to store long long int + int writen = snprintf(buf, 32, "%li", value); + if (writen <= 31) { + celix_properties_set(props, key, buf); + } else { + fprintf(stderr,"buf to small for value '%li'\n", value); + } +} + +double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue) { + double result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char *enptr = NULL; + errno = 0; + double r = strtod(val, &enptr); + if (enptr != val && errno == 0) { + result = r; + } + } + return result; +} + +void celix_properties_setDouble(celix_properties_t *props, const char *key, double val) { + char buf[32]; //should be enough to store long long int + int writen = snprintf(buf, 32, "%f", val); + if (writen <= 31) { + celix_properties_set(props, key, buf); + } else { + fprintf(stderr,"buf to small for value '%f'\n", val); + } +} + +bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue) { + bool result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char buf[32]; + snprintf(buf, 32, "%s", val); + char *trimmed = utils_stringTrim(buf); + if (strncasecmp("true", trimmed, strlen("true")) == 0) { + result = true; + } else if (strncasecmp("false", trimmed, strlen("false")) == 0) { + result = false; + } + } + return result; +} + +void celix_properties_setBool(celix_properties_t *props, const char *key, bool val) { + celix_properties_set(props, key, val ? "true" : "false"); +} + +int celix_properties_size(const celix_properties_t *properties) { + return (int)celix_stringHashMap_size(properties->map); +} + +typedef struct { + celix_string_hash_map_iterator_t mapIter; + const celix_properties_t* props; +} celix_properties_iterator_internal_t; + +celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties) { + CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(celix_properties_iterator_t)); + celix_properties_iterator_internal_t internalIter; + internalIter.mapIter = celix_stringHashMap_begin(properties->map); + internalIter.props = properties; + + celix_properties_iterator_t iter; + memset(&iter._data, 0, sizeof(iter._data)); + memcpy(iter._data, &internalIter, sizeof(internalIter)); + return iter; +} + +bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter); +} + +const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + + //note assigning key first and then move the next, because celix string hash map iter start at the beginning + const char* key = internalIter.mapIter.key; + celix_stringHashMapIterator_next(&internalIter.mapIter); + + memcpy(iter, &internalIter, sizeof(internalIter)); + return key; +} + +bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b) { + celix_properties_iterator_internal_t internalIterA; + memcpy(&internalIterA, a, sizeof(internalIterA)); + celix_properties_iterator_internal_t internalIterB; + memcpy(&internalIterB, b, sizeof(internalIterB)); + return internalIterA.props == internalIterB.props && + internalIterA.mapIter.key == internalIterB.mapIter.key; +} + + +//TODO can the return be const? +//TODO and can this be removed -> look into C++ PropetiesIter +celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + return (celix_properties_t*)internalIter.props; +} diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c index 4ebb938c5..7bbe2f4c6 100644 --- a/libs/utils/src/utils.c +++ b/libs/utils/src/utils.c @@ -60,7 +60,7 @@ bool celix_utils_stringEquals(const char* a, const char* b) { } else if (a == NULL || b == NULL) { return false; } else { - return strncmp(a, b, 1024*124*10) == 0; + return strncmp(a, b, CELIX_UTILS_MAX_STRLEN) == 0; } } From 6156c7028b951d4e191653d75b015a3a6f373997 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Tue, 3 Jan 2023 00:00:25 +0100 Subject: [PATCH 03/21] Add initial primitive type support for celix properties entries --- libs/utils/CMakeLists.txt | 6 - libs/utils/gtest/CMakeLists.txt | 1 + libs/utils/gtest/src/PropertiesTestSuite.cc | 142 +++++++ libs/utils/gtest/src/VersionTestSuite.cc | 395 ++++++++++++++++++ libs/utils/include/celix/Properties.h | 2 +- libs/utils/include/celix_properties.h | 295 +++++++++++--- libs/utils/include/celix_version.h | 71 +++- libs/utils/private/test/version_test.cpp | 414 ------------------- libs/utils/src/properties.c | 431 ++++++++++++++++---- libs/utils/src/version.c | 9 + 10 files changed, 1185 insertions(+), 581 deletions(-) create mode 100644 libs/utils/gtest/src/VersionTestSuite.cc delete mode 100644 libs/utils/private/test/version_test.cpp diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 6de893b6d..475b7da35 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -126,10 +126,6 @@ if (ENABLE_TESTING) target_include_directories(filter_test PRIVATE include_deprecated) target_link_libraries(filter_test CppUTest::CppUTest Celix::utils pthread) - add_executable(version_test private/test/version_test.cpp) - target_include_directories(version_test PRIVATE include_deprecated) - target_link_libraries(version_test CppUTest::CppUTest Celix::utils pthread) - add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) add_test(NAME run_celix_threads_test COMMAND celix_threads_test) @@ -137,7 +133,6 @@ if (ENABLE_TESTING) add_test(NAME run_utils_test COMMAND utils_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) add_test(NAME filter_test COMMAND filter_test) - add_test(NAME version_test COMMAND version_test) setup_target_for_coverage(array_list_test) setup_target_for_coverage(hash_map_test) @@ -146,7 +141,6 @@ if (ENABLE_TESTING) setup_target_for_coverage(utils_test) setup_target_for_coverage(ip_utils_test) setup_target_for_coverage(filter_test) - setup_target_for_coverage(version_test) else () message(WARNING "Cannot find CppUTest, deprecated cpputest-based unit test will not be added") endif () #end CppUTest_FOUND diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index dee276f77..1e512fe61 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(test_utils src/ArrayListTestSuite.cc src/FileUtilsTestSuite.cc src/PropertiesTestSuite.cc + src/VersionTestSuite.cc ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 62bfa8dd1..b29802823 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -137,6 +137,22 @@ TEST_F(PropertiesTestSuite, getSet) { celix_properties_destroy(properties); } +TEST_F(PropertiesTestSuite, getSetWithNULL) { + auto* properties = celix_properties_create(); + + celix_properties_set(properties, nullptr, "value"); + EXPECT_EQ(celix_properties_size(properties), 0); //NULL key will be ignored + + celix_properties_set(properties, nullptr, nullptr); + EXPECT_EQ(celix_properties_size(properties), 0); //NULL key will be ignored + + celix_properties_set(properties, "key", nullptr); + EXPECT_EQ(celix_properties_size(properties), 0); //NULL value will be ignored + + celix_properties_destroy(properties); +} + + TEST_F(PropertiesTestSuite, setUnset) { auto* properties = celix_properties_create(); char keyA[] = "x"; @@ -237,3 +253,129 @@ TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_destroy(props); } + +TEST_F(PropertiesTestSuite, getType) { + auto* props = celix_properties_create(); + celix_properties_set(props, "string", "value"); + celix_properties_setLong(props, "long", 123); + celix_properties_setDouble(props, "double", 3.14); + celix_properties_setBool(props, "bool", true); + auto* version = celix_version_createVersion(1, 2, 3, nullptr); + celix_properties_setVersion(props, "version", version); + + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, celix_properties_getType(props, "string")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, celix_properties_getType(props, "long")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(props, "double")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, celix_properties_getType(props, "bool")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props, "version")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, celix_properties_getType(props, "missing")); + + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, getEntry) { + auto *props = celix_properties_create(); + celix_properties_set(props, "key1", "value1"); + celix_properties_setLong(props, "key2", 123); + celix_properties_setDouble(props, "key3", 123.456); + celix_properties_setBool(props, "key4", true); + auto *version = celix_version_createVersion(1, 2, 3, nullptr); + celix_properties_setVersion(props, "key5", version); + + auto entry = celix_properties_getEntry(props, "key1"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, entry.valueType); + EXPECT_STREQ("value1", entry.value); + EXPECT_STREQ("value1", entry.typed.strValue); + + entry = celix_properties_getEntry(props, "key2"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, entry.valueType); + EXPECT_STREQ("123", entry.value); + EXPECT_EQ(123, entry.typed.longValue); + + entry = celix_properties_getEntry(props, "key3"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, entry.valueType); + EXPECT_NE(strstr(entry.value, "123.456"), nullptr); + EXPECT_DOUBLE_EQ(123.456, entry.typed.doubleValue); + + entry = celix_properties_getEntry(props, "key4"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, entry.valueType); + EXPECT_STREQ("true", entry.value); + EXPECT_TRUE(entry.typed.boolValue); + + entry = celix_properties_getEntry(props, "key5"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, entry.valueType); + EXPECT_STREQ("1.2.3", entry.value); + EXPECT_EQ(1, celix_version_getMajor(entry.typed.versionValue)); + EXPECT_EQ(2, celix_version_getMinor(entry.typed.versionValue)); + EXPECT_EQ(3, celix_version_getMicro(entry.typed.versionValue)); + + entry = celix_properties_getEntry(props, "key6"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, entry.valueType); + + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, iteratorNextKey) { + auto *props = celix_properties_create(); + celix_properties_set(props, "key1", "value1"); + celix_properties_set(props, "key2", "value2"); + celix_properties_set(props, "key3", "value3"); + auto iter = celix_propertiesIterator_construct(props); + const char* key; + int count = 0; + while (celix_propertiesIterator_hasNext(&iter)) { + key = celix_propertiesIterator_nextKey(&iter); + EXPECT_NE(strstr(key, "key"), nullptr); + count++; + } + EXPECT_EQ(count, 3); + key = celix_propertiesIterator_nextKey(&iter); + EXPECT_EQ(nullptr, key) << "got key: " << key; + + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, iteratorNext) { + auto *props = celix_properties_create(); + celix_properties_set(props, "key1", "value1"); + celix_properties_set(props, "key2", "value2"); + celix_properties_set(props, "key3", "value3"); + + int count = 0; + auto iter = celix_properties_begin(props); + while (!celix_propertiesIterator_isEnd(&iter)) { + EXPECT_NE(strstr(iter.entry.key, "key"), nullptr); + EXPECT_NE(strstr(iter.entry.value, "value"), nullptr); + count++; + celix_propertiesIterator_next(&iter); + } + EXPECT_EQ(count, 3); + celix_propertiesIterator_next(&iter); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, iter.entry.valueType); + + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, iterateOverProperties) { + celix_properties_t* props = celix_properties_create(); + celix_properties_set(props, "key1", "value1"); + celix_properties_set(props, "key2", "value2"); + + int outerCount = 0; + int innerCount = 0; + CELIX_PROPERTIES_ITERATE(props, outerIter) { + outerCount++; + EXPECT_NE(strstr(outerIter.entry.key, "key"), nullptr); + + // Inner loop to test nested iteration + CELIX_PROPERTIES_ITERATE(props, innerIter) { + innerCount++; + EXPECT_NE(strstr(innerIter.entry.key, "key"), nullptr); + } + } + // Check that both entries were iterated over + EXPECT_EQ(outerCount, 2); + EXPECT_EQ(innerCount, 4); + + celix_properties_destroy(props); +} \ No newline at end of file diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc new file mode 100644 index 000000000..a0874ecf7 --- /dev/null +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -0,0 +1,395 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "celix_errno.h" +#include "celix_version.h" +#include "celix_utils.h" +#include "version.h" + +extern "C" +{ +#include "version_private.h" +} + +class VersionTestSuite : public ::testing::Test {}; + +TEST_F(VersionTestSuite, create) { + celix_version_t* version = nullptr; + char * str; + + str = celix_utils_strdup("abc"); + EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + EXPECT_STREQ("abc", version->qualifier); + + version_destroy(version); + version = nullptr; + EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, nullptr, &version)); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + EXPECT_STREQ("", version->qualifier); + + version_destroy(version); + version = nullptr; + free(str); + str = celix_utils_strdup("abc"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, &version)); + + version_destroy(version); + version = nullptr; + free(str); + str = celix_utils_strdup("abc|xyz"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, &version)); + + version_destroy(version); + free(str); +} + +TEST_F(VersionTestSuite, clone) { + celix_version_t* version = nullptr; + celix_version_t* clone = nullptr; + char * str; + + str = celix_utils_strdup("abc"); + EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); + EXPECT_EQ(CELIX_SUCCESS, version_clone(version, &clone)); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, clone->major); + EXPECT_EQ(2, clone->minor); + EXPECT_EQ(3, clone->micro); + EXPECT_STREQ("abc", clone->qualifier); + + version_destroy(clone); + version_destroy(version); + free(str); +} + +TEST_F(VersionTestSuite, createFromString) { + celix_version_t* version = nullptr; + celix_status_t status = CELIX_SUCCESS; + char * str; + + str = celix_utils_strdup("1"); + EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version)); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + + version_destroy(version); + + free(str); + str = celix_utils_strdup("a"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); + + free(str); + str = celix_utils_strdup("1.a"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); + + free(str); + str = celix_utils_strdup("1.1.a"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); + + free(str); + str = celix_utils_strdup("-1"); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); + + free(str); + str = celix_utils_strdup("1.2"); + version = nullptr; + EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version)); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + + version_destroy(version); + + free(str); + str = celix_utils_strdup("1.2.3"); + version = nullptr; + status = version_createVersionFromString(str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + + version_destroy(version); + free(str); + str = celix_utils_strdup("1.2.3.abc"); + version = nullptr; + status = version_createVersionFromString(str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + EXPECT_STREQ("abc", version->qualifier); + + version_destroy(version); + free(str); + str = celix_utils_strdup("1.2.3.abc_xyz"); + version = nullptr; + status = version_createVersionFromString(str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + EXPECT_STREQ("abc_xyz", version->qualifier); + + version_destroy(version); + free(str); + str = celix_utils_strdup("1.2.3.abc-xyz"); + version = nullptr; + status = version_createVersionFromString(str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(1, version->major); + EXPECT_EQ(2, version->minor); + EXPECT_EQ(3, version->micro); + EXPECT_STREQ("abc-xyz", version->qualifier); + + version_destroy(version); + free(str); + str = celix_utils_strdup("1.2.3.abc|xyz"); + status = version_createVersionFromString(str, &version); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, status); + + free(str); +} + +TEST_F(VersionTestSuite, createEmptyVersion) { + celix_version_t* version = nullptr; + celix_status_t status = CELIX_SUCCESS; + + status = version_createEmptyVersion(&version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + EXPECT_EQ(0, version->major); + EXPECT_EQ(0, version->minor); + EXPECT_EQ(0, version->micro); + EXPECT_STREQ("", version->qualifier); + + version_destroy(version); +} + +TEST_F(VersionTestSuite, getters) { + celix_version_t* version = nullptr; + celix_status_t status = CELIX_SUCCESS; + char * str; + int major, minor, micro; + const char *qualifier; + + str = celix_utils_strdup("abc"); + status = version_createVersion(1, 2, 3, str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + + version_getMajor(version, &major); + EXPECT_EQ(1, major); + + version_getMinor(version, &minor); + EXPECT_EQ(2, minor); + + version_getMicro(version, µ); + EXPECT_EQ(3, micro); + + version_getQualifier(version, &qualifier); + EXPECT_STREQ("abc", qualifier); + + version_destroy(version); + free(str); +} + +TEST_F(VersionTestSuite, compare) { + celix_version_t* version = nullptr; + celix_version_t* compare = nullptr; + celix_status_t status = CELIX_SUCCESS; + char * str; + int result; + + // Base version to compare + str = celix_utils_strdup("abc"); + status = version_createVersion(1, 2, 3, str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + + // Compare equality + free(str); + str = celix_utils_strdup("abc"); + compare = nullptr; + status = version_createVersion(1, 2, 3, str, &compare); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + status = version_compareTo(version, compare, &result); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_EQ(0, result); + + // Compare against a higher version + free(str); + str = celix_utils_strdup("bcd"); + version_destroy(compare); + compare = nullptr; + status = version_createVersion(1, 2, 3, str, &compare); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + status = version_compareTo(version, compare, &result); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(result < 0); + + // Compare againts a lower version + free(str); + str = celix_utils_strdup("abc"); + version_destroy(compare); + compare = nullptr; + status = version_createVersion(1, 1, 3, str, &compare); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + status = version_compareTo(version, compare, &result); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(result > 0); + + version_destroy(compare); + version_destroy(version); + free(str); +} + +TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) { + celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr); + celix_version_t *version2 = celix_version_createVersion(2, 2, 4, "qualifier"); + + EXPECT_EQ(0, celix_version_compareToMajorMinor(version1, 2, 2)); + EXPECT_EQ(0, celix_version_compareToMajorMinor(version2, 2, 2)); + + EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 2, 3) < 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 2, 3) < 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 3, 3) < 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 3, 3) < 0); + + + EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 2, 1) > 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 2, 1) > 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 1, 1) > 0); + EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 1, 1) > 0); + + celix_version_destroy(version1); + celix_version_destroy(version2); +} + +TEST_F(VersionTestSuite, toString) { + celix_version_t* version = nullptr; + celix_status_t status = CELIX_SUCCESS; + char * str; + char *result = nullptr; + + str = celix_utils_strdup("abc"); + status = version_createVersion(1, 2, 3, str, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + + status = version_toString(version, &result); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(result != nullptr); + EXPECT_STREQ("1.2.3.abc", result); + free(result); + + version_destroy(version); + version = nullptr; + status = version_createVersion(1, 2, 3, nullptr, &version); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(version != nullptr); + + status = version_toString(version, &result); + EXPECT_EQ(CELIX_SUCCESS, status); + EXPECT_TRUE(result != nullptr); + EXPECT_STREQ("1.2.3", result); + + version_destroy(version); + free(result); + free(str); +} + +TEST_F(VersionTestSuite,semanticCompatibility) { + celix_version_t* provider = nullptr; + celix_version_t* compatible_user = nullptr; + celix_version_t* incompatible_user_by_major = nullptr; + celix_version_t* incompatible_user_by_minor = nullptr; + celix_status_t status = CELIX_SUCCESS; + bool isCompatible = false; + + status = version_isCompatible(compatible_user, provider, &isCompatible); + EXPECT_EQ(CELIX_SUCCESS, status); + + version_createVersion(2, 3, 5, nullptr, &provider); + version_createVersion(2, 1, 9, nullptr, &compatible_user); + version_createVersion(1, 3, 5, nullptr, &incompatible_user_by_major); + version_createVersion(2, 5, 7, nullptr, &incompatible_user_by_minor); + + status = version_isCompatible(compatible_user, provider, &isCompatible); + EXPECT_TRUE(isCompatible == true); + EXPECT_EQ(CELIX_SUCCESS, status); + + status = version_isCompatible(incompatible_user_by_major, provider, &isCompatible); + EXPECT_TRUE(isCompatible == false); + EXPECT_EQ(CELIX_SUCCESS, status); + + status = version_isCompatible(incompatible_user_by_minor, provider, &isCompatible); + EXPECT_TRUE(isCompatible == false); + EXPECT_EQ(CELIX_SUCCESS, status); + + version_destroy(provider); + version_destroy(compatible_user); + version_destroy(incompatible_user_by_major); + version_destroy(incompatible_user_by_minor); +} + +TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) { + //nullptr or "" qualifier should be the same + auto* v1 = celix_version_createVersion(0, 0, 0, nullptr); + auto* v2 = celix_version_createVersion(0, 0, 0, ""); + EXPECT_EQ(0, celix_version_compareTo(v1, v1)); + EXPECT_EQ(0, celix_version_compareTo(v1, v2)); + EXPECT_EQ(0, celix_version_compareTo(v2, v2)); + + celix_version_destroy(v1); + celix_version_destroy(v2); +} + +TEST_F(VersionTestSuite, fillString) { + // Create a version object + celix_version_t* version = celix_version_createVersion(1, 2, 3, "alpha"); + + // Test with buffer large enough to hold the formatted string + char buffer[32]; + bool success = celix_version_fillString(version, buffer, 32); + EXPECT_TRUE(success); + EXPECT_STREQ("1.2.3.alpha", buffer); + + // Test with buffer too small to hold the formatted string + success = celix_version_fillString(version, buffer, 5); + EXPECT_FALSE(success); + + celix_version_destroy(version); +} \ No newline at end of file diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 3796b004a..da6536168 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -86,7 +86,7 @@ namespace celix { std::string first{}; std::string second{}; private: - celix_properties_iterator_t iter{._data = {}}; + celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}}; bool end{false}; }; diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index edd1b3cd4..3c8766978 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -22,16 +22,16 @@ * @brief Header file for the Celix Properties API. * * The Celix Properties API provides a means for storing and manipulating key-value pairs, called properties, - * which can be used to store configuration data or metadata for a service, component, or framework configuration. + * which can be used to store configuration data or metadata for a services, components, or framework configuration. * Functions are provided for creating and destroying property sets, loading and storing properties from/to a file * or stream, and setting, getting, and unsetting individual properties. There are also functions for converting * property values to various types (e.g. long, bool, double) and for iterating over the properties in a set. */ #include -#include #include "celix_errno.h" +#include "celix_version.h" #ifndef CELIX_PROPERTIES_H_ #define CELIX_PROPERTIES_H_ @@ -46,25 +46,59 @@ extern "C" { */ typedef struct celix_properties celix_properties_t; -//TODO rethink the iter. Maybe this can be more closely coupled to celix_string_hash_map? -//typedef struct celix_properties_iterator { -// //private data -// void* _data1; -// void* _data2; -// void* _data3; -// int _data4; -// int _data5; -//} celix_properties_iterator_t; +/** + * @brief Enum representing the possible types of a property value. + */ +typedef enum celix_properties_value_type { + CELIX_PROPERTIES_VALUE_TYPE_UNSET = 0, /**< Property value is not set. */ + CELIX_PROPERTIES_VALUE_TYPE_STRING = 1, /**< Property value is a string. */ + CELIX_PROPERTIES_VALUE_TYPE_LONG = 2, /**< Property value is a long integer. */ + CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ + CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ + CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5 /**< Property value is a Celix version. */ +} celix_properties_value_type_e; + +/** + * @brief A structure representing a single entry in a property set. + */ +typedef struct celix_properties_entry { + const char* key; /**< The key of the entry*/ + const char* value; /**< The string value or string representation of a non-string + typed value.*/ + celix_properties_value_type_e valueType; /**< The type of the value of the entry */ + + union { + const char* strValue; /**< The string value of the entry. */ + long longValue; /**< The long integer value of the entry. */ + double doubleValue; /**< The double-precision floating point value of the entry. */ + bool boolValue; /**< The boolean value of the entry. */ + celix_version_t* versionValue; /**< The Celix version value of the entry. */ + } typed; /**< The typed values of the entry. Only valid if valueType + is not CELIX_PROPERTIES_VALUE_TYPE_UNSET and only the matching + value types should be used. E.g typed.boolValue if valueType is + CELIX_PROPERTIES_VALUE_TYPE_BOOL. */ +} celix_properties_entry_t; /** - * @brief Type representing an iterator for iterating over the properties in a property set. + * @brief Represents an iterator for iterating over the entries in a celix_properties_t object. */ typedef struct celix_properties_iterator { - //private opaque data + /** + * @brief The index of the current iterator. + */ + int index; + + /** + * @brief The current entry. + */ + celix_properties_entry_t entry; + + /** + * @brief Private data used to implement the iterator. + */ char _data[56]; } celix_properties_iterator_t; - /** * @brief Creates a new empty property set. * @return A new empty property set. @@ -76,7 +110,7 @@ celix_properties_t* celix_properties_create(void); * * @param properties The property set to destroy. If properties is NULL, this function will do nothing. */ -void celix_properties_destroy(celix_properties_t *properties); +void celix_properties_destroy(celix_properties_t* properties); /** * @brief Loads properties from a file. @@ -112,8 +146,20 @@ celix_properties_t* celix_properties_loadFromString(const char *input); * @param properties The property set to store. * @param file The name of the file to store the properties to. * @param header An optional header to write to the file before the properties. + * @return CELIX_SUCCESS if the operation was successful, CELIX_FILE_IO_EXCEPTION if there was an error writing to the + * file. + */ +celix_status_t celix_properties_store(celix_properties_t* properties, const char* file, const char* header); + +/** + * @brief Gets the entry for a given key in a property set. + * + * @param properties The property set to search. + * @param key The key to search for. + * @return The entry for the given key, or a default entry with the valueType set to CELIX_PROPERTIES_VALUE_TYPE_UNSET + * if the key is not found. */ -void celix_properties_store(celix_properties_t *properties, const char *file, const char *header); //TODO add return status +celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* properties, const char* key); /** * @brief Gets the value of a property. @@ -123,7 +169,15 @@ void celix_properties_store(celix_properties_t *properties, const char *file, co * @param defaultValue The value to return if the property is not set. * @return The value of the property, or the default value if the property is not set. */ -const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue); +const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue); + +/** + * @brief Gets the type of a property value. + * @param properties The property set to search. + * @param key The key of the property to get the type of. + * @return The type of the property value, or CELIX_PROPERTIES_VALUE_TYPE_UNSET if the property is not set. + */ +celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key); /** * @brief Sets the value of a property. @@ -133,7 +187,7 @@ const char* celix_properties_get(const celix_properties_t *properties, const cha * @param key The key of the property to set. * @param value The value to set the property to. */ -void celix_properties_set(celix_properties_t *properties, const char *key, const char *value); +void celix_properties_set(celix_properties_t* properties, const char* key, const char *value); /** * @brief Sets the value of a property without copying the key and value strings. @@ -144,14 +198,14 @@ void celix_properties_set(celix_properties_t *properties, const char *key, const * @param value The value to set the property to. This string will be used directly, so it must not be freed or * modified after calling this function. */ -void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value); +void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char *value); /** * @brief Unsets a property, removing it from the property set. * @param properties The property set to modify. * @param key The key of the property to unset. */ -void celix_properties_unset(celix_properties_t *properties, const char *key); +void celix_properties_unset(celix_properties_t* properties, const char *key); /** * @brief Makes a copy of a property set. @@ -159,66 +213,100 @@ void celix_properties_unset(celix_properties_t *properties, const char *key); * @param properties The property set to copy. * @return A copy of the given property set. */ -celix_properties_t* celix_properties_copy(const celix_properties_t *properties); +celix_properties_t* celix_properties_copy(const celix_properties_t* properties); /** * @brief Gets the value of a property as a long integer. * - * @param props The property set to search. + * @param properties The property set to search. * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set or cannot be converted to a long. - * @return The value of the property as a long, or the default value if the property is not set or cannot be converted - * to a long. + * @param defaultValue The value to return if the property is not set, the value is not a long integer, + * or if the value cannot be converted to a long integer. + * @return The value of the property as a long integer, or the default value if the property is not set, + * the value is not a long integer, or if the value cannot be converted to a long integer. + * If the value is a string, it will be converted to a long integer if possible. */ -long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue); +long celix_properties_getAsLong(const celix_properties_t* properties, const char* key, long defaultValue); /** * @brief Sets the value of a property to a long integer. * - * @param props The property set to modify. + * @param properties The property set to modify. * @param key The key of the property to set. * @param value The long value to set the property to. */ -void celix_properties_setLong(celix_properties_t *props, const char *key, long value); +void celix_properties_setLong(celix_properties_t* properties, const char* key, long value); /** * @brief Gets the value of a property as a boolean. * - * @param props The property set to search. + * @param properties The property set to search. * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set or cannot be converted to a boolean. - * @return The value of the property as a boolean, or the default value if the property is not set or cannot be - * converted to a boolean. + * @param defaultValue The value to return if the property is not set, the value is not a boolean, or if the value + * cannot be converted to a boolean. + * @return The value of the property as a boolean, or the default value if the property is not set, the value is not a + * boolean, or if the value cannot be converted to a boolean. If the value is a string, it will be converted + * to a boolean if possible. */ -bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue); +bool celix_properties_getAsBool(const celix_properties_t* properties, const char* key, bool defaultValue); /** * @brief Sets the value of a property to a boolean. * - * @param props The property set to modify. + * @param properties The property set to modify. * @param key The key of the property to set. * @param val The boolean value to set the property to. */ -void celix_properties_setBool(celix_properties_t *props, const char *key, bool val); +void celix_properties_setBool(celix_properties_t* properties, const char* key, bool val); /** * @brief Sets the value of a property to a double. * - * @param props The property set to modify. + * @param properties The property set to modify. * @param key The key of the property to set. * @param val The double value to set the property to. */ -void celix_properties_setDouble(celix_properties_t *props, const char *key, double val); +void celix_properties_setDouble(celix_properties_t* properties, const char* key, double val); /** * @brief Gets the value of a property as a double. * - * @param props The property set to search. + * @param properties The property set to search. * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set or cannot be converted to a double. - * @return The value of the property as a double, or the default value if the property is not set or cannot be converted to a double. + * @param defaultValue The value to return if the property is not set, the value is not a double, + * or if the value cannot be converted to a double. + * @return The value of the property as a double, or the default value if the property is not set, the value is not + * a double, or if the value cannot be converted to a double. If the value is a string, it will be converted + * to a double if possible. + */ +double celix_properties_getAsDouble(const celix_properties_t* properties, const char* key, double defaultValue); + +/** + * @brief Sets the value of a property as a Celix version string. + * + * @param properties The property set to modify. + * @param key The key of the property to set. + * @param version The value to set. */ -double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue); +void celix_properties_setVersion(celix_properties_t* properties, const char* key, const celix_version_t* version); + +/** + * @brief Gets the value of a property as a Celix version. + * + * //TODO, maybe improve, now returns NULL if underlining type is not version + * + * @param properties The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set, the value is not a Celix version, or if + * the value cannot be converted to a Celix version. + * @return The value of the property as a Celix version, or the default value if the property is not set, + * the value is not a Celix version, or if the value cannot be converted to a Celix version. + * If the value is a string, it will be verified as a valid Celix version. + */ +const celix_version_t* celix_properties_getAsVersion( + const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue); /** * @brief Gets the number of properties in a property set. @@ -226,44 +314,81 @@ double celix_properties_getAsDouble(const celix_properties_t *props, const char * @param properties The property set to get the size of. * @return The number of properties in the property set. */ -int celix_properties_size(const celix_properties_t *properties); +int celix_properties_size(const celix_properties_t* properties); /** - * @brief Constructs a new iterator for iterating over the properties in a property set. + * @brief Constructs a new properties iterator. + * + * Note: The iterator is initialized to be before the first entry. To advance to the first entry, + * call `celix_propertiesIterator_nextEntry`. * - * @param properties The property set to iterate over. - * @return A new iterator for the given property set. + * //TODO deprecate? + * + * @param[in] properties The properties object to iterate over. + * @return The newly constructed iterator. */ -celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties); +celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties); /** - * @brief Determines whether the iterator has a next property. + * @brief Determines whether the iterator has more entries. + * + * //TODO deprecate? * - * @param iter The iterator to check. - * @return true if the iterator has a next property, false otherwise. + * @param[in] iter The iterator. + * @return true if the iterator has more entries, false otherwise. */ bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); /** - * @brief Gets the next key in the iterator. - * @param iter The iterator to get the next key from. - * @return The next key in the iterator. + * @brief Advances the iterator to the next entry and returns the key for the current entry. + * + * If the iterator has no more entries, this function returns NULL. + * + * //TODO deprecate? + * + * @param[in, out] iter The iterator. + * @return The key for the current entry, or NULL if the iterator has no more entries. + */ +const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter); + +/** + * @brief Constructs an iterator pointing to the first entry in the properties object. + * + * @param[in] properties The properties object to iterate over. + * @return The iterator pointing to the first entry in the properties object. + */ +celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties); + +/** + * @brief Advances the iterator to the next entry. + * + * @param[in, out] iter The iterator. + */ +void celix_propertiesIterator_next(celix_properties_iterator_t* iter); + +/** + * @brief Determines whether the iterator is pointing to an end position. + * + * An iterator is at an end position if it has no more entries to visit. + * + * @param[in] iter The iterator. + * @return true if the iterator is at an end position, false otherwise. */ -const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter); +bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); /** * @brief Gets the property set being iterated over. * - * @param iter The iterator to get the property set from. + * @param[in] iter The iterator to get the property set from. * @return The property set being iterated over. */ -celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter); +celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter); /** * @brief Determines whether two iterators are equal. * - * @param a The first iterator to compare. - * @param b The second iterator to compare. + * @param[in] a The first iterator to compare. + * @param[in] b The second iterator to compare. * @return true if the iterators are equal, false otherwise. */ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); @@ -271,21 +396,24 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const /** * @brief Macro for iterating over the properties in a property set. * - * @param props The property set to iterate over. - * @param key The variable to use for the current key in the loop. + * @param[in] properties The property set to iterate over. + * @param[out] key The variable to use for the current key in the loop. + * + * //TODO deprecate * * Example usage: * @code{.c} - * celix_properties_t *props = celix_properties_create(); - * celix_properties_set(props, "key1", "value1"); - * celix_properties_set(props, "key2", "value2"); - * celix_properties_set(props, "key3", "value3"); + * celix_properties_t* properties = celix_properties_create(); + * celix_properties_set(properties, "key1", "value1"); + * celix_properties_set(properties, "key2", "value2"); + * celix_properties_set(properties, "key3", "value3"); * * const char* key; - * CELIX_PROPERTIES_FOR_EACH(props, key) { - * printf("%s = %s\n", key, celix_properties_get(props, key, "")); + * CELIX_PROPERTIES_FOR_EACH(properties, key) { + * printf("%s = %s\n", key, celix_properties_get(properties, key, "")); * } * @endcode + * * Output: * @code{.c} * key1 = value1 @@ -293,9 +421,40 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const * key3 = value3 * @endcode */ -#define CELIX_PROPERTIES_FOR_EACH(props, key) \ - for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(props); \ - celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) +#define CELIX_PROPERTIES_FOR_EACH(map, keyName) \ + for (celix_properties_iterator_t iter_##keyName = celix_properties_begin(map); \ + (keyName) = iter_##keyName.entry.key, !celix_propertiesIterator_isEnd(&(iter_##keyName)); \ + celix_propertiesIterator_next(&(iter_##keyName))) + +/* +#define CELIX_PROPERTIES_FOR_EACH(properties, key) \ + for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(properties); \ + celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) +*/ + + +/** + * @brief Iterates over the entries in the specified celix_properties_t object. + * + * This macro allows you to easily iterate over the entries in a celix_properties_t object. + * The loop variable `iterName` will be of type celix_properties_iterator_t and will contain the current + * entry during each iteration. + * + * @param map The properties object to iterate over. + * @param iterName The name of the iterator variable to use in the loop. + * + * Example usage: + * @code{.c} + * // Iterate over all entries in the properties object + * CELIX_PROPERTIES_ITERATE(properties, iter) { + * // Print the key and value of the current entry + * printf("%s: %s\n", iter.entry.key, iter.entry.value); + * } + * @endcode + */ +#define CELIX_PROPERTIES_ITERATE(map, iterName) \ + for (celix_properties_iterator_t iterName = celix_properties_begin(map); \ + !celix_propertiesIterator_isEnd(&(iterName)); celix_propertiesIterator_next(&(iterName))) #ifdef __cplusplus } diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index a5b7073d9..be03aeef1 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -27,18 +27,18 @@ extern "C" { #include /** - * The definition of the celix_version_t* abstract data type. + * @brief The definition of the celix_version_t* abstract data type. */ typedef struct celix_version celix_version_t; /** - * Creates a new celix_version_t* using the supplied arguments. + * @brief Create a new celix_version_t* using the supplied arguments. * - * @param major Major component of the version identifier. - * @param minor Minor component of the version identifier. - * @param micro Micro component of the version identifier. - * @param qualifier Qualifier component of the version identifier. If - * null is specified, then the qualifier will be set to + * @param[in] major Major component of the version identifier. + * @param[in] minor Minor component of the version identifier. + * @param[in] micro Micro component of the version identifier. + * @param[in] qualifier Qualifier component of the version identifier. If + * NULL is specified, then the qualifier will be set to * the empty string. * @return The created version or NULL if the input was incorrect */ @@ -47,15 +47,15 @@ celix_version_t* celix_version_createVersion(int major, int minor, int micro, co void celix_version_destroy(celix_version_t* version); /** - * Creates a copy of version. + * @brief Create a copy of version. * - * @param version The version to copy + * @param[in] version The version to copy * @return the copied version */ celix_version_t* celix_version_copy(const celix_version_t* version); /** - * Creates a version identifier from the specified string. + * @brief Create a version identifier from the specified string. * *

* Here is the grammar for version strings. @@ -72,27 +72,50 @@ celix_version_t* celix_version_copy(const celix_version_t* version); * * There must be no whitespace in version. * - * @param versionStr String representation of the version identifier. + * @param[in] versionStr String representation of the version identifier. * @return The created version or NULL if the input was invalid. */ celix_version_t* celix_version_createVersionFromString(const char *versionStr); /** - * The empty version "0.0.0". - * + * @brief Create empty version "0.0.0". */ celix_version_t* celix_version_createEmptyVersion(); +/** + * @brief Gets the major version number of a celix version. + * + * @param[in] version The celix version. + * @return The major version number. + */ int celix_version_getMajor(const celix_version_t* version); +/** + * @brief Gets the minor version number of a celix version. + * + * @param[in] version The celix version. + * @return The minor version number. + */ int celix_version_getMinor(const celix_version_t* version); +/** + * @brief Gets the micro version number of a celix version. + * + * @param[in] version The celix version. + * @return The micro version number. + */ int celix_version_getMicro(const celix_version_t* version); +/** + * @brief Gets the version qualifier of a celix version. + * + * @param[in] version The celix version. + * @return The version qualifier, or NULL if no qualifier is present. + */ const char* celix_version_getQualifier(const celix_version_t* version); /** - * Compares this Version object to another object. + * @brief Compare this Version object to another object. * *

* A version is considered to be less than another version if its @@ -117,12 +140,12 @@ const char* celix_version_getQualifier(const celix_version_t* version); int celix_version_compareTo(const celix_version_t* version, const celix_version_t* compare); /** - * Creates a hash of the version + * @brief Create a hash of the version */ unsigned int celix_version_hash(const celix_version_t* version); /** - * Returns the string representation of version identifier. + * @brief Return the string representation of version identifier. * *

* The format of the version string will be major.minor.micro @@ -136,7 +159,17 @@ unsigned int celix_version_hash(const celix_version_t* version); char* celix_version_toString(const celix_version_t* version); /** - * Check if two versions are semantically compatible. + * @brief Fill a given string with the string representation of the given version. + * + * @param[in] version The version to fill the string with. + * @param[out] str The string to fill. + * @param[in] strLen The length of the string. + * @return true if the string was filled successfully, false otherwise. + */ +bool celix_version_fillString(const celix_version_t* version, char *str, size_t strLen); + +/** + * @brief Check if two versions are semantically compatible. * *

* The user version is compatible with the provider version if the provider version is in the range @@ -149,7 +182,7 @@ char* celix_version_toString(const celix_version_t* version); bool celix_version_isCompatible(const celix_version_t* user, const celix_version_t* provider); /** - * Check if two versions are semantically compatible. + * @brief Check if two versions are semantically compatible. * *

* The user version is compatible with the provider version if the provider version is in the range @@ -163,7 +196,7 @@ bool celix_version_isCompatible(const celix_version_t* user, const celix_version bool celix_version_isUserCompatible(const celix_version_t* user, int providerMajorVersionPart, int provideMinorVersionPart); /** - * Compare a provider celix version is with a provided major and minor version. Ignoring the patch version part. + * @brief Compare a provider celix version is with a provided major and minor version. Ignoring the patch version part. * * @param version The version to compare. * @param majorVersionPart The major version part to compare. diff --git a/libs/utils/private/test/version_test.cpp b/libs/utils/private/test/version_test.cpp deleted file mode 100644 index 1fa59e71a..000000000 --- a/libs/utils/private/test/version_test.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" - -#include "celix_version.h" -#include "version.h" -#include "celix_version.h" - -extern "C" -{ -#include "version_private.h" -} - -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} - -static char* my_strdup(const char* s){ - if (s == NULL) { - return NULL; - } - - size_t len = strlen(s); - - char *d = (char *) calloc(len + 1, sizeof(char)); - - if (d == NULL) { - return NULL; - } - - strncpy(d,s,len); - return d; -} - -TEST_GROUP(version) { - - void setup(void) { - } - - void teardown() { - } - -}; - - -TEST(version, create) { - version_pt version = NULL; - char * str; - -// str = my_strdup("abc"); -// status = version_createVersion(1, 2, 3, str, &version); -// LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, status); - - str = my_strdup("abc"); - LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - STRCMP_EQUAL("abc", version->qualifier); - - version_destroy(version); - version = NULL; - LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, NULL, &version)); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - STRCMP_EQUAL("", version->qualifier); - - version_destroy(version); - version = NULL; - free(str); - str = my_strdup("abc"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, &version)); - - version_destroy(version); - version = NULL; - free(str); - str = my_strdup("abc|xyz"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, &version)); - - version_destroy(version); - free(str); -} - -TEST(version, clone) { - version_pt version = NULL, clone = NULL; - char * str; - - str = my_strdup("abc"); - LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); - LONGS_EQUAL(CELIX_SUCCESS, version_clone(version, &clone)); - CHECK_C(version != NULL); - LONGS_EQUAL(1, clone->major); - LONGS_EQUAL(2, clone->minor); - LONGS_EQUAL(3, clone->micro); - STRCMP_EQUAL("abc", clone->qualifier); - - version_destroy(clone); - version_destroy(version); - free(str); -} - -TEST(version, createFromString) { - version_pt version = NULL; - celix_status_t status = CELIX_SUCCESS; - char * str; - - str = my_strdup("1"); - LONGS_EQUAL(CELIX_SUCCESS, version_createVersionFromString(str, &version)); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - - version_destroy(version); - - free(str); - str = my_strdup("a"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = my_strdup("1.a"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = my_strdup("1.1.a"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = my_strdup("-1"); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = my_strdup("1.2"); - version = NULL; - LONGS_EQUAL(CELIX_SUCCESS, version_createVersionFromString(str, &version)); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - - version_destroy(version); - - free(str); - str = my_strdup("1.2.3"); - version = NULL; - status = version_createVersionFromString(str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - - version_destroy(version); - free(str); - str = my_strdup("1.2.3.abc"); - version = NULL; - status = version_createVersionFromString(str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - STRCMP_EQUAL("abc", version->qualifier); - - version_destroy(version); - free(str); - str = my_strdup("1.2.3.abc_xyz"); - version = NULL; - status = version_createVersionFromString(str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - STRCMP_EQUAL("abc_xyz", version->qualifier); - - version_destroy(version); - free(str); - str = my_strdup("1.2.3.abc-xyz"); - version = NULL; - status = version_createVersionFromString(str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - LONGS_EQUAL(1, version->major); - LONGS_EQUAL(2, version->minor); - LONGS_EQUAL(3, version->micro); - STRCMP_EQUAL("abc-xyz", version->qualifier); - - version_destroy(version); - free(str); - str = my_strdup("1.2.3.abc|xyz"); - status = version_createVersionFromString(str, &version); - LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, status); - - free(str); -} - -TEST(version, createEmptyVersion) { - version_pt version = NULL; - celix_status_t status = CELIX_SUCCESS; - - status = version_createEmptyVersion(&version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - LONGS_EQUAL(0, version->major); - LONGS_EQUAL(0, version->minor); - LONGS_EQUAL(0, version->micro); - STRCMP_EQUAL("", version->qualifier); - - version_destroy(version); -} - -TEST(version, getters) { - version_pt version = NULL; - celix_status_t status = CELIX_SUCCESS; - char * str; - int major, minor, micro; - const char *qualifier; - - str = my_strdup("abc"); - status = version_createVersion(1, 2, 3, str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - - version_getMajor(version, &major); - LONGS_EQUAL(1, major); - - version_getMinor(version, &minor); - LONGS_EQUAL(2, minor); - - version_getMicro(version, µ); - LONGS_EQUAL(3, micro); - - version_getQualifier(version, &qualifier); - STRCMP_EQUAL("abc", qualifier); - - version_destroy(version); - free(str); -} - -TEST(version, compare) { - version_pt version = NULL, compare = NULL; - celix_status_t status = CELIX_SUCCESS; - char * str; - int result; - - // Base version to compare - str = my_strdup("abc"); - status = version_createVersion(1, 2, 3, str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - - // Compare equality - free(str); - str = my_strdup("abc"); - compare = NULL; - status = version_createVersion(1, 2, 3, str, &compare); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - status = version_compareTo(version, compare, &result); - LONGS_EQUAL(CELIX_SUCCESS, status); - LONGS_EQUAL(0, result); - - // Compare against a higher version - free(str); - str = my_strdup("bcd"); - version_destroy(compare); - compare = NULL; - status = version_createVersion(1, 2, 3, str, &compare); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - status = version_compareTo(version, compare, &result); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK(result < 0); - - // Compare againts a lower version - free(str); - str = my_strdup("abc"); - version_destroy(compare); - compare = NULL; - status = version_createVersion(1, 1, 3, str, &compare); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - status = version_compareTo(version, compare, &result); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK(result > 0); - - version_destroy(compare); - version_destroy(version); - free(str); -} - -TEST(version, celix_version_compareToMajorMinor) { - celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr); - celix_version_t *version2 = celix_version_createVersion(2, 2, 4, "qualifier"); - - CHECK_EQUAL(0, celix_version_compareToMajorMinor(version1, 2, 2)); - CHECK_EQUAL(0, celix_version_compareToMajorMinor(version2, 2, 2)); - - CHECK_TRUE(celix_version_compareToMajorMinor(version1, 2, 3) < 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version2, 2, 3) < 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version1, 3, 3) < 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version2, 3, 3) < 0); - - - CHECK_TRUE(celix_version_compareToMajorMinor(version1, 2, 1) > 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version2, 2, 1) > 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version1, 1, 1) > 0); - CHECK_TRUE(celix_version_compareToMajorMinor(version2, 1, 1) > 0); - - celix_version_destroy(version1); - celix_version_destroy(version2); -} - -TEST(version, toString) { - version_pt version = NULL; - celix_status_t status = CELIX_SUCCESS; - char * str; - char *result = NULL; - - str = my_strdup("abc"); - status = version_createVersion(1, 2, 3, str, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - - status = version_toString(version, &result); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(result != NULL); - STRCMP_EQUAL("1.2.3.abc", result); - free(result); - - version_destroy(version); - version = NULL; - status = version_createVersion(1, 2, 3, NULL, &version); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(version != NULL); - - status = version_toString(version, &result); - LONGS_EQUAL(CELIX_SUCCESS, status); - CHECK_C(result != NULL); - STRCMP_EQUAL("1.2.3", result); - - version_destroy(version); - free(result); - free(str); -} - -TEST(version,semanticCompatibility) { - version_pt provider = NULL; - version_pt compatible_user = NULL; - version_pt incompatible_user_by_major = NULL; - version_pt incompatible_user_by_minor = NULL; - celix_status_t status = CELIX_SUCCESS; - bool isCompatible = false; - - status = version_isCompatible(compatible_user, provider, &isCompatible); - LONGS_EQUAL(CELIX_SUCCESS, status); - - version_createVersion(2, 3, 5, NULL, &provider); - version_createVersion(2, 1, 9, NULL, &compatible_user); - version_createVersion(1, 3, 5, NULL, &incompatible_user_by_major); - version_createVersion(2, 5, 7, NULL, &incompatible_user_by_minor); - - status = version_isCompatible(compatible_user, provider, &isCompatible); - CHECK(isCompatible == true); - LONGS_EQUAL(CELIX_SUCCESS, status); - - status = version_isCompatible(incompatible_user_by_major, provider, &isCompatible); - CHECK(isCompatible == false); - LONGS_EQUAL(CELIX_SUCCESS, status); - - status = version_isCompatible(incompatible_user_by_minor, provider, &isCompatible); - CHECK(isCompatible == false); - LONGS_EQUAL(CELIX_SUCCESS, status); - - version_destroy(provider); - version_destroy(compatible_user); - version_destroy(incompatible_user_by_major); - version_destroy(incompatible_user_by_minor); -} - -TEST(version, compareEmptyAndNullQualifier) { - //nullptr or "" qualifier should be the same - auto* v1 = celix_version_createVersion(0, 0, 0, nullptr); - auto* v2 = celix_version_createVersion(0, 0, 0, ""); - CHECK_EQUAL(0, celix_version_compareTo(v1, v1)); - CHECK_EQUAL(0, celix_version_compareTo(v1, v2)); - CHECK_EQUAL(0, celix_version_compareTo(v2, v2)); - - celix_version_destroy(v1); - celix_version_destroy(v2); -} \ No newline at end of file diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index a83211248..af8b892f1 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "celix_build_assert.h" #include "utils.h" //TODO try to remove @@ -33,18 +34,37 @@ #include "celix_string_hash_map.h" -#define PROPERTIES_INITIAL_HASHMAP_CAPACITY 10 -#define SHORT_PROPERTIES_OPTIMIZATION_SIZE 1024 +#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE 1024 +#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE 16 + +static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true"; +static const char* const CELIX_PROPERTIES_BOOL_FALSE_STRVAL = "false"; struct celix_properties { celix_string_hash_map_t* map; /** - * buffer used to store the first key/value entries so that no additional memory allocation is needed. + * String buffer used to store the first key/value entries, so that in many cases additional memory allocation + * can be prevented. */ - char buffer[SHORT_PROPERTIES_OPTIMIZATION_SIZE]; -}; + char stringBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE]; + + /** + * The current string buffer index. + */ + int currentStringBufferIndex; + + /** + * Entries buffer used to store the first entries, so that in many cases additional memory allocation + * can be prevented. + */ + celix_properties_entry_t entriesBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE]; + /** + * The current string buffer index. + */ + int currentEntriesBufferIndex; +}; #define MALLOC_BLOCK_SIZE 5 @@ -75,7 +95,7 @@ properties_pt properties_loadFromString(const char *input){ * Header is ignored for now, cannot handle comments yet */ void properties_store(properties_pt properties, const char* filename, const char* header) { - return celix_properties_store(properties, filename, header); + celix_properties_store(properties, filename, header); } celix_status_t properties_copy(properties_pt properties, properties_pt *out) { @@ -217,19 +237,187 @@ static void parseLine(const char* line, celix_properties_t *props) { } +/** + * Create a new string from the provided str by either using strup or storing the string the short properties + * optimization string buffer. + */ +static char* celix_properties_createString(celix_properties_t* properties, const char* str) { + size_t len = str == NULL ? 0 : strnlen(str, CELIX_UTILS_MAX_STRLEN) + 1; + size_t left = CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE - properties->currentStringBufferIndex; + char* result; + if (len < left) { + memcpy(&properties->stringBuffer[properties->currentStringBufferIndex], str, len); + result = &properties->stringBuffer[properties->currentStringBufferIndex]; + properties->currentStringBufferIndex += (int)len; + } else { + result = celix_utils_strdup(str); + } + return result; +} + +/** + * Fill entry and optional use the short properties optimization string buffer. + */ +static celix_status_t celix_properties_fillEntry( + celix_properties_t *properties, + celix_properties_entry_t* entry, + const char *key, + const char *strValue, + const long* longValue, + const double* doubleValue, + const bool* boolValue, + const celix_version_t* versionValue) { + char convertedValueBuffer[32]; + entry->key = celix_properties_createString(properties, key); + if (strValue != NULL) { + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; + entry->value = celix_properties_createString(properties, strValue); + entry->typed.strValue = entry->value; + } else if (longValue != NULL) { + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG; + entry->typed.longValue = *longValue; + int written = snprintf(convertedValueBuffer, sizeof(convertedValueBuffer), "%li", entry->typed.longValue); + if (written < 0 || written >= sizeof(convertedValueBuffer)) { + entry->value = celix_properties_createString(properties, convertedValueBuffer); + } else { + char* val = NULL; + asprintf(&val, "%li", entry->typed.longValue); + entry->value = val; + } + } else if (doubleValue != NULL) { + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE; + entry->typed.doubleValue = *doubleValue; + int written = snprintf(convertedValueBuffer, sizeof(convertedValueBuffer), "%f", entry->typed.doubleValue); + if (written < 0 || written >= sizeof(convertedValueBuffer)) { + entry->value = celix_properties_createString(properties, convertedValueBuffer); + } else { + char* val = NULL; + asprintf(&val, "%f", entry->typed.doubleValue); + entry->value = val; + } + } else if (boolValue != NULL) { + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL; + entry->typed.boolValue = *boolValue; + entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; + } else /*versionValue*/ { + assert(versionValue != NULL); + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION; + entry->typed.versionValue = celix_version_copy(versionValue); + + bool written = celix_version_fillString(versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); + if (written) { + entry->value = celix_properties_createString(properties, convertedValueBuffer); + } else { + entry->value = celix_version_toString(versionValue); + } + } + if (entry->key == NULL || entry->value == NULL) { + return CELIX_ENOMEM; + } + return CELIX_SUCCESS; +} + +/** + * Allocate entry and optionally use the short properties optimization entries buffer. + */ +static celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t* properties) { + celix_properties_entry_t* entry; + if (properties->currentEntriesBufferIndex < CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE) { + entry = &properties->entriesBuffer[properties->currentEntriesBufferIndex++]; + } else { + entry = malloc(sizeof(*entry)); + } + return entry; +} + +/** + * Create entry and optionally use the short properties optimization entries buffer and take ownership of the + * provided key and value strings. + */ +static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_properties_t *properties, + char *key, + char *strValue) { + celix_properties_entry_t* entry = celix_properties_allocEntry(properties); + if (entry == NULL) { + return NULL; + } + entry->key = key; + entry->value = strValue; + entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; + entry->typed.strValue = strValue; + return entry; +} + + +/** + * Create entry and optionally use the short properties optimization buffers. + * Only 1 of the types values (strValue, LongValue, etc) should be provided. + */ +static celix_properties_entry_t* celix_properties_createEntry( + celix_properties_t *properties, + const char *key, + const char *strValue, + const long* longValue, + const double* doubleValue, + const bool* boolValue, + const celix_version_t* versionValue) { + celix_properties_entry_t* entry = celix_properties_allocEntry(properties); + if (entry == NULL) { + return NULL; + } + + celix_status_t status = celix_properties_fillEntry(properties, entry, key, strValue, longValue, doubleValue, + boolValue, versionValue); + if (status != CELIX_SUCCESS) { + free(entry); + entry = NULL; + } + return entry; +} + +/** + * Create and add entry and optionally use the short properties optimization buffers. + * Only 1 of the types values (strValue, LongValue, etc) should be provided. + */ +static void celix_properties_createAndSetEntry( + celix_properties_t *properties, + const char *key, + const char *strValue, + const long* longValue, + const double* doubleValue, + const bool* boolValue, + const celix_version_t* versionValue) { + celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue, + boolValue, versionValue); + if (entry != NULL) { + celix_stringHashMap_put(properties->map, key, entry); + } +} + -/********************************************************************************************************************** - ********************************************************************************************************************** - * Updated API - ********************************************************************************************************************** - **********************************************************************************************************************/ +static void celix_properties_freeString(celix_properties_t* properties, char* str) { + if (str == CELIX_PROPERTIES_BOOL_TRUE_STRVAL || str == CELIX_PROPERTIES_BOOL_FALSE_STRVAL) { + //str is static const char* const -> nop + } else if (str >= properties->stringBuffer && + str < (properties->stringBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE)) { + //str is part of the properties string buffer -> nop + } else { + free(str); + } +} -static void celix_properties_removeEntryCallback(void* handle, const char* key, celix_hash_map_value_t val) { - //celix_properties_t* props = handle; - //TODO add check of short properties optimization buffer - free((char*)key); - free(val.ptrValue); +static void celix_properties_removeEntryCallback(void* handle, const char* key __attribute__((unused)), celix_hash_map_value_t val) { + celix_properties_t* properties = handle; + celix_properties_entry_t* entry = val.ptrValue; + celix_properties_freeString(properties, (char*)entry->key); + celix_properties_freeString(properties, (char*)entry->value); + if (entry >= properties->entriesBuffer && + entry <= (properties->entriesBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE)) { + //entry is part of the properties entries buffer -> nop. + } else { + free(entry); + } } @@ -238,10 +426,12 @@ celix_properties_t* celix_properties_create(void) { if (props != NULL) { celix_string_hash_map_create_options_t opts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS; opts.storeKeysWeakly = true; - opts.initialCapacity = PROPERTIES_INITIAL_HASHMAP_CAPACITY; + opts.initialCapacity = CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE; opts.removedCallbackData = props; opts.removedCallback = celix_properties_removeEntryCallback; props->map = celix_stringHashMap_createWithOptions(&opts); + props->currentStringBufferIndex = 0; + props->currentEntriesBufferIndex = 0; } return props; } @@ -329,32 +519,47 @@ celix_properties_t* celix_properties_loadFromString(const char *input) { /** * @brief Store properties string to file and escape the characters '#', '!', '=' and ':' if encountered. */ -static void celix_properties_storeEscapedString(FILE* file, const char* str) { +static int celix_properties_storeEscapedString(FILE* file, const char* str) { + int rc = 0; for (int i = 0; i < strlen(str); i += 1) { if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') { - fputc('\\', file); + rc = fputc('\\', file); + if (rc == EOF) { + break; + } } - fputc(str[i], file); + rc = fputc(str[i], file); } + return rc; } -void celix_properties_store(celix_properties_t *properties, const char *filename, const char *header) { +celix_status_t celix_properties_store(celix_properties_t *properties, const char *filename, const char *header) { FILE *file = fopen (filename, "w+" ); if (file == NULL) { - perror("File is null"); - return; + return CELIX_FILE_IO_EXCEPTION; } + int rc = 0; CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { const char* val = iter.value.ptrValue; - celix_properties_storeEscapedString(file, iter.key); - fputc('=', file); - celix_properties_storeEscapedString(file, val); - fputc('\n', file); - + if (rc != EOF) { + rc = celix_properties_storeEscapedString(file, iter.key); + } + if (rc != EOF) { + rc = fputc('=', file); + } + if (rc != EOF) { + rc = celix_properties_storeEscapedString(file, val); + } + if (rc != EOF) { + rc = fputc('\n', file); + } } - fclose(file); + if (rc != EOF) { + rc = fclose(file); + } + return rc != EOF ? CELIX_SUCCESS : CELIX_FILE_IO_EXCEPTION; } celix_properties_t* celix_properties_copy(const celix_properties_t *properties) { @@ -363,30 +568,55 @@ celix_properties_t* celix_properties_copy(const celix_properties_t *properties) return copy; } - CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { - celix_properties_set(copy, iter.key, iter.value.ptrValue); + CELIX_PROPERTIES_ITERATE(properties, iter) { + if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_properties_set(copy, iter.entry.key, iter.entry.value); + } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { + celix_properties_setLong(copy, iter.entry.key, iter.entry.typed.longValue); + } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { + celix_properties_setDouble(copy, iter.entry.key, iter.entry.typed.doubleValue); + } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { + celix_properties_setBool(copy, iter.entry.key, iter.entry.typed.boolValue); + } else /*version*/ { + assert(iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION); + celix_properties_setVersion(copy, iter.entry.key, iter.entry.typed.versionValue); + } } return copy; } +celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key) { + celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); + return entry == NULL ? CELIX_PROPERTIES_VALUE_TYPE_UNSET : entry->valueType; +} + const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue) { - const char* value = NULL; + celix_properties_entry_t* entry = NULL; if (properties != NULL) { - value = celix_stringHashMap_get(properties->map, key); + entry = celix_stringHashMap_get(properties->map, key); } - return value == NULL ? defaultValue : value; + return entry == NULL ? defaultValue : entry->value; +} + +celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* properties, const char* key) { + celix_properties_entry_t invalidEntry; + memset(&invalidEntry, 0, sizeof(invalidEntry)); + invalidEntry.valueType = CELIX_PROPERTIES_VALUE_TYPE_UNSET; + celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); + return entry == NULL ? invalidEntry : *entry; } void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { - if (properties != NULL) { - celix_stringHashMap_put(properties->map, celix_utils_strdup(key), celix_utils_strdup(value)); + if (properties != NULL && key != NULL && value != NULL) { + celix_properties_createAndSetEntry(properties, key, value, NULL, NULL, NULL, NULL); } } void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) { - if (properties != NULL) { - if (properties != NULL) { - celix_stringHashMap_put(properties->map, key, value); + if (properties != NULL && key != NULL && value != NULL) { + celix_properties_entry_t* entry = celix_properties_createEntryWithNoCopy(properties, key, value); + if (entry != NULL) { + celix_stringHashMap_put(properties->map, entry->key, entry); } } } @@ -399,12 +629,14 @@ void celix_properties_unset(celix_properties_t *properties, const char *key) { long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue) { long result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { + celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { + return entry->typed.longValue; + } else if (entry != NULL) { char *enptr = NULL; errno = 0; - long r = strtol(val, &enptr, 10); - if (enptr != val && errno == 0) { + long r = strtol(entry->value, &enptr, 10); + if (enptr != entry->value && errno == 0) { result = r; } } @@ -412,23 +644,19 @@ long celix_properties_getAsLong(const celix_properties_t *props, const char *key } void celix_properties_setLong(celix_properties_t *props, const char *key, long value) { - char buf[32]; //should be enough to store long long int - int writen = snprintf(buf, 32, "%li", value); - if (writen <= 31) { - celix_properties_set(props, key, buf); - } else { - fprintf(stderr,"buf to small for value '%li'\n", value); - } + celix_properties_createAndSetEntry(props, key, NULL, &value, NULL, NULL, NULL); } double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue) { double result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { + celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { + return entry->typed.doubleValue; + } else if (entry != NULL) { char *enptr = NULL; errno = 0; - double r = strtod(val, &enptr); - if (enptr != val && errno == 0) { + double r = strtod(entry->value, &enptr); + if (enptr != entry->value && errno == 0) { result = r; } } @@ -436,21 +664,17 @@ double celix_properties_getAsDouble(const celix_properties_t *props, const char } void celix_properties_setDouble(celix_properties_t *props, const char *key, double val) { - char buf[32]; //should be enough to store long long int - int writen = snprintf(buf, 32, "%f", val); - if (writen <= 31) { - celix_properties_set(props, key, buf); - } else { - fprintf(stderr,"buf to small for value '%f'\n", val); - } + celix_properties_createAndSetEntry(props, key, NULL, NULL, &val, NULL, NULL); } bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue) { bool result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { + celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { + return entry->typed.boolValue; + } else if (entry != NULL) { char buf[32]; - snprintf(buf, 32, "%s", val); + snprintf(buf, 32, "%s", entry->value); char *trimmed = utils_stringTrim(buf); if (strncasecmp("true", trimmed, strlen("true")) == 0) { result = true; @@ -462,9 +686,29 @@ bool celix_properties_getAsBool(const celix_properties_t *props, const char *key } void celix_properties_setBool(celix_properties_t *props, const char *key, bool val) { - celix_properties_set(props, key, val ? "true" : "false"); + celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, &val, NULL); +} + +const celix_version_t* celix_properties_getAsVersion( + const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue) { + celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + return entry->typed.versionValue; + } else if (entry != NULL) { + //NOTE not converting to version, due to ownership + //TODO improve? + return NULL; + } + return NULL; } +void celix_properties_setVersion(celix_properties_t *props, const char *key, const celix_version_t* version) { + celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, NULL, version); +} + + int celix_properties_size(const celix_properties_t *properties) { return (int)celix_stringHashMap_size(properties->map); } @@ -475,12 +719,15 @@ typedef struct { } celix_properties_iterator_internal_t; celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties) { - CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(celix_properties_iterator_t)); celix_properties_iterator_internal_t internalIter; internalIter.mapIter = celix_stringHashMap_begin(properties->map); internalIter.props = properties; celix_properties_iterator_t iter; + iter.index = -1; + memset(&iter.entry, 0, sizeof(iter.entry)); + + CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(iter._data)); memset(&iter._data, 0, sizeof(iter._data)); memcpy(iter._data, &internalIter, sizeof(internalIter)); return iter; @@ -488,22 +735,63 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { celix_properties_iterator_internal_t internalIter; - memcpy(&internalIter, iter, sizeof(internalIter)); + memcpy(&internalIter, iter->_data, sizeof(internalIter)); + //celix_stringHashMapIterator_next(&internalIter.mapIter); return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter); } const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) { celix_properties_iterator_internal_t internalIter; - memcpy(&internalIter, iter, sizeof(internalIter)); + memcpy(&internalIter, iter->_data, sizeof(internalIter)); //note assigning key first and then move the next, because celix string hash map iter start at the beginning const char* key = internalIter.mapIter.key; + iter->index = (int)internalIter.mapIter.index; + celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue; + if (entry != NULL) { + memcpy(&iter->entry, iter, sizeof(iter->entry)); + } else { + memset(&iter->entry, 0, sizeof(iter->entry)); + } celix_stringHashMapIterator_next(&internalIter.mapIter); - memcpy(iter, &internalIter, sizeof(internalIter)); + memcpy(iter->_data, &internalIter, sizeof(internalIter)); return key; } +celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties) { + CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(celix_properties_iterator_t)); + celix_properties_iterator_internal_t internalIter; + internalIter.mapIter = celix_stringHashMap_begin(properties->map); + internalIter.props = properties; + + celix_properties_iterator_t iter; + iter.index = 0; + memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); + + memset(&iter._data, 0, sizeof(iter._data)); + memcpy(iter._data, &internalIter, sizeof(internalIter)); + return iter; +} + +void celix_propertiesIterator_next(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter->_data, sizeof(internalIter)); + celix_stringHashMapIterator_next(&internalIter.mapIter); + memcpy(iter->_data, &internalIter, sizeof(internalIter)); + if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { + memset(&iter->entry, 0, sizeof(iter->entry)); + } else { + memcpy(&iter->entry, internalIter.mapIter.value.ptrValue, sizeof(iter->entry)); + } +} + +bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter->_data, sizeof(internalIter)); + return celix_stringHashMapIterator_isEnd(&internalIter.mapIter); +} + bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b) { celix_properties_iterator_internal_t internalIterA; memcpy(&internalIterA, a, sizeof(internalIterA)); @@ -513,10 +801,7 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const internalIterA.mapIter.key == internalIterB.mapIter.key; } - -//TODO can the return be const? -//TODO and can this be removed -> look into C++ PropetiesIter -celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter) { +celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter) { celix_properties_iterator_internal_t internalIter; memcpy(&internalIter, iter, sizeof(internalIter)); return (celix_properties_t*)internalIter.props; diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index f67bacc1d..f83fe6555 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -286,6 +286,15 @@ char* celix_version_toString(const celix_version_t* version) { return string; } +bool celix_version_fillString(const celix_version_t* version, char *str, size_t strLen) { + int written; + if (strnlen(version->qualifier, 1) > 0) { + written = snprintf(str, strLen, "%d.%d.%d.%s", version->major, version->minor, version->micro, version->qualifier); + } else { + written = snprintf(str, strLen, "%d.%d.%d", version->major, version->minor, version->micro); + } + return written >= 0 && written < strLen; +} bool celix_version_isCompatible(const celix_version_t* user, const celix_version_t* provider) { if (user == NULL && provider == NULL) { From 02b08a110d221aab7f65bf14b3af7d4d48692fb0 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Tue, 3 Jan 2023 22:55:24 +0100 Subject: [PATCH 04/21] Improve celix properties iterator --- libs/utils/gtest/src/PropertiesTestSuite.cc | 61 +++++++- libs/utils/include/celix/Properties.h | 26 ++-- libs/utils/include/celix_long_hash_map.h | 9 +- libs/utils/include/celix_properties.h | 145 ++++++++++---------- libs/utils/include/celix_string_hash_map.h | 6 +- libs/utils/src/properties.c | 15 +- 6 files changed, 158 insertions(+), 104 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index b29802823..1528ddb27 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -233,6 +233,20 @@ TEST_F(PropertiesTestSuite, boolTest) { celix_properties_destroy(properties); } +TEST_F(PropertiesTestSuite, fillTest) { + celix_properties_t *props = celix_properties_create(); + int testCount = 1000; + for (int i = 0; i < 1000; ++i) { + char k[5]; + snprintf(k, sizeof(k), "%i", i); + const char* v = "a"; + celix_properties_set(props, k, v); + } + EXPECT_EQ(celix_properties_size(props), testCount); + celix_properties_destroy(props); +} + + TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_t *props = celix_properties_create(); EXPECT_EQ(0, celix_properties_size(props)); @@ -274,12 +288,12 @@ TEST_F(PropertiesTestSuite, getType) { } TEST_F(PropertiesTestSuite, getEntry) { - auto *props = celix_properties_create(); + auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_setLong(props, "key2", 123); celix_properties_setDouble(props, "key3", 123.456); celix_properties_setBool(props, "key4", true); - auto *version = celix_version_createVersion(1, 2, 3, nullptr); + auto* version = celix_version_createVersion(1, 2, 3, nullptr); celix_properties_setVersion(props, "key5", version); auto entry = celix_properties_getEntry(props, "key1"); @@ -316,7 +330,7 @@ TEST_F(PropertiesTestSuite, getEntry) { } TEST_F(PropertiesTestSuite, iteratorNextKey) { - auto *props = celix_properties_create(); + auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); celix_properties_set(props, "key3", "value3"); @@ -336,7 +350,7 @@ TEST_F(PropertiesTestSuite, iteratorNextKey) { } TEST_F(PropertiesTestSuite, iteratorNext) { - auto *props = celix_properties_create(); + auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); celix_properties_set(props, "key3", "value3"); @@ -357,7 +371,7 @@ TEST_F(PropertiesTestSuite, iteratorNext) { } TEST_F(PropertiesTestSuite, iterateOverProperties) { - celix_properties_t* props = celix_properties_create(); + auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); @@ -378,4 +392,41 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) { EXPECT_EQ(innerCount, 4); celix_properties_destroy(props); + + + props = celix_properties_create(); + int count = 0; + CELIX_PROPERTIES_ITERATE(props, outerIter) { + count++; + } + EXPECT_EQ(count, 0); + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, getAsVersion) { + auto* properties = celix_properties_create(); + + // Test getting a version property + auto* expected = celix_version_createVersion(1, 2, 3, "test"); + celix_properties_setVersion(properties, "key", expected); + const auto* actual = celix_properties_getAsVersion(properties, "key", nullptr); + EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); + EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual)); + EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual)); + EXPECT_STREQ(celix_version_getQualifier(expected), celix_version_getQualifier(actual)); + + // Test getting a non-version property + celix_properties_set(properties, "key2", "value"); + auto* emptyVersion = celix_version_createEmptyVersion(); + actual = celix_properties_getAsVersion(properties, "key2", emptyVersion); + EXPECT_EQ(celix_version_getMajor(actual), 0); + EXPECT_EQ(celix_version_getMinor(actual), 0); + EXPECT_EQ(celix_version_getMicro(actual), 0); + EXPECT_STREQ(celix_version_getQualifier(actual), ""); + + EXPECT_EQ(celix_properties_getAsVersion(properties, "non-existent", nullptr), nullptr); + + celix_version_destroy(expected); + celix_version_destroy(emptyVersion); + celix_properties_destroy(properties); } \ No newline at end of file diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index da6536168..111c7fe47 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -34,14 +34,8 @@ namespace celix { */ class PropertiesIterator { public: - explicit PropertiesIterator(celix_properties_t* props) { - iter = celix_propertiesIterator_construct(props); - next(); - } - explicit PropertiesIterator(const celix_properties_t* props) { - iter = celix_propertiesIterator_construct(props); - next(); + iter = celix_properties_begin(props); } PropertiesIterator& operator++() { @@ -65,17 +59,17 @@ namespace celix { } void next() { - if (celix_propertiesIterator_hasNext(&iter)) { - auto* k = celix_propertiesIterator_nextKey(&iter); - auto* props = celix_propertiesIterator_properties(&iter); - auto *v = celix_properties_get(props, k, ""); - first = std::string{(const char*)k}; - second = std::string{(const char*)v}; - } else { + celix_propertiesIterator_next(&iter); + if (celix_propertiesIterator_isEnd(&iter)) { moveToEnd(); + } else { + first = iter.entry.key; + second = iter.entry.value; + end = false; } } + //TODO try to remove moveToEnd void moveToEnd() { first = {}; second = {}; @@ -85,6 +79,7 @@ namespace celix { //TODO for C++17 try to update first and second to stringview std::string first{}; std::string second{}; + //TODO iter? private: celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}}; bool end{false}; @@ -135,6 +130,8 @@ namespace celix { } #endif + //TODO operator= with long, double, boolean and version + [[nodiscard]] const char* getValue() const { if (charKey == nullptr) { return celix_properties_get(props.get(), stringKey.c_str(), nullptr); @@ -306,6 +303,7 @@ namespace celix { std::string_view view{value}; celix_properties_set(cProps.get(), key.data(), view.data()); } else { + //TODO spit up to use setLong, setBool, setDouble and setVersion using namespace std; celix_properties_set(cProps.get(), key.data(), to_string(value).c_str()); } diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index c704ab602..50bf810d3 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -261,8 +261,10 @@ bool celix_longHashMap_remove(celix_long_hash_map_t* map, long key); void celix_longHashMap_clear(celix_long_hash_map_t* map); /** - * @brief Create and return a hash map iterator for the beginning of the hash map. + * @brief Returns an iterator pointing to the first element in the map. * + * @param map The map to get the iterator for. + * @return An iterator pointing to the first element in the map. */ celix_long_hash_map_iterator_t celix_longHashMap_begin(const celix_long_hash_map_t* map); @@ -284,7 +286,7 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); * @brief Marco to loop over all the entries of a long hash map. * * Small example of how to use the iterate macro: - * @code + * @code{.c} * celix_long_hash_map_t* map = ... * CELIX_LONG_HASH_MAP_ITERATE(map, iter) { * printf("Visiting hash map entry with key %li\n", inter.key); @@ -294,7 +296,6 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); * @param map The (const celix_long_hash_map_t*) map to iterate over. * @param iterName A iterName which will be of type celix_long_hash_map_iterator_t to hold the iterator. */ -//TODO test if the macro can be used nested #define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \ for (celix_long_hash_map_iterator_t iterName = celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); celix_longHashMapIterator_next(&(iterName))) @@ -302,7 +303,7 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); * @brief Remove the hash map entry for the provided iterator and updates the iterator to the next hash map entry * * Small example of how to use the celix_longHashMapIterator_remove function: - * @code + * @code{.c} * //remove all even entries * celix_long_hash_map_t* map = ... * celix_long_hash_map_iterator_t iter = celix_longHashMap_begin(map); diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 3c8766978..45846506c 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -43,6 +43,8 @@ extern "C" { /** * @brief celix_properties_t is a type that represents a set of key-value pairs called properties, * which can be used to store configuration data or metadata for a services, components or framework configuration. + * + * @note Not thread safe. */ typedef struct celix_properties celix_properties_t; @@ -293,15 +295,13 @@ void celix_properties_setVersion(celix_properties_t* properties, const char* key /** * @brief Gets the value of a property as a Celix version. * - * //TODO, maybe improve, now returns NULL if underlining type is not version + * This function does not convert a string property value to a Celix version automatically. * - * @param properties The property set to search. - * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set, the value is not a Celix version, or if - * the value cannot be converted to a Celix version. - * @return The value of the property as a Celix version, or the default value if the property is not set, - * the value is not a Celix version, or if the value cannot be converted to a Celix version. - * If the value is a string, it will be verified as a valid Celix version. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. + * @return The value of the property if it is a Celix version, or the default value if the property is not set or the + * value is not a Celix version. */ const celix_version_t* celix_properties_getAsVersion( const celix_properties_t* properties, @@ -316,41 +316,6 @@ const celix_version_t* celix_properties_getAsVersion( */ int celix_properties_size(const celix_properties_t* properties); -/** - * @brief Constructs a new properties iterator. - * - * Note: The iterator is initialized to be before the first entry. To advance to the first entry, - * call `celix_propertiesIterator_nextEntry`. - * - * //TODO deprecate? - * - * @param[in] properties The properties object to iterate over. - * @return The newly constructed iterator. - */ -celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties); - -/** - * @brief Determines whether the iterator has more entries. - * - * //TODO deprecate? - * - * @param[in] iter The iterator. - * @return true if the iterator has more entries, false otherwise. - */ -bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); - -/** - * @brief Advances the iterator to the next entry and returns the key for the current entry. - * - * If the iterator has no more entries, this function returns NULL. - * - * //TODO deprecate? - * - * @param[in, out] iter The iterator. - * @return The key for the current entry, or NULL if the iterator has no more entries. - */ -const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter); - /** * @brief Constructs an iterator pointing to the first entry in the properties object. * @@ -393,13 +358,76 @@ celix_properties_t* celix_propertiesIterator_properties(const celix_properties_i */ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); +/** + * @brief Iterates over the entries in the specified celix_properties_t object. + * + * This macro allows you to easily iterate over the entries in a celix_properties_t object. + * The loop variable `iterName` will be of type celix_properties_iterator_t and will contain the current + * entry during each iteration. + * + * @param map The properties object to iterate over. + * @param iterName The name of the iterator variable to use in the loop. + * + * Example usage: + * @code{.c} + * // Iterate over all entries in the properties object + * CELIX_PROPERTIES_ITERATE(properties, iter) { + * // Print the key and value of the current entry + * printf("%s: %s\n", iter.entry.key, iter.entry.value); + * } + * @endcode + */ +#define CELIX_PROPERTIES_ITERATE(map, iterName) \ + for (celix_properties_iterator_t iterName = celix_properties_begin(map); \ + !celix_propertiesIterator_isEnd(&(iterName)); celix_propertiesIterator_next(&(iterName))) + + + + +/**** Deprecated API *************************************************************************************************/ + +/** + * @brief Constructs a new properties iterator. + * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. + * + * Note: The iterator is initialized to be before the first entry. To advance to the first entry, + * call `celix_propertiesIterator_nextEntry`. + * + * @param[in] properties The properties object to iterate over. + * @return The newly constructed iterator. + */ +celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties) + __attribute__((deprecated("celix_propertiesIterator_construct is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); + +/** + * @brief Determines whether the iterator has more entries. + * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. + * + * @param[in] iter The iterator. + * @return true if the iterator has more entries, false otherwise. + */ +bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) + __attribute__((deprecated("celix_propertiesIterator_hasNext is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); + +/** + * @brief Advances the iterator to the next entry and returns the key for the current entry. + * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. + * + * If the iterator has no more entries, this function returns NULL. + * + * @param[in, out] iter The iterator. + * @return The key for the current entry, or NULL if the iterator has no more entries. + */ +const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter) + __attribute__((deprecated("celix_propertiesIterator_nextKey is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); + /** * @brief Macro for iterating over the properties in a property set. + * @deprecated This macro is deprecated, use CELIX_PROPERTIES_ITERATE instead. * * @param[in] properties The property set to iterate over. * @param[out] key The variable to use for the current key in the loop. * - * //TODO deprecate * * Example usage: * @code{.c} @@ -421,40 +449,11 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const * key3 = value3 * @endcode */ -#define CELIX_PROPERTIES_FOR_EACH(map, keyName) \ - for (celix_properties_iterator_t iter_##keyName = celix_properties_begin(map); \ - (keyName) = iter_##keyName.entry.key, !celix_propertiesIterator_isEnd(&(iter_##keyName)); \ - celix_propertiesIterator_next(&(iter_##keyName))) - -/* #define CELIX_PROPERTIES_FOR_EACH(properties, key) \ for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(properties); \ celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) -*/ -/** - * @brief Iterates over the entries in the specified celix_properties_t object. - * - * This macro allows you to easily iterate over the entries in a celix_properties_t object. - * The loop variable `iterName` will be of type celix_properties_iterator_t and will contain the current - * entry during each iteration. - * - * @param map The properties object to iterate over. - * @param iterName The name of the iterator variable to use in the loop. - * - * Example usage: - * @code{.c} - * // Iterate over all entries in the properties object - * CELIX_PROPERTIES_ITERATE(properties, iter) { - * // Print the key and value of the current entry - * printf("%s: %s\n", iter.entry.key, iter.entry.value); - * } - * @endcode - */ -#define CELIX_PROPERTIES_ITERATE(map, iterName) \ - for (celix_properties_iterator_t iterName = celix_properties_begin(map); \ - !celix_propertiesIterator_isEnd(&(iterName)); celix_propertiesIterator_next(&(iterName))) #ifdef __cplusplus } diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index 97c1735b6..1e5fbe828 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -270,7 +270,10 @@ bool celix_stringHashMap_remove(celix_string_hash_map_t* map, const char* key); void celix_stringHashMap_clear(celix_string_hash_map_t* map); /** - * @brief Create and return a hash map iterator for the beginning of the hash map. + * @brief Returns an iterator pointing to the first element in the map. + * + * @param map The map to get the iterator for. + * @return An iterator pointing to the first element in the map. */ celix_string_hash_map_iterator_t celix_stringHashMap_begin(const celix_string_hash_map_t* map); @@ -303,7 +306,6 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); * @param map The (const celix_string_hash_map_t*) map to iterate over. * @param iterName A iterName which will be of type celix_string_hash_map_iterator_t to hold the iterator. */ -//TODO test if the macro can be used nested #define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \ for (celix_string_hash_map_iterator_t iterName = celix_stringHashMap_begin(map); !celix_stringHashMapIterator_isEnd(&(iterName)); celix_stringHashMapIterator_next(&(iterName))) diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index af8b892f1..9185fa64b 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -387,6 +387,9 @@ static void celix_properties_createAndSetEntry( const double* doubleValue, const bool* boolValue, const celix_version_t* versionValue) { + if (properties == NULL) { + return; + } celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue, boolValue, versionValue); if (entry != NULL) { @@ -696,12 +699,8 @@ const celix_version_t* celix_properties_getAsVersion( celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { return entry->typed.versionValue; - } else if (entry != NULL) { - //NOTE not converting to version, due to ownership - //TODO improve? - return NULL; } - return NULL; + return defaultValue; } void celix_properties_setVersion(celix_properties_t *props, const char *key, const celix_version_t* version) { @@ -767,7 +766,11 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro celix_properties_iterator_t iter; iter.index = 0; - memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); + if (!celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { + memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); + } else { + memset(&iter.entry, 0, sizeof(iter.entry)); + } memset(&iter._data, 0, sizeof(iter._data)); memcpy(iter._data, &internalIter, sizeof(internalIter)); From 2c5194ff1b254d88c8a112c28307233a044ca970 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Wed, 4 Jan 2023 20:21:32 +0100 Subject: [PATCH 05/21] Add unit test for celix types properties and fix some mem issues --- libs/utils/gtest/src/PropertiesTestSuite.cc | 23 +- libs/utils/gtest/src/VersionTestSuite.cc | 46 ++-- libs/utils/include/celix/Properties.h | 44 +-- libs/utils/include/celix_properties.h | 26 +- libs/utils/include/celix_utils.h | 16 +- libs/utils/include/celix_version.h | 8 +- libs/utils/src/properties.c | 281 ++++++++++---------- libs/utils/src/utils.c | 19 +- libs/utils/src/version.c | 9 +- 9 files changed, 273 insertions(+), 199 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 1528ddb27..f0537f752 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -284,6 +284,7 @@ TEST_F(PropertiesTestSuite, getType) { EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props, "version")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, celix_properties_getType(props, "missing")); + celix_version_destroy(version); celix_properties_destroy(props); } @@ -326,6 +327,7 @@ TEST_F(PropertiesTestSuite, getEntry) { entry = celix_properties_getEntry(props, "key6"); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, entry.valueType); + celix_version_destroy(version); celix_properties_destroy(props); } @@ -403,13 +405,14 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, getAsVersion) { +TEST_F(PropertiesTestSuite, getVersion) { auto* properties = celix_properties_create(); + auto* emptyVersion = celix_version_createEmptyVersion(); // Test getting a version property auto* expected = celix_version_createVersion(1, 2, 3, "test"); celix_properties_setVersion(properties, "key", expected); - const auto* actual = celix_properties_getAsVersion(properties, "key", nullptr); + const auto* actual = celix_properties_getVersion(properties, "key", nullptr); EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual)); EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual)); @@ -417,16 +420,22 @@ TEST_F(PropertiesTestSuite, getAsVersion) { // Test getting a non-version property celix_properties_set(properties, "key2", "value"); - auto* emptyVersion = celix_version_createEmptyVersion(); - actual = celix_properties_getAsVersion(properties, "key2", emptyVersion); + actual = celix_properties_getVersion(properties, "key2", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 0); EXPECT_EQ(celix_version_getMinor(actual), 0); EXPECT_EQ(celix_version_getMicro(actual), 0); EXPECT_STREQ(celix_version_getQualifier(actual), ""); + EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", nullptr), nullptr); + celix_version_destroy(expected); - EXPECT_EQ(celix_properties_getAsVersion(properties, "non-existent", nullptr), nullptr); + // Test setting without copy + celix_properties_setVersionWithoutCopy(properties, "key3", celix_version_createVersion(3,3,3,"")); + actual = celix_properties_getVersion(properties, "key3", emptyVersion); + EXPECT_EQ(celix_version_getMajor(actual), 3); + EXPECT_EQ(celix_version_getMinor(actual), 3); + EXPECT_EQ(celix_version_getMicro(actual), 3); + EXPECT_STREQ(celix_version_getQualifier(actual), ""); - celix_version_destroy(expected); celix_version_destroy(emptyVersion); celix_properties_destroy(properties); -} \ No newline at end of file +} diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index a0874ecf7..90e5e9f1c 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -68,23 +68,25 @@ TEST_F(VersionTestSuite, create) { free(str); } -TEST_F(VersionTestSuite, clone) { - celix_version_t* version = nullptr; - celix_version_t* clone = nullptr; - char * str; - - str = celix_utils_strdup("abc"); - EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); - EXPECT_EQ(CELIX_SUCCESS, version_clone(version, &clone)); - EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, clone->major); - EXPECT_EQ(2, clone->minor); - EXPECT_EQ(3, clone->micro); - EXPECT_STREQ("abc", clone->qualifier); +TEST_F(VersionTestSuite, copy) { + auto* version = celix_version_create(1, 2, 3, "abc"); + auto* copy = celix_version_copy(version); + EXPECT_NE(nullptr, version); + EXPECT_NE(nullptr, copy); + EXPECT_EQ(1, celix_version_getMajor(copy)); + EXPECT_EQ(2, celix_version_getMinor(copy)); + EXPECT_EQ(3, celix_version_getMicro(copy)); + EXPECT_STREQ("abc", celix_version_getQualifier(copy)); + celix_version_destroy(copy); + celix_version_destroy(version); - version_destroy(clone); - version_destroy(version); - free(str); + copy = celix_version_copy(nullptr); //returns "empty" version + EXPECT_NE(nullptr, copy); + EXPECT_EQ(0, celix_version_getMajor(copy)); + EXPECT_EQ(0, celix_version_getMinor(copy)); + EXPECT_EQ(0, celix_version_getMicro(copy)); + EXPECT_STREQ("", celix_version_getQualifier(copy)); + celix_version_destroy(copy); } TEST_F(VersionTestSuite, createFromString) { @@ -277,8 +279,8 @@ TEST_F(VersionTestSuite, compare) { } TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) { - celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr); - celix_version_t *version2 = celix_version_createVersion(2, 2, 4, "qualifier"); + auto* version1 = celix_version_create(2, 2, 0, nullptr); + auto* version2 = celix_version_create(2, 2, 4, "qualifier"); EXPECT_EQ(0, celix_version_compareToMajorMinor(version1, 2, 2)); EXPECT_EQ(0, celix_version_compareToMajorMinor(version2, 2, 2)); @@ -367,8 +369,8 @@ TEST_F(VersionTestSuite,semanticCompatibility) { TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) { //nullptr or "" qualifier should be the same - auto* v1 = celix_version_createVersion(0, 0, 0, nullptr); - auto* v2 = celix_version_createVersion(0, 0, 0, ""); + auto* v1 = celix_version_create(0, 0, 0, nullptr); + auto* v2 = celix_version_create(0, 0, 0, ""); EXPECT_EQ(0, celix_version_compareTo(v1, v1)); EXPECT_EQ(0, celix_version_compareTo(v1, v2)); EXPECT_EQ(0, celix_version_compareTo(v2, v2)); @@ -379,7 +381,7 @@ TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) { TEST_F(VersionTestSuite, fillString) { // Create a version object - celix_version_t* version = celix_version_createVersion(1, 2, 3, "alpha"); + auto* version = celix_version_create(1, 2, 3, "alpha"); // Test with buffer large enough to hold the formatted string char buffer[32]; @@ -392,4 +394,4 @@ TEST_F(VersionTestSuite, fillString) { EXPECT_FALSE(success); celix_version_destroy(version); -} \ No newline at end of file +} diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 111c7fe47..682fe490c 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -36,6 +36,7 @@ namespace celix { public: explicit PropertiesIterator(const celix_properties_t* props) { iter = celix_properties_begin(props); + setFields(); } PropertiesIterator& operator++() { @@ -60,27 +61,27 @@ namespace celix { void next() { celix_propertiesIterator_next(&iter); - if (celix_propertiesIterator_isEnd(&iter)) { - moveToEnd(); - } else { - first = iter.entry.key; - second = iter.entry.value; - end = false; - } + setFields(); } - //TODO try to remove moveToEnd void moveToEnd() { first = {}; second = {}; end = true; } - //TODO for C++17 try to update first and second to stringview std::string first{}; std::string second{}; - //TODO iter? private: + void setFields() { + if (celix_propertiesIterator_isEnd(&iter)) { + moveToEnd(); + } else { + first = iter.entry.key; + second = iter.entry.value; + } + } + celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}}; bool end{false}; }; @@ -130,8 +131,6 @@ namespace celix { } #endif - //TODO operator= with long, double, boolean and version - [[nodiscard]] const char* getValue() const { if (charKey == nullptr) { return celix_properties_get(props.get(), stringKey.c_str(), nullptr); @@ -140,6 +139,8 @@ namespace celix { } } + //TODO get typed value + operator std::string() const { auto *cstr = getValue(); return cstr == nullptr ? std::string{} : std::string{cstr}; @@ -290,6 +291,10 @@ namespace celix { return celix_properties_getAsBool(cProps.get(), key.data(), defaultValue); } + //TODO getType + + //TODO getAsVersion + /** * @brief Sets a T&& property. Will use (std::) to_string to convert the value to string. */ @@ -367,6 +372,8 @@ namespace celix { using namespace std; celix_properties_set(cProps.get(), key.c_str(), to_string(value).c_str()); } + + //TODO set long, double, boolean and version #endif /** @@ -382,7 +389,7 @@ namespace celix { [[nodiscard]] std::map convertToMap() const { std::map result{}; for (const auto& pair : *this) { - result[pair.first] = pair.second; + result[std::string{pair.first}] = pair.second; } return result; } @@ -393,7 +400,7 @@ namespace celix { [[nodiscard]] std::unordered_map convertToUnorderedMap() const { std::unordered_map result{}; for (const auto& pair : *this) { - result[pair.first] = pair.second; + result[std::string{pair.first}] = pair.second; } return result; } @@ -411,8 +418,13 @@ namespace celix { #endif - //TODO save - //TODO load + //TODO test + void store(const std::string& file, const std::string& header = {}) const { + celix_properties_store(cProps.get(), file.c_str(), header.empty() ? nullptr : header.c_str()); + } + + //TODO laod + private: explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {} diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 45846506c..e484a436a 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -284,14 +284,30 @@ void celix_properties_setDouble(celix_properties_t* properties, const char* key, double celix_properties_getAsDouble(const celix_properties_t* properties, const char* key, double defaultValue); /** - * @brief Sets the value of a property as a Celix version string. + * @brief Sets the value of a property as a Celix version. * - * @param properties The property set to modify. - * @param key The key of the property to set. - * @param version The value to set. + * This function will make a copy of the provided celix_version_t object and store it in the property set. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] version The value to set. The function will make a copy of this object and store it in the property set. */ void celix_properties_setVersion(celix_properties_t* properties, const char* key, const celix_version_t* version); +/** + * @brief Sets the value of a property as a Celix version. + * + * This function will store a reference to the provided celix_version_t object in the property set and takes + * ownership of the provided version. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] version The value to set. The function will store a reference to this object in the property set and + * takes ownership of the provided version. + */ +void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version); + + /** * @brief Gets the value of a property as a Celix version. * @@ -303,7 +319,7 @@ void celix_properties_setVersion(celix_properties_t* properties, const char* key * @return The value of the property if it is a Celix version, or the default value if the property is not set or the * value is not a Celix version. */ -const celix_version_t* celix_properties_getAsVersion( +const celix_version_t* celix_properties_getVersion( const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); diff --git a/libs/utils/include/celix_utils.h b/libs/utils/include/celix_utils.h index e0db27312..791d8f89b 100644 --- a/libs/utils/include/celix_utils.h +++ b/libs/utils/include/celix_utils.h @@ -54,11 +54,23 @@ bool celix_utils_stringEquals(const char* a, const char* b); /** * @brief Returns a trimmed string. * - * The trim will remove eny leading and trailing whitespaces (' ', '\t', etc based on `isspace`)/ - * Caller is owner of the returned string. + * This function will remove any leading and trailing whitespaces (' ', '\t', etc based on isspace) from the + * input string. + * + * @param[in] string The input string to be trimmed. + * @return A trimmed version of the input string. The caller is responsible for freeing the memory of this string. */ char* celix_utils_trim(const char* string); +/** + * @brief Trims a string in place. + * + * The trim will remove any leading and trailing whitespaces (' ', '\t', etc based on isspace) from the input string. + * The input string is modified in place. + * @param[in,out] string The string to trim. + */ +void celix_utils_trimInPlace(char* string); + /** * @brief Check if a string is NULL or empty "". */ diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index be03aeef1..f4cea3907 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -42,7 +42,13 @@ typedef struct celix_version celix_version_t; * the empty string. * @return The created version or NULL if the input was incorrect */ -celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char* qualifier); +celix_version_t* celix_version_create(int major, int minor, int micro, const char* qualifier); + +/** + * @brief Same as celix_version_create, but deprecated + */ +celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char* qualifier) + __attribute__((deprecated(("celix_version_createVersion is deprecated use celix_version_create instead")))); void celix_version_destroy(celix_version_t* version); diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 9185fa64b..7be3e7135 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -137,106 +137,6 @@ static void updateBuffers(char **key, char ** value, char **output, int outputPo } } -static void parseLine(const char* line, celix_properties_t *props) { - int linePos = 0; - bool precedingCharIsBackslash = false; - bool isComment = false; - int outputPos = 0; - char *output = NULL; - int key_len = MALLOC_BLOCK_SIZE; - int value_len = MALLOC_BLOCK_SIZE; - linePos = 0; - precedingCharIsBackslash = false; - isComment = false; - output = NULL; - outputPos = 0; - - //Ignore empty lines - if (line[0] == '\n' && line[1] == '\0') { - return; - } - - char *key = calloc(1, key_len); - char *value = calloc(1, value_len); - key[0] = '\0'; - value[0] = '\0'; - - while (line[linePos] != '\0') { - if (line[linePos] == ' ' || line[linePos] == '\t') { - if (output == NULL) { - //ignore - linePos += 1; - continue; - } - } - else { - if (output == NULL) { - output = key; - } - } - if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') { - if (precedingCharIsBackslash) { - //escaped special character - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - precedingCharIsBackslash = false; - } - else { - if (line[linePos] == '#' || line[linePos] == '!') { - if (outputPos == 0) { - isComment = true; - break; - } - else { - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - } - else { // = or : - if (output == value) { //already have a seperator - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - else { - output[outputPos++] = '\0'; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - output = value; - outputPos = 0; - } - } - } - } - else if (line[linePos] == '\\') { - if (precedingCharIsBackslash) { //double backslash -> backslash - output[outputPos++] = '\\'; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - precedingCharIsBackslash = true; - } - else { //normal character - precedingCharIsBackslash = false; - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - linePos += 1; - } - if (output != NULL) { - output[outputPos] = '\0'; - } - - if (!isComment) { - //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); - celix_properties_set(props, utils_stringTrim(key), utils_stringTrim(value)); - } - if(key) { - free(key); - } - if(value) { - free(value); - } - -} - /** * Create a new string from the provided str by either using strup or storing the string the short properties * optimization string buffer. @@ -266,7 +166,7 @@ static celix_status_t celix_properties_fillEntry( const long* longValue, const double* doubleValue, const bool* boolValue, - const celix_version_t* versionValue) { + celix_version_t* versionValue) { char convertedValueBuffer[32]; entry->key = celix_properties_createString(properties, key); if (strValue != NULL) { @@ -302,8 +202,7 @@ static celix_status_t celix_properties_fillEntry( } else /*versionValue*/ { assert(versionValue != NULL); entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION; - entry->typed.versionValue = celix_version_copy(versionValue); - + entry->typed.versionValue = versionValue; bool written = celix_version_fillString(versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); if (written) { entry->value = celix_properties_createString(properties, convertedValueBuffer); @@ -360,7 +259,7 @@ static celix_properties_entry_t* celix_properties_createEntry( const long* longValue, const double* doubleValue, const bool* boolValue, - const celix_version_t* versionValue) { + celix_version_t* versionValue) { celix_properties_entry_t* entry = celix_properties_allocEntry(properties); if (entry == NULL) { return NULL; @@ -386,7 +285,7 @@ static void celix_properties_createAndSetEntry( const long* longValue, const double* doubleValue, const bool* boolValue, - const celix_version_t* versionValue) { + celix_version_t* versionValue) { if (properties == NULL) { return; } @@ -415,6 +314,9 @@ static void celix_properties_removeEntryCallback(void* handle, const char* key _ celix_properties_entry_t* entry = val.ptrValue; celix_properties_freeString(properties, (char*)entry->key); celix_properties_freeString(properties, (char*)entry->value); + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + celix_version_destroy(entry->typed.versionValue); + } if (entry >= properties->entriesBuffer && entry <= (properties->entriesBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE)) { //entry is part of the properties entries buffer -> nop. @@ -423,7 +325,6 @@ static void celix_properties_removeEntryCallback(void* handle, const char* key _ } } - celix_properties_t* celix_properties_create(void) { celix_properties_t* props = malloc(sizeof(*props)); if (props != NULL) { @@ -456,38 +357,143 @@ celix_properties_t* celix_properties_load(const char *filename) { return props; } -celix_properties_t* celix_properties_loadWithStream(FILE *file) { - celix_properties_t *props = NULL; - - if (file != NULL ) { - char *saveptr; - char *filebuffer = NULL; - char *line = NULL; - size_t file_size = 0; - - props = celix_properties_create(); - fseek(file, 0, SEEK_END); - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - if (file_size > 0) { - filebuffer = calloc(file_size + 1, sizeof(char)); - if (filebuffer) { - size_t rs = fread(filebuffer, sizeof(char), file_size, file); - if (rs != file_size) { - fprintf(stderr,"fread read only %lu bytes out of %lu\n", (long unsigned int) rs, (long unsigned int) file_size); +static void parseLine(const char* line, celix_properties_t *props) { + int linePos = 0; + bool precedingCharIsBackslash = false; + bool isComment = false; + int outputPos = 0; + char *output = NULL; + int key_len = MALLOC_BLOCK_SIZE; + int value_len = MALLOC_BLOCK_SIZE; + linePos = 0; + precedingCharIsBackslash = false; + isComment = false; + output = NULL; + outputPos = 0; + + //Ignore empty lines + if (line[0] == '\n' && line[1] == '\0') { + return; + } + + char *key = calloc(1, key_len); + char *value = calloc(1, value_len); + key[0] = '\0'; + value[0] = '\0'; + + while (line[linePos] != '\0') { + if (line[linePos] == ' ' || line[linePos] == '\t') { + if (output == NULL) { + //ignore + linePos += 1; + continue; + } + } + else { + if (output == NULL) { + output = key; + } + } + if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') { + if (precedingCharIsBackslash) { + //escaped special character + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + precedingCharIsBackslash = false; + } + else { + if (line[linePos] == '#' || line[linePos] == '!') { + if (outputPos == 0) { + isComment = true; + break; + } + else { + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } } - filebuffer[file_size]='\0'; - line = strtok_r(filebuffer, "\n", &saveptr); - while (line != NULL) { - parseLine(line, props); - line = strtok_r(NULL, "\n", &saveptr); + else { // = or : + if (output == value) { //already have a seperator + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + else { + output[outputPos++] = '\0'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + output = value; + outputPos = 0; + } } - free(filebuffer); } } + else if (line[linePos] == '\\') { + if (precedingCharIsBackslash) { //double backslash -> backslash + output[outputPos++] = '\\'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + precedingCharIsBackslash = true; + } + else { //normal character + precedingCharIsBackslash = false; + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + linePos += 1; + } + if (output != NULL) { + output[outputPos] = '\0'; } + if (!isComment) { + //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); + celix_utils_trimInPlace(key); + celix_utils_trimInPlace(value); + celix_properties_setWithoutCopy(props, key, value); + } else { + free(key); + free(value); + } + + +} + +celix_properties_t* celix_properties_loadWithStream(FILE *file) { + if (file == NULL) { + return NULL; + } + + celix_properties_t *props = celix_properties_create(); + if (props == NULL) { + return NULL; + } + + fseek(file, 0, SEEK_END); + size_t fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + if (fileSize == 0) { + return props; + } + + char* fileBuffer = malloc(fileSize + 1); + if (fileBuffer == NULL) { + celix_properties_destroy(props); + return NULL; + } + + size_t rs = fread(fileBuffer, sizeof(char), fileSize, file); + if (rs < fileSize) { + fprintf(stderr,"fread read only %zu bytes out of %zu\n", rs, fileSize); + } + fileBuffer[fileSize]='\0'; //ensure a '\0' at the end of the fileBuffer + + char* savePtr = NULL; + char* line = strtok_r(fileBuffer, "\n", &savePtr); + while (line != NULL) { + parseLine(line, props); + line = strtok_r(NULL, "\n", &savePtr); + } + free(fileBuffer); + return props; } @@ -692,7 +698,7 @@ void celix_properties_setBool(celix_properties_t *props, const char *key, bool v celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, &val, NULL); } -const celix_version_t* celix_properties_getAsVersion( +const celix_version_t* celix_properties_getVersion( const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue) { @@ -703,10 +709,13 @@ const celix_version_t* celix_properties_getAsVersion( return defaultValue; } -void celix_properties_setVersion(celix_properties_t *props, const char *key, const celix_version_t* version) { - celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, NULL, version); +void celix_properties_setVersion(celix_properties_t *properties, const char *key, const celix_version_t* version) { + celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, NULL, celix_version_copy(version)); } +void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version) { + celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, NULL, version); +} int celix_properties_size(const celix_properties_t *properties) { return (int)celix_stringHashMap_size(properties->map); @@ -748,7 +757,7 @@ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) iter->index = (int)internalIter.mapIter.index; celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue; if (entry != NULL) { - memcpy(&iter->entry, iter, sizeof(iter->entry)); + memcpy(&iter->entry, entry, sizeof(iter->entry)); } else { memset(&iter->entry, 0, sizeof(iter->entry)); } diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c index 7bbe2f4c6..6ff882a87 100644 --- a/libs/utils/src/utils.c +++ b/libs/utils/src/utils.c @@ -82,12 +82,12 @@ char * string_ndup(const char *s, size_t n) { return ret; } -static char* celix_utilsTrimInternal(char *string) { +void celix_utils_trimInPlace(char* string) { if (string == NULL) { - return NULL; + return; } - char* begin = string; //save begin to correctly free in the end. + char* begin = string; //save begin to check in the end. char *end; // Trim leading space @@ -103,7 +103,7 @@ static char* celix_utilsTrimInternal(char *string) { } if (string != begin) { - //beginning whitespaces -> move char in copy to to begin string + //beginning whitespaces -> move chars to the beginning of string //This to ensure free still works on the same pointer. char* nstring = begin; while(*string != '\0') { @@ -111,16 +111,17 @@ static char* celix_utilsTrimInternal(char *string) { } (*nstring) = '\0'; } - - return begin; } char* celix_utils_trim(const char* string) { - return celix_utilsTrimInternal(celix_utils_strdup(string)); + char* result = celix_utils_strdup(string); + celix_utils_trimInPlace(result); + return result; } char* utils_stringTrim(char* string) { - return celix_utilsTrimInternal(string); + celix_utils_trimInPlace(string); + return string; } bool utils_isStringEmptyOrNull(const char * const str) { @@ -264,4 +265,4 @@ void celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(const char * } else { *outNamespace = namespace; } -} \ No newline at end of file +} diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index f83fe6555..3d0ee1ab7 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -96,6 +96,10 @@ celix_status_t version_isCompatible(version_pt user, version_pt provider, bool* } celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char* qualifier) { + return celix_version_create(major, minor, micro, qualifier); +} + +celix_version_t* celix_version_create(int major, int minor, int micro, const char* qualifier) { if (major < 0 || minor < 0 || micro < 0) { return NULL; } @@ -141,6 +145,9 @@ void celix_version_destroy(celix_version_t* version) { celix_version_t* celix_version_copy(const celix_version_t* version) { + if (version == NULL) { + return celix_version_createEmptyVersion(); + } return celix_version_createVersion(version->major, version->minor, version->micro, version->qualifier); } @@ -322,4 +329,4 @@ int celix_version_compareToMajorMinor(const celix_version_t* version, int majorV result = version->minor - minorVersionPart; } return result; -} \ No newline at end of file +} From 740bbbfbd41c713c018ca50bc1f1df3e564fbb4c Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Thu, 5 Jan 2023 22:31:56 +0100 Subject: [PATCH 06/21] Add properties type support to C++ properties --- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 29 ++ libs/utils/gtest/src/HashMapTestSuite.cc | 28 ++ libs/utils/gtest/src/PropertiesTestSuite.cc | 22 ++ libs/utils/include/celix/Properties.h | 277 +++++++++++++++--- libs/utils/include/celix/Version.h | 121 ++++++++ libs/utils/include/celix_long_hash_map.h | 21 +- libs/utils/include/celix_properties.h | 143 +++++---- libs/utils/include/celix_string_hash_map.h | 26 +- libs/utils/src/celix_hash_map.c | 18 +- libs/utils/src/properties.c | 31 +- libs/utils/src/version.c | 13 +- 11 files changed, 603 insertions(+), 126 deletions(-) create mode 100644 libs/utils/include/celix/Version.h diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index d5acac775..bc60adbc2 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -105,6 +105,35 @@ TEST_F(CxxPropertiesTestSuite, testWrap) { celix_properties_destroy(props); } +TEST_F(CxxPropertiesTestSuite, getType) { + celix::Properties props{}; + + props.set("bool", true); + props.set("long1", 1l); + props.set("long2", (int)1); //should lead to long; + props.set("long3", (unsigned int)1); //should lead to long; + props.set("long4", (short)1); //should lead to long; + props.set("long5", (unsigned short)1); //should lead to long; + props.set("long6", (char)1); //should lead to long; + props.set("long7", (unsigned char)1); //should lead to long; + props.set("double1", 1.0); + props.set("double2", 1.0f); //set float should lead to double + //TODO version + + EXPECT_EQ(props.getType("bool"), celix::Properties::ValueType::Bool); + EXPECT_EQ(props.getType("long1"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long2"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long3"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long4"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long5"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long6"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("long7"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("double1"), celix::Properties::ValueType::Double); + EXPECT_EQ(props.getType("double2"), celix::Properties::ValueType::Double); + +} + + #if __cplusplus >= 201703L //C++17 or higher TEST_F(CxxPropertiesTestSuite, testStringView) { constexpr std::string_view stringViewKey = "KEY1"; diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc b/libs/utils/gtest/src/HashMapTestSuite.cc index fa0e7f622..85a4944f0 100644 --- a/libs/utils/gtest/src/HashMapTestSuite.cc +++ b/libs/utils/gtest/src/HashMapTestSuite.cc @@ -546,3 +546,31 @@ TEST_F(HashMapTestSuite, IterateWithRemoveTest) { EXPECT_TRUE(celix_longHashMapIterator_isEnd(&iter2)); celix_longHashMap_destroy(lMap); } + +TEST_F(HashMapTestSuite, IterateEndTest) { + auto* sMap1 = createStringHashMap(0); + auto* sMap2 = createStringHashMap(6); + auto* lMap1 = createLongHashMap(0); + auto* lMap2 = createLongHashMap(6); + + auto sIter1 = celix_stringHashMap_end(sMap1); + auto sIter2 = celix_stringHashMap_end(sMap2); + auto lIter1 = celix_longHashMap_end(lMap1); + auto lIter2 = celix_longHashMap_end(lMap2); + + EXPECT_EQ(sIter1.index, 0); + EXPECT_EQ(sIter2.index, 6); + EXPECT_EQ(lIter1.index, 0); + EXPECT_EQ(lIter2.index, 6); + EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter1)); + EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter2)); + EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter1)); + EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter2)); + + //TODO loop and test with index. + + celix_stringHashMap_destroy(sMap1); + celix_stringHashMap_destroy(sMap2); + celix_longHashMap_destroy(lMap1); + celix_longHashMap_destroy(lMap2); +} diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index f0537f752..b9b6eb95d 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -439,3 +439,25 @@ TEST_F(PropertiesTestSuite, getVersion) { celix_version_destroy(emptyVersion); celix_properties_destroy(properties); } + +TEST_F(PropertiesTestSuite, TestEndOfProperties) { + auto* props = celix_properties_create(); + celix_properties_set(props, "key1", "value1"); + celix_properties_set(props, "key2", "value2"); + + celix_properties_iterator_t endIter = celix_properties_end(props); + EXPECT_EQ(endIter.index, 2); + EXPECT_TRUE(celix_propertiesIterator_isEnd(&endIter)); + + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) { + auto* props = celix_properties_create(); + + celix_properties_iterator_t endIter = celix_properties_end(props); + EXPECT_EQ(endIter.index, 0); + EXPECT_TRUE(celix_propertiesIterator_isEnd(&endIter)); + + celix_properties_destroy(props); +} diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 682fe490c..c75119a3c 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -26,6 +26,7 @@ #include "celix_properties.h" #include "celix_utils.h" +#include "celix/Version.h" namespace celix { @@ -39,6 +40,11 @@ namespace celix { setFields(); } + explicit PropertiesIterator(celix_properties_iterator_t _iter) { + iter = std::move(_iter); + setFields(); + } + PropertiesIterator& operator++() { next(); return *this; @@ -64,18 +70,13 @@ namespace celix { setFields(); } - void moveToEnd() { - first = {}; - second = {}; - end = true; - } - std::string first{}; std::string second{}; private: void setFields() { if (celix_propertiesIterator_isEnd(&iter)) { - moveToEnd(); + first = {}; + second = {}; } else { first = iter.entry.key; second = iter.entry.value; @@ -97,6 +98,19 @@ namespace celix { public: using const_iterator = PropertiesIterator; + /** + * @brief Enum representing the possible types of a property value. + */ + enum class ValueType { + Unset, /**< Property value is not set. */ + String, /**< Property value is a string. */ + Long, /**< Property value is a long integer. */ + Double, /**< Property value is a double. */ + Bool, /**< Property value is a boolean. */ + Version /**< Property value is a Celix version. */ + }; + + class ValueRef { public: #if __cplusplus >= 201703L //C++17 or higher @@ -139,8 +153,6 @@ namespace celix { } } - //TODO get typed value - operator std::string() const { auto *cstr = getValue(); return cstr == nullptr ? std::string{} : std::string{cstr}; @@ -240,9 +252,7 @@ namespace celix { * @brief end iterator */ [[nodiscard]] const_iterator end() const noexcept { - auto iter = PropertiesIterator{cProps.get()}; - iter.moveToEnd(); - return iter; + return PropertiesIterator{celix_properties_end(cProps.get())}; } /** @@ -256,9 +266,7 @@ namespace celix { * @brief constant end iterator */ [[nodiscard]] const_iterator cend() const noexcept { - auto iter = PropertiesIterator{cProps.get()}; - iter.moveToEnd(); - return iter; + return PropertiesIterator{celix_properties_end(cProps.get())}; } #if __cplusplus >= 201703L //C++17 or higher @@ -271,32 +279,85 @@ namespace celix { } /** - * @brief Get the value as long for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a long. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a long. + * @return The long value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] long getAsLong(std::string_view key, long defaultValue) const { return celix_properties_getAsLong(cProps.get(), key.data(), defaultValue); } /** - * @brief Get the value as double for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a double. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a double. + * @return The double value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] double getAsDouble(std::string_view key, double defaultValue) const { return celix_properties_getAsDouble(cProps.get(), key.data(), defaultValue); } /** - * @brief Get the value as bool for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a boolean. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a boolean. + * @return The boolean value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] bool getAsBool(std::string_view key, bool defaultValue) const { return celix_properties_getAsBool(cProps.get(), key.data(), defaultValue); } - //TODO getType + /** + * @brief Get the value of the property with key as a Celix version. + * + * Note that this function does not automatically convert a string property value to a Celix version. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix + * version. + * @return The value of the property if it is a Celix version, or the default value if the property is not set + * or the value is not a Celix version. + */ + //TODO test + [[nodiscard]] celix::Version getVersion(std::string_view key, celix::Version defaultValue = {}) { + auto* cVersion = celix_properties_getVersion(cProps.get(), key.data(), nullptr); + if (cVersion) { + return celix::Version{ + celix_version_getMajor(cVersion), + celix_version_getMinor(cVersion), + celix_version_getMicro(cVersion), + celix_version_getQualifier(cVersion)}; + } + return defaultValue; + } - //TODO getAsVersion + /** + * @brief Get the type of the property with key. + * + * @param[in] key The key of the property to get the type for. + * @return The type of the property with the given key, or ValueType::Unset if the property + * does not exist. + */ + //TODO test + [[nodiscard]] ValueType getType(std::string_view key) { + return getAndConvertType(cProps, key.data()); + } /** - * @brief Sets a T&& property. Will use (std::) to_string to convert the value to string. + * @brief Set the value of a property. + * + * @tparam T The type of the value to set. This can be one of: bool, std::string_view, a type that is + * convertible to std::string_view, bool, long, double, celix::Version, or celix_version_t*. + * @param[im] key The key of the property to set. + * @param[in] value The value to set. If the type of the value is not one of the above types, it will be + * converted to a string using std::to_string before being set. */ template void set(std::string_view key, T&& value) { @@ -307,8 +368,17 @@ namespace celix { } else if constexpr (std::is_convertible_v) { std::string_view view{value}; celix_properties_set(cProps.get(), key.data(), view.data()); + } else if constexpr (std::is_same_v, bool>) { + celix_properties_setBool(cProps.get(), key.data(), value); + } else if constexpr (std::is_convertible_v, long>) { + celix_properties_setLong(cProps.get(), key.data(), value); + } else if constexpr (std::is_convertible_v, double>) { + celix_properties_setDouble(cProps.get(), key.data(), value); + } else if constexpr (std::is_same_v, celix::Version>) { + celix_properties_setVersion(cProps.get(), key.data(), value.getCVersion()); + } else if constexpr (std::is_same_v) { + celix_properties_setVersion(cProps.get(), key.data(), value); } else { - //TODO spit up to use setLong, setBool, setDouble and setVersion using namespace std; celix_properties_set(cProps.get(), key.data(), to_string(value).c_str()); } @@ -323,68 +393,181 @@ namespace celix { } /** - * @brief Get the value as long for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a long. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a long. + * @return The long value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] long getAsLong(const std::string& key, long defaultValue) const { return celix_properties_getAsLong(cProps.get(), key.c_str(), defaultValue); } /** - * @brief Get the value as double for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a double. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a double. + * @return The double value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] double getAsDouble(const std::string &key, double defaultValue) const { return celix_properties_getAsDouble(cProps.get(), key.c_str(), defaultValue); } /** - * @brief Get the value as bool for a property key or return the defaultValue if the key does not exists. + * @brief Get the value of the property with key as a boolean. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a boolean. + * @return The boolean value of the property if it exists and can be converted, or the default value otherwise. */ [[nodiscard]] bool getAsBool(const std::string &key, bool defaultValue) const { return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue); } + + /** + * @brief Get the value of the property with key as a Celix version. + * + * Note that this function does not automatically convert a string property value to a Celix version. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix + * version. + * @return The value of the property if it is a Celix version, or the default value if the property is not set + * or the value is not a Celix version. + */ + [[nodiscard]] celix::Version getVersion(const std::string& key, celix::Version defaultValue = {}) { + auto* cVersion = celix_properties_getVersion(cProps.get(), key.data(), nullptr); + if (cVersion) { + return celix::Version{ + celix_version_getMajor(cVersion), + celix_version_getMinor(cVersion), + celix_version_getMicro(cVersion), + celix_version_getQualifier(cVersion)}; + } + return defaultValue; + } /** - * @brief Sets a property + * @brief Get the type of the property with key. + * + * @param[in] key The key of the property to get the type for. + * @return The type of the property with the given key, or ValueType::Unset if the property + * does not exist. + */ + [[nodiscard]] ValueType getType(const std::string& key) { + return getAndConvertType(cProps, key.data()); + } + + /** + * @brief Set the value of a property. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set the property to. */ void set(const std::string& key, const std::string& value) { - celix_properties_set(cProps.get(), key.c_str(), value.c_str()); + celix_properties_set(cProps.get(), key.data(), value.c_str()); } /** - * @brief Sets a bool property + * @brief Set the value of a property to a boolean. + * + * @param[in] key The key of the property to set. + * @param[in] value The boolean value to set the property to. */ void set(const std::string& key, bool value) { - celix_properties_setBool(cProps.get(), key.c_str(), value); + celix_properties_setBool(cProps.get(), key.data(), value); } /** - * @brief Sets a const char* property + * @brief Set the value of a property. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set the property to. */ void set(const std::string& key, const char* value) { - celix_properties_set(cProps.get(), key.c_str(), value); + celix_properties_set(cProps.get(), key.data(), value); } /** - * @brief Sets a T property. Will use (std::) to_string to convert the value to string. + * @brief Sets a property with a value of type T. + * + * This function will use the std::to_string function to convert the value of type T to a string, + * which will be used as the value for the property. + * + * @tparam T The type of the value to set. + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. */ template void set(const std::string& key, T&& value) { using namespace std; - celix_properties_set(cProps.get(), key.c_str(), to_string(value).c_str()); + celix_properties_set(cProps.get(), key.data()(), to_string(value).c_str()); + } + + /** + * @brief Sets a bool property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, bool value) { + celix_properties_setBool(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a long property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, long value) { + celix_properties_setLong(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a double property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, double value) { + celix_properties_setDouble(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a celix::Version property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, const celix::Version& value) { + celix_properties_setVersion(cProps.get(), key.data(), value.getCVersion()); } - //TODO set long, double, boolean and version + /** + * @brief Sets a celix_version_t* property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, const celix_version_t* value) { + celix_properties_setVersion(cProps.get(), key.data(), value); + } #endif /** - * @brief Returns the nr of properties. + * @brief Returns the number of properties in the Properties object. */ [[nodiscard]] std::size_t size() const { return celix_properties_size(cProps.get()); } /** - * @brief Converts the properties a (new) std::string, std::string map. + * @brief Convert the properties a (new) std::string, std::string map. */ [[nodiscard]] std::map convertToMap() const { std::map result{}; @@ -395,7 +578,7 @@ namespace celix { } /** - * @brief Converts the properties a (new) std::string, std::string unordered map. + * @brief Convert the properties a (new) std::string, std::string unordered map. */ [[nodiscard]] std::unordered_map convertToUnorderedMap() const { std::unordered_map result{}; @@ -428,6 +611,26 @@ namespace celix { private: explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {} + static celix::Properties::ValueType getAndConvertType( + const std::shared_ptr& cProperties, + const char* key) { + auto cType = celix_properties_getType(cProperties.get(), key); + switch (cType) { + case CELIX_PROPERTIES_VALUE_TYPE_STRING: + return ValueType::String; + case CELIX_PROPERTIES_VALUE_TYPE_LONG: + return ValueType::Long; + case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE: + return ValueType::Double; + case CELIX_PROPERTIES_VALUE_TYPE_BOOL: + return ValueType::Bool; + case CELIX_PROPERTIES_VALUE_TYPE_VERSION: + return ValueType::Version; + default: /*unset*/ + return ValueType::Unset; + } + } + std::shared_ptr cProps; }; diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h new file mode 100644 index 000000000..ce70d8d4c --- /dev/null +++ b/libs/utils/include/celix/Version.h @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pragma once + +#include +#include + +#include "celix_version.h" + +namespace celix { + + //TODO CxxVersionTestSuite + //TODO doxygen + class Version { + public: + Version() : cVersion{createVersion(celix_version_createEmptyVersion())} {} +#if __cplusplus >= 201703L //C++17 or higher + explicit Version(int major, int minor, int micro, std::string_view qualifier = {}) : + cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))} {} +#else + explicit Version(int major, int minor, int micro, const& std::string qualifier = {}) : + cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.c_str()))} {} +#endif + + + Version(Version&&) = default; + + Version& operator=(Version&&) = default; + + Version(const Version& rhs) : cVersion{createVersion(celix_version_copy(rhs.cVersion.get()))} {} + + Version& operator=(const Version& rhs) { + if (this != &rhs) { + cVersion = createVersion(rhs.cVersion.get()); + } + return *this; + } + + bool operator==(const Version& rhs) { + return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) == 0; + } + + bool operator<(const Version& rhs) { + return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) < 0; + } + + //TODO rest of the operators + + /** + * @brief Warps a C Celix Version to a C++ Celix Version, but takes no ownership. + * Dealloction is still the responsibility of the caller. + */ + static Version wrap(celix_version_t* v) { + return Version{v}; + } + + /** + * @brief Get the underlining C Celix Version object. + * + * @warning Try not the depend on the C API from a C++ bundle. If features are missing these should be added to + * the C++ API. + */ + [[nodiscard]] celix_version_t * getCVersion() const { + return cVersion.get(); + } + + [[nodiscard]] int getMajor() const { + return celix_version_getMajor(cVersion.get()); + } + + [[nodiscard]] int getMinor() const { + return celix_version_getMinor(cVersion.get()); + } + + [[nodiscard]] int getMicro() const { + return celix_version_getMicro(cVersion.get()); + } + + [[nodiscard]] std::string getQualifier() const { + return std::string{celix_version_getQualifier(cVersion.get())}; + } + + /** + * @brief Return whether the version is an empty version (0.0.0.""). + */ + [[nodiscard]] bool emptyVersion() const { + //TODO celix_version_isEmpty(cVersion.get()); + return false; + } + private: + static std::shared_ptr createVersion(celix_version_t* cVersion) { + return std::shared_ptr{cVersion, [](celix_version_t *v) { + celix_version_destroy(v); + }}; + } + + /** + * @brief Create a wrap around a C Celix version without taking ownership. + */ + explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t *){/*nop*/}} {} + + std::shared_ptr cVersion; + }; +} \ No newline at end of file diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index 50bf810d3..40e23bffd 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -261,24 +261,33 @@ bool celix_longHashMap_remove(celix_long_hash_map_t* map, long key); void celix_longHashMap_clear(celix_long_hash_map_t* map); /** - * @brief Returns an iterator pointing to the first element in the map. + * @brief Get an iterator pointing to the first element in the map. * - * @param map The map to get the iterator for. + * @param[in] map The map to get the iterator for. * @return An iterator pointing to the first element in the map. */ celix_long_hash_map_iterator_t celix_longHashMap_begin(const celix_long_hash_map_t* map); /** - * @brief Check if the iterator is the end of the hash map. + * @brief Get an iterator pointing to the element following the last element in the map. * - * @note the end iterator should not be used to retrieve a key of value. + * @param[in] map The map to get the iterator for. + * @return An iterator pointing to the element following the last element in the map. + */ +celix_long_hash_map_iterator_t celix_longHashMap_end(const celix_long_hash_map_t* map); + +/** + * + * @brief Determine if the iterator points to the element following the last element in the map. * - * @return true if the iterator is the end. + * @param[in] iter The iterator to check. + * @return true if the iterator points to the element following the last element in the map, false otherwise. */ bool celix_longHashMapIterator_isEnd(const celix_long_hash_map_iterator_t* iter); /** - * @brief Moves the provided iterator to the next entry in the hash map. + * @brief Advance the iterator to the next element in the map. + * @param[in] iter The iterator to advance. */ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index e484a436a..7794661ec 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -103,6 +103,7 @@ typedef struct celix_properties_iterator { /** * @brief Creates a new empty property set. + * * @return A new empty property set. */ celix_properties_t* celix_properties_create(void); @@ -110,14 +111,14 @@ celix_properties_t* celix_properties_create(void); /** * @brief Destroys a property set, freeing all associated resources. * - * @param properties The property set to destroy. If properties is NULL, this function will do nothing. + * @param[in] properties The property set to destroy. If properties is NULL, this function will do nothing. */ void celix_properties_destroy(celix_properties_t* properties); /** * @brief Loads properties from a file. * - * @param filename The name of the file to load properties from. + * @param[in] filename The name of the file to load properties from. * @return A property set containing the properties from the file. * @retval NULL If an error occurred (e.g. file not found). */ @@ -127,7 +128,7 @@ celix_properties_t* celix_properties_load(const char *filename); /** * @brief Loads properties from a stream. * - * @param stream The stream to load properties from. + * @param[in,out] stream The stream to load properties from. * @return A property set containing the properties from the stream. * @retval NULL If an error occurred (e.g. invalid format). */ @@ -136,7 +137,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE *stream); /** * @brief Loads properties from a string. * - * @param input The string to load properties from. + * @param[in] input The string to load properties from. * @return A property set containing the properties from the string. * @retval NULL If an error occurred (e.g. invalid format). */ @@ -145,9 +146,9 @@ celix_properties_t* celix_properties_loadFromString(const char *input); /** * @brief Stores properties to a file. * - * @param properties The property set to store. - * @param file The name of the file to store the properties to. - * @param header An optional header to write to the file before the properties. + * @param[in] properties The property set to store. + * @param[in] file The name of the file to store the properties to. + * @param[in] header An optional header to write to the file before the properties. * @return CELIX_SUCCESS if the operation was successful, CELIX_FILE_IO_EXCEPTION if there was an error writing to the * file. */ @@ -156,8 +157,8 @@ celix_status_t celix_properties_store(celix_properties_t* properties, const char /** * @brief Gets the entry for a given key in a property set. * - * @param properties The property set to search. - * @param key The key to search for. + * @param[in] properties The property set to search. + * @param[in] key The key to search for. * @return The entry for the given key, or a default entry with the valueType set to CELIX_PROPERTIES_VALUE_TYPE_UNSET * if the key is not found. */ @@ -166,63 +167,64 @@ celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* pro /** * @brief Gets the value of a property. * - * @param properties The property set to search. - * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set. * @return The value of the property, or the default value if the property is not set. */ const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue); /** * @brief Gets the type of a property value. - * @param properties The property set to search. - * @param key The key of the property to get the type of. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get the type of. * @return The type of the property value, or CELIX_PROPERTIES_VALUE_TYPE_UNSET if the property is not set. */ celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key); /** - * @brief Sets the value of a property. + * @brief Set the value of a property. * - * - * @param properties The property set to modify. - * @param key The key of the property to set. - * @param value The value to set the property to. + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] value The value to set the property to. */ void celix_properties_set(celix_properties_t* properties, const char* key, const char *value); /** - * @brief Sets the value of a property without copying the key and value strings. + * @brief Set the value of a property without copying the key and value strings. * - * @param properties The property set to modify. - * @param key The key of the property to set. This string will be used directly, so it must not be freed or modified + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. This string will be used directly, so it must not be freed or modified * after calling this function. - * @param value The value to set the property to. This string will be used directly, so it must not be freed or + * @param[in] value The value to set the property to. This string will be used directly, so it must not be freed or * modified after calling this function. */ void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char *value); /** - * @brief Unsets a property, removing it from the property set. - * @param properties The property set to modify. - * @param key The key of the property to unset. + * @brief Unset a property, removing it from the property set. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to unset. */ void celix_properties_unset(celix_properties_t* properties, const char *key); /** - * @brief Makes a copy of a property set. + * @brief Make a copy of a property set. * - * @param properties The property set to copy. + * @param[in] properties The property set to copy. * @return A copy of the given property set. */ celix_properties_t* celix_properties_copy(const celix_properties_t* properties); /** - * @brief Gets the value of a property as a long integer. + * @brief Get the value of a property as a long integer. * - * @param properties The property set to search. - * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set, the value is not a long integer, + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set, the value is not a long integer, * or if the value cannot be converted to a long integer. * @return The value of the property as a long integer, or the default value if the property is not set, * the value is not a long integer, or if the value cannot be converted to a long integer. @@ -231,20 +233,20 @@ celix_properties_t* celix_properties_copy(const celix_properties_t* properties); long celix_properties_getAsLong(const celix_properties_t* properties, const char* key, long defaultValue); /** - * @brief Sets the value of a property to a long integer. + * @brief Set the value of a property to a long integer. * - * @param properties The property set to modify. - * @param key The key of the property to set. - * @param value The long value to set the property to. + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] value The long value to set the property to. */ void celix_properties_setLong(celix_properties_t* properties, const char* key, long value); /** - * @brief Gets the value of a property as a boolean. + * @brief Get the value of a property as a boolean. * - * @param properties The property set to search. - * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set, the value is not a boolean, or if the value + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set, the value is not a boolean, or if the value * cannot be converted to a boolean. * @return The value of the property as a boolean, or the default value if the property is not set, the value is not a * boolean, or if the value cannot be converted to a boolean. If the value is a string, it will be converted @@ -253,29 +255,29 @@ void celix_properties_setLong(celix_properties_t* properties, const char* key, l bool celix_properties_getAsBool(const celix_properties_t* properties, const char* key, bool defaultValue); /** - * @brief Sets the value of a property to a boolean. + * @brief Set the value of a property to a boolean. * - * @param properties The property set to modify. - * @param key The key of the property to set. - * @param val The boolean value to set the property to. + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] val The boolean value to set the property to. */ void celix_properties_setBool(celix_properties_t* properties, const char* key, bool val); /** - * @brief Sets the value of a property to a double. + * @brief Set the value of a property to a double. * - * @param properties The property set to modify. - * @param key The key of the property to set. - * @param val The double value to set the property to. + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] val The double value to set the property to. */ void celix_properties_setDouble(celix_properties_t* properties, const char* key, double val); /** - * @brief Gets the value of a property as a double. + * @brief Get the value of a property as a double. * - * @param properties The property set to search. - * @param key The key of the property to get. - * @param defaultValue The value to return if the property is not set, the value is not a double, + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set, the value is not a double, * or if the value cannot be converted to a double. * @return The value of the property as a double, or the default value if the property is not set, the value is not * a double, or if the value cannot be converted to a double. If the value is a string, it will be converted @@ -284,7 +286,7 @@ void celix_properties_setDouble(celix_properties_t* properties, const char* key, double celix_properties_getAsDouble(const celix_properties_t* properties, const char* key, double defaultValue); /** - * @brief Sets the value of a property as a Celix version. + * @brief Set the value of a property as a Celix version. * * This function will make a copy of the provided celix_version_t object and store it in the property set. * @@ -295,7 +297,7 @@ double celix_properties_getAsDouble(const celix_properties_t* properties, const void celix_properties_setVersion(celix_properties_t* properties, const char* key, const celix_version_t* version); /** - * @brief Sets the value of a property as a Celix version. + * @brief Set the value of a property as a Celix version. * * This function will store a reference to the provided celix_version_t object in the property set and takes * ownership of the provided version. @@ -309,7 +311,7 @@ void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, cons /** - * @brief Gets the value of a property as a Celix version. + * @brief Get the value of a property as a Celix version. * * This function does not convert a string property value to a Celix version automatically. * @@ -325,15 +327,15 @@ const celix_version_t* celix_properties_getVersion( const celix_version_t* defaultValue); /** - * @brief Gets the number of properties in a property set. + * @brief Get the number of properties in a property set. * - * @param properties The property set to get the size of. + * @param[in] properties The property set to get the size of. * @return The number of properties in the property set. */ int celix_properties_size(const celix_properties_t* properties); /** - * @brief Constructs an iterator pointing to the first entry in the properties object. + * @brief Construct an iterator pointing to the first entry in the properties object. * * @param[in] properties The properties object to iterate over. * @return The iterator pointing to the first entry in the properties object. @@ -341,14 +343,25 @@ int celix_properties_size(const celix_properties_t* properties); celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties); /** - * @brief Advances the iterator to the next entry. + * @brief Construct an iterator pointing to the past-the-end entry in the properties object. + * + * This iterator is used to mark the end of the properties object and is not associated with any element in the + * properties object. + * + * @param[in] properties The properties object to iterate over. + * @return The iterator pointing to the past-the-end entry in the properties object. + */ +celix_properties_iterator_t celix_properties_end(const celix_properties_t* properties); + +/** + * @brief Advance the iterator to the next entry. * * @param[in, out] iter The iterator. */ void celix_propertiesIterator_next(celix_properties_iterator_t* iter); /** - * @brief Determines whether the iterator is pointing to an end position. + * @brief Determine whether the iterator is pointing to an end position. * * An iterator is at an end position if it has no more entries to visit. * @@ -358,7 +371,7 @@ void celix_propertiesIterator_next(celix_properties_iterator_t* iter); bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); /** - * @brief Gets the property set being iterated over. + * @brief Get the property set being iterated over. * * @param[in] iter The iterator to get the property set from. * @return The property set being iterated over. @@ -366,7 +379,7 @@ bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter); /** - * @brief Determines whether two iterators are equal. + * @brief Determine whether two iterators are equal. * * @param[in] a The first iterator to compare. * @param[in] b The second iterator to compare. @@ -375,14 +388,14 @@ celix_properties_t* celix_propertiesIterator_properties(const celix_properties_i bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); /** - * @brief Iterates over the entries in the specified celix_properties_t object. + * @brief Iterate over the entries in the specified celix_properties_t object. * * This macro allows you to easily iterate over the entries in a celix_properties_t object. * The loop variable `iterName` will be of type celix_properties_iterator_t and will contain the current * entry during each iteration. * - * @param map The properties object to iterate over. - * @param iterName The name of the iterator variable to use in the loop. + * @param[in] map The properties object to iterate over. + * @param[in] iterName The name of the iterator variable to use in the loop. * * Example usage: * @code{.c} diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index 1e5fbe828..c3ae78bc5 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -270,33 +270,41 @@ bool celix_stringHashMap_remove(celix_string_hash_map_t* map, const char* key); void celix_stringHashMap_clear(celix_string_hash_map_t* map); /** - * @brief Returns an iterator pointing to the first element in the map. + * @brief Get an iterator pointing to the first element in the map. * - * @param map The map to get the iterator for. + * @param[in] map The map to get the iterator for. * @return An iterator pointing to the first element in the map. */ celix_string_hash_map_iterator_t celix_stringHashMap_begin(const celix_string_hash_map_t* map); /** - * @brief Check if the iterator is the end of the hash map. + * @brief Get an iterator pointing to the element following the last element in the map. * - * @note the end iterator should not be used to retrieve a key of value. + * @param[in] map The map to get the iterator for. + * @return An iterator pointing to the element following the last element in the map. + */ +celix_string_hash_map_iterator_t celix_stringHashMap_end(const celix_string_hash_map_t* map); + +/** * - * @return true if the iterator is the end. + * @brief Determine if the iterator points to the element following the last element in the map. + * + * @param[in] iter The iterator to check. + * @return true if the iterator points to the element following the last element in the map, false otherwise. */ bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* iter); /** - * @brief Moves the provided iterator to the next entry in the hash map. + * @brief Advance the iterator to the next element in the map. + * @param[in] iter The iterator to advance. */ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); /** * @brief Marco to loop over all the entries of a string hash map. * - * * Small example of how to use the iterate macro: - * @code + * @code{.c} * celix_string_hash_map_t* map = ... * CELIX_STRING_HASH_MAP_ITERATE(map, iter) { * printf("Visiting hash map entry with key %s\n", inter.key); @@ -313,7 +321,7 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); * @brief Remove the hash map entry for the provided iterator and updates the iterator to the next hash map entry * * Small example of how to use the celix_stringHashMapIterator_remove function: - * @code + * @code{.c} * //remove all even entries from hash map * celix_string_hash_map_t* map = ... * celix_string_hash_map_iterator_t iter = celix_stringHashMap_begin(map); diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c index 1ef9b75ee..32bd7da35 100644 --- a/libs/utils/src/celix_hash_map.c +++ b/libs/utils/src/celix_hash_map.c @@ -588,6 +588,22 @@ celix_long_hash_map_iterator_t celix_longHashMap_begin(const celix_long_hash_map return iter; } +celix_string_hash_map_iterator_t celix_stringHashMap_end(const celix_string_hash_map_t* map) { + celix_string_hash_map_iterator_t iter; + memset(&iter, 0, sizeof(iter)); + iter.index = map->genericMap.size; + iter._internal[0] = (void*)&map->genericMap; + return iter; +} + +celix_long_hash_map_iterator_t celix_longHashMap_end(const celix_long_hash_map_t* map) { + celix_long_hash_map_iterator_t iter; + memset(&iter, 0, sizeof(iter)); + iter.index = map->genericMap.size; + iter._internal[0] = (void*)&map->genericMap; + return iter; +} + bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* iter) { return iter->_internal[1] == NULL; //check if entry is NULL } @@ -600,9 +616,9 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter) { const celix_hash_map_t* map = iter->_internal[0]; celix_hash_map_entry_t *entry = iter->_internal[1]; entry = celix_hashMap_nextEntry(map, entry); + iter->index += 1; if (entry != NULL) { iter->_internal[1] = entry; - iter->index += 1; iter->key = entry->key.strKey; iter->value = entry->value; } else { diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 7be3e7135..fbd60715b 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -342,6 +342,15 @@ celix_properties_t* celix_properties_create(void) { void celix_properties_destroy(celix_properties_t *props) { if (props != NULL) { + //TODO measure print nr of entries and total size of the string keys and values +// fprintf(stdout, "Properties size; %d", celix_properties_size(props)); +// size_t size = 0; +// CELIX_PROPERTIES_ITERATE(props, iter) { +// size += strlen(iter.entry.key) + 1; +// size += strlen(iter.entry.value) + 1; +// } +// fprintf(stdout, "Properties string size: %zu", size); + celix_stringHashMap_destroy(props->map); free(props); } @@ -448,12 +457,14 @@ static void parseLine(const char* line, celix_properties_t *props) { //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); celix_utils_trimInPlace(key); celix_utils_trimInPlace(value); - celix_properties_setWithoutCopy(props, key, value); +// celix_properties_setWithoutCopy(props, key, value); + celix_properties_set(props, key, value); } else { - free(key); - free(value); +// free(key); +// free(value); } - + free(key); + free(value); } @@ -786,6 +797,18 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro return iter; } +celix_properties_iterator_t celix_properties_end(const celix_properties_t* properties) { + celix_properties_iterator_internal_t internalIter; + internalIter.mapIter = celix_stringHashMap_end(properties->map); + internalIter.props = properties; + + celix_properties_iterator_t iter; + memset(&iter, 0, sizeof(iter)); + iter.index = (int)internalIter.mapIter.index; //TODO make inter.index size_t + memcpy(iter._data, &internalIter, sizeof(internalIter)); + return iter; +} + void celix_propertiesIterator_next(celix_properties_iterator_t *iter) { celix_properties_iterator_internal_t internalIter; memcpy(&internalIter, iter->_data, sizeof(internalIter)); diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 3d0ee1ab7..72a379426 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -27,6 +27,8 @@ #include "celix_errno.h" #include "version_private.h" +static const char* const CELIX_VERSION_EMPTY_QUALIFIER = ""; + celix_status_t version_createVersion(int major, int minor, int micro, const char * qualifier, version_pt *version) { *version = celix_version_createVersion(major, minor, micro, qualifier); return *version == NULL ? CELIX_ILLEGAL_ARGUMENT : CELIX_SUCCESS; @@ -107,7 +109,8 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha if (qualifier == NULL) { qualifier = ""; } - for (int i = 0; i < strlen(qualifier); i++) { + size_t qualifierLen = strlen(qualifier); + for (int i = 0; i < qualifierLen; i++) { char ch = qualifier[i]; if (('A' <= ch) && (ch <= 'Z')) { continue; @@ -125,12 +128,12 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha return NULL; } - celix_version_t* version = calloc(1, sizeof(*version)); + celix_version_t* version = malloc(sizeof(*version)); version->major = major; version->minor = minor; version->micro = micro; - version->qualifier = celix_utils_strdup(qualifier); + version->qualifier = qualifierLen == 0 ? (char*)CELIX_VERSION_EMPTY_QUALIFIER : celix_utils_strdup(qualifier); return version; @@ -138,7 +141,9 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha void celix_version_destroy(celix_version_t* version) { if (version != NULL) { - free(version->qualifier); + if (version->qualifier != CELIX_VERSION_EMPTY_QUALIFIER) { + free(version->qualifier); + } free(version); } } From efa1017e0ab52cf7d9f5cfb7b840bc7843434bf3 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 7 Jan 2023 20:31:52 +0100 Subject: [PATCH 07/21] Improve test coverage and fix issues in properties and string/long hash map. --- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 41 +++++++-- libs/utils/gtest/src/HashMapTestSuite.cc | 86 ++++++++++++++--- libs/utils/gtest/src/PropertiesTestSuite.cc | 81 ++++++++++++---- libs/utils/include/celix/Properties.h | 73 +++++++-------- libs/utils/include/celix/Version.h | 61 ++++++------ libs/utils/include/celix_long_hash_map.h | 51 ++++++---- libs/utils/include/celix_properties.h | 59 ++++++++---- libs/utils/include/celix_string_hash_map.h | 52 +++++++---- libs/utils/src/celix_hash_map.c | 77 ++++++++++------ libs/utils/src/properties.c | 92 +++++++++++-------- 10 files changed, 450 insertions(+), 223 deletions(-) diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index bc60adbc2..142570c4b 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -52,6 +52,13 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) { EXPECT_EQ(props.getAsBool("key5", false), true); int count = 0; + for (auto it = props.begin(); it != props.end(); ++it) { + EXPECT_NE(it.first, ""); + count++; + } + EXPECT_EQ(5, count); + + count = 0; for (const auto& pair : props) { EXPECT_NE(pair.first, ""); count++; @@ -118,7 +125,7 @@ TEST_F(CxxPropertiesTestSuite, getType) { props.set("long7", (unsigned char)1); //should lead to long; props.set("double1", 1.0); props.set("double2", 1.0f); //set float should lead to double - //TODO version + props.set("version", celix::Version{1, 2, 3}); EXPECT_EQ(props.getType("bool"), celix::Properties::ValueType::Bool); EXPECT_EQ(props.getType("long1"), celix::Properties::ValueType::Long); @@ -130,9 +137,31 @@ TEST_F(CxxPropertiesTestSuite, getType) { EXPECT_EQ(props.getType("long7"), celix::Properties::ValueType::Long); EXPECT_EQ(props.getType("double1"), celix::Properties::ValueType::Double); EXPECT_EQ(props.getType("double2"), celix::Properties::ValueType::Double); - + EXPECT_EQ(props.getType("version"), celix::Properties::ValueType::Version); } +TEST_F(CxxPropertiesTestSuite, testGetAsVersion) { + celix::Properties props; + + // Test getting a version from a string property + props.set("key", "1.2.3"); + celix::Version ver{1, 2, 3}; + EXPECT_TRUE(props.getAsVersion("key") == ver); + + // Test getting a version from a version property + props.set("key", celix::Version{2, 3, 4}); + ver = celix::Version{2, 3, 4}; + EXPECT_EQ(props.getAsVersion("key"), ver); + + // Test getting default value when property is not set + ver = celix::Version{3, 4, 5}; + EXPECT_EQ(props.getAsVersion("non_existent_key", celix::Version{3, 4, 5}), ver); + + // Test getting default value when property value is not a valid version string + props.set("key", "invalid_version_string"); + ver = celix::Version{4, 5, 6}; + EXPECT_EQ(props.getAsVersion("key", celix::Version{4, 5, 6}), ver); +} #if __cplusplus >= 201703L //C++17 or higher TEST_F(CxxPropertiesTestSuite, testStringView) { @@ -183,10 +212,10 @@ TEST_F(CxxPropertiesTestSuite, testStringView) { props.set(key, 1L); //long EXPECT_EQ(1L, props.getAsLong(key, -1)); - props.set(key, 1.0); //double - EXPECT_EQ(1.0, props.getAsDouble(key, -1)); - props.set(key, true); //bool - EXPECT_EQ(true, props.getAsBool(key, false)); + props.set(key, 2.0); //double + EXPECT_EQ(2.0, props.getAsDouble(key, -1)); + props.set(key, false); //bool + EXPECT_EQ(false, props.getAsBool(key, true)); } } diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc b/libs/utils/gtest/src/HashMapTestSuite.cc index 85a4944f0..27f0589af 100644 --- a/libs/utils/gtest/src/HashMapTestSuite.cc +++ b/libs/utils/gtest/src/HashMapTestSuite.cc @@ -221,7 +221,7 @@ TEST_F(HashMapTestSuite, DestroyHashMapWithSimpleRemovedCallback) { celix_longHashMap_destroy(lMap); } -TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) { +TEST_F(HashMapTestSuite, ReplaceAndClearHashMapWithRemovedCallback) { std::atomic count{0}; celix_string_hash_map_create_options_t sOpts{}; sOpts.removedCallbackData = &count; @@ -229,17 +229,19 @@ TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) { auto* c = static_cast*>(data); if (celix_utils_stringEquals(key, "key1")) { c->fetch_add(1); - EXPECT_EQ(value.longValue, 1); + EXPECT_TRUE(value.longValue == 1 || value.longValue == 2); } else if (celix_utils_stringEquals(key, "key2")) { c->fetch_add(1); - EXPECT_EQ(value.longValue, 2); + EXPECT_TRUE(value.longValue == 3 || value.longValue == 4); } }; auto* sMap = celix_stringHashMap_createWithOptions(&sOpts); celix_stringHashMap_putLong(sMap, "key1", 1); - celix_stringHashMap_putLong(sMap, "key2", 2); - celix_stringHashMap_clear(sMap); - EXPECT_EQ(count.load(), 2); + celix_stringHashMap_putLong(sMap, "key1", 2); //replacing old value, count +1 + celix_stringHashMap_putLong(sMap, "key2", 3); + celix_stringHashMap_putLong(sMap, "key2", 4); //replacing old value, count +1 + celix_stringHashMap_clear(sMap); //count +2 + EXPECT_EQ(count.load(), 4); EXPECT_EQ(celix_stringHashMap_size(sMap), 0); celix_stringHashMap_destroy(sMap); @@ -250,17 +252,19 @@ TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) { auto* c = static_cast*>(data); if (key == 1) { c->fetch_add(1); - EXPECT_EQ(value.longValue, 1); + EXPECT_TRUE(value.longValue == 1 || value.longValue == 2); } else if (key == 2) { c->fetch_add(1); - EXPECT_EQ(value.longValue, 2); + EXPECT_TRUE(value.longValue == 3 || value.longValue == 4); } }; auto* lMap = celix_longHashMap_createWithOptions(&lOpts); celix_longHashMap_putLong(lMap, 1, 1); - celix_longHashMap_putLong(lMap, 2, 2); - celix_longHashMap_clear(lMap); - EXPECT_EQ(count.load(), 2); + celix_longHashMap_putLong(lMap, 1, 2); //replacing old value, count +1 + celix_longHashMap_putLong(lMap, 2, 3); + celix_longHashMap_putLong(lMap, 2, 4); //replacing old value, count +1 + celix_longHashMap_clear(lMap); //count +2 + EXPECT_EQ(count.load(), 4); EXPECT_EQ(celix_longHashMap_size(lMap), 0); celix_longHashMap_destroy(lMap); } @@ -518,7 +522,7 @@ TEST_F(HashMapTestSuite, IterateWithRemoveTest) { auto iter1 = celix_stringHashMap_begin(sMap); while (!celix_stringHashMapIterator_isEnd(&iter1)) { if (iter1.index % 2 == 0) { - //note only removing entries where the iter index is even + //note only removing entries where the iter key is even celix_stringHashMapIterator_remove(&iter1); } else { celix_stringHashMapIterator_next(&iter1); @@ -567,10 +571,64 @@ TEST_F(HashMapTestSuite, IterateEndTest) { EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter1)); EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter2)); - //TODO loop and test with index. - celix_stringHashMap_destroy(sMap1); celix_stringHashMap_destroy(sMap2); celix_longHashMap_destroy(lMap1); celix_longHashMap_destroy(lMap2); } + + +TEST_F(HashMapTestSuite, EqualsTest) { + auto* sMap = createStringHashMap(2); + auto sIter1 = celix_stringHashMap_begin(sMap); + auto sIter2 = celix_stringHashMap_begin(sMap); + + // Test equal iterators + EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2)); + + // Test unequal iterators after only 1 modification + celix_stringHashMapIterator_next(&sIter1); + EXPECT_FALSE(celix_stringHashMapIterator_equals(&sIter1, &sIter2)); + + // Test equal iterators after both modification + celix_stringHashMapIterator_next(&sIter2); + EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2)); + + + //Same for long hash map + auto *lMap = createLongHashMap(1); + auto lIter1 = celix_longHashMap_begin(lMap); + auto lIter2 = celix_longHashMap_begin(lMap); + + // Test equal iterators + EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2)); + + // Test unequal iterators after only 1 modification + celix_longHashMapIterator_next(&lIter1); + EXPECT_FALSE(celix_longHashMapIterator_equals(&lIter1, &lIter2)); + + // Test equal iterators after both modification + celix_longHashMapIterator_next(&lIter2); + EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2)); + + celix_stringHashMap_destroy(sMap); + celix_longHashMap_destroy(lMap); +} + +TEST_F(HashMapTestSuite, EqualsZeroSizeMapTest) { + // Because map size is 0, begin iter should equal end iter + auto* sMap = createStringHashMap(0); + auto sIter1 = celix_stringHashMap_begin(sMap); + auto sEnd = celix_stringHashMap_end(sMap); + EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sEnd)); + + + // Because map size is 0, begin iter should equal end iter + auto *lMap = createLongHashMap(0); + auto lIter1 = celix_longHashMap_begin(lMap); + auto lEnd = celix_longHashMap_end(lMap); + EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lEnd)); + + celix_stringHashMap_destroy(sMap); + celix_longHashMap_destroy(lMap); +} diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index b9b6eb95d..74e96f6e7 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -246,6 +246,29 @@ TEST_F(PropertiesTestSuite, fillTest) { celix_properties_destroy(props); } +TEST_F(PropertiesTestSuite, setOverwrite) { + auto* props = celix_properties_create(); + auto* version = celix_version_createEmptyVersion(); + const char* key = "key"; + + celix_properties_set(props, key, "str1"); + EXPECT_STREQ("str1", celix_properties_get(props, key, "")); + celix_properties_set(props, key, "str2"); + EXPECT_STREQ("str2", celix_properties_get(props, key, "")); + celix_properties_setLong(props, key, 1); + EXPECT_EQ(1, celix_properties_getAsLong(props, key, -1L)); + celix_properties_setDouble(props, key, 2.0); + EXPECT_EQ(2.0, celix_properties_getAsLong(props, key, -2.0)); + celix_properties_setBool(props, key, false); + EXPECT_EQ(false, celix_properties_getAsBool(props, key, true)); + celix_properties_setVersionWithoutCopy(props, key, version); + EXPECT_EQ(version, celix_properties_getVersion(props, key, nullptr)); + celix_properties_set(props, key, "last"); + + celix_properties_destroy(props); +} + + TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_t *props = celix_properties_create(); @@ -297,35 +320,35 @@ TEST_F(PropertiesTestSuite, getEntry) { auto* version = celix_version_createVersion(1, 2, 3, nullptr); celix_properties_setVersion(props, "key5", version); - auto entry = celix_properties_getEntry(props, "key1"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, entry.valueType); - EXPECT_STREQ("value1", entry.value); - EXPECT_STREQ("value1", entry.typed.strValue); + auto* entry = celix_properties_getEntry(props, "key1"); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, entry->valueType); + EXPECT_STREQ("value1", entry->value); + EXPECT_STREQ("value1", entry->typed.strValue); entry = celix_properties_getEntry(props, "key2"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, entry.valueType); - EXPECT_STREQ("123", entry.value); - EXPECT_EQ(123, entry.typed.longValue); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, entry->valueType); + EXPECT_STREQ("123", entry->value); + EXPECT_EQ(123, entry->typed.longValue); entry = celix_properties_getEntry(props, "key3"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, entry.valueType); - EXPECT_NE(strstr(entry.value, "123.456"), nullptr); - EXPECT_DOUBLE_EQ(123.456, entry.typed.doubleValue); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, entry->valueType); + EXPECT_NE(strstr(entry->value, "123.456"), nullptr); + EXPECT_DOUBLE_EQ(123.456, entry->typed.doubleValue); entry = celix_properties_getEntry(props, "key4"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, entry.valueType); - EXPECT_STREQ("true", entry.value); - EXPECT_TRUE(entry.typed.boolValue); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, entry->valueType); + EXPECT_STREQ("true", entry->value); + EXPECT_TRUE(entry->typed.boolValue); entry = celix_properties_getEntry(props, "key5"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, entry.valueType); - EXPECT_STREQ("1.2.3", entry.value); - EXPECT_EQ(1, celix_version_getMajor(entry.typed.versionValue)); - EXPECT_EQ(2, celix_version_getMinor(entry.typed.versionValue)); - EXPECT_EQ(3, celix_version_getMicro(entry.typed.versionValue)); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, entry->valueType); + EXPECT_STREQ("1.2.3", entry->value); + EXPECT_EQ(1, celix_version_getMajor(entry->typed.versionValue)); + EXPECT_EQ(2, celix_version_getMinor(entry->typed.versionValue)); + EXPECT_EQ(3, celix_version_getMicro(entry->typed.versionValue)); entry = celix_properties_getEntry(props, "key6"); - EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, entry.valueType); + EXPECT_EQ(nullptr, entry); celix_version_destroy(version); celix_properties_destroy(props); @@ -436,6 +459,26 @@ TEST_F(PropertiesTestSuite, getVersion) { EXPECT_EQ(celix_version_getMicro(actual), 3); EXPECT_STREQ(celix_version_getQualifier(actual), ""); + + // Test getAsVersion + celix_properties_set(properties, "string_version", "1.1.1"); + auto* ver1 = celix_properties_getAsVersion(properties, "non-existing", emptyVersion); + auto* ver2 = celix_properties_getAsVersion(properties, "non-existing", nullptr); + auto* ver3 = celix_properties_getAsVersion(properties, "string_version", emptyVersion); + auto* ver4 = celix_properties_getAsVersion(properties, "key", emptyVersion); + EXPECT_NE(ver1, nullptr); + EXPECT_EQ(ver2, nullptr); + EXPECT_EQ(celix_version_getMajor(ver3), 1); + EXPECT_EQ(celix_version_getMinor(ver3), 1); + EXPECT_EQ(celix_version_getMicro(ver3), 1); + EXPECT_EQ(celix_version_getMajor(ver4), 1); + EXPECT_EQ(celix_version_getMinor(ver4), 2); + EXPECT_EQ(celix_version_getMicro(ver4), 3); + celix_version_destroy(ver1); + celix_version_destroy(ver3); + celix_version_destroy(ver4); + + celix_version_destroy(emptyVersion); celix_properties_destroy(properties); } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index c75119a3c..45bb0c8be 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -33,35 +33,32 @@ namespace celix { /** * @brief A iterator for celix::Properties. */ - class PropertiesIterator { + class ConstPropertiesIterator { public: - explicit PropertiesIterator(const celix_properties_t* props) { + explicit ConstPropertiesIterator(const celix_properties_t* props) { iter = celix_properties_begin(props); setFields(); } - explicit PropertiesIterator(celix_properties_iterator_t _iter) { - iter = std::move(_iter); + explicit ConstPropertiesIterator(celix_properties_iterator_t _iter) { + iter = _iter; setFields(); } - PropertiesIterator& operator++() { + ConstPropertiesIterator& operator++() { next(); return *this; } - PropertiesIterator& operator*() { + const ConstPropertiesIterator& operator*() { return *this; } - bool operator==(const celix::PropertiesIterator& rhs) const { - if (end || rhs.end) { - return end && rhs.end; - } + bool operator==(const celix::ConstPropertiesIterator& rhs) const { return celix_propertiesIterator_equals(&iter, &rhs.iter); } - bool operator!=(const celix::PropertiesIterator& rhs) const { + bool operator!=(const celix::ConstPropertiesIterator& rhs) const { return !operator==(rhs); } @@ -84,7 +81,6 @@ namespace celix { } celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}}; - bool end{false}; }; @@ -96,7 +92,7 @@ namespace celix { */ class Properties { public: - using const_iterator = PropertiesIterator; + using const_iterator = ConstPropertiesIterator; //note currently only a const iterator is supported. /** * @brief Enum representing the possible types of a property value. @@ -245,28 +241,28 @@ namespace celix { * @brief begin iterator */ [[nodiscard]] const_iterator begin() const noexcept { - return PropertiesIterator{cProps.get()}; + return ConstPropertiesIterator{cProps.get()}; } /** * @brief end iterator */ [[nodiscard]] const_iterator end() const noexcept { - return PropertiesIterator{celix_properties_end(cProps.get())}; + return ConstPropertiesIterator{celix_properties_end(cProps.get())}; } /** * @brief constant begin iterator */ [[nodiscard]] const_iterator cbegin() const noexcept { - return PropertiesIterator{cProps.get()}; + return ConstPropertiesIterator{cProps.get()}; } /** * @brief constant end iterator */ [[nodiscard]] const_iterator cend() const noexcept { - return PropertiesIterator{celix_properties_end(cProps.get())}; + return ConstPropertiesIterator{celix_properties_end(cProps.get())}; } #if __cplusplus >= 201703L //C++17 or higher @@ -317,23 +313,22 @@ namespace celix { /** * @brief Get the value of the property with key as a Celix version. * - * Note that this function does not automatically convert a string property value to a Celix version. - * * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix - * version. - * @return The value of the property if it is a Celix version, or the default value if the property is not set - * or the value is not a Celix version. + * @param[in] defaultValue The value to return if the property is not set or if the value cannot be converted + * to a Celix version. + * @return The Celix version value of the property if it exists and can be converted, + * or the default value otherwise. */ - //TODO test - [[nodiscard]] celix::Version getVersion(std::string_view key, celix::Version defaultValue = {}) { - auto* cVersion = celix_properties_getVersion(cProps.get(), key.data(), nullptr); + [[nodiscard]] celix::Version getAsVersion(std::string_view key, celix::Version defaultValue = {}) { + auto* cVersion = celix_properties_getAsVersion(cProps.get(), key.data(), nullptr); if (cVersion) { - return celix::Version{ + celix::Version version{ celix_version_getMajor(cVersion), celix_version_getMinor(cVersion), celix_version_getMicro(cVersion), celix_version_getQualifier(cVersion)}; + celix_version_destroy(cVersion); + return version; } return defaultValue; } @@ -345,7 +340,6 @@ namespace celix { * @return The type of the property with the given key, or ValueType::Unset if the property * does not exist. */ - //TODO test [[nodiscard]] ValueType getType(std::string_view key) { return getAndConvertType(cProps, key.data()); } @@ -361,20 +355,21 @@ namespace celix { */ template void set(std::string_view key, T&& value) { - if constexpr (std::is_same_v, bool>) { + using DecayedT = std::decay_t; + if constexpr (std::is_same_v) { celix_properties_setBool(cProps.get(), key.data(), value); - } else if constexpr (std::is_same_v, std::string_view>) { + } else if constexpr (std::is_same_v) { celix_properties_set(cProps.get(), key.data(), value.data()); } else if constexpr (std::is_convertible_v) { std::string_view view{value}; celix_properties_set(cProps.get(), key.data(), view.data()); - } else if constexpr (std::is_same_v, bool>) { + } else if constexpr (std::is_same_v) { celix_properties_setBool(cProps.get(), key.data(), value); - } else if constexpr (std::is_convertible_v, long>) { + } else if constexpr (std::is_integral_v and std::is_convertible_v) { celix_properties_setLong(cProps.get(), key.data(), value); - } else if constexpr (std::is_convertible_v, double>) { + } else if constexpr (std::is_convertible_v) { celix_properties_setDouble(cProps.get(), key.data(), value); - } else if constexpr (std::is_same_v, celix::Version>) { + } else if constexpr (std::is_same_v) { celix_properties_setVersion(cProps.get(), key.data(), value.getCVersion()); } else if constexpr (std::is_same_v) { celix_properties_setVersion(cProps.get(), key.data(), value); @@ -439,16 +434,18 @@ namespace celix { * @return The value of the property if it is a Celix version, or the default value if the property is not set * or the value is not a Celix version. */ - [[nodiscard]] celix::Version getVersion(const std::string& key, celix::Version defaultValue = {}) { - auto* cVersion = celix_properties_getVersion(cProps.get(), key.data(), nullptr); + [[nodiscard]] celix::Version getAsVersion(const std::string& key, celix::Version defaultValue = {}) { + auto* cVersion = celix_properties_getAsVersion(cProps.get(), key.data(), nullptr); if (cVersion) { - return celix::Version{ + celix::Version version{ celix_version_getMajor(cVersion), celix_version_getMinor(cVersion), celix_version_getMicro(cVersion), celix_version_getQualifier(cVersion)}; + celix_version_destroy(cVersion); + return version; } - return defaultValue; + return defaultValue } /** diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h index ce70d8d4c..ef50be0f9 100644 --- a/libs/utils/include/celix/Version.h +++ b/libs/utils/include/celix/Version.h @@ -28,40 +28,40 @@ namespace celix { //TODO CxxVersionTestSuite //TODO doxygen + //TODO test in unordered map and set + //TODO test in map and set class Version { public: - Version() : cVersion{createVersion(celix_version_createEmptyVersion())} {} + Version() : + cVersion{createVersion(celix_version_createEmptyVersion())}, + qualifier{celix_version_getQualifier(cVersion.get())} {} + #if __cplusplus >= 201703L //C++17 or higher explicit Version(int major, int minor, int micro, std::string_view qualifier = {}) : - cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))} {} + cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))}, + qualifier{celix_version_getQualifier(cVersion.get())} {} #else - explicit Version(int major, int minor, int micro, const& std::string qualifier = {}) : - cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.c_str()))} {} + explicit Version(int major, int minor, int micro, const std::string& qualifier = {}) : + cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.c_str()))}, + qualifier{celix_version_getQualifier(cVersion.get())} {} #endif Version(Version&&) = default; + Version(const Version& rhs) = default; Version& operator=(Version&&) = default; + Version& operator=(const Version& rhs) = default; - Version(const Version& rhs) : cVersion{createVersion(celix_version_copy(rhs.cVersion.get()))} {} - - Version& operator=(const Version& rhs) { - if (this != &rhs) { - cVersion = createVersion(rhs.cVersion.get()); - } - return *this; - } - - bool operator==(const Version& rhs) { + bool operator==(const Version& rhs) const { return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) == 0; } - bool operator<(const Version& rhs) { + bool operator<(const Version& rhs) const { return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) < 0; } - //TODO rest of the operators + //TODO rest of the operators, is that needed? /** * @brief Warps a C Celix Version to a C++ Celix Version, but takes no ownership. @@ -81,28 +81,24 @@ namespace celix { return cVersion.get(); } + //TODO doc [[nodiscard]] int getMajor() const { return celix_version_getMajor(cVersion.get()); } + //TODO doc [[nodiscard]] int getMinor() const { return celix_version_getMinor(cVersion.get()); } + //TODO doc [[nodiscard]] int getMicro() const { return celix_version_getMicro(cVersion.get()); } - [[nodiscard]] std::string getQualifier() const { - return std::string{celix_version_getQualifier(cVersion.get())}; - } - - /** - * @brief Return whether the version is an empty version (0.0.0.""). - */ - [[nodiscard]] bool emptyVersion() const { - //TODO celix_version_isEmpty(cVersion.get()); - return false; + //TODO doc + [[nodiscard]] const std::string& getQualifier() const { + return qualifier; } private: static std::shared_ptr createVersion(celix_version_t* cVersion) { @@ -117,5 +113,18 @@ namespace celix { explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t *){/*nop*/}} {} std::shared_ptr cVersion; + std::string qualifier; //cached qualifier of the const char* from celix_version_getQualifier + }; +} + +namespace std { + template<> + struct hash { + size_t operator()(const celix::Version& v) const { + return std::hash()(v.getMajor()) ^ + std::hash()(v.getMinor()) ^ + std::hash()(v.getMicro()) ^ + std::hash()(v.getQualifier()); + } }; } \ No newline at end of file diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index 40e23bffd..c0c36968c 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -159,12 +159,15 @@ void celix_longHashMap_destroy(celix_long_hash_map_t* map); size_t celix_longHashMap_size(const celix_long_hash_map_t* map); /** - * @brief add pointer entry the string hash map. + * @brief Add pointer entry the string hash map. * - * @param map The hashmap - * @param key The key to use. - * @param value The value to store with the key - * @return The previous key or NULL of no key was set. Note also returns NULL if the previous value for the key was NULL. + * @note The returned previous value can be already freed by a removed callback (if configured). + * + * @param[in] map The hashmap + * @param[in] key The key to use. + * @param[in] value The value to store with the key + * @return The previous value or NULL of no value was set for th provided key. + * Note also returns NULL if the previous value for the key was NULL. */ void* celix_longHashMap_put(celix_long_hash_map_t* map, long key, void* value); @@ -292,21 +295,14 @@ bool celix_longHashMapIterator_isEnd(const celix_long_hash_map_iterator_t* iter) void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); /** - * @brief Marco to loop over all the entries of a long hash map. - * - * Small example of how to use the iterate macro: - * @code{.c} - * celix_long_hash_map_t* map = ... - * CELIX_LONG_HASH_MAP_ITERATE(map, iter) { - * printf("Visiting hash map entry with key %li\n", inter.key); - * } - * @endcode - * - * @param map The (const celix_long_hash_map_t*) map to iterate over. - * @param iterName A iterName which will be of type celix_long_hash_map_iterator_t to hold the iterator. + * @brief Compares two celix_long_hash_map_iterator_t objects for equality. + * @param[in] iterator The first iterator to compare. + * @param[in] other The second iterator to compare. + * @return true if the iterators point to the same entry in the same hash map, false otherwise. */ -#define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \ - for (celix_long_hash_map_iterator_t iterName = celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); celix_longHashMapIterator_next(&(iterName))) +bool celix_longHashMapIterator_equals( + const celix_long_hash_map_iterator_t* iterator, + const celix_long_hash_map_iterator_t* other); /** * @brief Remove the hash map entry for the provided iterator and updates the iterator to the next hash map entry @@ -327,6 +323,23 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter); */ void celix_longHashMapIterator_remove(celix_long_hash_map_iterator_t* iter); +/** + * @brief Marco to loop over all the entries of a long hash map. + * + * Small example of how to use the iterate macro: + * @code{.c} + * celix_long_hash_map_t* map = ... + * CELIX_LONG_HASH_MAP_ITERATE(map, iter) { + * printf("Visiting hash map entry with key %li\n", inter.key); + * } + * @endcode + * + * @param map The (const celix_long_hash_map_t*) map to iterate over. + * @param iterName A iterName which will be of type celix_long_hash_map_iterator_t to hold the iterator. + */ +#define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \ + for (celix_long_hash_map_iterator_t iterName = celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); celix_longHashMapIterator_next(&(iterName))) + #ifdef __cplusplus } #endif diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 7794661ec..f20a2fe85 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -22,10 +22,17 @@ * @brief Header file for the Celix Properties API. * * The Celix Properties API provides a means for storing and manipulating key-value pairs, called properties, - * which can be used to store configuration data or metadata for a services, components, or framework configuration. + * which can be used to store configuration data or metadata for a service, component, or framework configuration. * Functions are provided for creating and destroying property sets, loading and storing properties from/to a file * or stream, and setting, getting, and unsetting individual properties. There are also functions for converting * property values to various types (e.g. long, bool, double) and for iterating over the properties in a set. + * + * Supported property value types include: + * - string (char*) + * - long + * - double + * - bool + * - celix_version_t* */ #include @@ -41,8 +48,7 @@ extern "C" { #endif /** - * @brief celix_properties_t is a type that represents a set of key-value pairs called properties, - * which can be used to store configuration data or metadata for a services, components or framework configuration. + * @brief celix_properties_t is a type that represents a set of key-value pairs called properties. * * @note Not thread safe. */ @@ -102,21 +108,21 @@ typedef struct celix_properties_iterator { } celix_properties_iterator_t; /** - * @brief Creates a new empty property set. + * @brief Create a new empty property set. * * @return A new empty property set. */ celix_properties_t* celix_properties_create(void); /** - * @brief Destroys a property set, freeing all associated resources. + * @brief Destroy a property set, freeing all associated resources. * * @param[in] properties The property set to destroy. If properties is NULL, this function will do nothing. */ void celix_properties_destroy(celix_properties_t* properties); /** - * @brief Loads properties from a file. + * @brief Load properties from a file. * * @param[in] filename The name of the file to load properties from. * @return A property set containing the properties from the file. @@ -126,7 +132,7 @@ celix_properties_t* celix_properties_load(const char *filename); /** - * @brief Loads properties from a stream. + * @brief Load properties from a stream. * * @param[in,out] stream The stream to load properties from. * @return A property set containing the properties from the stream. @@ -135,7 +141,7 @@ celix_properties_t* celix_properties_load(const char *filename); celix_properties_t* celix_properties_loadWithStream(FILE *stream); /** - * @brief Loads properties from a string. + * @brief Load properties from a string. * * @param[in] input The string to load properties from. * @return A property set containing the properties from the string. @@ -144,7 +150,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE *stream); celix_properties_t* celix_properties_loadFromString(const char *input); /** - * @brief Stores properties to a file. + * @brief Store properties to a file. * * @param[in] properties The property set to store. * @param[in] file The name of the file to store the properties to. @@ -155,17 +161,16 @@ celix_properties_t* celix_properties_loadFromString(const char *input); celix_status_t celix_properties_store(celix_properties_t* properties, const char* file, const char* header); /** - * @brief Gets the entry for a given key in a property set. + * @brief Get the entry for a given key in a property set. * * @param[in] properties The property set to search. * @param[in] key The key to search for. - * @return The entry for the given key, or a default entry with the valueType set to CELIX_PROPERTIES_VALUE_TYPE_UNSET - * if the key is not found. + * @return The entry for the given key, or a NULL if the key is not found. */ -celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* properties, const char* key); +celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key); /** - * @brief Gets the value of a property. + * @brief Get the value of a property. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. @@ -175,7 +180,7 @@ celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* pro const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue); /** - * @brief Gets the type of a property value. + * @brief Get the type of a property value. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get the type of. @@ -311,7 +316,7 @@ void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, cons /** - * @brief Get the value of a property as a Celix version. + * @brief Get the Celix version value of a property. * * This function does not convert a string property value to a Celix version automatically. * @@ -326,6 +331,28 @@ const celix_version_t* celix_properties_getVersion( const char* key, const celix_version_t* defaultValue); +/** + * @brief Get the value of a property as a Celix version. + * + * If the property value is a Celix version, a copy of this version will be returned. + * If the property value is a string, this function will attempt to convert it to a new Celix version. + * If the property is not set or is not a valid Celix version string, a copy of the provided defaultValue is returned. + * + * @note The caller is responsible for deallocating the memory of the returned version. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. + * @return A copy of the property value if it is a Celix version, or a new Celix version created from the property + * value string if it can be converted, or a copy of the default value if the property is not set or the value + * is not a valid Celix version. + * @retval NULL if version cannot be found/converted and the defaultValue is NULL. + */ +celix_version_t* celix_properties_getAsVersion( + const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue); + /** * @brief Get the number of properties in a property set. * diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index c3ae78bc5..323755cb6 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -169,12 +169,15 @@ void celix_stringHashMap_destroy(celix_string_hash_map_t* map); size_t celix_stringHashMap_size(const celix_string_hash_map_t* map); /** - * @brief add pointer entry the string hash map. + * @brief Add pointer entry the string hash map. * - * @param map The hashmap. - * @param key The key to use. The hashmap will create a copy if needed. - * @param value The value to store with the key. - * @return The previous key or NULL of no key was set. Note also returns NULL if the previous value for the key was NULL. + * @note The returned previous value can be already freed by a removed callback (if configured). + * + * @param[in] map The hashmap. + * @param[in] key The key to use. The hashmap will create a copy if needed. + * @param[in] value The value to store with the key. + * @return The previous value or NULL of no value was set for th provided key. + * Note also returns NULL if the previous value for the key was NULL. */ void* celix_stringHashMap_put(celix_string_hash_map_t* map, const char* key, void* value); @@ -301,21 +304,14 @@ bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* i void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); /** - * @brief Marco to loop over all the entries of a string hash map. - * - * Small example of how to use the iterate macro: - * @code{.c} - * celix_string_hash_map_t* map = ... - * CELIX_STRING_HASH_MAP_ITERATE(map, iter) { - * printf("Visiting hash map entry with key %s\n", inter.key); - * } - * @endcode - * - * @param map The (const celix_string_hash_map_t*) map to iterate over. - * @param iterName A iterName which will be of type celix_string_hash_map_iterator_t to hold the iterator. + * @brief Compares two celix_string_hash_map_iterator_t objects for equality. + * @param[in] iterator The first iterator to compare. + * @param[in] other The second iterator to compare. + * @return true if the iterators point to the same entry in the same hash map, false otherwise. */ -#define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \ - for (celix_string_hash_map_iterator_t iterName = celix_stringHashMap_begin(map); !celix_stringHashMapIterator_isEnd(&(iterName)); celix_stringHashMapIterator_next(&(iterName))) +bool celix_stringHashMapIterator_equals( + const celix_string_hash_map_iterator_t* iterator, + const celix_string_hash_map_iterator_t* other); /** * @brief Remove the hash map entry for the provided iterator and updates the iterator to the next hash map entry @@ -336,6 +332,24 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter); */ void celix_stringHashMapIterator_remove(celix_string_hash_map_iterator_t* iter); +/** + * @brief Marco to loop over all the entries of a string hash map. + * + * Small example of how to use the iterate macro: + * @code{.c} + * celix_string_hash_map_t* map = ... + * CELIX_STRING_HASH_MAP_ITERATE(map, iter) { + * printf("Visiting hash map entry with key %s\n", inter.key); + * } + * @endcode + * + * @param map The (const celix_string_hash_map_t*) map to iterate over. + * @param iterName A iterName which will be of type celix_string_hash_map_iterator_t to hold the iterator. + */ +#define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \ + for (celix_string_hash_map_iterator_t iterName = celix_stringHashMap_begin(map); !celix_stringHashMapIterator_isEnd(&(iterName)); celix_stringHashMapIterator_next(&(iterName))) + + #ifdef __cplusplus } #endif diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c index 32bd7da35..42e37ec62 100644 --- a/libs/utils/src/celix_hash_map.c +++ b/libs/utils/src/celix_hash_map.c @@ -224,6 +224,24 @@ static void celix_hashMap_resize(celix_hash_map_t* map, size_t newCapacity) { } #endif +static void celix_hashMap_callRemovedCallback(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) { + if (map->simpleRemovedCallback) { + map->simpleRemovedCallback(removedEntry->value.ptrValue); + } else if (map->removedLongKeyCallback) { + map->removedLongKeyCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value); + } else if (map->removedStringKeyCallback) { + map->removedStringKeyCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value); + } +} + +static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) { + celix_hashMap_callRemovedCallback(map, removedEntry); + if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) { + free((char*)removedEntry->key.strKey); + } + free(removedEntry); +} + static void celix_hashMap_addEntry(celix_hash_map_t* map, unsigned int hash, const celix_hash_map_key_t* key, const celix_hash_map_value_t* value, unsigned int bucketIndex) { celix_hash_map_entry_t* entry = map->buckets[bucketIndex]; celix_hash_map_entry_t* newEntry = malloc(sizeof(*newEntry)); @@ -256,6 +274,7 @@ static bool celix_hashMap_putValue(celix_hash_map_t* map, const char* strKey, lo if (replacedValueOut != NULL) { *replacedValueOut = entry->value; } + celix_hashMap_callRemovedCallback(map, entry); memcpy(&entry->value, value, sizeof(*value)); return true; } @@ -293,20 +312,6 @@ static bool celix_hashMap_putBool(celix_hash_map_t* map, const char* strKey, lon return celix_hashMap_putValue(map, strKey, longKey, &value, NULL); } -static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) { - if (map->simpleRemovedCallback) { - map->simpleRemovedCallback(removedEntry->value.ptrValue); - } else if (map->removedLongKeyCallback) { - map->removedLongKeyCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value); - } else if (map->removedStringKeyCallback) { - map->removedStringKeyCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value); - } - if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) { - free((char*)removedEntry->key.strKey); - } - free(removedEntry); -} - static bool celix_hashMap_remove(celix_hash_map_t* map, const char* strKey, long longKey) { celix_hash_map_key_t key; if (map->keyType == CELIX_HASH_MAP_STRING_KEY) { @@ -590,17 +595,21 @@ celix_long_hash_map_iterator_t celix_longHashMap_begin(const celix_long_hash_map celix_string_hash_map_iterator_t celix_stringHashMap_end(const celix_string_hash_map_t* map) { celix_string_hash_map_iterator_t iter; - memset(&iter, 0, sizeof(iter)); - iter.index = map->genericMap.size; iter._internal[0] = (void*)&map->genericMap; + iter._internal[1] = NULL; + iter.index = map->genericMap.size; + iter.key = ""; + memset(&iter.value, 0, sizeof(iter.value)); return iter; } celix_long_hash_map_iterator_t celix_longHashMap_end(const celix_long_hash_map_t* map) { celix_long_hash_map_iterator_t iter; - memset(&iter, 0, sizeof(iter)); - iter.index = map->genericMap.size; iter._internal[0] = (void*)&map->genericMap; + iter._internal[1] = NULL; + iter.index = map->genericMap.size; + iter.key = 0L; + memset(&iter.value, 0, sizeof(iter.value)); return iter; } @@ -615,33 +624,49 @@ bool celix_longHashMapIterator_isEnd(const celix_long_hash_map_iterator_t* iter) void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter) { const celix_hash_map_t* map = iter->_internal[0]; celix_hash_map_entry_t *entry = iter->_internal[1]; - entry = celix_hashMap_nextEntry(map, entry); iter->index += 1; - if (entry != NULL) { + entry = celix_hashMap_nextEntry(map, entry); + if (entry) { iter->_internal[1] = entry; iter->key = entry->key.strKey; iter->value = entry->value; } else { - memset(iter, 0, sizeof(*iter)); - iter->_internal[0] = (void*)map; + iter->_internal[1] = NULL; + iter->key = NULL; + memset(&iter->value, 0, sizeof(iter->value)); } } void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter) { const celix_hash_map_t* map = iter->_internal[0]; celix_hash_map_entry_t *entry = iter->_internal[1]; + iter->index += 1; entry = celix_hashMap_nextEntry(map, entry); - if (entry != NULL) { + if (entry) { iter->_internal[1] = entry; - iter->index += 1; iter->key = entry->key.longKey; iter->value = entry->value; } else { - memset(iter, 0, sizeof(*iter)); - iter->_internal[0] = (void*)map; + iter->_internal[1] = NULL; + iter->key = 0L; + memset(&iter->value, 0, sizeof(iter->value)); } } +bool celix_stringHashMapIterator_equals( + const celix_string_hash_map_iterator_t* iterator, + const celix_string_hash_map_iterator_t* other) { + return iterator->_internal[0] == other->_internal[0] /* same map */ && + iterator->_internal[1] == other->_internal[1] /* same entry */; +} + +bool celix_longHashMapIterator_equals( + const celix_long_hash_map_iterator_t* iterator, + const celix_long_hash_map_iterator_t* other) { + return iterator->_internal[0] == other->_internal[0] /* same map */ && + iterator->_internal[1] == other->_internal[1] /* same entry */; +} + void celix_stringHashMapIterator_remove(celix_string_hash_map_iterator_t* iter) { celix_hash_map_t* map = iter->_internal[0]; celix_hash_map_entry_t *entry = iter->_internal[1]; diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index fbd60715b..422a2bb70 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -23,13 +23,12 @@ #include #include #include -#include #include #include #include +#include #include "celix_build_assert.h" -#include "utils.h" //TODO try to remove #include "celix_utils.h" #include "celix_string_hash_map.h" @@ -68,7 +67,7 @@ struct celix_properties { #define MALLOC_BLOCK_SIZE 5 -static void parseLine(const char* line, celix_properties_t *props); +static void celix_properties_parseLine(const char* line, celix_properties_t *props); properties_pt properties_create(void) { return celix_properties_create(); @@ -292,7 +291,7 @@ static void celix_properties_createAndSetEntry( celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue, boolValue, versionValue); if (entry != NULL) { - celix_stringHashMap_put(properties->map, key, entry); + celix_stringHashMap_put(properties->map, entry->key, entry); } } @@ -330,7 +329,7 @@ celix_properties_t* celix_properties_create(void) { if (props != NULL) { celix_string_hash_map_create_options_t opts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS; opts.storeKeysWeakly = true; - opts.initialCapacity = CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE; + opts.initialCapacity = (unsigned int)ceil(CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE / 0.75); opts.removedCallbackData = props; opts.removedCallback = celix_properties_removeEntryCallback; props->map = celix_stringHashMap_createWithOptions(&opts); @@ -366,7 +365,7 @@ celix_properties_t* celix_properties_load(const char *filename) { return props; } -static void parseLine(const char* line, celix_properties_t *props) { +static void celix_properties_parseLine(const char* line, celix_properties_t *props) { int linePos = 0; bool precedingCharIsBackslash = false; bool isComment = false; @@ -454,18 +453,13 @@ static void parseLine(const char* line, celix_properties_t *props) { } if (!isComment) { - //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); celix_utils_trimInPlace(key); celix_utils_trimInPlace(value); -// celix_properties_setWithoutCopy(props, key, value); - celix_properties_set(props, key, value); + celix_properties_setWithoutCopy(props, key, value); } else { -// free(key); -// free(value); + free(key); + free(value); } - free(key); - free(value); - } celix_properties_t* celix_properties_loadWithStream(FILE *file) { @@ -473,6 +467,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE *file) { return NULL; } + //TODO create properties with no internal short properties buffer, so celix_properties_createWithOptions() celix_properties_t *props = celix_properties_create(); if (props == NULL) { return NULL; @@ -500,7 +495,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE *file) { char* savePtr = NULL; char* line = strtok_r(fileBuffer, "\n", &savePtr); while (line != NULL) { - parseLine(line, props); + celix_properties_parseLine(line, props); line = strtok_r(NULL, "\n", &savePtr); } free(fileBuffer); @@ -528,7 +523,7 @@ celix_properties_t* celix_properties_loadFromString(const char *input) { break; } - parseLine(line, props); + celix_properties_parseLine(line, props); } while(line != NULL); free(in); @@ -611,19 +606,19 @@ celix_properties_value_type_e celix_properties_getType(const celix_properties_t* } const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue) { - celix_properties_entry_t* entry = NULL; - if (properties != NULL) { - entry = celix_stringHashMap_get(properties->map, key); + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL) { + return entry->value; } - return entry == NULL ? defaultValue : entry->value; + return defaultValue; } -celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* properties, const char* key) { - celix_properties_entry_t invalidEntry; - memset(&invalidEntry, 0, sizeof(invalidEntry)); - invalidEntry.valueType = CELIX_PROPERTIES_VALUE_TYPE_UNSET; - celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); - return entry == NULL ? invalidEntry : *entry; +celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key) { + celix_properties_entry_t* entry = NULL; + if (properties) { + entry = celix_stringHashMap_get(properties->map, key); + } + return entry; } void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { @@ -649,7 +644,7 @@ void celix_properties_unset(celix_properties_t *properties, const char *key) { long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue) { long result = defaultValue; - celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { return entry->typed.longValue; } else if (entry != NULL) { @@ -669,7 +664,7 @@ void celix_properties_setLong(celix_properties_t *props, const char *key, long v double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue) { double result = defaultValue; - celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { return entry->typed.doubleValue; } else if (entry != NULL) { @@ -689,16 +684,16 @@ void celix_properties_setDouble(celix_properties_t *props, const char *key, doub bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue) { bool result = defaultValue; - celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key); + celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { return entry->typed.boolValue; } else if (entry != NULL) { char buf[32]; snprintf(buf, 32, "%s", entry->value); - char *trimmed = utils_stringTrim(buf); - if (strncasecmp("true", trimmed, strlen("true")) == 0) { + celix_utils_trimInPlace(buf); + if (strncasecmp("true", buf, strlen("true")) == 0) { result = true; - } else if (strncasecmp("false", trimmed, strlen("false")) == 0) { + } else if (strncasecmp("false", buf, strlen("false")) == 0) { result = false; } } @@ -713,13 +708,30 @@ const celix_version_t* celix_properties_getVersion( const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue) { - celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, key); + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { return entry->typed.versionValue; } return defaultValue; } +celix_version_t* celix_properties_getAsVersion( + const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + return celix_version_copy(entry->typed.versionValue); + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_version_t* createdVersion = celix_version_createVersionFromString(entry->value); + if (createdVersion != NULL) { + return createdVersion; + } + } + return defaultValue == NULL ? NULL : celix_version_copy(defaultValue); +} + void celix_properties_setVersion(celix_properties_t *properties, const char *key, const celix_version_t* version) { celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, NULL, celix_version_copy(version)); } @@ -786,10 +798,10 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro celix_properties_iterator_t iter; iter.index = 0; - if (!celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { - memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); - } else { + if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { memset(&iter.entry, 0, sizeof(iter.entry)); + } else { + memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); } memset(&iter._data, 0, sizeof(iter._data)); @@ -814,6 +826,7 @@ void celix_propertiesIterator_next(celix_properties_iterator_t *iter) { memcpy(&internalIter, iter->_data, sizeof(internalIter)); celix_stringHashMapIterator_next(&internalIter.mapIter); memcpy(iter->_data, &internalIter, sizeof(internalIter)); + iter->index = internalIter.mapIter.index; if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { memset(&iter->entry, 0, sizeof(iter->entry)); } else { @@ -829,11 +842,10 @@ bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter) { bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b) { celix_properties_iterator_internal_t internalIterA; - memcpy(&internalIterA, a, sizeof(internalIterA)); + memcpy(&internalIterA, a->_data, sizeof(internalIterA)); celix_properties_iterator_internal_t internalIterB; - memcpy(&internalIterB, b, sizeof(internalIterB)); - return internalIterA.props == internalIterB.props && - internalIterA.mapIter.key == internalIterB.mapIter.key; + memcpy(&internalIterB, b->_data, sizeof(internalIterB)); + return celix_stringHashMapIterator_equals(&internalIterA.mapIter, &internalIterB.mapIter); } celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter) { From ced3279168aae20863106951921b3b8e02ecea28 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 14:58:10 +0100 Subject: [PATCH 08/21] Refactor and improve properties and string hash map test suite and impl --- libs/framework/include/celix/Exception.h | 4 + libs/utils/gtest/CMakeLists.txt | 1 + .../utils/gtest/src/CxxPropertiesTestSuite.cc | 34 ++- libs/utils/gtest/src/CxxVersionTestSuite.cc | 93 +++++++ libs/utils/gtest/src/HashMapTestSuite.cc | 20 ++ libs/utils/gtest/src/PropertiesTestSuite.cc | 110 ++++---- libs/utils/gtest/src/VersionTestSuite.cc | 244 +++++++----------- libs/utils/include/celix/IOException.h | 51 ++++ libs/utils/include/celix/Properties.h | 70 ++++- libs/utils/include/celix/Version.h | 9 +- libs/utils/include/celix_long_hash_map.h | 8 + libs/utils/include/celix_properties.h | 14 +- libs/utils/include/celix_string_hash_map.h | 33 ++- libs/utils/src/celix_hash_map.c | 48 +++- libs/utils/src/properties.c | 51 ++-- libs/utils/src/version.c | 2 +- 16 files changed, 523 insertions(+), 269 deletions(-) create mode 100644 libs/utils/gtest/src/CxxVersionTestSuite.cc create mode 100644 libs/utils/include/celix/IOException.h diff --git a/libs/framework/include/celix/Exception.h b/libs/framework/include/celix/Exception.h index 1675567d4..b7186634c 100644 --- a/libs/framework/include/celix/Exception.h +++ b/libs/framework/include/celix/Exception.h @@ -27,7 +27,11 @@ namespace celix { */ class Exception : public std::exception { public: +#if __cplusplus >= 201703L //C++17 or higher + explicit Exception(std::string_view msg) : w{msg} {} +#else explicit Exception(std::string msg) : w{std::move(msg)} {} +#endif Exception(const Exception&) = default; Exception(Exception&&) = default; diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 1e512fe61..894fba324 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(test_utils src/FileUtilsTestSuite.cc src/PropertiesTestSuite.cc src/VersionTestSuite.cc + src/CxxVersionTestSuite.cc ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 142570c4b..b2f39192a 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -27,12 +27,12 @@ class CxxPropertiesTestSuite : public ::testing::Test { public: }; -TEST_F(CxxPropertiesTestSuite, testCreateDestroy) { +TEST_F(CxxPropertiesTestSuite, CreateDestroyTest) { celix::Properties props{}; EXPECT_EQ(0, props.size()); } -TEST_F(CxxPropertiesTestSuite, testFillAndLoop) { +TEST_F(CxxPropertiesTestSuite, FillAndLoopTest) { celix::Properties props{}; EXPECT_EQ(0, props.size()); @@ -66,7 +66,7 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) { EXPECT_EQ(5, count); } -TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) { +TEST_F(CxxPropertiesTestSuite, LoopForSize0And1Test) { celix::Properties props0{}; for (const auto& pair : props0) { FAIL() << "Should not get an loop entry with a properties size of 0. got key: " << pair.first; @@ -82,7 +82,7 @@ TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) { EXPECT_EQ(1, count); } -TEST_F(CxxPropertiesTestSuite, testCopy) { +TEST_F(CxxPropertiesTestSuite, CopyTest) { celix::Properties props{}; props["key1"] = "value1"; @@ -97,7 +97,7 @@ TEST_F(CxxPropertiesTestSuite, testCopy) { EXPECT_EQ(v2, "value1_new"); } -TEST_F(CxxPropertiesTestSuite, testWrap) { +TEST_F(CxxPropertiesTestSuite, WrapTest) { auto *props = celix_properties_create(); celix_properties_set(props, "test", "test"); @@ -112,7 +112,7 @@ TEST_F(CxxPropertiesTestSuite, testWrap) { celix_properties_destroy(props); } -TEST_F(CxxPropertiesTestSuite, getType) { +TEST_F(CxxPropertiesTestSuite, GetTypeTest) { celix::Properties props{}; props.set("bool", true); @@ -140,7 +140,7 @@ TEST_F(CxxPropertiesTestSuite, getType) { EXPECT_EQ(props.getType("version"), celix::Properties::ValueType::Version); } -TEST_F(CxxPropertiesTestSuite, testGetAsVersion) { +TEST_F(CxxPropertiesTestSuite, GetAsVersionTest) { celix::Properties props; // Test getting a version from a string property @@ -163,8 +163,24 @@ TEST_F(CxxPropertiesTestSuite, testGetAsVersion) { EXPECT_EQ(props.getAsVersion("key", celix::Version{4, 5, 6}), ver); } +TEST_F(CxxPropertiesTestSuite, StoreAndLoadTest) { + std::string path{"cxx_store_and_load_test.properties"}; + + celix::Properties props{}; + props.set("key1", 1); + props.set("key2", 2); + + EXPECT_NO_THROW(props.store(path)); + + celix::Properties loadedProps{}; + EXPECT_NO_THROW(loadedProps = celix::Properties::load(path)); + EXPECT_EQ(props.size(), loadedProps.size()); + + EXPECT_THROW(loadedProps = celix::Properties::load("non-existence"), celix::IOException); +} + #if __cplusplus >= 201703L //C++17 or higher -TEST_F(CxxPropertiesTestSuite, testStringView) { +TEST_F(CxxPropertiesTestSuite, StringViewTest) { constexpr std::string_view stringViewKey = "KEY1"; constexpr std::string_view stringViewValue = "VALUE1"; std::string stringKey{"KEY2"}; @@ -219,7 +235,7 @@ TEST_F(CxxPropertiesTestSuite, testStringView) { } } -TEST_F(CxxPropertiesTestSuite, testUseOfConstexprInSetMethod) { +TEST_F(CxxPropertiesTestSuite, UseOfConstexprInSetMethodTest) { celix::Properties props{}; //Test if different bool "types" are correctly handled diff --git a/libs/utils/gtest/src/CxxVersionTestSuite.cc b/libs/utils/gtest/src/CxxVersionTestSuite.cc new file mode 100644 index 000000000..0055d5c4a --- /dev/null +++ b/libs/utils/gtest/src/CxxVersionTestSuite.cc @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "celix/Version.h" + +class CxxVersionTestSuite : public ::testing::Test {}; + +#include "gtest/gtest.h" +#include "celix/Version.h" + +TEST_F(CxxVersionTestSuite, DefaultConstructorTest) { + celix::Version v; + EXPECT_EQ(0, v.getMajor()); + EXPECT_EQ(0, v.getMinor()); + EXPECT_EQ(0, v.getMicro()); + EXPECT_EQ("", v.getQualifier()); +} + +TEST_F(CxxVersionTestSuite, ConstructorTest) { + celix::Version v1{1, 2, 3, "qualifier"}; + EXPECT_EQ(1, v1.getMajor()); + EXPECT_EQ(2, v1.getMinor()); + EXPECT_EQ(3, v1.getMicro()); + EXPECT_EQ("qualifier", v1.getQualifier()); + + #if __cplusplus >= 201703L //C++17 or higher + celix::Version v2{1, 2, 3, std::string_view{"qualifier"}}; + EXPECT_EQ(1, v2.getMajor()); + EXPECT_EQ(2, v2.getMinor()); + EXPECT_EQ(3, v2.getMicro()); + EXPECT_EQ("qualifier", v2.getQualifier()); + #endif +} + +TEST_F(CxxVersionTestSuite, MoveConstructorTest) { + celix::Version v1{2, 3, 4, "qualifier"}; + celix::Version v2{std::move(v1)}; + + //v2 should have the values of v1 before the move + EXPECT_EQ(v2.getMajor(), 2); + EXPECT_EQ(v2.getMinor(), 3); + EXPECT_EQ(v2.getMicro(), 4); + EXPECT_EQ(v2.getQualifier(), "qualifier"); +} + +TEST_F(CxxVersionTestSuite, CopyConstructorTest) { + celix::Version v1{1, 2, 3, "qualifier"}; + celix::Version v2 = v1; + + EXPECT_EQ(v1, v2); + EXPECT_EQ(v1.getMajor(), v2.getMajor()); + EXPECT_EQ(v1.getMinor(), v2.getMinor()); + EXPECT_EQ(v1.getMicro(), v2.getMicro()); + EXPECT_EQ(v1.getQualifier(), v2.getQualifier()); +} + +TEST_F(CxxVersionTestSuite, MapKeyTest) { + // Test using Version as key in std::map + std::map map; + map[celix::Version{1, 2, 3}] = 1; + map[celix::Version{3, 2, 1}] = 2; + + EXPECT_EQ(map[(celix::Version{1, 2, 3})], 1); + EXPECT_EQ(map[(celix::Version{3, 2, 1})], 2); +} + +TEST_F(CxxVersionTestSuite, UnorderedMapKeyTest) { + // Test using Version as key in std::unordered_map + std::unordered_map unorderedMap; + unorderedMap[celix::Version{1, 2, 3}] = 1; + unorderedMap[celix::Version{3, 2, 1}] = 2; + + EXPECT_EQ(unorderedMap[(celix::Version{1, 2, 3})], 1); + EXPECT_EQ(unorderedMap[(celix::Version{3, 2, 1})], 2); +} diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc b/libs/utils/gtest/src/HashMapTestSuite.cc index 27f0589af..7f9010e3a 100644 --- a/libs/utils/gtest/src/HashMapTestSuite.cc +++ b/libs/utils/gtest/src/HashMapTestSuite.cc @@ -632,3 +632,23 @@ TEST_F(HashMapTestSuite, EqualsZeroSizeMapTest) { celix_stringHashMap_destroy(sMap); celix_longHashMap_destroy(lMap); } + +TEST_F(HashMapTestSuite, StoreKeysWeaklyTest) { + celix_string_hash_map_create_options_t opts{}; + opts.removedCallbackData = (void*)0x1; + opts.storeKeysWeakly = true; + opts.removedKeyCallback = [](void* data, char* key) { + EXPECT_EQ(data, (void*)0x1); + free(key); + }; + auto* sMap = celix_stringHashMap_createWithOptions(&opts); + EXPECT_FALSE(celix_stringHashMap_putLong(sMap, celix_utils_strdup("key1"), 1)); //new key -> takes ownership + EXPECT_TRUE(celix_stringHashMap_putLong(sMap, "key1", 2)); //replace key -> takes no ownership + + EXPECT_FALSE(celix_stringHashMap_putLong(sMap, celix_utils_strdup("key2"), 3)); //new key -> takes ownership + EXPECT_TRUE(celix_stringHashMap_putLong(sMap, "key2", 4)); //replace key -> takes no ownership + celix_stringHashMap_remove(sMap, "key1"); + + celix_stringHashMap_destroy(sMap); +} + diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 74e96f6e7..f21b4049c 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -20,6 +20,7 @@ #include #include "celix_properties.h" +#include "celix_utils.h" using ::testing::MatchesRegex; @@ -28,14 +29,14 @@ class PropertiesTestSuite : public ::testing::Test { }; -TEST_F(PropertiesTestSuite, create) { +TEST_F(PropertiesTestSuite, CreateTest) { auto* properties = celix_properties_create(); EXPECT_TRUE(properties); celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, load) { +TEST_F(PropertiesTestSuite, LoadTest) { char propertiesFile[] = "resources-test/properties.txt"; auto* properties = celix_properties_load(propertiesFile); EXPECT_EQ(4, celix_properties_size(properties)); @@ -55,7 +56,21 @@ TEST_F(PropertiesTestSuite, load) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, asLong) { +TEST_F(PropertiesTestSuite, StoreTest) { + const char* propertiesFile = "resources-test/properties_out.txt"; + auto* properties = celix_properties_create(); + char keyA[] = "x"; + char keyB[] = "y"; + char valueA[] = "1"; + char valueB[] = "2"; + celix_properties_set(properties, keyA, valueA); + celix_properties_set(properties, keyB, valueB); + celix_properties_store(properties, propertiesFile, nullptr); + + celix_properties_destroy(properties); +} + +TEST_F(PropertiesTestSuite, GetAsLongTest) { celix_properties_t *props = celix_properties_create(); celix_properties_set(props, "t1", "42"); celix_properties_set(props, "t2", "-42"); @@ -84,21 +99,7 @@ TEST_F(PropertiesTestSuite, asLong) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, store) { - char propertiesFile[] = "resources-test/properties_out.txt"; - auto* properties = celix_properties_create(); - char keyA[] = "x"; - char keyB[] = "y"; - char valueA[] = "1"; - char valueB[] = "2"; - celix_properties_set(properties, keyA, valueA); - celix_properties_set(properties, keyB, valueB); - celix_properties_store(properties, propertiesFile, nullptr); - - celix_properties_destroy(properties); -} - -TEST_F(PropertiesTestSuite, copy) { +TEST_F(PropertiesTestSuite, CopyTest) { char propertiesFile[] = "resources-test/properties.txt"; auto* properties = celix_properties_load(propertiesFile); EXPECT_EQ(4, celix_properties_size(properties)); @@ -115,7 +116,7 @@ TEST_F(PropertiesTestSuite, copy) { celix_properties_destroy(copy); } -TEST_F(PropertiesTestSuite, getSet) { +TEST_F(PropertiesTestSuite, GetSetTest) { auto* properties = celix_properties_create(); char keyA[] = "x"; char keyB[] = "y"; @@ -137,7 +138,7 @@ TEST_F(PropertiesTestSuite, getSet) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, getSetWithNULL) { +TEST_F(PropertiesTestSuite, GetSetWithNullTest) { auto* properties = celix_properties_create(); celix_properties_set(properties, nullptr, "value"); @@ -153,7 +154,7 @@ TEST_F(PropertiesTestSuite, getSetWithNULL) { } -TEST_F(PropertiesTestSuite, setUnset) { +TEST_F(PropertiesTestSuite, SetUnsetTest) { auto* properties = celix_properties_create(); char keyA[] = "x"; char *keyD = strndup("a", 1); @@ -171,7 +172,7 @@ TEST_F(PropertiesTestSuite, setUnset) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, longTest) { +TEST_F(PropertiesTestSuite, GetLongTest) { auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "2"); @@ -201,7 +202,7 @@ TEST_F(PropertiesTestSuite, longTest) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, boolTest) { +TEST_F(PropertiesTestSuite, GetBoolTest) { auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "true"); @@ -233,7 +234,7 @@ TEST_F(PropertiesTestSuite, boolTest) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, fillTest) { +TEST_F(PropertiesTestSuite, GetFillTest) { celix_properties_t *props = celix_properties_create(); int testCount = 1000; for (int i = 0; i < 1000; ++i) { @@ -246,31 +247,34 @@ TEST_F(PropertiesTestSuite, fillTest) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, setOverwrite) { +TEST_F(PropertiesTestSuite, GetSetOverwrite) { auto* props = celix_properties_create(); auto* version = celix_version_createEmptyVersion(); - const char* key = "key"; - - celix_properties_set(props, key, "str1"); - EXPECT_STREQ("str1", celix_properties_get(props, key, "")); - celix_properties_set(props, key, "str2"); - EXPECT_STREQ("str2", celix_properties_get(props, key, "")); - celix_properties_setLong(props, key, 1); - EXPECT_EQ(1, celix_properties_getAsLong(props, key, -1L)); - celix_properties_setDouble(props, key, 2.0); - EXPECT_EQ(2.0, celix_properties_getAsLong(props, key, -2.0)); - celix_properties_setBool(props, key, false); - EXPECT_EQ(false, celix_properties_getAsBool(props, key, true)); - celix_properties_setVersionWithoutCopy(props, key, version); - EXPECT_EQ(version, celix_properties_getVersion(props, key, nullptr)); - celix_properties_set(props, key, "last"); + + { + char* key = celix_utils_strdup("key"); + celix_properties_set(props, key, "str1"); + free(key); + } + EXPECT_STREQ("str1", celix_properties_get(props, "key", "")); + celix_properties_set(props, "key", "str2"); + EXPECT_STREQ("str2", celix_properties_get(props, "key", "")); + celix_properties_setLong(props, "key", 1); + EXPECT_EQ(1, celix_properties_getAsLong(props, "key", -1L)); + celix_properties_setDouble(props, "key", 2.0); + EXPECT_EQ(2.0, celix_properties_getAsLong(props, "key", -2.0)); + celix_properties_setBool(props, "key", false); + EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true)); + celix_properties_setVersionWithoutCopy(props, "key", version); + EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr)); + celix_properties_set(props, "key", "last"); celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { +TEST_F(PropertiesTestSuite, SizeAndIteratorTest) { celix_properties_t *props = celix_properties_create(); EXPECT_EQ(0, celix_properties_size(props)); celix_properties_set(props, "a", "1"); @@ -291,7 +295,7 @@ TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, getType) { +TEST_F(PropertiesTestSuite, GetTypeTest) { auto* props = celix_properties_create(); celix_properties_set(props, "string", "value"); celix_properties_setLong(props, "long", 123); @@ -311,7 +315,7 @@ TEST_F(PropertiesTestSuite, getType) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, getEntry) { +TEST_F(PropertiesTestSuite, GetEntryTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_setLong(props, "key2", 123); @@ -354,7 +358,7 @@ TEST_F(PropertiesTestSuite, getEntry) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, iteratorNextKey) { +TEST_F(PropertiesTestSuite, IteratorNextKeyTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); @@ -374,7 +378,7 @@ TEST_F(PropertiesTestSuite, iteratorNextKey) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, iteratorNext) { +TEST_F(PropertiesTestSuite, IteratorNextTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); @@ -383,7 +387,7 @@ TEST_F(PropertiesTestSuite, iteratorNext) { int count = 0; auto iter = celix_properties_begin(props); while (!celix_propertiesIterator_isEnd(&iter)) { - EXPECT_NE(strstr(iter.entry.key, "key"), nullptr); + EXPECT_NE(strstr(iter.key, "key"), nullptr); EXPECT_NE(strstr(iter.entry.value, "value"), nullptr); count++; celix_propertiesIterator_next(&iter); @@ -395,7 +399,7 @@ TEST_F(PropertiesTestSuite, iteratorNext) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, iterateOverProperties) { +TEST_F(PropertiesTestSuite, IterateOverPropertiesTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); @@ -404,12 +408,12 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) { int innerCount = 0; CELIX_PROPERTIES_ITERATE(props, outerIter) { outerCount++; - EXPECT_NE(strstr(outerIter.entry.key, "key"), nullptr); + EXPECT_NE(strstr(outerIter.key, "key"), nullptr); // Inner loop to test nested iteration CELIX_PROPERTIES_ITERATE(props, innerIter) { innerCount++; - EXPECT_NE(strstr(innerIter.entry.key, "key"), nullptr); + EXPECT_NE(strstr(innerIter.key, "key"), nullptr); } } // Check that both entries were iterated over @@ -428,7 +432,7 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, getVersion) { +TEST_F(PropertiesTestSuite, GetVersionTest) { auto* properties = celix_properties_create(); auto* emptyVersion = celix_version_createEmptyVersion(); @@ -483,7 +487,7 @@ TEST_F(PropertiesTestSuite, getVersion) { celix_properties_destroy(properties); } -TEST_F(PropertiesTestSuite, TestEndOfProperties) { +TEST_F(PropertiesTestSuite, EndOfPropertiesTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); celix_properties_set(props, "key2", "value2"); @@ -495,7 +499,7 @@ TEST_F(PropertiesTestSuite, TestEndOfProperties) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) { +TEST_F(PropertiesTestSuite, EndOfEmptyPropertiesTest) { auto* props = celix_properties_create(); celix_properties_iterator_t endIter = celix_properties_end(props); @@ -504,3 +508,5 @@ TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) { celix_properties_destroy(props); } + +//TODO test replace and replace WithCopy \ No newline at end of file diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 90e5e9f1c..54539c7b9 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -24,208 +24,134 @@ #include "celix_utils.h" #include "version.h" -extern "C" -{ -#include "version_private.h" -} - -class VersionTestSuite : public ::testing::Test {}; - -TEST_F(VersionTestSuite, create) { +class VersionTestSuite : public ::testing::Test { +public: + void expectVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier = "") { + if (version) { + EXPECT_EQ(major, celix_version_getMajor(version)); + EXPECT_EQ(minor, celix_version_getMinor(version)); + EXPECT_EQ(micro, celix_version_getMicro(version)); + EXPECT_STREQ(qualifier, celix_version_getQualifier(version)); + } + } +}; + +TEST_F(VersionTestSuite, CreateTest) { celix_version_t* version = nullptr; - char * str; - str = celix_utils_strdup("abc"); - EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version)); + version = celix_version_create(1, 2, 3, "abc"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - EXPECT_STREQ("abc", version->qualifier); + expectVersion(version, 1, 2, 3, "abc"); + celix_version_destroy(version); - version_destroy(version); + EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc")); + EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc|xyz")); + + + //Testing deprecated api version = nullptr; EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, nullptr, &version)); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - EXPECT_STREQ("", version->qualifier); - + int major; + int minor; + int micro; + const char* q; + EXPECT_EQ(CELIX_SUCCESS, version_getMajor(version, &major)); + EXPECT_EQ(CELIX_SUCCESS, version_getMinor(version, &minor)); + EXPECT_EQ(CELIX_SUCCESS, version_getMicro(version, µ)); + EXPECT_EQ(CELIX_SUCCESS, version_getQualifier(version, &q)); + EXPECT_EQ(1, major); + EXPECT_EQ(2, minor); + EXPECT_EQ(3, micro); + EXPECT_STREQ("", q); version_destroy(version); - version = nullptr; - free(str); - str = celix_utils_strdup("abc"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, &version)); - version_destroy(version); version = nullptr; - free(str); - str = celix_utils_strdup("abc|xyz"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, &version)); - - version_destroy(version); - free(str); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, "abc", &version)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, "abc|xyz", &version)); } -TEST_F(VersionTestSuite, copy) { +TEST_F(VersionTestSuite, CopyTest) { auto* version = celix_version_create(1, 2, 3, "abc"); auto* copy = celix_version_copy(version); EXPECT_NE(nullptr, version); EXPECT_NE(nullptr, copy); - EXPECT_EQ(1, celix_version_getMajor(copy)); - EXPECT_EQ(2, celix_version_getMinor(copy)); - EXPECT_EQ(3, celix_version_getMicro(copy)); - EXPECT_STREQ("abc", celix_version_getQualifier(copy)); + expectVersion(version, 1, 2, 3, "abc"); celix_version_destroy(copy); celix_version_destroy(version); copy = celix_version_copy(nullptr); //returns "empty" version EXPECT_NE(nullptr, copy); - EXPECT_EQ(0, celix_version_getMajor(copy)); - EXPECT_EQ(0, celix_version_getMinor(copy)); - EXPECT_EQ(0, celix_version_getMicro(copy)); - EXPECT_STREQ("", celix_version_getQualifier(copy)); + expectVersion(copy, 0, 0, 0, ""); celix_version_destroy(copy); } -TEST_F(VersionTestSuite, createFromString) { - celix_version_t* version = nullptr; - celix_status_t status = CELIX_SUCCESS; - char * str; - - str = celix_utils_strdup("1"); - EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version)); +TEST_F(VersionTestSuite, CreateFromStringTest) { + auto* version = celix_version_createVersionFromString("1"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - + expectVersion(version, 1, 0, 0); version_destroy(version); - free(str); - str = celix_utils_strdup("a"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = celix_utils_strdup("1.a"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = celix_utils_strdup("1.1.a"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); - - free(str); - str = celix_utils_strdup("-1"); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version)); + EXPECT_EQ(nullptr, celix_version_createVersionFromString("a")); + EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.a")); + EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.1.a")); + EXPECT_EQ(nullptr, celix_version_createVersionFromString("-1")); + EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.2.3.abc|xyz")); - free(str); - str = celix_utils_strdup("1.2"); - version = nullptr; - EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version)); + version = celix_version_createVersionFromString("1.2"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - + expectVersion(version, 1, 2, 0); version_destroy(version); - free(str); - str = celix_utils_strdup("1.2.3"); - version = nullptr; - status = version_createVersionFromString(str, &version); - EXPECT_EQ(CELIX_SUCCESS, status); + version = celix_version_createVersionFromString("1.2.3"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - + expectVersion(version, 1, 2, 3); version_destroy(version); - free(str); - str = celix_utils_strdup("1.2.3.abc"); - version = nullptr; - status = version_createVersionFromString(str, &version); - EXPECT_EQ(CELIX_SUCCESS, status); - EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - EXPECT_STREQ("abc", version->qualifier); - version_destroy(version); - free(str); - str = celix_utils_strdup("1.2.3.abc_xyz"); - version = nullptr; - status = version_createVersionFromString(str, &version); - EXPECT_EQ(CELIX_SUCCESS, status); + version = celix_version_createVersionFromString("1.2.3.abc"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - EXPECT_STREQ("abc_xyz", version->qualifier); - + expectVersion(version, 1, 2, 3, "abc"); version_destroy(version); - free(str); - str = celix_utils_strdup("1.2.3.abc-xyz"); - version = nullptr; - status = version_createVersionFromString(str, &version); - EXPECT_EQ(CELIX_SUCCESS, status); - EXPECT_TRUE(version != nullptr); - EXPECT_EQ(1, version->major); - EXPECT_EQ(2, version->minor); - EXPECT_EQ(3, version->micro); - EXPECT_STREQ("abc-xyz", version->qualifier); + version = celix_version_createVersionFromString("1.2.3.abc_xyz"); + EXPECT_TRUE(version != nullptr); + expectVersion(version, 1, 2, 3, "abc_xyz"); version_destroy(version); - free(str); - str = celix_utils_strdup("1.2.3.abc|xyz"); - status = version_createVersionFromString(str, &version); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, status); - free(str); -} - -TEST_F(VersionTestSuite, createEmptyVersion) { - celix_version_t* version = nullptr; - celix_status_t status = CELIX_SUCCESS; - status = version_createEmptyVersion(&version); - EXPECT_EQ(CELIX_SUCCESS, status); + version = celix_version_createVersionFromString("1.2.3.abc-xyz"); EXPECT_TRUE(version != nullptr); - EXPECT_EQ(0, version->major); - EXPECT_EQ(0, version->minor); - EXPECT_EQ(0, version->micro); - EXPECT_STREQ("", version->qualifier); - + expectVersion(version, 1, 2, 3, "abc-xyz"); version_destroy(version); -} -TEST_F(VersionTestSuite, getters) { - celix_version_t* version = nullptr; - celix_status_t status = CELIX_SUCCESS; - char * str; - int major, minor, micro; - const char *qualifier; - str = celix_utils_strdup("abc"); - status = version_createVersion(1, 2, 3, str, &version); - EXPECT_EQ(CELIX_SUCCESS, status); + //Testing deprecated api + version = nullptr; + EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString("1", &version)); EXPECT_TRUE(version != nullptr); + expectVersion(version, 1, 0, 0); + version_destroy(version); - version_getMajor(version, &major); - EXPECT_EQ(1, major); - - version_getMinor(version, &minor); - EXPECT_EQ(2, minor); - - version_getMicro(version, µ); - EXPECT_EQ(3, micro); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString("a", &version)); +} - version_getQualifier(version, &qualifier); - EXPECT_STREQ("abc", qualifier); +TEST_F(VersionTestSuite, CreateEmptyVersionTest) { + auto* version = celix_version_createEmptyVersion(); + EXPECT_TRUE(version != nullptr); + expectVersion(version, 0, 0, 0, ""); + version_destroy(version); + //Testing deprecated api + version = nullptr; + EXPECT_EQ(CELIX_SUCCESS, version_createEmptyVersion(&version)); + EXPECT_TRUE(version != nullptr); + expectVersion(version, 0, 0, 0, ""); version_destroy(version); - free(str); } -TEST_F(VersionTestSuite, compare) { +TEST_F(VersionTestSuite, CompareTest) { + //TODO update to updated API + celix_version_t* version = nullptr; celix_version_t* compare = nullptr; celix_status_t status = CELIX_SUCCESS; @@ -261,7 +187,7 @@ TEST_F(VersionTestSuite, compare) { EXPECT_EQ(CELIX_SUCCESS, status); EXPECT_TRUE(result < 0); - // Compare againts a lower version + // Compare against a lower version free(str); str = celix_utils_strdup("abc"); version_destroy(compare); @@ -278,7 +204,7 @@ TEST_F(VersionTestSuite, compare) { free(str); } -TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) { +TEST_F(VersionTestSuite, CompareToMajorMinorTest) { auto* version1 = celix_version_create(2, 2, 0, nullptr); auto* version2 = celix_version_create(2, 2, 4, "qualifier"); @@ -300,7 +226,9 @@ TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) { celix_version_destroy(version2); } -TEST_F(VersionTestSuite, toString) { +TEST_F(VersionTestSuite, ToStringTest) { + //TODO update to updated API + celix_version_t* version = nullptr; celix_status_t status = CELIX_SUCCESS; char * str; @@ -333,7 +261,9 @@ TEST_F(VersionTestSuite, toString) { free(str); } -TEST_F(VersionTestSuite,semanticCompatibility) { +TEST_F(VersionTestSuite, SemanticCompatibilityTest) { + //TODO update to updated API + celix_version_t* provider = nullptr; celix_version_t* compatible_user = nullptr; celix_version_t* incompatible_user_by_major = nullptr; @@ -367,7 +297,7 @@ TEST_F(VersionTestSuite,semanticCompatibility) { version_destroy(incompatible_user_by_minor); } -TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) { +TEST_F(VersionTestSuite, CompareEmptyAndNullQualifierTest) { //nullptr or "" qualifier should be the same auto* v1 = celix_version_create(0, 0, 0, nullptr); auto* v2 = celix_version_create(0, 0, 0, ""); @@ -379,7 +309,7 @@ TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) { celix_version_destroy(v2); } -TEST_F(VersionTestSuite, fillString) { +TEST_F(VersionTestSuite, FillStringTest) { // Create a version object auto* version = celix_version_create(1, 2, 3, "alpha"); diff --git a/libs/utils/include/celix/IOException.h b/libs/utils/include/celix/IOException.h new file mode 100644 index 000000000..1715801e3 --- /dev/null +++ b/libs/utils/include/celix/IOException.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#pragma once + +#include +#if __cplusplus >= 201703L //C++17 or higher +#include +#endif + +namespace celix { + + /** + * @brief Celix runtime IO Exception + */ + class IOException : public std::exception { + public: +#if __cplusplus >= 201703L //C++17 or higher + explicit IOException(std::string_view msg) : w{msg} {} +#else + explicit IOException(std::string msg) : w{std::move(msg)} {} +#endif + + IOException(const IOException&) = default; + IOException(IOException&&) = default; + IOException& operator=(const IOException&) = default; + IOException& operator=(IOException&&) = default; + + [[nodiscard]] const char* what() const noexcept override { + return w.c_str(); + } + private: + std::string w; + }; + +} diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 45bb0c8be..5be5a7621 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -27,6 +27,7 @@ #include "celix_properties.h" #include "celix_utils.h" #include "celix/Version.h" +#include "celix/IOException.h" namespace celix { @@ -75,12 +76,12 @@ namespace celix { first = {}; second = {}; } else { - first = iter.entry.key; + first = iter.key; second = iter.entry.value; } } - celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}}; + celix_properties_iterator_t iter{.index = 0, .key = nullptr, .entry = {}, ._data = {}}; }; @@ -190,11 +191,19 @@ namespace celix { #endif /** - * @brief Wraps C properties, but does not take ownership -> dtor will not destroy properties + * @brief Wrap C properties and returns it as const in a shared_ptr, + * but does not take ownership -> dtor will not destroy C properties. */ static std::shared_ptr wrap(const celix_properties_t* wrapProps) { auto* cp = const_cast(wrapProps); - return std::shared_ptr{new Properties{cp}}; + return std::shared_ptr{new Properties{cp, false}}; + } + + /** + * @brief Wrap C properties and take ownership -> dtor will destroy C properties. + */ + static Properties own(celix_properties_t* wrapProps) { + return Properties{wrapProps, true}; } /** @@ -598,15 +607,43 @@ namespace celix { #endif - //TODO test - void store(const std::string& file, const std::string& header = {}) const { - celix_properties_store(cProps.get(), file.c_str(), header.empty() ? nullptr : header.c_str()); + + /** + * @brief Store the property set to the given file path. + * + * This function writes the properties in the given set to the specified file path in a format suitable + * for loading with the load() function. + * If a non-empty header string is provided, it will be written as a comment at the beginning of the file. + * + * @param[in] file The file to store the properties to. + * @param[in] header An optional header string to include as a comment at the beginning of the file. + * @throws celix::IOException If an error occurs while writing to the file. + */ +#if __cplusplus >= 201703L //C++17 or higher + void store(std::string_view path, std::string_view header = {}) const { + storeTo(path.data(), header.empty() ? nullptr : header.data()); + } +#else + void store(const std::string& path, const std::string& header = {}) const { + storeTo(path.data(), header.empty() ? nullptr : header.data()); } +#endif - //TODO laod + /** + * @brief Loads properties from the file at the given path. + * @param[in] path The path to the file containing the properties. + * @return A new Properties object containing the properties from the file. + * @throws celix::IOException If the file cannot be opened or read. + */ +#if __cplusplus >= 201703L //C++17 or higher + static celix::Properties load(std::string_view path) { return loadFrom(path.data()); } +#else + static celix::Properties load(const std::string& path) { return loadFrom(path.data()); } +#endif private: - explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {} + Properties(celix_properties_t* props, bool takeOwnership) : + cProps{props, [ownership = takeOwnership](celix_properties_t* p){ if (ownership) { celix_properties_destroy(p); }}} {} static celix::Properties::ValueType getAndConvertType( const std::shared_ptr& cProperties, @@ -628,6 +665,21 @@ namespace celix { } } + static celix::Properties loadFrom(const char* path) { + auto* cProps = celix_properties_load(path); + if (cProps) { + return celix::Properties::own(cProps); + } + throw celix::IOException{"Cannot load celix::Properties from path " + std::string{path}}; + } + + void storeTo(const char* path, const char* header) const { + auto status = celix_properties_store(cProps.get(), path, header); + if (status != CELIX_SUCCESS) { + throw celix::IOException{"Cannot store celix::Properties to " + std::string{path}}; + } + } + std::shared_ptr cProps; }; diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h index ef50be0f9..c8d2943d4 100644 --- a/libs/utils/include/celix/Version.h +++ b/libs/utils/include/celix/Version.h @@ -26,10 +26,7 @@ namespace celix { - //TODO CxxVersionTestSuite //TODO doxygen - //TODO test in unordered map and set - //TODO test in map and set class Version { public: Version() : @@ -37,7 +34,7 @@ namespace celix { qualifier{celix_version_getQualifier(cVersion.get())} {} #if __cplusplus >= 201703L //C++17 or higher - explicit Version(int major, int minor, int micro, std::string_view qualifier = {}) : + Version(int major, int minor, int micro, std::string_view qualifier = {}) : cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))}, qualifier{celix_version_getQualifier(cVersion.get())} {} #else @@ -110,7 +107,9 @@ namespace celix { /** * @brief Create a wrap around a C Celix version without taking ownership. */ - explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t *){/*nop*/}} {} + explicit Version(celix_version_t* v) : + cVersion{v, [](celix_version_t *){/*nop*/}}, + qualifier{celix_version_getQualifier(cVersion.get())} {} std::shared_ptr cVersion; std::string qualifier; //cached qualifier of the const char* from celix_version_getQualifier diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index c0c36968c..aacfef589 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -69,6 +69,9 @@ typedef struct celix_long_hash_map_create_options { * only the simpleRemovedCallback will be used. * * Default is NULL. + * + * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the + * hash map and can be freed. */ void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT; @@ -89,6 +92,11 @@ typedef struct celix_long_hash_map_create_options { * only the simpleRemovedCallback will be used. * * Default is NULL. + * + * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData. + * @param[in] removedKey The key of the value that was removed from the hash map. + * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the + * hash map and can be freed. */ void (*removedCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue) CELIX_OPTS_INIT; diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index f20a2fe85..71bc0f536 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -70,7 +70,6 @@ typedef enum celix_properties_value_type { * @brief A structure representing a single entry in a property set. */ typedef struct celix_properties_entry { - const char* key; /**< The key of the entry*/ const char* value; /**< The string value or string representation of a non-string typed value.*/ celix_properties_value_type_e valueType; /**< The type of the value of the entry */ @@ -94,10 +93,15 @@ typedef struct celix_properties_iterator { /** * @brief The index of the current iterator. */ - int index; + size_t index; /** - * @brief The current entry. + * @brief Te current key. + */ + const char* key; + + /** + * @brief The current value entry. */ celix_properties_entry_t entry; @@ -152,6 +156,8 @@ celix_properties_t* celix_properties_loadFromString(const char *input); /** * @brief Store properties to a file. * + * @note Properties values are always stored as string values, regardless of their actual underlining types. + * * @param[in] properties The property set to store. * @param[in] file The name of the file to store the properties to. * @param[in] header An optional header to write to the file before the properties. @@ -200,6 +206,8 @@ void celix_properties_set(celix_properties_t* properties, const char* key, const /** * @brief Set the value of a property without copying the key and value strings. * + * @note If the setWithoutCopy replaced an exising value, the key will be freed by callee. + * * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. This string will be used directly, so it must not be freed or modified * after calling this function. diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index 323755cb6..7dfe661f7 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -68,11 +68,14 @@ typedef struct celix_string_hash_map_create_options { * only the simpledRemoveCallback will be used. * * Default is NULL. + * + * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the + * hash map and can be freed. */ - void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT; + void (*simpleRemovedCallback)(void* removedValue) CELIX_OPTS_INIT; /** - * Optional callback data, which will be provided to the removedCallback callback. + * Optional callback data, which will be provided to the removedCallback and removedKeyCallback callback. * * Default is NULL. */ @@ -88,14 +91,37 @@ typedef struct celix_string_hash_map_create_options { * only the simpledRemoveCallback will be used. * * Default is NULL. + * + * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData. + * @param[in] removedKey The key of the value that was removed from the hash map. + * Note that the removedKey can still be in use if the a value entry for the same key is + * replaced, so this callback should not free the removedKey. + * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the + * hash map and can be freed. */ void (*removedCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue) CELIX_OPTS_INIT; + /** + * @brief A removed key callback, which if provided will be called if a key is no longer used in the hash map. + * + * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData. + * @param[in] key The key that is no longer used in the hash map. if `storeKeysWeakly` was configured as true, + * the key can be freed. + */ + void (*removedKeyCallback)(void* data, char* key) CELIX_OPTS_INIT; + /** * @brief If set to true, the string hash map will not make of copy of the keys and assumes * that the keys are in scope/memory for the complete lifecycle of the string hash map. * - * Note that this changes the default behaviour of the celix_stringHashMap_put* functions. + * When keys are stored weakly it is the caller responsibility to check the return value of + * celix_stringHashMap_put* function calls. + * If a celix_stringHashMap_put* function call returns true, the key is used in the hash map and the key + * should never be freed or freed in a configured removedKeyCallback. + * If a celix_stringHashMap_put* function call returns false, a value is replaced and the already existing + * key is reused. If the needed the caller should free the provided key. + * + * @note This changes the default behaviour of the celix_stringHashMap_put* functions. * * Default is false. */ @@ -137,6 +163,7 @@ typedef struct celix_string_hash_map_create_options { .simpleRemovedCallback = NULL, \ .removedCallbackData = NULL, \ .removedCallback = NULL, \ + .removedKeyCallback = NULL, \ .storeKeysWeakly = false, \ .initialCapacity = 0, \ .loadFactor = 0 \ diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c index 42e37ec62..995b0e6bb 100644 --- a/libs/utils/src/celix_hash_map.c +++ b/libs/utils/src/celix_hash_map.c @@ -73,8 +73,9 @@ typedef struct celix_hash_map { bool (*equalsKeyFunction)(const celix_hash_map_key_t* key1, const celix_hash_map_key_t* key2); void (*simpleRemovedCallback)(void* value); void* removedCallbackData; - void (*removedStringKeyCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue); - void (*removedLongKeyCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue); + void (*removedStringEntryCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue); + void (*removedStringKeyCallback)(void* data, char* key); + void (*removedLongEntryCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue); bool storeKeysWeakly; } celix_hash_map_t; @@ -227,18 +228,24 @@ static void celix_hashMap_resize(celix_hash_map_t* map, size_t newCapacity) { static void celix_hashMap_callRemovedCallback(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) { if (map->simpleRemovedCallback) { map->simpleRemovedCallback(removedEntry->value.ptrValue); - } else if (map->removedLongKeyCallback) { - map->removedLongKeyCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value); - } else if (map->removedStringKeyCallback) { - map->removedStringKeyCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value); + } else if (map->removedLongEntryCallback) { + map->removedLongEntryCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value); + } else if (map->removedStringEntryCallback) { + map->removedStringEntryCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value); + } +} + +static void celix_hashMap_destroyRemovedKey(celix_hash_map_t* map, char* removedKey) { + if (map->removedStringKeyCallback) { + map->removedStringKeyCallback(map->removedCallbackData, removedKey); + } + if (!map->storeKeysWeakly) { + free(removedKey); } } static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) { celix_hashMap_callRemovedCallback(map, removedEntry); - if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) { - free((char*)removedEntry->key.strKey); - } free(removedEntry); } @@ -344,7 +351,14 @@ static bool celix_hashMap_remove(celix_hash_map_t* map, const char* strKey, long visit = visit->next; } if (removedEntry != NULL) { + char* removedKey = NULL; + if (map->keyType == CELIX_HASH_MAP_STRING_KEY) { + removedKey = (char*)removedEntry->key.strKey; + } celix_hashMap_destroyRemovedEntry(map, removedEntry); + if (removedKey) { + celix_hashMap_destroyRemovedKey(map, removedKey); + } return true; } return false; @@ -367,7 +381,8 @@ static void celix_hashMap_init( map->equalsKeyFunction = equalsKeyFn; map->simpleRemovedCallback = NULL; map->removedCallbackData = NULL; - map->removedLongKeyCallback = NULL; + map->removedLongEntryCallback = NULL; + map->removedStringEntryCallback = NULL; map->removedStringKeyCallback = NULL; map->storeKeysWeakly = false; } @@ -378,7 +393,15 @@ static void celix_hashMap_clear(celix_hash_map_t* map) { while (entry != NULL) { celix_hash_map_entry_t* removedEntry = entry; entry = entry->next; + \ + char* removedKey = NULL; + if (map->keyType == CELIX_HASH_MAP_STRING_KEY) { + removedKey = (char*)removedEntry->key.strKey; + } celix_hashMap_destroyRemovedEntry(map, removedEntry); + if (removedKey) { + celix_hashMap_destroyRemovedKey(map, removedKey); + } } map->buckets[i] = NULL; } @@ -427,7 +450,8 @@ celix_string_hash_map_t* celix_stringHashMap_createWithOptions(const celix_strin celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_STRING_KEY, cap, fac, celix_stringHashMap_hash, celix_stringHashMap_equals); map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback; map->genericMap.removedCallbackData = opts->removedCallbackData; - map->genericMap.removedStringKeyCallback = opts->removedCallback; + map->genericMap.removedStringEntryCallback = opts->removedCallback; + map->genericMap.removedStringKeyCallback = opts->removedKeyCallback; map->genericMap.storeKeysWeakly = opts->storeKeysWeakly; return map; } @@ -444,7 +468,7 @@ celix_long_hash_map_t* celix_longHashMap_createWithOptions(const celix_long_hash celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_LONG_KEY, cap, fac, celix_longHashMap_hash, celix_longHashMap_equals); map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback; map->genericMap.removedCallbackData = opts->removedCallbackData; - map->genericMap.removedLongKeyCallback = opts->removedCallback; + map->genericMap.removedLongEntryCallback = opts->removedCallback; map->genericMap.storeKeysWeakly = false; return map; } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 422a2bb70..dbbc20481 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -160,14 +160,12 @@ static char* celix_properties_createString(celix_properties_t* properties, const static celix_status_t celix_properties_fillEntry( celix_properties_t *properties, celix_properties_entry_t* entry, - const char *key, const char *strValue, const long* longValue, const double* doubleValue, const bool* boolValue, celix_version_t* versionValue) { char convertedValueBuffer[32]; - entry->key = celix_properties_createString(properties, key); if (strValue != NULL) { entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; entry->value = celix_properties_createString(properties, strValue); @@ -209,7 +207,7 @@ static celix_status_t celix_properties_fillEntry( entry->value = celix_version_toString(versionValue); } } - if (entry->key == NULL || entry->value == NULL) { + if (entry->value == NULL) { return CELIX_ENOMEM; } return CELIX_SUCCESS; @@ -233,13 +231,11 @@ static celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t* * provided key and value strings. */ static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_properties_t *properties, - char *key, char *strValue) { celix_properties_entry_t* entry = celix_properties_allocEntry(properties); if (entry == NULL) { return NULL; } - entry->key = key; entry->value = strValue; entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; entry->typed.strValue = strValue; @@ -264,7 +260,7 @@ static celix_properties_entry_t* celix_properties_createEntry( return NULL; } - celix_status_t status = celix_properties_fillEntry(properties, entry, key, strValue, longValue, doubleValue, + celix_status_t status = celix_properties_fillEntry(properties, entry, strValue, longValue, doubleValue, boolValue, versionValue); if (status != CELIX_SUCCESS) { free(entry); @@ -290,8 +286,13 @@ static void celix_properties_createAndSetEntry( } celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue, boolValue, versionValue); + const char* mapKey = key; + if (!celix_stringHashMap_hasKey(properties->map, key)) { + //new entry, needs new allocated key; + mapKey = celix_properties_createString(properties, key); + } if (entry != NULL) { - celix_stringHashMap_put(properties->map, entry->key, entry); + celix_stringHashMap_put(properties->map, mapKey, entry); } } @@ -308,10 +309,14 @@ static void celix_properties_freeString(celix_properties_t* properties, char* st } } +static void celix_properties_removeKeyCallback(void* handle, char* key) { + celix_properties_t* properties = handle; + celix_properties_freeString(properties, key); +} + static void celix_properties_removeEntryCallback(void* handle, const char* key __attribute__((unused)), celix_hash_map_value_t val) { celix_properties_t* properties = handle; celix_properties_entry_t* entry = val.ptrValue; - celix_properties_freeString(properties, (char*)entry->key); celix_properties_freeString(properties, (char*)entry->value); if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { celix_version_destroy(entry->typed.versionValue); @@ -332,6 +337,7 @@ celix_properties_t* celix_properties_create(void) { opts.initialCapacity = (unsigned int)ceil(CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE / 0.75); opts.removedCallbackData = props; opts.removedCallback = celix_properties_removeEntryCallback; + opts.removedKeyCallback = celix_properties_removeKeyCallback; props->map = celix_stringHashMap_createWithOptions(&opts); props->currentStringBufferIndex = 0; props->currentEntriesBufferIndex = 0; @@ -585,16 +591,16 @@ celix_properties_t* celix_properties_copy(const celix_properties_t *properties) CELIX_PROPERTIES_ITERATE(properties, iter) { if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_properties_set(copy, iter.entry.key, iter.entry.value); + celix_properties_set(copy, iter.key, iter.entry.value); } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { - celix_properties_setLong(copy, iter.entry.key, iter.entry.typed.longValue); + celix_properties_setLong(copy, iter.key, iter.entry.typed.longValue); } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { - celix_properties_setDouble(copy, iter.entry.key, iter.entry.typed.doubleValue); + celix_properties_setDouble(copy, iter.key, iter.entry.typed.doubleValue); } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { - celix_properties_setBool(copy, iter.entry.key, iter.entry.typed.boolValue); + celix_properties_setBool(copy, iter.key, iter.entry.typed.boolValue); } else /*version*/ { assert(iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION); - celix_properties_setVersion(copy, iter.entry.key, iter.entry.typed.versionValue); + celix_properties_setVersion(copy, iter.key, iter.entry.typed.versionValue); } } return copy; @@ -629,9 +635,12 @@ void celix_properties_set(celix_properties_t *properties, const char *key, const void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) { if (properties != NULL && key != NULL && value != NULL) { - celix_properties_entry_t* entry = celix_properties_createEntryWithNoCopy(properties, key, value); + celix_properties_entry_t* entry = celix_properties_createEntryWithNoCopy(properties, value); if (entry != NULL) { - celix_stringHashMap_put(properties->map, entry->key, entry); + bool replaced = celix_stringHashMap_put(properties->map, key, entry); + if (replaced) { + free(key); + } } } } @@ -755,7 +764,8 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope internalIter.props = properties; celix_properties_iterator_t iter; - iter.index = -1; + iter.index = 0; + iter.key = NULL; memset(&iter.entry, 0, sizeof(iter.entry)); CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(iter._data)); @@ -767,7 +777,6 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { celix_properties_iterator_internal_t internalIter; memcpy(&internalIter, iter->_data, sizeof(internalIter)); - //celix_stringHashMapIterator_next(&internalIter.mapIter); return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter); } @@ -780,8 +789,10 @@ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) iter->index = (int)internalIter.mapIter.index; celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue; if (entry != NULL) { + iter->key = internalIter.mapIter.key; memcpy(&iter->entry, entry, sizeof(iter->entry)); } else { + iter->key = NULL; memset(&iter->entry, 0, sizeof(iter->entry)); } celix_stringHashMapIterator_next(&internalIter.mapIter); @@ -799,8 +810,10 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro celix_properties_iterator_t iter; iter.index = 0; if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { + iter.key = NULL; memset(&iter.entry, 0, sizeof(iter.entry)); } else { + iter.key = internalIter.mapIter.key; memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry)); } @@ -816,7 +829,7 @@ celix_properties_iterator_t celix_properties_end(const celix_properties_t* prope celix_properties_iterator_t iter; memset(&iter, 0, sizeof(iter)); - iter.index = (int)internalIter.mapIter.index; //TODO make inter.index size_t + iter.index = internalIter.mapIter.index; memcpy(iter._data, &internalIter, sizeof(internalIter)); return iter; } @@ -828,8 +841,10 @@ void celix_propertiesIterator_next(celix_properties_iterator_t *iter) { memcpy(iter->_data, &internalIter, sizeof(internalIter)); iter->index = internalIter.mapIter.index; if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) { + iter->key = NULL; memset(&iter->entry, 0, sizeof(iter->entry)); } else { + iter->key = internalIter.mapIter.key; memcpy(&iter->entry, internalIter.mapIter.value.ptrValue, sizeof(iter->entry)); } } diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 72a379426..b44b123f2 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -107,7 +107,7 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha } if (qualifier == NULL) { - qualifier = ""; + qualifier = CELIX_VERSION_EMPTY_QUALIFIER; } size_t qualifierLen = strlen(qualifier); for (int i = 0; i < qualifierLen; i++) { From 71daacf3d0dda6f16817cae707a302b3eab9ce8a Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 14:59:04 +0100 Subject: [PATCH 09/21] Remove old properties.c file --- libs/utils/src/properties_v1.c | 513 --------------------------------- 1 file changed, 513 deletions(-) delete mode 100644 libs/utils/src/properties_v1.c diff --git a/libs/utils/src/properties_v1.c b/libs/utils/src/properties_v1.c deleted file mode 100644 index 3d962b02e..000000000 --- a/libs/utils/src/properties_v1.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include - -#include "properties.h" -#include "celix_build_assert.h" -#include "celix_properties.h" -#include "utils.h" -#include "hash_map_private.h" -#include -#include "hash_map.h" - -#define MALLOC_BLOCK_SIZE 5 - -static void parseLine(const char* line, celix_properties_t *props); - -properties_pt properties_create(void) { - return celix_properties_create(); -} - -void properties_destroy(properties_pt properties) { - celix_properties_destroy(properties); -} - -properties_pt properties_load(const char* filename) { - return celix_properties_load(filename); -} - -properties_pt properties_loadWithStream(FILE *file) { - return celix_properties_loadWithStream(file); -} - -properties_pt properties_loadFromString(const char *input){ - return celix_properties_loadFromString(input); -} - - -/** - * Header is ignored for now, cannot handle comments yet - */ -void properties_store(properties_pt properties, const char* filename, const char* header) { - return celix_properties_store(properties, filename, header); -} - -celix_status_t properties_copy(properties_pt properties, properties_pt *out) { - celix_properties_t *copy = celix_properties_copy(properties); - *out = copy; - return copy == NULL ? CELIX_BUNDLE_EXCEPTION : CELIX_SUCCESS; -} - -const char* properties_get(properties_pt properties, const char* key) { - return celix_properties_get(properties, key, NULL); -} - -const char* properties_getWithDefault(properties_pt properties, const char* key, const char* defaultValue) { - return celix_properties_get(properties, key, defaultValue); -} - -void properties_set(properties_pt properties, const char* key, const char* value) { - celix_properties_set(properties, key, value); -} - -void properties_unset(properties_pt properties, const char* key) { - celix_properties_unset(properties, key); -} - -static void updateBuffers(char **key, char ** value, char **output, int outputPos, int *key_len, int *value_len) { - if (*output == *key) { - if (outputPos == (*key_len) - 1) { - (*key_len) += MALLOC_BLOCK_SIZE; - *key = realloc(*key, *key_len); - *output = *key; - } - } - else { - if (outputPos == (*value_len) - 1) { - (*value_len) += MALLOC_BLOCK_SIZE; - *value = realloc(*value, *value_len); - *output = *value; - } - } -} - -static void parseLine(const char* line, celix_properties_t *props) { - int linePos = 0; - bool precedingCharIsBackslash = false; - bool isComment = false; - int outputPos = 0; - char *output = NULL; - int key_len = MALLOC_BLOCK_SIZE; - int value_len = MALLOC_BLOCK_SIZE; - linePos = 0; - precedingCharIsBackslash = false; - isComment = false; - output = NULL; - outputPos = 0; - - //Ignore empty lines - if (line[0] == '\n' && line[1] == '\0') { - return; - } - - char *key = calloc(1, key_len); - char *value = calloc(1, value_len); - key[0] = '\0'; - value[0] = '\0'; - - while (line[linePos] != '\0') { - if (line[linePos] == ' ' || line[linePos] == '\t') { - if (output == NULL) { - //ignore - linePos += 1; - continue; - } - } - else { - if (output == NULL) { - output = key; - } - } - if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') { - if (precedingCharIsBackslash) { - //escaped special character - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - precedingCharIsBackslash = false; - } - else { - if (line[linePos] == '#' || line[linePos] == '!') { - if (outputPos == 0) { - isComment = true; - break; - } - else { - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - } - else { // = or : - if (output == value) { //already have a seperator - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - else { - output[outputPos++] = '\0'; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - output = value; - outputPos = 0; - } - } - } - } - else if (line[linePos] == '\\') { - if (precedingCharIsBackslash) { //double backslash -> backslash - output[outputPos++] = '\\'; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - precedingCharIsBackslash = true; - } - else { //normal character - precedingCharIsBackslash = false; - output[outputPos++] = line[linePos]; - updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); - } - linePos += 1; - } - if (output != NULL) { - output[outputPos] = '\0'; - } - - if (!isComment) { - //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); - celix_properties_set(props, utils_stringTrim(key), utils_stringTrim(value)); - } - if(key) { - free(key); - } - if(value) { - free(value); - } - -} - - - -/********************************************************************************************************************** - ********************************************************************************************************************** - * Updated API - ********************************************************************************************************************** - **********************************************************************************************************************/ - - - -celix_properties_t* celix_properties_create(void) { - return hashMap_create(utils_stringHash, utils_stringHash, utils_stringEquals, utils_stringEquals); -} - -void celix_properties_destroy(celix_properties_t *properties) { - if (properties != NULL) { - hash_map_iterator_pt iter = hashMapIterator_create(properties); - while (hashMapIterator_hasNext(iter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(iter); - hashMapEntry_clear(entry, true, true); - } - hashMapIterator_destroy(iter); - hashMap_destroy(properties, false, false); - } -} - -celix_properties_t* celix_properties_load(const char *filename) { - FILE *file = fopen(filename, "r"); - if (file == NULL) { - return NULL; - } - celix_properties_t *props = celix_properties_loadWithStream(file); - fclose(file); - return props; -} - -celix_properties_t* celix_properties_loadWithStream(FILE *file) { - celix_properties_t *props = NULL; - - if (file != NULL ) { - char *saveptr; - char *filebuffer = NULL; - char *line = NULL; - size_t file_size = 0; - - props = celix_properties_create(); - fseek(file, 0, SEEK_END); - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - if (file_size > 0) { - filebuffer = calloc(file_size + 1, sizeof(char)); - if (filebuffer) { - size_t rs = fread(filebuffer, sizeof(char), file_size, file); - if (rs != file_size) { - fprintf(stderr,"fread read only %lu bytes out of %lu\n", (long unsigned int) rs, (long unsigned int) file_size); - } - filebuffer[file_size]='\0'; - line = strtok_r(filebuffer, "\n", &saveptr); - while (line != NULL) { - parseLine(line, props); - line = strtok_r(NULL, "\n", &saveptr); - } - free(filebuffer); - } - } - } - - return props; -} - -celix_properties_t* celix_properties_loadFromString(const char *input) { - celix_properties_t *props = celix_properties_create(); - - char *in = strdup(input); - char *line = NULL; - char *saveLinePointer = NULL; - - bool firstTime = true; - do { - if (firstTime){ - line = strtok_r(in, "\n", &saveLinePointer); - firstTime = false; - }else { - line = strtok_r(NULL, "\n", &saveLinePointer); - } - - if (line == NULL){ - break; - } - - parseLine(line, props); - } while(line != NULL); - - free(in); - - return props; -} - -void celix_properties_store(celix_properties_t *properties, const char *filename, const char *header) { - FILE *file = fopen (filename, "w+" ); - char *str; - - if (file != NULL) { - if (hashMap_size(properties) > 0) { - hash_map_iterator_pt iterator = hashMapIterator_create(properties); - while (hashMapIterator_hasNext(iterator)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(iterator); - str = hashMapEntry_getKey(entry); - for (int i = 0; i < strlen(str); i += 1) { - if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') { - fputc('\\', file); - } - fputc(str[i], file); - } - - fputc('=', file); - - str = hashMapEntry_getValue(entry); - for (int i = 0; i < strlen(str); i += 1) { - if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') { - fputc('\\', file); - } - fputc(str[i], file); - } - - fputc('\n', file); - - } - hashMapIterator_destroy(iterator); - } - fclose(file); - } else { - perror("File is null"); - } -} - -celix_properties_t* celix_properties_copy(const celix_properties_t *properties) { - celix_properties_t *copy = celix_properties_create(); - if (properties != NULL) { - hash_map_iterator_t iter = hashMapIterator_construct((hash_map_t*)properties); - while (hashMapIterator_hasNext(&iter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(&iter); - char *key = hashMapEntry_getKey(entry); - char *value = hashMapEntry_getValue(entry); - celix_properties_set(copy, key, value); - } - } - return copy; -} - -const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue) { - const char* value = NULL; - if (properties != NULL) { - value = hashMap_get((hash_map_t*)properties, (void*)key); - } - return value == NULL ? defaultValue : value; -} - -void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { - if (properties != NULL) { - hash_map_entry_pt entry = hashMap_getEntry(properties, key); - char *oldVal = NULL; - char *newVal = value == NULL ? NULL : strndup(value, 1024 * 1024); - if (entry != NULL) { - char *oldKey = hashMapEntry_getKey(entry); - oldVal = hashMapEntry_getValue(entry); - hashMap_put(properties, oldKey, newVal); - } else { - hashMap_put(properties, strndup(key, 1024 * 1024), newVal); - } - free(oldVal); - } -} - -void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) { - if (properties != NULL) { - hash_map_entry_pt entry = hashMap_getEntry(properties, key); - char *oldVal = NULL; - if (entry != NULL) { - char *oldKey = hashMapEntry_getKey(entry); - oldVal = hashMapEntry_getValue(entry); - hashMap_put(properties, oldKey, value); - } else { - hashMap_put(properties, key, value); - } - free(oldVal); - } -} - -void celix_properties_unset(celix_properties_t *properties, const char *key) { - char* oldValue = hashMap_removeFreeKey(properties, key); - free(oldValue); -} - -long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue) { - long result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { - char *enptr = NULL; - errno = 0; - long r = strtol(val, &enptr, 10); - if (enptr != val && errno == 0) { - result = r; - } - } - return result; -} - -void celix_properties_setLong(celix_properties_t *props, const char *key, long value) { - char buf[32]; //should be enough to store long long int - int writen = snprintf(buf, 32, "%li", value); - if (writen <= 31) { - celix_properties_set(props, key, buf); - } else { - fprintf(stderr,"buf to small for value '%li'\n", value); - } -} - -double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue) { - double result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { - char *enptr = NULL; - errno = 0; - double r = strtod(val, &enptr); - if (enptr != val && errno == 0) { - result = r; - } - } - return result; -} - -void celix_properties_setDouble(celix_properties_t *props, const char *key, double val) { - char buf[32]; //should be enough to store long long int - int writen = snprintf(buf, 32, "%f", val); - if (writen <= 31) { - celix_properties_set(props, key, buf); - } else { - fprintf(stderr,"buf to small for value '%f'\n", val); - } -} - -bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue) { - bool result = defaultValue; - const char *val = celix_properties_get(props, key, NULL); - if (val != NULL) { - char buf[32]; - snprintf(buf, 32, "%s", val); - char *trimmed = utils_stringTrim(buf); - if (strncasecmp("true", trimmed, strlen("true")) == 0) { - result = true; - } else if (strncasecmp("false", trimmed, strlen("false")) == 0) { - result = false; - } - } - return result; -} - -void celix_properties_setBool(celix_properties_t *props, const char *key, bool val) { - celix_properties_set(props, key, val ? "true" : "false"); -} - -int celix_properties_size(const celix_properties_t *properties) { - return hashMap_size((hash_map_t*)properties); -} - -celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties) { - celix_properties_iterator_t iter; - CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_t) == sizeof(hash_map_iterator_t)); - CELIX_BUILD_ASSERT(__alignof__(celix_properties_iterator_t) == __alignof__(hash_map_iterator_t)); - hash_map_iterator_t mapIter = hashMapIterator_construct((hash_map_t*)properties); - iter._data1 = mapIter.map; - iter._data2 = mapIter.next; - iter._data3 = mapIter.current; - iter._data4 = mapIter.expectedModCount; - iter._data5 = mapIter.index; - return iter; -} - -bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { - hash_map_iterator_t mapIter; - mapIter.map = iter->_data1; - mapIter.next = iter->_data2; - mapIter.current = iter->_data3; - mapIter.expectedModCount = iter->_data4; - mapIter.index = iter->_data5; - bool hasNext = hashMapIterator_hasNext(&mapIter); - return hasNext; -} -const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) { - hash_map_iterator_t mapIter; - mapIter.map = iter->_data1; - mapIter.next = iter->_data2; - mapIter.current = iter->_data3; - mapIter.expectedModCount = iter->_data4; - mapIter.index = iter->_data5; - const char* result = (const char*)hashMapIterator_nextKey(&mapIter); - iter->_data1 = mapIter.map; - iter->_data2 = mapIter.next; - iter->_data3 = mapIter.current; - iter->_data4 = mapIter.expectedModCount; - iter->_data5 = mapIter.index; - return result; -} - -celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter) { - return (celix_properties_t*)iter->_data1; -} From efb74c19a30c51ad14f4c1a0a50ce5ce31208030 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 16:32:54 +0100 Subject: [PATCH 10/21] Fix Properties implementation for C++11 and C++14. --- libs/utils/gtest/src/VersionTestSuite.cc | 6 -- libs/utils/include/celix/Properties.h | 102 ++++++++++++++++++----- libs/utils/include/celix/Version.h | 67 ++++++++++++--- libs/utils/include/celix_version.h | 11 +++ 4 files changed, 149 insertions(+), 37 deletions(-) diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 54539c7b9..5707e4272 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -150,8 +150,6 @@ TEST_F(VersionTestSuite, CreateEmptyVersionTest) { } TEST_F(VersionTestSuite, CompareTest) { - //TODO update to updated API - celix_version_t* version = nullptr; celix_version_t* compare = nullptr; celix_status_t status = CELIX_SUCCESS; @@ -227,8 +225,6 @@ TEST_F(VersionTestSuite, CompareToMajorMinorTest) { } TEST_F(VersionTestSuite, ToStringTest) { - //TODO update to updated API - celix_version_t* version = nullptr; celix_status_t status = CELIX_SUCCESS; char * str; @@ -262,8 +258,6 @@ TEST_F(VersionTestSuite, ToStringTest) { } TEST_F(VersionTestSuite, SemanticCompatibilityTest) { - //TODO update to updated API - celix_version_t* provider = nullptr; celix_version_t* compatible_user = nullptr; celix_version_t* incompatible_user_by_major = nullptr; diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 5be5a7621..e451ddc1d 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -23,6 +23,12 @@ #include #include #include +#if __cplusplus >= 201703L //C++17 or higher +#include +#else +#include +#endif + #include "celix_properties.h" #include "celix_utils.h" @@ -31,6 +37,48 @@ namespace celix { + +#if __cplusplus < 201703L //lower than C++17 + template + struct IsIntegralDoubleBoolOrVersion : std::false_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template <> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template<> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template<> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template<> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template<> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; + + template<> + struct IsIntegralDoubleBoolOrVersion : std::true_type {}; +#endif + /** * @brief A iterator for celix::Properties. */ @@ -431,7 +479,7 @@ namespace celix { [[nodiscard]] bool getAsBool(const std::string &key, bool defaultValue) const { return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue); } - + /** * @brief Get the value of the property with key as a Celix version. * @@ -454,7 +502,7 @@ namespace celix { celix_version_destroy(cVersion); return version; } - return defaultValue + return defaultValue; } /** @@ -478,16 +526,6 @@ namespace celix { celix_properties_set(cProps.get(), key.data(), value.c_str()); } - /** - * @brief Set the value of a property to a boolean. - * - * @param[in] key The key of the property to set. - * @param[in] value The boolean value to set the property to. - */ - void set(const std::string& key, bool value) { - celix_properties_setBool(cProps.get(), key.data(), value); - } - /** * @brief Set the value of a property. * @@ -509,9 +547,21 @@ namespace celix { * @param[in] value The value to set for the property. */ template - void set(const std::string& key, T&& value) { + typename std::enable_if::value, void>::type + set(const std::string& key, T&& value) { + std::cout << "Setting with T&&" << std::endl; using namespace std; - celix_properties_set(cProps.get(), key.data()(), to_string(value).c_str()); + celix_properties_set(cProps.get(), key.data(), to_string(value).c_str()); + } + + /** + * @brief Sets a celix::Version property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, celix::Version&& value) { + celix_properties_setVersion(cProps.get(), key.data(), value.getCVersion()); } /** @@ -535,23 +585,33 @@ namespace celix { } /** - * @brief Sets a double property value for a given key. + * @brief Sets a long property value for a given key. * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. */ - void set(const std::string& key, double value) { - celix_properties_setDouble(cProps.get(), key.data(), value); + void set(const std::string& key, int value) { + celix_properties_setLong(cProps.get(), key.data(), value); } /** - * @brief Sets a celix::Version property value for a given key. + * @brief Sets a long property value for a given key. * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. */ - void set(const std::string& key, const celix::Version& value) { - celix_properties_setVersion(cProps.get(), key.data(), value.getCVersion()); + void set(const std::string& key, unsigned int value) { + celix_properties_setLong(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a double property value for a given key. + * + * @param[in] key The key of the property to set. + * @param[in] value The value to set for the property. + */ + void set(const std::string& key, double value) { + celix_properties_setDouble(cProps.get(), key.data(), value); } /** @@ -643,7 +703,7 @@ namespace celix { private: Properties(celix_properties_t* props, bool takeOwnership) : - cProps{props, [ownership = takeOwnership](celix_properties_t* p){ if (ownership) { celix_properties_destroy(p); }}} {} + cProps{props, [takeOwnership](celix_properties_t* p){ if (takeOwnership) { celix_properties_destroy(p); }}} {} static celix::Properties::ValueType getAndConvertType( const std::shared_ptr& cProperties, diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h index c8d2943d4..556f4e92a 100644 --- a/libs/utils/include/celix/Version.h +++ b/libs/utils/include/celix/Version.h @@ -26,14 +26,34 @@ namespace celix { - //TODO doxygen + /** + * @class Version + * @brief Class for storing and manipulating version information. + * + * The Version class represents a version number that follows the Semantic Versioning specification (SemVer). + * It consists of three non-negative integers for the major, minor, and micro version, and an optional string for + * the qualifier. + * The Version class provides comparison operators and functions for getting the individual version components. + * + * @note This class is a thin wrapper around the C API defined in celix_version.h. + */ class Version { public: + + ///@brief Constructs a new empty version with all components set to zero. Version() : cVersion{createVersion(celix_version_createEmptyVersion())}, qualifier{celix_version_getQualifier(cVersion.get())} {} #if __cplusplus >= 201703L //C++17 or higher + + /** + * @brief Constructs a new version with the given components and qualifier. + * @param major The major component of the version. + * @param minor The minor component of the version. + * @param micro The micro component of the version. + * @param qualifier The qualifier string of the version. + */ Version(int major, int minor, int micro, std::string_view qualifier = {}) : cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))}, qualifier{celix_version_getQualifier(cVersion.get())} {} @@ -43,26 +63,31 @@ namespace celix { qualifier{celix_version_getQualifier(cVersion.get())} {} #endif - + ///@brief Move-constructs a new version from an existing one. Version(Version&&) = default; + + ///@brief Copy constructor for a Celix Version object. Version(const Version& rhs) = default; + ///@brief Move assignment operator for the Celix Version class. Version& operator=(Version&&) = default; + + ///@brief Copy assignment operator for the Celix Version class. Version& operator=(const Version& rhs) = default; + ///@brief Test whether two Version objects are equal. bool operator==(const Version& rhs) const { return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) == 0; } + ///@brief Overload the < operator to compare two Version objects. bool operator<(const Version& rhs) const { return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) < 0; } - //TODO rest of the operators, is that needed? - /** - * @brief Warps a C Celix Version to a C++ Celix Version, but takes no ownership. - * Dealloction is still the responsibility of the caller. + * @brief Warp a C Celix Version to a C++ Celix Version, but takes no ownership. + * De-allocation is still the responsibility of the caller. */ static Version wrap(celix_version_t* v) { return Version{v}; @@ -78,22 +103,38 @@ namespace celix { return cVersion.get(); } - //TODO doc + /** + * @brief Get the major component of the version. + * The major component designates the primary release. + * @return The major component of the version. + */ [[nodiscard]] int getMajor() const { return celix_version_getMajor(cVersion.get()); } - //TODO doc + /** + * @brief Get the minor component of the version. + * The minor component designates a new or improved feature. + * @return The minor component of the version. + */ [[nodiscard]] int getMinor() const { return celix_version_getMinor(cVersion.get()); } - //TODO doc + /** + * @brief Get the micro component of the version. + * The micro component designates a bug fix. + * @return The micro component of the version. + */ [[nodiscard]] int getMicro() const { return celix_version_getMicro(cVersion.get()); } - //TODO doc + /** + * @brief Get the qualifier component of the version. + * The qualifier component designates additional information about the version. + * @return The qualifier component of the version. + */ [[nodiscard]] const std::string& getQualifier() const { return qualifier; } @@ -117,6 +158,12 @@ namespace celix { } namespace std { + + /** + * @brief The hash celix::Version struct provides a std::hash compatible hashing function for the celix::Version + * class. This allows celix::Version objects to be used as keys in std::unordered_map and std::unordered_set. + * @see std::hash + */ template<> struct hash { size_t operator()(const celix::Version& v) const { diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index f4cea3907..c539dd561 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -26,6 +26,17 @@ extern "C" { #include + +/** + * @file celix_version.h + * @brief Header file for the Celix Version API. + * + * The Celix Version API provides a means for storing and manipulating version information, which consists of + * three non-negative integers for the major, minor, and micro version, and an optional string for the qualifier. + * This implementation is based on the Semantic Versioning specification (SemVer). + * Functions are provided for creating and destroying version objects, comparing versions, and extracting the individual version components. + */ + /** * @brief The definition of the celix_version_t* abstract data type. */ From 4f86a3d3ac585446710fa1662c2b8665381a0063 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 22:12:33 +0100 Subject: [PATCH 11/21] Improve codecov and replace usage of CELIX_PROPERTIES_FOR_EACH with _ITERATE --- .../private/src/pubsub_subscriber.c | 6 +- .../gtest/PubSubInterceptorTestSuite.cc | 5 +- .../src/pubsub_udpmc_admin.c | 5 +- .../src/pubsub_websocket_admin.c | 10 +- .../src/pubsub_discovery_impl.c | 6 +- .../src/pubsub_wire_protocol_common.c | 9 +- .../pubsub/pubsub_spi/src/pubsub_endpoint.c | 5 +- .../src/endpoint_descriptor_writer.c | 11 +- .../src/remote_service_admin_dfi.c | 15 +-- bundles/shell/shell/src/query_command.c | 12 +- .../include/celix/dm/DependencyManager_Impl.h | 7 +- libs/framework/src/celix_launcher.c | 30 ++--- libs/framework/src/service_reference.c | 5 +- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 7 +- libs/utils/gtest/src/CxxVersionTestSuite.cc | 2 + libs/utils/gtest/src/PropertiesTestSuite.cc | 112 +++++++++++++++--- libs/utils/include/celix/Version.h | 3 +- libs/utils/include/celix_properties.h | 10 -- libs/utils/src/properties.c | 36 +++--- 19 files changed, 165 insertions(+), 131 deletions(-) diff --git a/bundles/pubsub/examples/pubsub/subscriber/private/src/pubsub_subscriber.c b/bundles/pubsub/examples/pubsub/subscriber/private/src/pubsub_subscriber.c index 272ff746e..c05df1ee0 100644 --- a/bundles/pubsub/examples/pubsub/subscriber/private/src/pubsub_subscriber.c +++ b/bundles/pubsub/examples/pubsub/subscriber/private/src/pubsub_subscriber.c @@ -62,10 +62,8 @@ int pubsub_subscriber_recv(void* handle, const char* msgType, unsigned int msgTy if (metadata == NULL || celix_properties_size(metadata) == 0) { printf("No metadata\n"); } else { - const char *key; - CELIX_PROPERTIES_FOR_EACH(metadata, key) { - const char *val = celix_properties_get(metadata, key, "!Error!"); - printf("%s=%s\n", key, val); + CELIX_PROPERTIES_ITERATE(metadata, iter) { + printf("%s=%s\n", iter.key, iter.entry.value); } } diff --git a/bundles/pubsub/integration/gtest/PubSubInterceptorTestSuite.cc b/bundles/pubsub/integration/gtest/PubSubInterceptorTestSuite.cc index e8218ccf4..0c4c7c0b3 100644 --- a/bundles/pubsub/integration/gtest/PubSubInterceptorTestSuite.cc +++ b/bundles/pubsub/integration/gtest/PubSubInterceptorTestSuite.cc @@ -110,9 +110,8 @@ class PubSubInterceptorTestSuite : public ::testing::Test { const auto *msg = static_cast(rawMsg); EXPECT_GE(msg->seqNr, 0); EXPECT_STREQ(celix_properties_get(metadata, "test", nullptr), "preSend"); - const char *key; - CELIX_PROPERTIES_FOR_EACH(metadata, key) { - printf("got property %s=%s\n", key, celix_properties_get(metadata, key, nullptr)); + CELIX_PROPERTIES_ITERATE(metadata, iter) { + printf("got property %s=%s\n", iter.key, iter.entry.value); } fprintf(stdout, "Got message in postSend interceptor %s/%s for type %s and ser %s with seq nr %i\n", intProps->scope, intProps->topic, intProps->psaType, intProps->serializationType, msg->seqNr); diff --git a/bundles/pubsub/pubsub_admin_udp_mc/src/pubsub_udpmc_admin.c b/bundles/pubsub/pubsub_admin_udp_mc/src/pubsub_udpmc_admin.c index 9257ac876..91ec6b1bf 100644 --- a/bundles/pubsub/pubsub_admin_udp_mc/src/pubsub_udpmc_admin.c +++ b/bundles/pubsub/pubsub_admin_udp_mc/src/pubsub_udpmc_admin.c @@ -455,9 +455,8 @@ static celix_status_t pubsub_udpmcAdmin_connectEndpointToReceiver(pubsub_udpmc_a if (sockAddress == NULL || sockPort < 0) { L_WARN("[PSA UPDMC] Error got endpoint without udpmc socket address/port or endpoint type. Properties:"); - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(endpoint, key) { - L_WARN("[PSA UPDMC] |- %s=%s\n", key, celix_properties_get(endpoint, key, NULL)); + CELIX_PROPERTIES_ITERATE(endpoint, iter) { + L_WARN("[PSA UPDMC] |- %s=%s\n", iter.key, iter.entry.value); } status = CELIX_BUNDLE_EXCEPTION; } else { diff --git a/bundles/pubsub/pubsub_admin_websocket/src/pubsub_websocket_admin.c b/bundles/pubsub/pubsub_admin_websocket/src/pubsub_websocket_admin.c index 2a103eec2..765e416d9 100644 --- a/bundles/pubsub/pubsub_admin_websocket/src/pubsub_websocket_admin.c +++ b/bundles/pubsub/pubsub_admin_websocket/src/pubsub_websocket_admin.c @@ -379,9 +379,8 @@ static celix_status_t pubsub_websocketAdmin_connectEndpointToReceiver(pubsub_web if (publisher && (sockAddress == NULL || sockPort < 0)) { L_WARN("[PSA WEBSOCKET] Error got endpoint without websocket address/port or endpoint type. Properties:"); - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(endpoint, key) { - L_WARN("[PSA WEBSOCKET] |- %s=%s\n", key, celix_properties_get(endpoint, key, NULL)); + CELIX_PROPERTIES_ITERATE(endpoint, iter) { + L_WARN("[PSA WEBSOCKET] |- %s=%s\n", iter.key, iter.entry.value); } status = CELIX_BUNDLE_EXCEPTION; } else { @@ -436,9 +435,8 @@ static celix_status_t pubsub_websocketAdmin_disconnectEndpointFromReceiver(pubsu if (publisher && (sockAddress == NULL || sockPort < 0)) { L_WARN("[PSA WEBSOCKET] Error got endpoint without websocket address/port or endpoint type. Properties:"); - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(endpoint, key) { - L_WARN("[PSA WEBSOCKET] |- %s=%s\n", key, celix_properties_get(endpoint, key, NULL)); + CELIX_PROPERTIES_ITERATE(endpoint, iter) { + L_WARN("[PSA WEBSOCKET] |- %s=%s\n", iter.key, iter.entry.value); } status = CELIX_BUNDLE_EXCEPTION; } else if (eTopic != NULL && strncmp(eTopic, topic, 1024 * 1024) == 0) { diff --git a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c index 4815bd920..d7c2501d5 100644 --- a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c +++ b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c @@ -557,10 +557,8 @@ static char* pubsub_discovery_createJsonEndpoint(const celix_properties_t *props //note props is already check for validity (pubsubEndpoint_isValid) json_t *jsEndpoint = json_object(); - const char* propKey = NULL; - CELIX_PROPERTIES_FOR_EACH(props, propKey) { - const char* val = celix_properties_get(props, propKey, NULL); - json_object_set_new(jsEndpoint, propKey, json_string(val)); + CELIX_PROPERTIES_ITERATE(props, iter) { + json_object_set_new(jsEndpoint, iter.key, json_string(iter.entry.value)); } char* str = json_dumps(jsEndpoint, JSON_COMPACT); json_decref(jsEndpoint); diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c index 1d123d7b7..390b43e41 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c +++ b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c @@ -199,16 +199,13 @@ celix_status_t pubsubProtocol_encodeMetadata(pubsub_protocol_message_t *message, size_t len = 0; if (message->metadata.metadata != NULL && celix_properties_size(message->metadata.metadata) > 0) { - const char *key; char *keyNetString = NULL; int netStringMemoryLength = 0; - CELIX_PROPERTIES_FOR_EACH(message->metadata.metadata, key) { - const char *val = celix_properties_get(message->metadata.metadata, key, "!Error!"); - + CELIX_PROPERTIES_ITERATE(message->metadata.metadata, iter) { //refactoring these two copies to a function leads to a slow down of about 2x int strlenKeyNetString = 0; - status = pubsubProtocol_createNetstring(key, &keyNetString, &strlenKeyNetString, &netStringMemoryLength); + status = pubsubProtocol_createNetstring(iter.key, &keyNetString, &strlenKeyNetString, &netStringMemoryLength); if (status != CELIX_SUCCESS) { break; } @@ -227,7 +224,7 @@ celix_status_t pubsubProtocol_encodeMetadata(pubsub_protocol_message_t *message, memcpy(line + idx, keyNetString, strlenKeyNetString); idx += strlenKeyNetString; - status = pubsubProtocol_createNetstring(val, &keyNetString, &strlenKeyNetString, &netStringMemoryLength); + status = pubsubProtocol_createNetstring(iter.entry.value, &keyNetString, &strlenKeyNetString, &netStringMemoryLength); if (status != CELIX_SUCCESS) { break; } diff --git a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c index 9d1ffe9e5..25f133e15 100644 --- a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c +++ b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c @@ -46,9 +46,8 @@ static void pubsubEndpoint_setFields(celix_properties_t *ep, const char* fwUUID, //copy topic properties if (topic_props != NULL) { - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH((celix_properties_t *) topic_props, key) { - celix_properties_set(ep, key, celix_properties_get(topic_props, key, NULL)); + CELIX_PROPERTIES_ITERATE((celix_properties_t *) topic_props, iter) { + celix_properties_set(ep, iter.key, iter.entry.value); } } diff --git a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c index 66580b5a6..5c4b585cb 100644 --- a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c +++ b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c @@ -139,16 +139,15 @@ static celix_status_t endpointDescriptorWriter_writeEndpoint(endpoint_descriptor } else { xmlTextWriterStartElement(writer->writer, ENDPOINT_DESCRIPTION); - const char* propertyName; - CELIX_PROPERTIES_FOR_EACH(endpoint->properties, propertyName) { - const xmlChar* propertyValue = (const xmlChar*) celix_properties_get(endpoint->properties, propertyName, ""); + CELIX_PROPERTIES_ITERATE(endpoint->properties, iter) { + const xmlChar* propertyValue = (const xmlChar*) celix_properties_get(endpoint->properties, iter.key, ""); xmlTextWriterStartElement(writer->writer, PROPERTY); - xmlTextWriterWriteAttribute(writer->writer, NAME, (const xmlChar*)propertyName); + xmlTextWriterWriteAttribute(writer->writer, NAME, (const xmlChar*)iter.key); - if (strcmp(OSGI_FRAMEWORK_OBJECTCLASS, (char*) propertyName) == 0) { + if (strcmp(OSGI_FRAMEWORK_OBJECTCLASS, (char*) iter.key) == 0) { // objectClass *must* be represented as array of string values... endpointDescriptorWriter_writeArrayValue(writer->writer, propertyValue); - } else if (strcmp(OSGI_RSA_ENDPOINT_SERVICE_ID, (char*) propertyName) == 0) { + } else if (strcmp(OSGI_RSA_ENDPOINT_SERVICE_ID, (char*) iter.key) == 0) { // endpoint.service.id *must* be represented as long value... endpointDescriptorWriter_writeTypedValue(writer->writer, VALUE_TYPE_LONG, propertyValue); } else { diff --git a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c index 656b37734..2b5fc3356 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c @@ -747,9 +747,8 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic celix_properties_set(endpointProperties, RSA_DFI_ENDPOINT_URL, url); if (props != NULL) { - const char* key; - CELIX_PROPERTIES_FOR_EACH(props, key) { - celix_properties_set(endpointProperties, key, celix_properties_get(props, key, "")); + CELIX_PROPERTIES_ITERATE(props, iter) { + celix_properties_set(endpointProperties, iter.key, iter.entry.value); } } @@ -947,14 +946,10 @@ static celix_status_t remoteServiceAdmin_send(void *handle, endpoint_description } else { struct curl_slist *metadataHeader = NULL; if (metadata != NULL && celix_properties_size(metadata) > 0) { - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(metadata, key) { - const char *val = celix_properties_get(metadata, key, ""); - size_t length = strlen(key) + strlen(val) + 18; // "X-RSA-Metadata-key: val\0" - + CELIX_PROPERTIES_ITERATE(metadata, iter) { + size_t length = strlen(iter.key) + strlen(iter.entry.value) + 18; // "X-RSA-Metadata-key: val\0" char header[length]; - - snprintf(header, length, "X-RSA-Metadata-%s: %s", key, val); + snprintf(header, length, "X-RSA-Metadata-%s: %s", iter.key, iter.entry.value); metadataHeader = curl_slist_append(metadataHeader, header); } diff --git a/bundles/shell/shell/src/query_command.c b/bundles/shell/shell/src/query_command.c index 739d34500..93cf69cb8 100644 --- a/bundles/shell/shell/src/query_command.c +++ b/bundles/shell/shell/src/query_command.c @@ -95,18 +95,10 @@ static void queryCommand_callback(void *handle, const celix_bundle_t *bnd) { queryCommand_printBundleHeader(data->sout, bnd, &printBundleCalled); fprintf(data->sout, "|- Provided service '%s' [id = %li]\n", entry->serviceName, entry->serviceId); if (data->opts->verbose) { - const char *cmpUUID = celix_properties_get(entry->serviceProperties, "component.uuid", NULL); - if (cmpUUID != NULL) { - //TODO add context to opts - //TODO add celix_dependencyManager_createInfoForUUID() } - //TODO print component name - } fprintf(data->sout, " |- Is factory: %s\n", entry->factory ? "true" : "false"); fprintf(data->sout, " |- Properties:\n"); - const char *key; - CELIX_PROPERTIES_FOR_EACH(entry->serviceProperties, key) { - const char *val = celix_properties_get(entry->serviceProperties, key, "!ERROR!"); - fprintf(data->sout, " |- %20s = %s\n", key, val); + CELIX_PROPERTIES_ITERATE(entry->serviceProperties, iter) { + fprintf(data->sout, " |- %20s = %s\n", iter.key, iter.entry.value); } } } diff --git a/libs/framework/include/celix/dm/DependencyManager_Impl.h b/libs/framework/include/celix/dm/DependencyManager_Impl.h index f3e9f075d..05e262d96 100644 --- a/libs/framework/include/celix/dm/DependencyManager_Impl.h +++ b/libs/framework/include/celix/dm/DependencyManager_Impl.h @@ -174,10 +174,8 @@ static celix::dm::DependencyManagerInfo createDepManInfoFromC(celix_dependency_m auto* cIntInfo = static_cast(celix_arrayList_get(cCmpInfo->interfaces, k)); celix::dm::InterfaceInfo intInfo{}; intInfo.serviceName = std::string{cIntInfo->name}; - const char* key; - CELIX_PROPERTIES_FOR_EACH(cIntInfo->properties, key) { - const char* val =celix_properties_get(cIntInfo->properties, key, nullptr); - intInfo.properties[std::string{key}] = std::string{val}; + CELIX_PROPERTIES_ITERATE(cIntInfo->properties, iter) { + intInfo.properties[std::string{iter.key}] = std::string{iter.entry.value}; } cmpInfo.interfacesInfo.emplace_back(std::move(intInfo)); } @@ -206,7 +204,6 @@ inline celix::dm::DependencyManagerInfo DependencyManager::getInfo() const { return result; } - inline std::vector DependencyManager::getInfos() const { std::vector result{}; auto* cInfos = celix_dependencyManager_createInfos(cDependencyManager()); diff --git a/libs/framework/src/celix_launcher.c b/libs/framework/src/celix_launcher.c index 60b0c8ec1..394e0a15d 100644 --- a/libs/framework/src/celix_launcher.c +++ b/libs/framework/src/celix_launcher.c @@ -188,17 +188,15 @@ void celixLauncher_stop(celix_framework_t* framework) { } static void show_properties(celix_properties_t *embeddedProps, const char *configFile) { - const char *key = NULL; celix_properties_t *keys = celix_properties_create(); //only to store the keys printf("Embedded properties:\n"); if (embeddedProps == NULL || celix_properties_size(embeddedProps) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(embeddedProps, key) { - const char *val = celix_properties_get(embeddedProps, key, "!Error!"); - printf("|- %s=%s\n", key, val); - celix_properties_set(keys, key, NULL); + CELIX_PROPERTIES_ITERATE(embeddedProps, iter) { + printf("|- %s=%s\n", iter.key, iter.entry.value); + celix_properties_set(keys, iter.key, NULL); } } printf("\n"); @@ -211,10 +209,9 @@ static void show_properties(celix_properties_t *embeddedProps, const char *confi if (runtimeProps == NULL || celix_properties_size(runtimeProps) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(runtimeProps, key) { - const char *val = celix_properties_get(runtimeProps, key, "!Error!"); - printf("|- %s=%s\n", key, val); - celix_properties_set(keys, key, NULL); + CELIX_PROPERTIES_ITERATE(runtimeProps, iter) { + printf("|- %s=%s\n", iter.key, iter.entry.value); + celix_properties_set(keys, iter.key, NULL); } } printf("\n"); @@ -225,13 +222,13 @@ static void show_properties(celix_properties_t *embeddedProps, const char *confi if (celix_properties_size(keys) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(keys, key) { - const char *valEm = celix_properties_get(embeddedProps, key, NULL); - const char *valRt = celix_properties_get(runtimeProps, key, NULL); - const char *envVal = getenv(key); + CELIX_PROPERTIES_ITERATE(keys, iter) { + const char *valEm = celix_properties_get(embeddedProps, iter.key, NULL); + const char *valRt = celix_properties_get(runtimeProps, iter.key, NULL); + const char *envVal = getenv(iter.key); const char *val = envVal != NULL ? envVal : valRt != NULL ? valRt : valEm; const char *source = envVal != NULL ? "environment" : valRt != NULL ? "runtime" : "embedded"; - printf("|- %s=%s (source %s)\n", key, val, source); + printf("|- %s=%s (source %s)\n", iter.key, val, source); } } printf("\n"); @@ -258,9 +255,8 @@ static void printEmbeddedBundles() { static void combine_properties(celix_properties_t *original, const celix_properties_t *append) { if (original != NULL && append != NULL) { - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(append, key) { - celix_properties_set(original, key, celix_properties_get(append, key, NULL)); + CELIX_PROPERTIES_ITERATE(append, iter) { + celix_properties_set(original, iter.key, iter.entry.value); } } } diff --git a/libs/framework/src/service_reference.c b/libs/framework/src/service_reference.c index a7faebd77..0160ae8ef 100644 --- a/libs/framework/src/service_reference.c +++ b/libs/framework/src/service_reference.c @@ -204,9 +204,8 @@ FRAMEWORK_EXPORT celix_status_t serviceReference_getPropertyKeys(service_referen int vsize = celix_properties_size(props); *size = (unsigned int)vsize; *keys = malloc(vsize * sizeof(**keys)); - const char* key; - CELIX_PROPERTIES_FOR_EACH(props, key) { - (*keys)[i++] = (char*)key; + CELIX_PROPERTIES_ITERATE(props, iter) { + (*keys)[i++] = (char*)iter.key; } return status; } diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index b2f39192a..7d4f35323 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -176,7 +176,12 @@ TEST_F(CxxPropertiesTestSuite, StoreAndLoadTest) { EXPECT_NO_THROW(loadedProps = celix::Properties::load(path)); EXPECT_EQ(props.size(), loadedProps.size()); - EXPECT_THROW(loadedProps = celix::Properties::load("non-existence"), celix::IOException); + try { + loadedProps = celix::Properties::load("non-existence"); + FAIL() << "Expected exception not thrown"; + } catch (const celix::IOException& e) { + EXPECT_TRUE(strstr(e.what(), "Cannot load celix::Properties")); + } } #if __cplusplus >= 201703L //C++17 or higher diff --git a/libs/utils/gtest/src/CxxVersionTestSuite.cc b/libs/utils/gtest/src/CxxVersionTestSuite.cc index 0055d5c4a..ac7266c3f 100644 --- a/libs/utils/gtest/src/CxxVersionTestSuite.cc +++ b/libs/utils/gtest/src/CxxVersionTestSuite.cc @@ -18,6 +18,8 @@ */ #include +#include +#include #include "celix/Version.h" diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index f21b4049c..6904652e0 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -21,6 +21,7 @@ #include "celix_properties.h" #include "celix_utils.h" +#include "properties.h" using ::testing::MatchesRegex; @@ -56,6 +57,16 @@ TEST_F(PropertiesTestSuite, LoadTest) { celix_properties_destroy(properties); } +TEST_F(PropertiesTestSuite, LoadFromStringTest) { + const char* string = "key1=value1\nkey2=value2"; + auto* props = celix_properties_loadFromString(string); + EXPECT_EQ(2, celix_properties_size(props)); + EXPECT_STREQ("value1", celix_properties_get(props, "key1", "")); + EXPECT_STREQ("value2", celix_properties_get(props, "key2", "")); + celix_properties_destroy(props); +} + + TEST_F(PropertiesTestSuite, StoreTest) { const char* propertiesFile = "resources-test/properties_out.txt"; auto* properties = celix_properties_create(); @@ -99,23 +110,6 @@ TEST_F(PropertiesTestSuite, GetAsLongTest) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, CopyTest) { - char propertiesFile[] = "resources-test/properties.txt"; - auto* properties = celix_properties_load(propertiesFile); - EXPECT_EQ(4, celix_properties_size(properties)); - - celix_properties_t *copy = celix_properties_copy(properties); - - char keyA[] = "a"; - const char *valueA = celix_properties_get(copy, keyA, nullptr); - EXPECT_STREQ("b", valueA); - const char keyB[] = "b"; - EXPECT_STREQ("c \t d", celix_properties_get(copy, keyB, nullptr)); - - celix_properties_destroy(properties); - celix_properties_destroy(copy); -} - TEST_F(PropertiesTestSuite, GetSetTest) { auto* properties = celix_properties_create(); char keyA[] = "x"; @@ -169,6 +163,13 @@ TEST_F(PropertiesTestSuite, SetUnsetTest) { celix_properties_unset(properties, keyD); EXPECT_EQ(nullptr, celix_properties_get(properties, keyA, nullptr)); EXPECT_EQ(nullptr, celix_properties_get(properties, "a", nullptr)); + EXPECT_EQ(0, celix_properties_size(properties)); + + celix_properties_set(properties, keyA, nullptr); + EXPECT_EQ(1, celix_properties_size(properties)); + celix_properties_unset(properties, keyA); + EXPECT_EQ(0, celix_properties_size(properties)); + celix_properties_destroy(properties); } @@ -202,6 +203,35 @@ TEST_F(PropertiesTestSuite, GetLongTest) { celix_properties_destroy(properties); } +TEST_F(PropertiesTestSuite, GetAsDoubleTest) { + auto* properties = celix_properties_create(); + + celix_properties_set(properties, "a", "2"); + celix_properties_set(properties, "b", "-10032L"); + celix_properties_set(properties, "c", "1.2"); + celix_properties_setDouble(properties, "d", 1.4); + celix_properties_set(properties, "e", ""); + celix_properties_set(properties, "f", "garbage"); + + double a = celix_properties_getAsDouble(properties, "a", -1); + double b = celix_properties_getAsDouble(properties, "b", -1); + double c = celix_properties_getAsDouble(properties, "c", -1); + double d = celix_properties_getAsDouble(properties, "d", -1); + double e = celix_properties_getAsDouble(properties, "e", -1); + double f = celix_properties_getAsDouble(properties, "f", -1); + double g = celix_properties_getAsDouble(properties, "g", -1); //does not exist + + EXPECT_EQ(2, a); + EXPECT_EQ(-10032L, b); + EXPECT_EQ(1.2, c); + EXPECT_EQ(1.4, d); + EXPECT_EQ(-1L, e); + EXPECT_EQ(-1L, f); + EXPECT_EQ(-1L, g); + + celix_properties_destroy(properties); +} + TEST_F(PropertiesTestSuite, GetBoolTest) { auto* properties = celix_properties_create(); @@ -295,7 +325,7 @@ TEST_F(PropertiesTestSuite, SizeAndIteratorTest) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, GetTypeTest) { +TEST_F(PropertiesTestSuite, GetTypeAndCopyTest) { auto* props = celix_properties_create(); celix_properties_set(props, "string", "value"); celix_properties_setLong(props, "long", 123); @@ -304,6 +334,7 @@ TEST_F(PropertiesTestSuite, GetTypeTest) { auto* version = celix_version_createVersion(1, 2, 3, nullptr); celix_properties_setVersion(props, "version", version); + EXPECT_EQ(5, celix_properties_size(props)); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, celix_properties_getType(props, "string")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, celix_properties_getType(props, "long")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(props, "double")); @@ -311,8 +342,17 @@ TEST_F(PropertiesTestSuite, GetTypeTest) { EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props, "version")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, celix_properties_getType(props, "missing")); + auto* copy = celix_properties_copy(props); + EXPECT_EQ(5, celix_properties_size(copy)); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, celix_properties_getType(copy, "string")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, celix_properties_getType(copy, "long")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(copy, "double")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, celix_properties_getType(copy, "bool")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(copy, "version")); + celix_version_destroy(version); celix_properties_destroy(props); + celix_properties_destroy(copy); } TEST_F(PropertiesTestSuite, GetEntryTest) { @@ -509,4 +549,38 @@ TEST_F(PropertiesTestSuite, EndOfEmptyPropertiesTest) { celix_properties_destroy(props); } -//TODO test replace and replace WithCopy \ No newline at end of file +TEST_F(PropertiesTestSuite, SetWithCopyTest) { + auto* props = celix_properties_create(); + celix_properties_setWithoutCopy(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); + //replace, should free old value and provided key + celix_properties_setWithoutCopy(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); + EXPECT_EQ(1, celix_properties_size(props)); + celix_properties_destroy(props); +} + +TEST_F(PropertiesTestSuite, DeprecatedApiTest) { + //Check if the deprecated api can still be used + auto* props = properties_create(); + properties_set(props, "key", "value"); + EXPECT_EQ(1, celix_properties_size(props)); + EXPECT_STREQ("value", properties_get(props, "key")); + EXPECT_STREQ("notfound", properties_getWithDefault(props, "non-existing", "notfound")); + properties_unset(props, "key"); + EXPECT_EQ(0, celix_properties_size(props)); + celix_properties_t * copy = nullptr; + EXPECT_EQ(CELIX_SUCCESS, properties_copy(props, ©)); + properties_destroy(copy); + + + properties_store(props, "deprecated-api-stored.properties", ""); + auto* loaded = properties_load("deprecated-api-stored.properties"); + EXPECT_NE(nullptr, loaded); + properties_destroy(loaded); + + loaded = properties_loadFromString("key=value"); + EXPECT_EQ(1, celix_properties_size(loaded)); + properties_destroy(loaded); + + + properties_destroy(props); +} diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h index 556f4e92a..c019d14a9 100644 --- a/libs/utils/include/celix/Version.h +++ b/libs/utils/include/celix/Version.h @@ -45,8 +45,6 @@ namespace celix { cVersion{createVersion(celix_version_createEmptyVersion())}, qualifier{celix_version_getQualifier(cVersion.get())} {} -#if __cplusplus >= 201703L //C++17 or higher - /** * @brief Constructs a new version with the given components and qualifier. * @param major The major component of the version. @@ -54,6 +52,7 @@ namespace celix { * @param micro The micro component of the version. * @param qualifier The qualifier string of the version. */ +#if __cplusplus >= 201703L //C++17 or higher Version(int major, int minor, int micro, std::string_view qualifier = {}) : cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))}, qualifier{celix_version_getQualifier(cVersion.get())} {} diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 71bc0f536..45bbef172 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -405,14 +405,6 @@ void celix_propertiesIterator_next(celix_properties_iterator_t* iter); */ bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); -/** - * @brief Get the property set being iterated over. - * - * @param[in] iter The iterator to get the property set from. - * @return The property set being iterated over. - */ -celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter); - /** * @brief Determine whether two iterators are equal. * @@ -517,8 +509,6 @@ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter) for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(properties); \ celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) - - #ifdef __cplusplus } #endif diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index dbbc20481..cf9df2644 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -141,7 +141,10 @@ static void updateBuffers(char **key, char ** value, char **output, int outputPo * optimization string buffer. */ static char* celix_properties_createString(celix_properties_t* properties, const char* str) { - size_t len = str == NULL ? 0 : strnlen(str, CELIX_UTILS_MAX_STRLEN) + 1; + if (str == NULL) { + return NULL; + } + size_t len = strnlen(str, CELIX_UTILS_MAX_STRLEN) + 1; size_t left = CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE - properties->currentStringBufferIndex; char* result; if (len < left) { @@ -160,7 +163,7 @@ static char* celix_properties_createString(celix_properties_t* properties, const static celix_status_t celix_properties_fillEntry( celix_properties_t *properties, celix_properties_entry_t* entry, - const char *strValue, + const char** strValue, const long* longValue, const double* doubleValue, const bool* boolValue, @@ -168,7 +171,7 @@ static celix_status_t celix_properties_fillEntry( char convertedValueBuffer[32]; if (strValue != NULL) { entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; - entry->value = celix_properties_createString(properties, strValue); + entry->value = celix_properties_createString(properties, *strValue); entry->typed.strValue = entry->value; } else if (longValue != NULL) { entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG; @@ -250,7 +253,7 @@ static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_pr static celix_properties_entry_t* celix_properties_createEntry( celix_properties_t *properties, const char *key, - const char *strValue, + const char** strValue, const long* longValue, const double* doubleValue, const bool* boolValue, @@ -276,7 +279,7 @@ static celix_properties_entry_t* celix_properties_createEntry( static void celix_properties_createAndSetEntry( celix_properties_t *properties, const char *key, - const char *strValue, + const char** strValue, const long* longValue, const double* doubleValue, const bool* boolValue, @@ -348,13 +351,13 @@ celix_properties_t* celix_properties_create(void) { void celix_properties_destroy(celix_properties_t *props) { if (props != NULL) { //TODO measure print nr of entries and total size of the string keys and values -// fprintf(stdout, "Properties size; %d", celix_properties_size(props)); -// size_t size = 0; -// CELIX_PROPERTIES_ITERATE(props, iter) { -// size += strlen(iter.entry.key) + 1; -// size += strlen(iter.entry.value) + 1; -// } -// fprintf(stdout, "Properties string size: %zu", size); + fprintf(stdout, "Properties entries size: %d\n", celix_properties_size(props)); + size_t size = 0; + CELIX_PROPERTIES_ITERATE(props, iter) { + size += strlen(iter.key) + 1; + size += strlen(iter.entry.value) + 1; + } + fprintf(stdout, "Properties total string size: %zu\n\n", size); celix_stringHashMap_destroy(props->map); free(props); @@ -628,8 +631,8 @@ celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* pr } void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { - if (properties != NULL && key != NULL && value != NULL) { - celix_properties_createAndSetEntry(properties, key, value, NULL, NULL, NULL, NULL); + if (properties != NULL && key != NULL) { + celix_properties_createAndSetEntry(properties, key, &value, NULL, NULL, NULL, NULL); } } @@ -863,8 +866,3 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const return celix_stringHashMapIterator_equals(&internalIterA.mapIter, &internalIterB.mapIter); } -celix_properties_t* celix_propertiesIterator_properties(const celix_properties_iterator_t *iter) { - celix_properties_iterator_internal_t internalIter; - memcpy(&internalIter, iter, sizeof(internalIter)); - return (celix_properties_t*)internalIter.props; -} From c15bf710dc0f20ea14a6469318f035f72e535511 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 22:28:48 +0100 Subject: [PATCH 12/21] Adjust short properties optimization values based on some basic testing --- libs/utils/src/properties.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index cf9df2644..778e00da3 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -33,7 +33,7 @@ #include "celix_string_hash_map.h" -#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE 1024 +#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE 512 #define CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE 16 static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true"; @@ -43,8 +43,11 @@ struct celix_properties { celix_string_hash_map_t* map; /** - * String buffer used to store the first key/value entries, so that in many cases additional memory allocation - * can be prevented. + * String buffer used to store the first key/value entries, + * so that in many cases - for usage in service properties - additional memory allocations are not needed. + * + * @note based on some small testing most services properties seem to max out at 11 entries. + * So 16 (next factor 2 based value) seems like a good fit. */ char stringBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE]; @@ -56,6 +59,9 @@ struct celix_properties { /** * Entries buffer used to store the first entries, so that in many cases additional memory allocation * can be prevented. + * + * @note based on some small testing most services properties seem to max around 300 bytes. + * So 512 (next factor 2 based value) seems like a good fit. */ celix_properties_entry_t entriesBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE]; @@ -350,15 +356,6 @@ celix_properties_t* celix_properties_create(void) { void celix_properties_destroy(celix_properties_t *props) { if (props != NULL) { - //TODO measure print nr of entries and total size of the string keys and values - fprintf(stdout, "Properties entries size: %d\n", celix_properties_size(props)); - size_t size = 0; - CELIX_PROPERTIES_ITERATE(props, iter) { - size += strlen(iter.key) + 1; - size += strlen(iter.entry.value) + 1; - } - fprintf(stdout, "Properties total string size: %zu\n\n", size); - celix_stringHashMap_destroy(props->map); free(props); } @@ -476,7 +473,6 @@ celix_properties_t* celix_properties_loadWithStream(FILE *file) { return NULL; } - //TODO create properties with no internal short properties buffer, so celix_properties_createWithOptions() celix_properties_t *props = celix_properties_create(); if (props == NULL) { return NULL; From fe434a03fe99bab2228df8dd2292aa3ddf0d13e1 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 8 Jan 2023 22:48:12 +0100 Subject: [PATCH 13/21] Fix issue with setting properties with NULL values --- libs/utils/gtest/src/PropertiesTestSuite.cc | 5 +-- libs/utils/src/properties.c | 40 ++++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 6904652e0..bfc3efbfc 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -141,8 +141,9 @@ TEST_F(PropertiesTestSuite, GetSetWithNullTest) { celix_properties_set(properties, nullptr, nullptr); EXPECT_EQ(celix_properties_size(properties), 0); //NULL key will be ignored - celix_properties_set(properties, "key", nullptr); - EXPECT_EQ(celix_properties_size(properties), 0); //NULL value will be ignored + celix_properties_set(properties, "key", nullptr); //NULL value will result in empty string value + EXPECT_STREQ("", celix_properties_get(properties, "key", "not found")); + EXPECT_EQ(celix_properties_size(properties), 1); celix_properties_destroy(properties); } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 778e00da3..0ef19cc20 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -38,6 +38,7 @@ static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true"; static const char* const CELIX_PROPERTIES_BOOL_FALSE_STRVAL = "false"; +static const char* const CELIX_PROPERTIES_EMPTY_STRVAL = ""; struct celix_properties { celix_string_hash_map_t* map; @@ -148,7 +149,7 @@ static void updateBuffers(char **key, char ** value, char **output, int outputPo */ static char* celix_properties_createString(celix_properties_t* properties, const char* str) { if (str == NULL) { - return NULL; + return (char*)CELIX_PROPERTIES_EMPTY_STRVAL; } size_t len = strnlen(str, CELIX_UTILS_MAX_STRLEN) + 1; size_t left = CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE - properties->currentStringBufferIndex; @@ -162,6 +163,22 @@ static char* celix_properties_createString(celix_properties_t* properties, const } return result; } +/** + * Free string, but first check if it a static const char* const string or part of the short properties + * optimization. + */ +static void celix_properties_freeString(celix_properties_t* properties, char* str) { + if (str == CELIX_PROPERTIES_BOOL_TRUE_STRVAL || + str == CELIX_PROPERTIES_BOOL_FALSE_STRVAL || + str == CELIX_PROPERTIES_EMPTY_STRVAL) { + //str is static const char* const -> nop + } else if (str >= properties->stringBuffer && + str < (properties->stringBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE)) { + //str is part of the properties string buffer -> nop + } else { + free(str); + } +} /** * Fill entry and optional use the short properties optimization string buffer. @@ -272,7 +289,12 @@ static celix_properties_entry_t* celix_properties_createEntry( celix_status_t status = celix_properties_fillEntry(properties, entry, strValue, longValue, doubleValue, boolValue, versionValue); if (status != CELIX_SUCCESS) { - free(entry); + if (entry >= properties->entriesBuffer && + entry <= (properties->entriesBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE)) { + //entry is part of the properties entries buffer -> nop. + } else { + free(entry); + } entry = NULL; } return entry; @@ -305,19 +327,6 @@ static void celix_properties_createAndSetEntry( } } - - -static void celix_properties_freeString(celix_properties_t* properties, char* str) { - if (str == CELIX_PROPERTIES_BOOL_TRUE_STRVAL || str == CELIX_PROPERTIES_BOOL_FALSE_STRVAL) { - //str is static const char* const -> nop - } else if (str >= properties->stringBuffer && - str < (properties->stringBuffer + CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE)) { - //str is part of the properties string buffer -> nop - } else { - free(str); - } -} - static void celix_properties_removeKeyCallback(void* handle, char* key) { celix_properties_t* properties = handle; celix_properties_freeString(properties, key); @@ -861,4 +870,3 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const memcpy(&internalIterB, b->_data, sizeof(internalIterB)); return celix_stringHashMapIterator_equals(&internalIterA.mapIter, &internalIterB.mapIter); } - From 0c0bf1485337ccab24c41927329a26ce78baa6a5 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 May 2023 15:50:56 +0200 Subject: [PATCH 14/21] Refactor properties and version after merge with master --- libs/framework/src/service_reference.c | 32 +++++---- libs/utils/CMakeLists.txt | 13 ---- libs/utils/gtest/CMakeLists.txt | 6 ++ .../ConvertUtilsErrorInjectionTestSuite.cc | 2 +- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 2 +- libs/utils/gtest/src/PropertiesTestSuite.cc | 8 +-- libs/utils/gtest/src/VersionRangeTestSuite.cc | 4 +- libs/utils/gtest/src/VersionTestSuite.cc | 4 +- libs/utils/include/celix_properties.h | 68 +++++++++---------- libs/utils/include/celix_version.h | 16 +++-- libs/utils/private/test/version_ei_test.cc | 6 +- libs/utils/src/properties.c | 4 +- libs/utils/src/version.c | 10 +-- 13 files changed, 89 insertions(+), 86 deletions(-) diff --git a/libs/framework/src/service_reference.c b/libs/framework/src/service_reference.c index 6c5d1d36c..c207b0486 100644 --- a/libs/framework/src/service_reference.c +++ b/libs/framework/src/service_reference.c @@ -196,23 +196,27 @@ celix_status_t serviceReference_getProperty(service_reference_pt ref, const char } celix_status_t serviceReference_getPropertyKeys(service_reference_pt ref, char **keys[], unsigned int *size) { - celix_status_t status = CELIX_SUCCESS; - properties_pt props = NULL; + if (!keys || !size) { + return CELIX_ILLEGAL_ARGUMENT; + } + + celix_properties_t* props = NULL; + celix_status_t status = serviceRegistration_getProperties(ref->registration, &props); + if (status != CELIX_SUCCESS) { + return status; + } + + *keys = malloc(celix_properties_size(props) * sizeof(**keys)); + if (!*keys) { + return ENOMEM; + } - status = serviceRegistration_getProperties(ref->registration, &props); - assert(status == CELIX_SUCCESS); - hash_map_iterator_pt it; int i = 0; - int vsize = hashMap_size(props); - *size = (unsigned int)vsize; - *keys = malloc(vsize * sizeof(**keys)); - it = hashMapIterator_create(props); - while (hashMapIterator_hasNext(it)) { - (*keys)[i] = hashMapIterator_nextKey(it); - i++; + CELIX_PROPERTIES_ITERATE(props, entry) { + (*keys)[i++] = (char*)entry.key; } - hashMapIterator_destroy(it); - return status; + + return CELIX_SUCCESS; } celix_status_t serviceReference_invalidateCache(service_reference_pt reference) { diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index eca648115..df8909a08 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -143,19 +143,10 @@ if (ENABLE_TESTING) target_include_directories(linked_list_test PRIVATE include_deprecated) target_link_libraries(linked_list_test utils_cut CppUTest::CppUTest pthread) - add_executable(properties_test private/test/properties_test.cpp) - target_include_directories(properties_test PRIVATE include_deprecated) - target_link_libraries(properties_test CppUTest::CppUTest CppUTest::CppUTestExt utils_cut pthread) - add_executable(ip_utils_test private/test/ip_utils_test.cpp) target_include_directories(ip_utils_test PRIVATE include_deprecated) target_link_libraries(ip_utils_test CppUTest::CppUTest utils_cut pthread) - add_executable(version_test private/test/version_test.cpp) - target_include_directories(version_test PRIVATE include_deprecated) - target_link_libraries(version_test CppUTest::CppUTest utils_cut pthread) - - if (LINKER_WRAP_SUPPORTED) add_executable(version_ei_test private/test/version_ei_test.cc) target_include_directories(version_ei_test PRIVATE include_deprecated) @@ -163,14 +154,10 @@ if (ENABLE_TESTING) add_test(NAME version_ei_test COMMAND version_ei_test) endif () - configure_file(private/resources-test/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) - add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) add_test(NAME run_linked_list_test COMMAND linked_list_test) - add_test(NAME run_properties_test COMMAND properties_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) - add_test(NAME version_test COMMAND version_test) setup_target_for_coverage(array_list_test) setup_target_for_coverage(hash_map_test) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 06af219dc..8fc7264b5 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -21,6 +21,7 @@ set(CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS src/CxxUtilsTestSuite.cc src/CxxPropertiesTestSuite.cc src/CxxFilterTestSuite.cc + src/CxxVersionTestSuite.cc ) add_executable(test_utils @@ -33,6 +34,8 @@ add_executable(test_utils src/FilterTestSuite.cc src/CelixUtilsTestSuite.cc src/ConvertUtilsTestSuite.cc + src/PropertiesTestSuite.cc + src/VersionTestSuite.cc src/ErrTestSuite.cc src/ThreadsTestSuite.cc src/CelixErrnoTestSuite.cc @@ -43,6 +46,9 @@ target_link_libraries(test_utils PRIVATE utils_cut GTest::gtest GTest::gtest_mai target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) celix_deprecated_utils_headers(test_utils) +configure_file(resources/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) + + ####### generating zip file used for testing ########################################################################## file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/top.properties" CONTENT "level=1\n") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/subdir/sub.properties" CONTENT "level=2\n") diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index 37d3c8a05..730f9dcec 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -37,7 +37,7 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, CovertToBoolTest) { } TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToVersionTest) { - celix_version_t* defaultVersion = celix_version_createVersion(1, 2, 3, "B"); + celix_version_t* defaultVersion = celix_version_create(1, 2, 3, "B"); celix_ei_expect_celix_utils_writeOrCreateString(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); celix_version_t* result = celix_utils_convertStringToVersion("1.2.3", nullptr, nullptr); EXPECT_EQ(nullptr, result); diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index 91ed43d72..bbe24ada8 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -204,7 +204,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToBoolTest) { } TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { - celix_version_t* defaultVersion = celix_version_createVersion(1, 2, 3, "B"); + celix_version_t* defaultVersion = celix_version_create(1, 2, 3, "B"); //test for a valid string celix_version_t* result = celix_utils_convertStringToVersion("1.2.3", nullptr, nullptr); diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index bfc3efbfc..5317af76a 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -332,7 +332,7 @@ TEST_F(PropertiesTestSuite, GetTypeAndCopyTest) { celix_properties_setLong(props, "long", 123); celix_properties_setDouble(props, "double", 3.14); celix_properties_setBool(props, "bool", true); - auto* version = celix_version_createVersion(1, 2, 3, nullptr); + auto* version = celix_version_create(1, 2, 3, nullptr); celix_properties_setVersion(props, "version", version); EXPECT_EQ(5, celix_properties_size(props)); @@ -362,7 +362,7 @@ TEST_F(PropertiesTestSuite, GetEntryTest) { celix_properties_setLong(props, "key2", 123); celix_properties_setDouble(props, "key3", 123.456); celix_properties_setBool(props, "key4", true); - auto* version = celix_version_createVersion(1, 2, 3, nullptr); + auto* version = celix_version_create(1, 2, 3, nullptr); celix_properties_setVersion(props, "key5", version); auto* entry = celix_properties_getEntry(props, "key1"); @@ -478,7 +478,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { auto* emptyVersion = celix_version_createEmptyVersion(); // Test getting a version property - auto* expected = celix_version_createVersion(1, 2, 3, "test"); + auto* expected = celix_version_create(1, 2, 3, "test"); celix_properties_setVersion(properties, "key", expected); const auto* actual = celix_properties_getVersion(properties, "key", nullptr); EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); @@ -497,7 +497,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { celix_version_destroy(expected); // Test setting without copy - celix_properties_setVersionWithoutCopy(properties, "key3", celix_version_createVersion(3,3,3,"")); + celix_properties_setVersionWithoutCopy(properties, "key3", celix_version_create(3,3,3,"")); actual = celix_properties_getVersion(properties, "key3", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 3); EXPECT_EQ(celix_version_getMinor(actual), 3); diff --git a/libs/utils/gtest/src/VersionRangeTestSuite.cc b/libs/utils/gtest/src/VersionRangeTestSuite.cc index 85fa1a7e1..b0dc1ba0a 100644 --- a/libs/utils/gtest/src/VersionRangeTestSuite.cc +++ b/libs/utils/gtest/src/VersionRangeTestSuite.cc @@ -46,7 +46,7 @@ TEST_F(VersionRangeTestSuite, create) { TEST_F(VersionRangeTestSuite, createInfinite) { celix_status_t status = CELIX_SUCCESS; version_range_pt range = nullptr; - version_pt version = celix_version_createVersion(1,2, 3, nullptr); + version_pt version = celix_version_create(1,2, 3, nullptr); status = versionRange_createInfiniteVersionRange(&range); EXPECT_EQ(CELIX_SUCCESS, status); @@ -65,7 +65,7 @@ TEST_F(VersionRangeTestSuite, createInfinite) { TEST_F(VersionRangeTestSuite, isInRange) { bool result; - version_pt version = celix_version_createVersion(1, 2, 3, nullptr); + version_pt version = celix_version_create(1, 2, 3, nullptr); { version_range_pt range = nullptr; diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 5707e4272..5e908fd85 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -44,8 +44,8 @@ TEST_F(VersionTestSuite, CreateTest) { expectVersion(version, 1, 2, 3, "abc"); celix_version_destroy(version); - EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc")); - EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc|xyz")); + EXPECT_EQ(nullptr, celix_version_create(-1, -2, -3, "abc")); + EXPECT_EQ(nullptr, celix_version_create(-1, -2, -3, "abc|xyz")); //Testing deprecated api diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 45bbef172..3b6bfec16 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -39,6 +39,7 @@ #include "celix_errno.h" #include "celix_version.h" +#include "celix_utils_export.h" #ifndef CELIX_PROPERTIES_H_ #define CELIX_PROPERTIES_H_ @@ -116,14 +117,14 @@ typedef struct celix_properties_iterator { * * @return A new empty property set. */ -celix_properties_t* celix_properties_create(void); +CELIX_UTILS_EXPORT celix_properties_t* celix_properties_create(void); /** * @brief Destroy a property set, freeing all associated resources. * * @param[in] properties The property set to destroy. If properties is NULL, this function will do nothing. */ -void celix_properties_destroy(celix_properties_t* properties); +CELIX_UTILS_EXPORT void celix_properties_destroy(celix_properties_t* properties); /** * @brief Load properties from a file. @@ -132,7 +133,7 @@ void celix_properties_destroy(celix_properties_t* properties); * @return A property set containing the properties from the file. * @retval NULL If an error occurred (e.g. file not found). */ -celix_properties_t* celix_properties_load(const char *filename); +CELIX_UTILS_EXPORT celix_properties_t* celix_properties_load(const char *filename); /** @@ -142,7 +143,7 @@ celix_properties_t* celix_properties_load(const char *filename); * @return A property set containing the properties from the stream. * @retval NULL If an error occurred (e.g. invalid format). */ -celix_properties_t* celix_properties_loadWithStream(FILE *stream); +CELIX_UTILS_EXPORT celix_properties_t* celix_properties_loadWithStream(FILE *stream); /** * @brief Load properties from a string. @@ -151,7 +152,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE *stream); * @return A property set containing the properties from the string. * @retval NULL If an error occurred (e.g. invalid format). */ -celix_properties_t* celix_properties_loadFromString(const char *input); +CELIX_UTILS_EXPORT celix_properties_t* celix_properties_loadFromString(const char *input); /** * @brief Store properties to a file. @@ -164,7 +165,7 @@ celix_properties_t* celix_properties_loadFromString(const char *input); * @return CELIX_SUCCESS if the operation was successful, CELIX_FILE_IO_EXCEPTION if there was an error writing to the * file. */ -celix_status_t celix_properties_store(celix_properties_t* properties, const char* file, const char* header); +CELIX_UTILS_EXPORT celix_status_t celix_properties_store(celix_properties_t* properties, const char* file, const char* header); /** * @brief Get the entry for a given key in a property set. @@ -173,7 +174,7 @@ celix_status_t celix_properties_store(celix_properties_t* properties, const char * @param[in] key The key to search for. * @return The entry for the given key, or a NULL if the key is not found. */ -celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key); +CELIX_UTILS_EXPORT celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key); /** * @brief Get the value of a property. @@ -183,7 +184,7 @@ celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* pr * @param[in] defaultValue The value to return if the property is not set. * @return The value of the property, or the default value if the property is not set. */ -const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue); +CELIX_UTILS_EXPORT const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue); /** * @brief Get the type of a property value. @@ -192,7 +193,7 @@ const char* celix_properties_get(const celix_properties_t* properties, const cha * @param[in] key The key of the property to get the type of. * @return The type of the property value, or CELIX_PROPERTIES_VALUE_TYPE_UNSET if the property is not set. */ -celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key); +CELIX_UTILS_EXPORT celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key); /** * @brief Set the value of a property. @@ -201,7 +202,7 @@ celix_properties_value_type_e celix_properties_getType(const celix_properties_t* * @param[in] key The key of the property to set. * @param[in] value The value to set the property to. */ -void celix_properties_set(celix_properties_t* properties, const char* key, const char *value); +CELIX_UTILS_EXPORT void celix_properties_set(celix_properties_t* properties, const char* key, const char *value); /** * @brief Set the value of a property without copying the key and value strings. @@ -214,7 +215,7 @@ void celix_properties_set(celix_properties_t* properties, const char* key, const * @param[in] value The value to set the property to. This string will be used directly, so it must not be freed or * modified after calling this function. */ -void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char *value); +CELIX_UTILS_EXPORT void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char *value); /** * @brief Unset a property, removing it from the property set. @@ -222,7 +223,7 @@ void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, * @param[in] properties The property set to modify. * @param[in] key The key of the property to unset. */ -void celix_properties_unset(celix_properties_t* properties, const char *key); +CELIX_UTILS_EXPORT void celix_properties_unset(celix_properties_t* properties, const char *key); /** * @brief Make a copy of a property set. @@ -230,7 +231,7 @@ void celix_properties_unset(celix_properties_t* properties, const char *key); * @param[in] properties The property set to copy. * @return A copy of the given property set. */ -celix_properties_t* celix_properties_copy(const celix_properties_t* properties); +CELIX_UTILS_EXPORT celix_properties_t* celix_properties_copy(const celix_properties_t* properties); /** * @brief Get the value of a property as a long integer. @@ -243,7 +244,7 @@ celix_properties_t* celix_properties_copy(const celix_properties_t* properties); * the value is not a long integer, or if the value cannot be converted to a long integer. * If the value is a string, it will be converted to a long integer if possible. */ -long celix_properties_getAsLong(const celix_properties_t* properties, const char* key, long defaultValue); +CELIX_UTILS_EXPORT long celix_properties_getAsLong(const celix_properties_t* properties, const char* key, long defaultValue); /** * @brief Set the value of a property to a long integer. @@ -252,7 +253,7 @@ long celix_properties_getAsLong(const celix_properties_t* properties, const char * @param[in] key The key of the property to set. * @param[in] value The long value to set the property to. */ -void celix_properties_setLong(celix_properties_t* properties, const char* key, long value); +CELIX_UTILS_EXPORT void celix_properties_setLong(celix_properties_t* properties, const char* key, long value); /** * @brief Get the value of a property as a boolean. @@ -265,7 +266,7 @@ void celix_properties_setLong(celix_properties_t* properties, const char* key, l * boolean, or if the value cannot be converted to a boolean. If the value is a string, it will be converted * to a boolean if possible. */ -bool celix_properties_getAsBool(const celix_properties_t* properties, const char* key, bool defaultValue); +CELIX_UTILS_EXPORT bool celix_properties_getAsBool(const celix_properties_t* properties, const char* key, bool defaultValue); /** * @brief Set the value of a property to a boolean. @@ -274,7 +275,7 @@ bool celix_properties_getAsBool(const celix_properties_t* properties, const char * @param[in] key The key of the property to set. * @param[in] val The boolean value to set the property to. */ -void celix_properties_setBool(celix_properties_t* properties, const char* key, bool val); +CELIX_UTILS_EXPORT void celix_properties_setBool(celix_properties_t* properties, const char* key, bool val); /** * @brief Set the value of a property to a double. @@ -283,7 +284,7 @@ void celix_properties_setBool(celix_properties_t* properties, const char* key, b * @param[in] key The key of the property to set. * @param[in] val The double value to set the property to. */ -void celix_properties_setDouble(celix_properties_t* properties, const char* key, double val); +CELIX_UTILS_EXPORT void celix_properties_setDouble(celix_properties_t* properties, const char* key, double val); /** * @brief Get the value of a property as a double. @@ -296,7 +297,7 @@ void celix_properties_setDouble(celix_properties_t* properties, const char* key, * a double, or if the value cannot be converted to a double. If the value is a string, it will be converted * to a double if possible. */ -double celix_properties_getAsDouble(const celix_properties_t* properties, const char* key, double defaultValue); +CELIX_UTILS_EXPORT double celix_properties_getAsDouble(const celix_properties_t* properties, const char* key, double defaultValue); /** * @brief Set the value of a property as a Celix version. @@ -307,7 +308,7 @@ double celix_properties_getAsDouble(const celix_properties_t* properties, const * @param[in] key The key of the property to set. * @param[in] version The value to set. The function will make a copy of this object and store it in the property set. */ -void celix_properties_setVersion(celix_properties_t* properties, const char* key, const celix_version_t* version); +CELIX_UTILS_EXPORT void celix_properties_setVersion(celix_properties_t* properties, const char* key, const celix_version_t* version); /** * @brief Set the value of a property as a Celix version. @@ -320,7 +321,7 @@ void celix_properties_setVersion(celix_properties_t* properties, const char* key * @param[in] version The value to set. The function will store a reference to this object in the property set and * takes ownership of the provided version. */ -void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version); +CELIX_UTILS_EXPORT void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version); /** @@ -334,7 +335,7 @@ void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, cons * @return The value of the property if it is a Celix version, or the default value if the property is not set or the * value is not a Celix version. */ -const celix_version_t* celix_properties_getVersion( +CELIX_UTILS_EXPORT const celix_version_t* celix_properties_getVersion( const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); @@ -356,7 +357,7 @@ const celix_version_t* celix_properties_getVersion( * is not a valid Celix version. * @retval NULL if version cannot be found/converted and the defaultValue is NULL. */ -celix_version_t* celix_properties_getAsVersion( +CELIX_UTILS_EXPORT celix_version_t* celix_properties_getAsVersion( const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); @@ -367,7 +368,7 @@ celix_version_t* celix_properties_getAsVersion( * @param[in] properties The property set to get the size of. * @return The number of properties in the property set. */ -int celix_properties_size(const celix_properties_t* properties); +CELIX_UTILS_EXPORT int celix_properties_size(const celix_properties_t* properties); /** * @brief Construct an iterator pointing to the first entry in the properties object. @@ -375,7 +376,7 @@ int celix_properties_size(const celix_properties_t* properties); * @param[in] properties The properties object to iterate over. * @return The iterator pointing to the first entry in the properties object. */ -celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties); +CELIX_UTILS_EXPORT celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties); /** * @brief Construct an iterator pointing to the past-the-end entry in the properties object. @@ -386,14 +387,14 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro * @param[in] properties The properties object to iterate over. * @return The iterator pointing to the past-the-end entry in the properties object. */ -celix_properties_iterator_t celix_properties_end(const celix_properties_t* properties); +CELIX_UTILS_EXPORT celix_properties_iterator_t celix_properties_end(const celix_properties_t* properties); /** * @brief Advance the iterator to the next entry. * * @param[in, out] iter The iterator. */ -void celix_propertiesIterator_next(celix_properties_iterator_t* iter); +CELIX_UTILS_EXPORT void celix_propertiesIterator_next(celix_properties_iterator_t* iter); /** * @brief Determine whether the iterator is pointing to an end position. @@ -403,7 +404,7 @@ void celix_propertiesIterator_next(celix_properties_iterator_t* iter); * @param[in] iter The iterator. * @return true if the iterator is at an end position, false otherwise. */ -bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); +CELIX_UTILS_EXPORT bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); /** * @brief Determine whether two iterators are equal. @@ -412,7 +413,7 @@ bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter); * @param[in] b The second iterator to compare. * @return true if the iterators are equal, false otherwise. */ -bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); +CELIX_UTILS_EXPORT bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); /** * @brief Iterate over the entries in the specified celix_properties_t object. @@ -452,8 +453,7 @@ bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const * @param[in] properties The properties object to iterate over. * @return The newly constructed iterator. */ -celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties) - __attribute__((deprecated("celix_propertiesIterator_construct is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); +CELIX_UTILS_DEPRECATED_EXPORT celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties); /** * @brief Determines whether the iterator has more entries. @@ -462,8 +462,7 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope * @param[in] iter The iterator. * @return true if the iterator has more entries, false otherwise. */ -bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) - __attribute__((deprecated("celix_propertiesIterator_hasNext is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); +CELIX_UTILS_DEPRECATED_EXPORT bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); /** * @brief Advances the iterator to the next entry and returns the key for the current entry. @@ -474,8 +473,7 @@ bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) * @param[in, out] iter The iterator. * @return The key for the current entry, or NULL if the iterator has no more entries. */ -const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter) - __attribute__((deprecated("celix_propertiesIterator_nextKey is deprecated use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead"))); +CELIX_UTILS_DEPRECATED_EXPORT const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter); /** * @brief Macro for iterating over the properties in a property set. diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index 5c5b03a26..71d6e37d0 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -20,14 +20,15 @@ #ifndef CELIX_CELIX_VERSION_H #define CELIX_CELIX_VERSION_H -#ifdef __cplusplus -extern "C" { -#endif - #include +#include #include "celix_utils_export.h" +#ifdef __cplusplus +extern "C" { +#endif + /** * @file celix_version.h * @brief Header file for the Celix Version API. @@ -54,7 +55,12 @@ typedef struct celix_version celix_version_t; * the empty string. * @return The created version or NULL if the input was incorrect */ -CELIX_UTILS_EXPORT celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char* qualifier); +CELIX_UTILS_EXPORT celix_version_t* celix_version_create(int major, int minor, int micro, const char* qualifier); + +/** + * @deprecated Use celix_version_create instead. + */ +CELIX_UTILS_DEPRECATED_EXPORT celix_version_t* celixversion_createVersion(int major, int minor, int micro, const char* qualifier); CELIX_UTILS_EXPORT void celix_version_destroy(celix_version_t* version); diff --git a/libs/utils/private/test/version_ei_test.cc b/libs/utils/private/test/version_ei_test.cc index 66606ae45..3bfd66e66 100644 --- a/libs/utils/private/test/version_ei_test.cc +++ b/libs/utils/private/test/version_ei_test.cc @@ -27,6 +27,8 @@ #include "celix_version.h" +//TODO move to gtest + int main(int argc, char** argv) { MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); return RUN_ALL_TESTS(argc, argv); @@ -47,10 +49,10 @@ TEST_GROUP(version_ei) { TEST(version_ei, create) { celix_ei_expect_calloc(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); - celix_version_t *version = celix_version_createVersion(2, 2, 0, nullptr); + celix_version_t *version = celix_version_create(2, 2, 0, nullptr); POINTERS_EQUAL(nullptr, version); celix_ei_expect_celix_utils_strdup(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); - version = celix_version_createVersion(2, 2, 0, nullptr); + version = celix_version_create(2, 2, 0, nullptr); POINTERS_EQUAL(nullptr, version); } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index d0cae6843..76a0e2661 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -570,8 +570,8 @@ celix_status_t celix_properties_store(celix_properties_t *properties, const char } int rc = 0; - CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { - const char* val = iter.value.ptrValue; + CELIX_PROPERTIES_ITERATE(properties, iter) { + const char* val = iter.entry.value; if (rc != EOF) { rc = celix_properties_storeEscapedString(file, iter.key); } diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index f7317633a..814caccd9 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -30,7 +30,7 @@ static const char* const CELIX_VERSION_EMPTY_QUALIFIER = ""; celix_status_t version_createVersion(int major, int minor, int micro, const char * qualifier, version_pt *version) { - *version = celix_version_createVersion(major, minor, micro, qualifier); + *version = celix_version_create(major, minor, micro, qualifier); return *version == NULL ? CELIX_ILLEGAL_ARGUMENT : CELIX_SUCCESS; } @@ -97,7 +97,7 @@ celix_status_t version_isCompatible(version_pt user, version_pt provider, bool* return CELIX_SUCCESS; } -celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char* qualifier) { +celix_version_t* celix_version_createVersion(int major, int minor, int micro, const char * qualifier) { return celix_version_create(major, minor, micro, qualifier); } @@ -156,7 +156,7 @@ celix_version_t* celix_version_copy(const celix_version_t* version) { if (version == NULL) { return celix_version_createEmptyVersion(); } - return celix_version_createVersion(version->major, version->minor, version->micro, version->qualifier); + return celix_version_create(version->major, version->minor, version->micro, version->qualifier); } @@ -228,7 +228,7 @@ celix_version_t* celix_version_createVersionFromString(const char *versionStr) { celix_version_t* version = NULL; if (status == CELIX_SUCCESS) { - version = celix_version_createVersion(major, minor, micro, qualifier); + version = celix_version_create(major, minor, micro, qualifier); } if (qualifier != NULL) { @@ -240,7 +240,7 @@ celix_version_t* celix_version_createVersionFromString(const char *versionStr) { celix_version_t* celix_version_createEmptyVersion() { - return celix_version_createVersion(0, 0, 0, NULL); + return celix_version_create(0, 0, 0, NULL); } int celix_version_getMajor(const celix_version_t* version) { From 78b94d588e4e96b295b422fbb03a224e25f34ffe Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 May 2023 19:20:37 +0200 Subject: [PATCH 15/21] Refactor cpputest version ei test to gtest --- libs/utils/CMakeLists.txt | 7 ------ libs/utils/gtest/CMakeLists.txt | 1 + .../src/VersionErrorInjectionTestSuite.cc} | 24 +++++++------------ 3 files changed, 10 insertions(+), 22 deletions(-) rename libs/utils/{private/test/version_ei_test.cc => gtest/src/VersionErrorInjectionTestSuite.cc} (79%) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index df8909a08..9fad8b863 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -147,13 +147,6 @@ if (ENABLE_TESTING) target_include_directories(ip_utils_test PRIVATE include_deprecated) target_link_libraries(ip_utils_test CppUTest::CppUTest utils_cut pthread) - if (LINKER_WRAP_SUPPORTED) - add_executable(version_ei_test private/test/version_ei_test.cc) - target_include_directories(version_ei_test PRIVATE include_deprecated) - target_link_libraries(version_ei_test CppUTest::CppUTest utils_cut Celix::malloc_ei Celix::utils_ei pthread) - add_test(NAME version_ei_test COMMAND version_ei_test) - endif () - add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) add_test(NAME run_linked_list_test COMMAND linked_list_test) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 8fc7264b5..8c5b37c09 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -93,6 +93,7 @@ if (LINKER_WRAP_SUPPORTED) src/IpUtilsErrorInjectionTestSuite.cc src/ArrayListErrorInjectionTestSuite.cc src/ErrErrorInjectionTestSuite.cc + src/VersionErrorInjectionTestSuite.cc ) target_link_libraries(test_utils_with_ei PRIVATE Celix::zip_ei Celix::stdio_ei Celix::stat_ei Celix::fts_ei utils_cut Celix::utils_ei Celix::ifaddrs_ei Celix::threads_ei Celix::malloc_ei GTest::gtest GTest::gtest_main) target_include_directories(test_utils_with_ei PRIVATE ../src) #for version_private (needs refactoring of test) diff --git a/libs/utils/private/test/version_ei_test.cc b/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc similarity index 79% rename from libs/utils/private/test/version_ei_test.cc rename to libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc index 3bfd66e66..d59da470b 100644 --- a/libs/utils/private/test/version_ei_test.cc +++ b/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc @@ -17,6 +17,7 @@ under the License. */ +#include #include #include "celix_utils_ei.h" @@ -27,32 +28,25 @@ #include "celix_version.h" -//TODO move to gtest -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} - -TEST_GROUP(version_ei) { - - void setup(void) { +class VersionErrorInjectionTestSuite : public ::testing::Test { +public: + VersionErrorInjectionTestSuite() { celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); } - - void teardown(void) { + ~VersionErrorInjectionTestSuite() noexcept override { celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); } }; -TEST(version_ei, create) { +TEST_F(VersionErrorInjectionTestSuite, CreateTest) { celix_ei_expect_calloc(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); celix_version_t *version = celix_version_create(2, 2, 0, nullptr); - POINTERS_EQUAL(nullptr, version); + EXPECT_EQ(nullptr, version); celix_ei_expect_celix_utils_strdup(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); - version = celix_version_create(2, 2, 0, nullptr); - POINTERS_EQUAL(nullptr, version); + version = celix_version_create(2, 2, 0, "qualifier"); + EXPECT_EQ(nullptr, version); } From 717869d6aa8dcc08bb6967e43db57bc23fff8e2b Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 May 2023 23:55:28 +0200 Subject: [PATCH 16/21] Add missing output param in serviceReference_getPropertyKeys --- libs/framework/src/service_reference.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/framework/src/service_reference.c b/libs/framework/src/service_reference.c index c207b0486..4bc9b73ca 100644 --- a/libs/framework/src/service_reference.c +++ b/libs/framework/src/service_reference.c @@ -206,7 +206,8 @@ celix_status_t serviceReference_getPropertyKeys(service_reference_pt ref, char * return status; } - *keys = malloc(celix_properties_size(props) * sizeof(**keys)); + *size = celix_properties_size(props); + *keys = malloc((*size) * sizeof(**keys)); if (!*keys) { return ENOMEM; } From e0326790ce689f800b964caa21bad355ab0728ae Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Wed, 23 Aug 2023 19:12:36 +0200 Subject: [PATCH 17/21] Refactor and correction after merge with master --- libs/utils/CMakeLists.txt | 12 +----------- .../celix_hash_map/CMakeLists.txt | 1 + .../celix_hash_map/include/celix_hash_map_ei.h | 2 ++ .../celix_hash_map/src/celix_hash_map_ei.cc | 9 +++++++++ libs/utils/gtest/CMakeLists.txt | 3 +-- .../src/PropertiesErrorInjectionTestSuite.cc | 16 ++++++++++++---- libs/utils/gtest/src/PropertiesTestSuite.cc | 4 ++++ libs/utils/include/celix_utils.h | 9 --------- libs/utils/src/properties.c | 4 ++++ libs/utils/src/utils.c | 18 +++++++++--------- 10 files changed, 43 insertions(+), 35 deletions(-) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index f902e8255..4f12f604b 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -159,20 +159,10 @@ if (UTILS) add_test(NAME run_linked_list_test COMMAND linked_list_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) - if (LINKER_WRAP_SUPPORTED) - add_executable(version_ei_test private/test/version_ei_test.cc) - target_include_directories(version_ei_test PRIVATE include_deprecated) - target_link_libraries(version_ei_test CppUTest::CppUTest utils_cut Celix::malloc_ei Celix::utils_ei pthread) - add_test(NAME version_ei_test COMMAND version_ei_test) - setup_target_for_coverage(version_ei_test) - endif () - - setup_target_for_coverage(array_list_test) setup_target_for_coverage(hash_map_test) + setup_target_for_coverage(array_list_test) setup_target_for_coverage(linked_list_test) - setup_target_for_coverage(properties_test) setup_target_for_coverage(ip_utils_test) - setup_target_for_coverage(version_test) else () message(WARNING "Cannot find CppUTest, deprecated cpputest-based unit test will not be added") endif () #end CppUTest_FOUND diff --git a/libs/utils/error_injector/celix_hash_map/CMakeLists.txt b/libs/utils/error_injector/celix_hash_map/CMakeLists.txt index 25f87d49e..19df3d4ac 100644 --- a/libs/utils/error_injector/celix_hash_map/CMakeLists.txt +++ b/libs/utils/error_injector/celix_hash_map/CMakeLists.txt @@ -21,5 +21,6 @@ target_include_directories(hash_map_ei PUBLIC include) target_link_libraries(hash_map_ei PUBLIC Celix::error_injector Celix::utils) target_link_options(hash_map_ei INTERFACE LINKER:--wrap,celix_stringHashMap_create + LINKER:--wrap,celix_stringHashMap_createWithOptions ) add_library(Celix::hash_map_ei ALIAS hash_map_ei) diff --git a/libs/utils/error_injector/celix_hash_map/include/celix_hash_map_ei.h b/libs/utils/error_injector/celix_hash_map/include/celix_hash_map_ei.h index 3c38ad419..5dbdd658d 100644 --- a/libs/utils/error_injector/celix_hash_map/include/celix_hash_map_ei.h +++ b/libs/utils/error_injector/celix_hash_map/include/celix_hash_map_ei.h @@ -28,6 +28,8 @@ extern "C" { CELIX_EI_DECLARE(celix_stringHashMap_create, celix_string_hash_map_t*); +CELIX_EI_DECLARE(celix_stringHashMap_createWithOptions, celix_string_hash_map_t*); + #ifdef __cplusplus } #endif diff --git a/libs/utils/error_injector/celix_hash_map/src/celix_hash_map_ei.cc b/libs/utils/error_injector/celix_hash_map/src/celix_hash_map_ei.cc index 4edbc3810..bbd3af6da 100644 --- a/libs/utils/error_injector/celix_hash_map/src/celix_hash_map_ei.cc +++ b/libs/utils/error_injector/celix_hash_map/src/celix_hash_map_ei.cc @@ -20,10 +20,19 @@ #include "celix_hash_map_ei.h" extern "C" { + celix_string_hash_map_t* __real_celix_stringHashMap_create(); CELIX_EI_DEFINE(celix_stringHashMap_create, celix_string_hash_map_t*); celix_string_hash_map_t* __wrap_celix_stringHashMap_create() { CELIX_EI_IMPL(celix_stringHashMap_create); return __real_celix_stringHashMap_create(); } + +celix_string_hash_map_t* __real_celix_stringHashMap_createWithOptions(const celix_string_hash_map_create_options_t* opts); +CELIX_EI_DEFINE(celix_stringHashMap_createWithOptions, celix_string_hash_map_t*); +celix_string_hash_map_t* __wrap_celix_stringHashMap_createWithOptions(const celix_string_hash_map_create_options_t* opts) { + CELIX_EI_IMPL(celix_stringHashMap_createWithOptions); + return __real_celix_stringHashMap_createWithOptions(opts); +} + } diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 0dd222d6e..89786deec 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -108,7 +108,7 @@ if (LINKER_WRAP_SUPPORTED) Celix::ifaddrs_ei Celix::threads_ei Celix::malloc_ei - Celix::hmap_ei + Celix::hash_map_ei GTest::gtest GTest::gtest_main ) target_include_directories(test_utils_with_ei PRIVATE ../src) #for version_private (needs refactoring of test) @@ -130,4 +130,3 @@ if (ENABLE_TESTING_FOR_CXX14) add_test(NAME test_utils_cxx_headers_with_cxx14 COMMAND test_utils_cxx_headers_with_cxx14) setup_target_for_coverage(test_utils_cxx_headers_with_cxx14 SCAN_DIR ..) endif () - diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 13f1f6b65..89aa2e692 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -18,7 +18,9 @@ * */ #include "celix_properties.h" -#include "hmap_ei.h" + +#include "malloc_ei.h" +#include "celix_hash_map_ei.h" #include @@ -26,13 +28,19 @@ class PropertiesErrorInjectionTestSuite : public ::testing::Test { public: PropertiesErrorInjectionTestSuite() = default; ~PropertiesErrorInjectionTestSuite() override { - celix_ei_expect_hashMap_create(nullptr, 0, nullptr); + celix_ei_expect_malloc(nullptr, 0, nullptr); + celix_ei_expect_celix_stringHashMap_createWithOptions(nullptr, 0, nullptr); } }; +TEST_F(PropertiesErrorInjectionTestSuite, CreateFailureTest) { + celix_ei_expect_malloc((void *)celix_properties_create, 0, nullptr); + ASSERT_EQ(nullptr, celix_properties_create()); +} + TEST_F(PropertiesErrorInjectionTestSuite, CopyFailureTest) { celix_autoptr(celix_properties_t) prop = celix_properties_create(); ASSERT_NE(nullptr, prop); - celix_ei_expect_hashMap_create((void *) &celix_properties_create, 0, nullptr); + celix_ei_expect_celix_stringHashMap_createWithOptions((void *)celix_properties_create, 0, nullptr); ASSERT_EQ(nullptr, celix_properties_copy(prop)); -} \ No newline at end of file +} diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 5317af76a..40a36f59e 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -585,3 +585,7 @@ TEST_F(PropertiesTestSuite, DeprecatedApiTest) { properties_destroy(props); } + +TEST_F(PropertiesTestSuite, PropertiesAutoCleanupTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); +} diff --git a/libs/utils/include/celix_utils.h b/libs/utils/include/celix_utils.h index b3969700a..30168409e 100644 --- a/libs/utils/include/celix_utils.h +++ b/libs/utils/include/celix_utils.h @@ -117,15 +117,6 @@ CELIX_UTILS_EXPORT char* celix_utils_trim(const char* string); */ CELIX_UTILS_EXPORT char* celix_utils_trimInPlace(char* string); -/** - * @brief Trims a string in place. - * - * The trim will remove any leading and trailing whitespaces (' ', '\t', etc based on isspace) from the input string. - * The input string is modified in place. - * @param[in,out] string The string to trim. - */ -void celix_utils_trimInPlace(char* string); - /** * @brief Check if a string is NULL or empty "". */ diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 116608891..6b208c3ab 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -359,6 +359,10 @@ celix_properties_t* celix_properties_create(void) { props->map = celix_stringHashMap_createWithOptions(&opts); props->currentStringBufferIndex = 0; props->currentEntriesBufferIndex = 0; + if (props->map == NULL) { + free(props); + props = NULL; + } } return props; } diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c index 01c91b00f..36919e0d3 100644 --- a/libs/utils/src/utils.c +++ b/libs/utils/src/utils.c @@ -116,12 +116,12 @@ char * string_ndup(const char *s, size_t n) { return ret; } -void celix_utils_trimInPlace(char* string) { +static char* celix_utilsTrimInternal(char* string) { if (string == NULL) { - return; + return NULL; } - char* begin = string; //save begin to check in the end. + char* begin = string; //save begin to correctly free in the end. char *end; // Trim leading space @@ -137,7 +137,7 @@ void celix_utils_trimInPlace(char* string) { } if (string != begin) { - //beginning whitespaces -> move chars to the beginning of string + //beginning whitespaces -> move char in copy to to begin string //This to ensure free still works on the same pointer. char* nstring = begin; while(*string != '\0') { @@ -145,20 +145,20 @@ void celix_utils_trimInPlace(char* string) { } (*nstring) = '\0'; } + + return begin; } char* celix_utils_trim(const char* string) { - char* result = celix_utils_strdup(string); - celix_utils_trimInPlace(result); - return result; + return celix_utilsTrimInternal(celix_utils_strdup(string)); } + char* celix_utils_trimInPlace(char* string) { return celix_utilsTrimInternal(string); } char* utils_stringTrim(char* string) { - celix_utils_trimInPlace(string); - return string; + return celix_utilsTrimInternal(string); } bool utils_isStringEmptyOrNull(const char * const str) { From 226a546a2d48173960cc553c0d06b2b1390d45c6 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Wed, 23 Aug 2023 20:35:35 +0200 Subject: [PATCH 18/21] Remove cpputest include from gtest --- libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc | 5 +++-- libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc | 5 ----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 89aa2e692..270e4ec38 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -17,13 +17,14 @@ * under the License. * */ + +#include + #include "celix_properties.h" #include "malloc_ei.h" #include "celix_hash_map_ei.h" -#include - class PropertiesErrorInjectionTestSuite : public ::testing::Test { public: PropertiesErrorInjectionTestSuite() = default; diff --git a/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc b/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc index d59da470b..cf570c6ca 100644 --- a/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/VersionErrorInjectionTestSuite.cc @@ -18,14 +18,9 @@ */ #include -#include #include "celix_utils_ei.h" -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" #include "malloc_ei.h" - #include "celix_version.h" From 09882913b182edfa05dbe908ea936328c4bc8599 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Thu, 24 Aug 2023 14:05:21 +0200 Subject: [PATCH 19/21] Fix properties iterator usage in disc zeroconf --- .../discovery_zeroconf/src/discovery_zeroconf_announcer.c | 7 +++---- libs/utils/include/celix_properties.h | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/bundles/remote_services/discovery_zeroconf/src/discovery_zeroconf_announcer.c b/bundles/remote_services/discovery_zeroconf/src/discovery_zeroconf_announcer.c index c79d1d1ee..40c38be76 100644 --- a/bundles/remote_services/discovery_zeroconf/src/discovery_zeroconf_announcer.c +++ b/bundles/remote_services/discovery_zeroconf/src/discovery_zeroconf_announcer.c @@ -342,11 +342,10 @@ static void discoveryZeroconfAnnouncer_revokeEndpoints(discovery_zeroconf_announ static bool discoveryZeroconfAnnouncer_copyPropertiesToTxtRecord(discovery_zeroconf_announcer_t *announcer, celix_properties_iterator_t *propIter, TXTRecordRef *txtRecord, uint16_t maxTxtLen, bool splitTxtRecord) { const char *key; const char *val; - celix_properties_t *props; while (celix_propertiesIterator_hasNext(propIter)) { - key = celix_propertiesIterator_nextKey(propIter); - props = celix_propertiesIterator_properties(propIter); - val = celix_properties_get(props, key, ""); + celix_propertiesIterator_next(propIter); + key = propIter->key; + val = propIter->entry.value; if (key) { DNSServiceErrorType err = TXTRecordSetValue(txtRecord, key, strlen(val), val); if (err != kDNSServiceErr_NoError) { diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 69efa33e7..c839473ac 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -426,7 +426,7 @@ CELIX_UTILS_EXPORT bool celix_propertiesIterator_equals(const celix_properties_i * The loop variable `iterName` will be of type celix_properties_iterator_t and will contain the current * entry during each iteration. * - * @param[in] map The properties object to iterate over. + * @param[in] props The properties object to iterate over. * @param[in] iterName The name of the iterator variable to use in the loop. * * Example usage: @@ -438,8 +438,8 @@ CELIX_UTILS_EXPORT bool celix_propertiesIterator_equals(const celix_properties_i * } * @endcode */ -#define CELIX_PROPERTIES_ITERATE(map, iterName) \ - for (celix_properties_iterator_t iterName = celix_properties_begin(map); \ +#define CELIX_PROPERTIES_ITERATE(props, iterName) \ + for (celix_properties_iterator_t iterName = celix_properties_begin((props)); \ !celix_propertiesIterator_isEnd(&(iterName)); celix_propertiesIterator_next(&(iterName))) From 1f5bf7907294122db30c7fb5c5ed6be617d61fe5 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 1 Oct 2023 18:49:11 +0200 Subject: [PATCH 20/21] #509: Update changes for celix properties with type support --- CHANGES.md | 10 ++++++++++ libs/utils/gtest/src/VersionRangeTestSuite.cc | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 26e98b052..54a0ccdc8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,16 @@ See the License for the specific language governing permissions and limitations under the License. --> +# Noteworthy Changes for 3.0.0 (TBD) + +## Backwards incompatible changes + +- C Properties are no longer a direct typedef o `hashmap`. + +## New Features + +- Minimal form of type support for celix Properties. + # Noteworthy Changes for 2.4.0 (2023-09-27) ## New Features diff --git a/libs/utils/gtest/src/VersionRangeTestSuite.cc b/libs/utils/gtest/src/VersionRangeTestSuite.cc index 663b7bc10..2c44a79fb 100644 --- a/libs/utils/gtest/src/VersionRangeTestSuite.cc +++ b/libs/utils/gtest/src/VersionRangeTestSuite.cc @@ -418,8 +418,8 @@ TEST_F(VersionRangeTestSuite, createLdapFilterInPlaceInfiniteHigh) { } TEST_F(VersionRangeTestSuite, createLdapFilterWithQualifier) { - celix_autoptr(celix_version_t) low = celix_version_createVersion(1, 2, 2, "0"); - celix_autoptr(celix_version_t) high = celix_version_createVersion(1, 2, 2, "0"); + celix_autoptr(celix_version_t) low = celix_version_create(1, 2, 2, "0"); + celix_autoptr(celix_version_t) high = celix_version_create(1, 2, 2, "0"); celix_autoptr(celix_version_range_t) range = celix_versionRange_createVersionRange(celix_steal_ptr(low), true, celix_steal_ptr(high), true); From 77f212aecbf885f1f79d6d2ae386875402ea4a31 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 7 Oct 2023 19:24:23 +0200 Subject: [PATCH 21/21] #509: Remove deprecated macro (incl usage) CELIX_PROPERTIES_FOR_EACH --- .../src/pubsub_wire_protocol_common.c | 8 +- libs/framework/gtest/src/ManifestTestSuite.cc | 5 +- libs/framework/include/celix/Trackers.h | 2 +- .../include/celix/UseServiceBuilder.h | 4 +- .../include/celix/dm/DependencyManager_Impl.h | 6 +- .../include/celix/dm/ServiceDependency_Impl.h | 32 +------- libs/framework/src/celix_launcher.c | 32 ++++---- libs/framework/src/manifest.c | 5 +- libs/rcm/src/celix_capability.c | 43 ++++------ libs/rcm/src/celix_requirement.c | 56 +++++-------- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 19 ++++- libs/utils/gtest/src/PropertiesTestSuite.cc | 51 ++++++------ libs/utils/include/celix/Properties.h | 19 ++++- libs/utils/include/celix_properties.h | 80 +++---------------- libs/utils/src/properties.c | 66 ++++++--------- 15 files changed, 161 insertions(+), 267 deletions(-) diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c index 7b78cea07..88a9bbff5 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c +++ b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/src/pubsub_wire_protocol_common.c @@ -101,19 +101,17 @@ celix_status_t pubsubProtocol_encodeMetadata(pubsub_protocol_message_t* message, *bufferInOut = newBuffer; *bufferLengthInOut = newLength; } - const char* key; if (metadataSize == 0) { encoded = true; continue; } celix_status_t status = CELIX_SUCCESS; - CELIX_PROPERTIES_FOR_EACH(message->metadata.metadata, key) { - const char *val = celix_properties_get(message->metadata.metadata, key, ""); + CELIX_PROPERTIES_ITERATE(message->metadata.metadata, iter) { if (status == CELIX_SUCCESS) { - status = pubsubProtocol_addNetstringEntryToBuffer(*bufferInOut, *bufferLengthInOut, &offset, key); + status = pubsubProtocol_addNetstringEntryToBuffer(*bufferInOut, *bufferLengthInOut, &offset, iter.key); } if (status == CELIX_SUCCESS) { - status = pubsubProtocol_addNetstringEntryToBuffer(*bufferInOut, *bufferLengthInOut, &offset, val); + status = pubsubProtocol_addNetstringEntryToBuffer(*bufferInOut, *bufferLengthInOut, &offset, iter.entry.value); } } if (status == CELIX_FILE_IO_EXCEPTION) { diff --git a/libs/framework/gtest/src/ManifestTestSuite.cc b/libs/framework/gtest/src/ManifestTestSuite.cc index 51cb2bf63..6cb668353 100644 --- a/libs/framework/gtest/src/ManifestTestSuite.cc +++ b/libs/framework/gtest/src/ManifestTestSuite.cc @@ -42,9 +42,8 @@ class ManifestTestSuite : public ::testing::Test { } void CheckPropertiesEqual(const celix_properties_t* prop1, const celix_properties_t* prop2) { EXPECT_EQ(celix_properties_size(prop1), celix_properties_size(prop2)); - const char* key = nullptr; - CELIX_PROPERTIES_FOR_EACH(prop1, key) { - EXPECT_STREQ(celix_properties_get(prop1, key, nullptr), celix_properties_get(prop2, key, nullptr)); + CELIX_PROPERTIES_ITERATE(prop1, iter) { + EXPECT_STREQ(celix_properties_get(prop1, iter.key, nullptr), celix_properties_get(prop2, iter.key, nullptr)); } } void CheckManifestEqual(const manifest_pt manifest1, const manifest_pt manifest2) { diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h index 5498607ca..f702c958e 100644 --- a/libs/framework/include/celix/Trackers.h +++ b/libs/framework/include/celix/Trackers.h @@ -447,7 +447,7 @@ namespace celix { long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L); long svcRanking = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_RANKING, 0); auto svc = std::shared_ptr{static_cast(voidSvc), [](I*){/*nop*/}}; - auto props = celix::Properties::wrap(cProps); + auto props = std::make_shared(celix::Properties::wrap(cProps)); auto owner = std::make_shared(const_cast(cBnd)); return std::make_shared(svcId, svcRanking, svc, props, owner); } diff --git a/libs/framework/include/celix/UseServiceBuilder.h b/libs/framework/include/celix/UseServiceBuilder.h index 1e86cee6c..5f37f95af 100644 --- a/libs/framework/include/celix/UseServiceBuilder.h +++ b/libs/framework/include/celix/UseServiceBuilder.h @@ -164,10 +164,10 @@ namespace celix { func(*svc); } for (const auto& func : builder->callbacksWithProperties) { - func(*svc, *props); + func(*svc, props); } for (const auto& func : builder->callbacksWithOwner) { - func(*svc, *props, bnd); + func(*svc, props, bnd); } }; diff --git a/libs/framework/include/celix/dm/DependencyManager_Impl.h b/libs/framework/include/celix/dm/DependencyManager_Impl.h index 76157a4a6..e364d9f10 100644 --- a/libs/framework/include/celix/dm/DependencyManager_Impl.h +++ b/libs/framework/include/celix/dm/DependencyManager_Impl.h @@ -174,10 +174,8 @@ static celix::dm::DependencyManagerInfo createDepManInfoFromC(celix_dependency_m auto* cIntInfo = static_cast(celix_arrayList_get(cCmpInfo->interfaces, k)); celix::dm::InterfaceInfo intInfo{}; intInfo.serviceName = std::string{cIntInfo->name}; - const char* key; - CELIX_PROPERTIES_FOR_EACH(cIntInfo->properties, key) { - const char* val =celix_properties_get(cIntInfo->properties, key, ""); - intInfo.properties[std::string{key}] = std::string{val}; + CELIX_PROPERTIES_ITERATE(cIntInfo->properties, iter) { + intInfo.properties[std::string{iter.key}] = std::string{iter.entry.value}; } cmpInfo.interfacesInfo.emplace_back(std::move(intInfo)); } diff --git a/libs/framework/include/celix/dm/ServiceDependency_Impl.h b/libs/framework/include/celix/dm/ServiceDependency_Impl.h index 8fc501045..566ad53ed 100644 --- a/libs/framework/include/celix/dm/ServiceDependency_Impl.h +++ b/libs/framework/include/celix/dm/ServiceDependency_Impl.h @@ -237,20 +237,8 @@ void CServiceDependency::setupCallbacks() { template int CServiceDependency::invokeCallback(std::function fp, const celix_properties_t *props, const void* service) { - Properties properties {}; - const char* key {nullptr}; - const char* value {nullptr}; - - if (props != nullptr) { - CELIX_PROPERTIES_FOR_EACH(props, key) { - value = celix_properties_get(props, key, ""); //note. C++ does not allow nullptr entries for std::string - //std::cout << "got property " << key << "=" << value << "\n"; - properties[key] = value; - } - } - + auto properties = Properties::copy(props); const I* srv = (const I*) service; - fp(srv, std::move(properties)); return 0; } @@ -504,19 +492,7 @@ ServiceDependency& ServiceDependency::setStrategy(DependencyUpdateStra template int ServiceDependency::invokeCallback(std::function fp, const celix_properties_t *props, const void* service) { I *svc = (I*)service; - - Properties properties {}; - const char* key {nullptr}; - const char* value {nullptr}; - - if (props != nullptr) { - CELIX_PROPERTIES_FOR_EACH(props, key) { - value = celix_properties_get(props, key, ""); - //std::cout << "got property " << key << "=" << value << "\n"; - properties[key] = value; - } - } - + auto properties = celix::Properties::wrap(props); fp(svc, std::move(properties)); //explicit move of lvalue properties. return 0; } @@ -539,7 +515,7 @@ void ServiceDependency::setupCallbacks() { std::weak_ptr replacedSvc = dep->setService.first; std::weak_ptr replacedProps = dep->setService.second; auto svc = std::shared_ptr{static_cast(rawSvc), [](I*){/*nop*/}}; - auto props = rawProps ? celix::Properties::wrap(rawProps) : nullptr; + auto props = rawProps ? std::make_shared(celix::Properties::wrap(rawProps)) : nullptr; dep->setService = std::make_pair(std::move(svc), std::move(props)); dep->setFpUsingSharedPtr(dep->setService.first, dep->setService.second); dep->waitForExpired(replacedSvc, svcId, "service pointer"); @@ -556,7 +532,7 @@ void ServiceDependency::setupCallbacks() { rc = dep->invokeCallback(dep->addFp, rawProps, rawSvc); } if (dep->addFpUsingSharedPtr) { - auto props = celix::Properties::wrap(rawProps); + auto props = std::make_shared(celix::Properties::wrap(rawProps)); auto svc = std::shared_ptr{static_cast(rawSvc), [](I*){/*nop*/}}; auto svcId = props->getAsLong(celix::SERVICE_ID, -1); dep->addFpUsingSharedPtr(svc, props); diff --git a/libs/framework/src/celix_launcher.c b/libs/framework/src/celix_launcher.c index ca0b1d1b3..532e2c20f 100644 --- a/libs/framework/src/celix_launcher.c +++ b/libs/framework/src/celix_launcher.c @@ -193,17 +193,15 @@ static void celixLauncher_printUsage(char* progName) { } static void celixLauncher_printProperties(celix_properties_t *embeddedProps, const char *configFile) { - const char *key = NULL; celix_properties_t *keys = celix_properties_create(); //only to store the keys printf("Embedded properties:\n"); if (embeddedProps == NULL || celix_properties_size(embeddedProps) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(embeddedProps, key) { - const char *val = celix_properties_get(embeddedProps, key, "!Error!"); - printf("|- %s=%s\n", key, val); - celix_properties_set(keys, key, NULL); + CELIX_PROPERTIES_ITERATE(embeddedProps, visit) { + printf("|- %s=%s\n", visit.key, visit.entry.value); + celix_properties_set(keys, visit.key, NULL); } } printf("\n"); @@ -216,11 +214,10 @@ static void celixLauncher_printProperties(celix_properties_t *embeddedProps, con if (runtimeProps == NULL || celix_properties_size(runtimeProps) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(runtimeProps, key) { - const char *val = celix_properties_get(runtimeProps, key, "!Error!"); - printf("|- %s=%s\n", key, val); - celix_properties_set(keys, key, NULL); - } + CELIX_PROPERTIES_ITERATE(runtimeProps, visit) { + printf("|- %s=%s\n", visit.key, visit.entry.value); + celix_properties_set(keys, visit.key, NULL); + } } printf("\n"); @@ -229,13 +226,13 @@ static void celixLauncher_printProperties(celix_properties_t *embeddedProps, con if (celix_properties_size(keys) == 0) { printf("|- Empty!\n"); } else { - CELIX_PROPERTIES_FOR_EACH(keys, key) { - const char *valEm = celix_properties_get(embeddedProps, key, NULL); - const char *valRt = celix_properties_get(runtimeProps, key, NULL); - const char *envVal = getenv(key); + CELIX_PROPERTIES_ITERATE(keys, visit) { + const char *valEm = celix_properties_get(embeddedProps, visit.key, NULL); + const char *valRt = celix_properties_get(runtimeProps, visit.key, NULL); + const char *envVal = getenv(visit.key); const char *val = envVal != NULL ? envVal : valRt != NULL ? valRt : valEm; const char *source = envVal != NULL ? "environment" : valRt != NULL ? "runtime" : "embedded"; - printf("|- %s=%s (source %s)\n", key, val, source); + printf("|- %s=%s (source %s)\n", visit.key, val, source); } } printf("\n"); @@ -279,9 +276,8 @@ static int celixLauncher_createBundleCache(celix_properties_t* embeddedPropertie static void celixLauncher_combineProperties(celix_properties_t *original, const celix_properties_t *append) { if (original != NULL && append != NULL) { - const char *key = NULL; - CELIX_PROPERTIES_FOR_EACH(append, key) { - celix_properties_set(original, key, celix_properties_get(append, key, NULL)); + CELIX_PROPERTIES_ITERATE(append, visit) { + celix_properties_setEntry(original, visit.key, &visit.entry); } } } diff --git a/libs/framework/src/manifest.c b/libs/framework/src/manifest.c index 4f0884e90..1ba4f9abf 100644 --- a/libs/framework/src/manifest.c +++ b/libs/framework/src/manifest.c @@ -72,9 +72,8 @@ manifest_pt manifest_clone(manifest_pt manifest) { celix_auto(manifest_pt) clone = NULL; status = manifest_create(&clone); if (status == CELIX_SUCCESS) { - const char* key = NULL; - CELIX_PROPERTIES_FOR_EACH(manifest->mainAttributes, key) { - celix_properties_set(clone->mainAttributes, key, celix_properties_get(manifest->mainAttributes, key, NULL)); + CELIX_PROPERTIES_ITERATE(manifest->mainAttributes, visit) { + celix_properties_set(clone->mainAttributes, visit.key, visit.entry.value); } hash_map_iterator_t iter = hashMapIterator_construct(manifest->attributes); while (hashMapIterator_hasNext(&iter)) { diff --git a/libs/rcm/src/celix_capability.c b/libs/rcm/src/celix_capability.c index 93aeaf5b2..35be9a717 100644 --- a/libs/rcm/src/celix_capability.c +++ b/libs/rcm/src/celix_capability.c @@ -83,11 +83,9 @@ bool celix_capability_equals(const celix_capability_t* cap1, const celix_capabil //compare attributes bool equals = true; - const char* visit; - CELIX_PROPERTIES_FOR_EACH(cap1->attributes, visit) { - const char* value1 = celix_properties_get(cap1->attributes, visit, NULL); - const char* value2 = celix_properties_get(cap2->attributes, visit, NULL); - if (!celix_utils_stringEquals(value1, value2)) { + CELIX_PROPERTIES_ITERATE(cap1->attributes, visit) { + const char* value2 = celix_properties_get(cap2->attributes, visit.key, NULL); + if (!celix_utils_stringEquals(visit.entry.value, value2)) { equals = false; break; } @@ -95,10 +93,9 @@ bool celix_capability_equals(const celix_capability_t* cap1, const celix_capabil if (!equals) { return false; } - CELIX_PROPERTIES_FOR_EACH(cap1->directives, visit) { - const char* value1 = celix_properties_get(cap1->directives, visit, NULL); - const char* value2 = celix_properties_get(cap2->directives, visit, NULL); - if (!celix_utils_stringEquals(value1, value2)) { + CELIX_PROPERTIES_ITERATE(cap1->directives, visit) { + const char* value2 = celix_properties_get(cap2->directives, visit.key, NULL); + if (!celix_utils_stringEquals(visit.entry.value, value2)) { equals = false; break; } @@ -108,17 +105,13 @@ bool celix_capability_equals(const celix_capability_t* cap1, const celix_capabil unsigned int celix_capability_hashCode(const celix_capability_t* cap) { unsigned int hash = celix_utils_stringHash(cap->ns); - const char* visit; - - CELIX_PROPERTIES_FOR_EACH(cap->attributes, visit) { - const char* value = celix_properties_get(cap->attributes, visit, NULL); - hash += celix_utils_stringHash(visit); - hash += celix_utils_stringHash(value); + CELIX_PROPERTIES_ITERATE(cap->attributes, visit) { + hash += celix_utils_stringHash(visit.key); + hash += celix_utils_stringHash(visit.entry.value); } - CELIX_PROPERTIES_FOR_EACH(cap->directives, visit) { - const char* value = celix_properties_get(cap->directives, visit, NULL); - hash += celix_utils_stringHash(visit); - hash += celix_utils_stringHash(value); + CELIX_PROPERTIES_ITERATE(cap->directives, visit) { + hash += celix_utils_stringHash(visit.key); + hash += celix_utils_stringHash(visit.entry.value); } return hash; } @@ -152,10 +145,8 @@ void celix_capability_addAttribute(celix_capability_t* cap, const char* key, con } void celix_capability_addAttributes(celix_capability_t* cap, const celix_properties_t* attributes) { - const char* visit; - CELIX_PROPERTIES_FOR_EACH(attributes, visit) { - const char* value = celix_properties_get(attributes, visit, NULL); - celix_properties_set(cap->attributes, visit, value); + CELIX_PROPERTIES_ITERATE(attributes, visit) { + celix_properties_set(cap->attributes, visit.key, visit.entry.value); } } @@ -164,9 +155,7 @@ void celix_capability_addDirective(celix_capability_t* cap, const char* key, con } void celix_capability_addDirectives(celix_capability_t* cap, const celix_properties_t* directives) { - const char* visit; - CELIX_PROPERTIES_FOR_EACH(directives, visit) { - const char* value = celix_properties_get(directives, visit, NULL); - celix_properties_set(cap->directives, visit, value); + CELIX_PROPERTIES_ITERATE(directives, visit) { + celix_properties_set(cap->directives, visit.key, visit.entry.value); } } diff --git a/libs/rcm/src/celix_requirement.c b/libs/rcm/src/celix_requirement.c index 48f3dd452..a6480ca7c 100644 --- a/libs/rcm/src/celix_requirement.c +++ b/libs/rcm/src/celix_requirement.c @@ -89,45 +89,33 @@ bool celix_requirement_equals(const celix_requirement_t* req1, const celix_requi } //compare attributes - bool equals = true; - const char* visit; - CELIX_PROPERTIES_FOR_EACH(req1->attributes, visit) { - const char* val1 = celix_properties_get(req1->attributes, visit, NULL); - const char* val2 = celix_properties_get(req2->attributes, visit, NULL); - if (!celix_utils_stringEquals(val1, val2)) { - equals = false; - break; + CELIX_PROPERTIES_ITERATE(req1->attributes, visit) { + const char* val2 = celix_properties_get(req2->attributes, visit.key, NULL); + if (!celix_utils_stringEquals(visit.entry.value, val2)) { + return false; } } - if (!equals) { - return false; - } //compare directives - CELIX_PROPERTIES_FOR_EACH(req1->directives, visit) { - const char* val1 = celix_properties_get(req1->directives, visit, NULL); - const char* val2 = celix_properties_get(req2->directives, visit, NULL); - if (!celix_utils_stringEquals(val1, val2)) { - equals = false; - break; + CELIX_PROPERTIES_ITERATE(req1->directives, visit) { + const char* val2 = celix_properties_get(req2->directives, visit.key, NULL); + if (!celix_utils_stringEquals(visit.entry.value, val2)) { + return false; } } - return equals; + + return true; } unsigned int celix_requirement_hashCode(const celix_requirement_t* req) { unsigned int hash = celix_utils_stringHash(req->ns); - const char* visit; - - CELIX_PROPERTIES_FOR_EACH(req->attributes, visit) { - const char* val = celix_properties_get(req->attributes, visit, NULL); - hash += celix_utils_stringHash(visit); - hash += celix_utils_stringHash(val); + CELIX_PROPERTIES_ITERATE(req->attributes, visit) { + hash += celix_utils_stringHash(visit.key); + hash += celix_utils_stringHash(visit.entry.value); } - CELIX_PROPERTIES_FOR_EACH(req->directives, visit) { - const char* val = celix_properties_get(req->directives, visit, NULL); - hash += celix_utils_stringHash(visit); - hash += celix_utils_stringHash(val); + CELIX_PROPERTIES_ITERATE(req->directives, visit) { + hash += celix_utils_stringHash(visit.key); + hash += celix_utils_stringHash(visit.entry.value); } return hash; } @@ -169,17 +157,13 @@ void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, c } void celix_requirement_addDirectives(celix_requirement_t* req, const celix_properties_t* directives) { - const char* visit; - CELIX_PROPERTIES_FOR_EACH(directives, visit) { - const char* val = celix_properties_get(directives, visit, NULL); - celix_requirement_addDirective(req, visit, val); + CELIX_PROPERTIES_ITERATE(directives, visit) { + celix_requirement_addDirective(req, visit.key, visit.entry.value); } } void celix_requirement_addAttributes(celix_requirement_t* req, const celix_properties_t* attributes) { - const char* visit; - CELIX_PROPERTIES_FOR_EACH(attributes, visit) { - const char* val = celix_properties_get(attributes, visit, NULL); - celix_requirement_addAttribute(req, visit, val); + CELIX_PROPERTIES_ITERATE(attributes, visit) { + celix_requirement_addAttribute(req, visit.key, visit.entry.value); } } diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 7d4f35323..106040866 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -104,8 +104,23 @@ TEST_F(CxxPropertiesTestSuite, WrapTest) { EXPECT_EQ(1, celix_properties_size(props)); { auto cxxProps = celix::Properties::wrap(props); - EXPECT_EQ(1, cxxProps->size()); - EXPECT_EQ(props, cxxProps->getCProperties()); + EXPECT_EQ(1, cxxProps.size()); + EXPECT_EQ(props, cxxProps.getCProperties()); + } //NOTE cxxProps out of scope, but will not destroy celix_properties + EXPECT_EQ(1, celix_properties_size(props)); + + celix_properties_destroy(props); +} + +TEST_F(CxxPropertiesTestSuite, CopyCPropsTest) { + auto *props = celix_properties_create(); + celix_properties_set(props, "test", "test"); + + EXPECT_EQ(1, celix_properties_size(props)); + { + auto cxxProps = celix::Properties::copy(props); + EXPECT_EQ(1, cxxProps.size()); + EXPECT_NE(props, cxxProps.getCProperties()); } //NOTE cxxProps out of scope, but will not destroy celix_properties EXPECT_EQ(1, celix_properties_size(props)); diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 40a36f59e..c03dcc383 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -316,9 +316,8 @@ TEST_F(PropertiesTestSuite, SizeAndIteratorTest) { EXPECT_EQ(4, celix_properties_size(props)); int count = 0; - const char *key; - CELIX_PROPERTIES_FOR_EACH(props, key) { - EXPECT_NE(key, nullptr); + CELIX_PROPERTIES_ITERATE(props, entry) { + EXPECT_NE(entry.key, nullptr); count++; } EXPECT_EQ(4, count); @@ -399,26 +398,6 @@ TEST_F(PropertiesTestSuite, GetEntryTest) { celix_properties_destroy(props); } -TEST_F(PropertiesTestSuite, IteratorNextKeyTest) { - auto* props = celix_properties_create(); - celix_properties_set(props, "key1", "value1"); - celix_properties_set(props, "key2", "value2"); - celix_properties_set(props, "key3", "value3"); - auto iter = celix_propertiesIterator_construct(props); - const char* key; - int count = 0; - while (celix_propertiesIterator_hasNext(&iter)) { - key = celix_propertiesIterator_nextKey(&iter); - EXPECT_NE(strstr(key, "key"), nullptr); - count++; - } - EXPECT_EQ(count, 3); - key = celix_propertiesIterator_nextKey(&iter); - EXPECT_EQ(nullptr, key) << "got key: " << key; - - celix_properties_destroy(props); -} - TEST_F(PropertiesTestSuite, IteratorNextTest) { auto* props = celix_properties_create(); celix_properties_set(props, "key1", "value1"); @@ -559,6 +538,32 @@ TEST_F(PropertiesTestSuite, SetWithCopyTest) { celix_properties_destroy(props); } +TEST_F(PropertiesTestSuite, SetEntryTest) { + auto* props1 = celix_properties_create(); + auto* props2 = celix_properties_create(); + celix_properties_set(props1, "key1", "value1"); + celix_properties_setLong(props1, "key2", 123); + celix_properties_setBool(props1, "key3", true); + celix_properties_setDouble(props1, "key4", 3.14); + auto* version = celix_version_create(1, 2, 3, nullptr); + celix_properties_setVersion(props1, "key5", version); + celix_version_destroy(version); + + CELIX_PROPERTIES_ITERATE(props1, visit) { + celix_properties_setEntry(props2, visit.key, &visit.entry); + } + + EXPECT_EQ(5, celix_properties_size(props2)); + EXPECT_STREQ("value1", celix_properties_get(props2, "key1", nullptr)); + EXPECT_EQ(123, celix_properties_getAsLong(props2, "key2", -1L)); + EXPECT_EQ(true, celix_properties_getAsBool(props2, "key3", false)); + EXPECT_EQ(3.14, celix_properties_getAsDouble(props2, "key4", -1.0)); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props2, "key5")); + + celix_properties_destroy(props1); + celix_properties_destroy(props2); +} + TEST_F(PropertiesTestSuite, DeprecatedApiTest) { //Check if the deprecated api can still be used auto* props = properties_create(); diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index e451ddc1d..6e7a2c312 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -242,9 +242,17 @@ namespace celix { * @brief Wrap C properties and returns it as const in a shared_ptr, * but does not take ownership -> dtor will not destroy C properties. */ - static std::shared_ptr wrap(const celix_properties_t* wrapProps) { + static const Properties wrap(const celix_properties_t* wrapProps) { auto* cp = const_cast(wrapProps); - return std::shared_ptr{new Properties{cp, false}}; + return Properties{cp, false}; + } + + /** + * @brief Wrap C properties and returns it as const in a shared_ptr, + * but does not take ownership -> dtor will not destroy C properties. + */ + static const Properties wrap(celix_properties_t* wrapProps) { + return Properties{wrapProps, false}; } /** @@ -254,6 +262,13 @@ namespace celix { return Properties{wrapProps, true}; } + /** + * @brief Copy C properties and take ownership -> dtor will destroy C properties. + */ + static Properties copy(const celix_properties_t* copyProps) { + return Properties{celix_properties_copy(copyProps), true}; + } + /** * Get the C properties object. * diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index c839473ac..bf9ff2484 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -70,7 +70,7 @@ typedef enum celix_properties_value_type { } celix_properties_value_type_e; /** - * @brief A structure representing a single entry in a property set. + * @brief A structure representing a single value entry in a property set. */ typedef struct celix_properties_entry { const char* value; /**< The string value or string representation of a non-string @@ -221,6 +221,15 @@ CELIX_UTILS_EXPORT void celix_properties_set(celix_properties_t* properties, con */ CELIX_UTILS_EXPORT void celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char *value); +/** + * @brief Set the value of a property based on the provided property entry, maintaining underlying type. + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] entry The entry to set the property to. The entry will be copied, so it can be freed after calling + * this function. + */ +CELIX_UTILS_EXPORT void celix_properties_setEntry(celix_properties_t* properties, const char* key, const celix_properties_entry_t* entry); + /** * @brief Unset a property, removing it from the property set. * @@ -442,75 +451,6 @@ CELIX_UTILS_EXPORT bool celix_propertiesIterator_equals(const celix_properties_i for (celix_properties_iterator_t iterName = celix_properties_begin((props)); \ !celix_propertiesIterator_isEnd(&(iterName)); celix_propertiesIterator_next(&(iterName))) - -/**** Deprecated API *************************************************************************************************/ - -/** - * @brief Constructs a new properties iterator. - * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. - * - * Note: The iterator is initialized to be before the first entry. To advance to the first entry, - * call `celix_propertiesIterator_nextEntry`. - * - * @param[in] properties The properties object to iterate over. - * @return The newly constructed iterator. - */ -CELIX_UTILS_DEPRECATED_EXPORT celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t* properties); - -/** - * @brief Determines whether the iterator has more entries. - * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. - * - * @param[in] iter The iterator. - * @return true if the iterator has more entries, false otherwise. - */ -CELIX_UTILS_DEPRECATED_EXPORT bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); - -/** - * @brief Advances the iterator to the next entry and returns the key for the current entry. - * @deprecated This function is deprecated, use celix_properties_begin, celix_propertiesIterator_next and celix_propertiesIterator_isEnd instead. - * - * If the iterator has no more entries, this function returns NULL. - * - * @param[in, out] iter The iterator. - * @return The key for the current entry, or NULL if the iterator has no more entries. - */ -CELIX_UTILS_DEPRECATED_EXPORT const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* iter); - -/** - * @brief Macro for iterating over the properties in a property set. - * @deprecated This macro is deprecated, use CELIX_PROPERTIES_ITERATE instead. - * - * @param[in] properties The property set to iterate over. - * @param[out] key The variable to use for the current key in the loop. - * - * - * Example usage: - * @code{.c} - * celix_properties_t* properties = celix_properties_create(); - * celix_properties_set(properties, "key1", "value1"); - * celix_properties_set(properties, "key2", "value2"); - * celix_properties_set(properties, "key3", "value3"); - * - * const char* key; - * CELIX_PROPERTIES_FOR_EACH(properties, key) { - * printf("%s = %s\n", key, celix_properties_get(properties, key, "")); - * } - * @endcode - * - * Output: - * @code{.c} - * key1 = value1 - * key2 = value2 - * key3 = value3 - * @endcode -*/ -#define CELIX_PROPERTIES_FOR_EACH(props, key) _CELIX_PROPERTIES_FOR_EACH(CELIX_UNIQUE_ID(iter), props, key) - -#define _CELIX_PROPERTIES_FOR_EACH(iter, props, key) \ - for(celix_properties_iterator_t iter = celix_propertiesIterator_construct(props); \ - celix_propertiesIterator_hasNext(&iter), (key) = celix_propertiesIterator_nextKey(&iter);) - #ifdef __cplusplus } #endif diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 6b208c3ab..7514c210e 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -317,6 +317,7 @@ static void celix_properties_createAndSetEntry( } celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue, boolValue, versionValue); + const char* mapKey = key; if (!celix_stringHashMap_hasKey(properties->map, key)) { //new entry, needs new allocated key; @@ -657,6 +658,28 @@ void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, } } +void celix_properties_setEntry(celix_properties_t* properties, const char* key, const celix_properties_entry_t* entry) { + if (properties != NULL && key != NULL && entry != NULL) { + switch (entry->valueType) { + case CELIX_PROPERTIES_VALUE_TYPE_LONG: + celix_properties_setLong(properties, key, entry->typed.longValue); + break; + case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE: + celix_properties_setDouble(properties, key, entry->typed.doubleValue); + break; + case CELIX_PROPERTIES_VALUE_TYPE_BOOL: + celix_properties_setBool(properties, key, entry->typed.boolValue); + break; + case CELIX_PROPERTIES_VALUE_TYPE_VERSION: + celix_properties_setVersion(properties, key, entry->typed.versionValue); + break; + default: //STRING + celix_properties_set(properties, key, entry->value); + break; + } + } +} + void celix_properties_unset(celix_properties_t *properties, const char *key) { if (properties != NULL) { celix_stringHashMap_remove(properties->map, key); @@ -770,49 +793,6 @@ typedef struct { const celix_properties_t* props; } celix_properties_iterator_internal_t; -celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties) { - celix_properties_iterator_internal_t internalIter; - internalIter.mapIter = celix_stringHashMap_begin(properties->map); - internalIter.props = properties; - - celix_properties_iterator_t iter; - iter.index = 0; - iter.key = NULL; - memset(&iter.entry, 0, sizeof(iter.entry)); - - CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(iter._data)); - memset(&iter._data, 0, sizeof(iter._data)); - memcpy(iter._data, &internalIter, sizeof(internalIter)); - return iter; -} - -bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { - celix_properties_iterator_internal_t internalIter; - memcpy(&internalIter, iter->_data, sizeof(internalIter)); - return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter); -} - -const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) { - celix_properties_iterator_internal_t internalIter; - memcpy(&internalIter, iter->_data, sizeof(internalIter)); - - //note assigning key first and then move the next, because celix string hash map iter start at the beginning - const char* key = internalIter.mapIter.key; - iter->index = (int)internalIter.mapIter.index; - celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue; - if (entry != NULL) { - iter->key = internalIter.mapIter.key; - memcpy(&iter->entry, entry, sizeof(iter->entry)); - } else { - iter->key = NULL; - memset(&iter->entry, 0, sizeof(iter->entry)); - } - celix_stringHashMapIterator_next(&internalIter.mapIter); - - memcpy(iter->_data, &internalIter, sizeof(internalIter)); - return key; -} - celix_properties_iterator_t celix_properties_begin(const celix_properties_t* properties) { CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(celix_properties_iterator_t)); celix_properties_iterator_internal_t internalIter;