This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/type_support_for_properties
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 1ff603ba0702464c928bd9c22475bf834628f85b
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sun Jan 1 20:13:06 2023 +0100

    Refactor celix properties to be based on celix_string_hash_map.
---
 .../pubsub_discovery/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
 libs/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(-)

diff --git a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c 
b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c
index c6d1aa95..4815bd92 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 d2e2421f..66580b5a 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 31bddb24..656b3773 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 b72cb95d..2d8c2bc9 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 f834ac52..691e72af 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 92bc86b6..72bfbd57 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 18302351..a7faebd7 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 e8f51369..6de893b6 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 7440357c..dee276f7 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 01241b99..d5acac77 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 613f6ef2..62bfa8dd 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     <a href="mailto:[email protected]";>Apache Celix Project 
Team</a>
- *  \copyright  Apache License, Version 2.0
- */
-
-#include <stdlib.h>
-#include <stdio.h>
 
-#include "CppUTest/TestHarness.h"
-#include "CppUTest/TestHarness_c.h"
-#include "CppUTest/CommandLineTestRunner.h"
-#include "CppUTestExt/MockSupport.h"
+#include <gtest/gtest.h>
 
-extern "C" {
-#include <string.h>
-
-#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 4e56860c..3796b004 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 3ff1e906..c704ab60 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 762fcff3..edd1b3cd 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 <stdio.h>
 #include <stdbool.h>
 
@@ -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 c402be4c..97c1735b 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 00e9c785..8bcbc3db 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 00000000..a8321124
--- /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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#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 4ebb938c..7bbe2f4c 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;
     }
 }
 

Reply via email to