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 6156c7028b951d4e191653d75b015a3a6f373997 Author: Pepijn Noltes <[email protected]> AuthorDate: Tue Jan 3 00:00:25 2023 +0100 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(-) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 6de893b6..475b7da3 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 dee276f7..1e512fe6 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 62bfa8dd..b2980282 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 00000000..a0874ecf --- /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 <gtest/gtest.h> + +#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 3796b004..da653616 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 edd1b3cd..3c876697 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 <stdio.h> -#include <stdbool.h> #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 a5b7073d..be03aeef 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -27,18 +27,18 @@ extern "C" { #include <stdbool.h> /** - * 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 - * <code>null</code> 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 + * <code>NULL</code> 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 <code>version</code>. + * @brief Create a copy of <code>version</code>. * - * @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. * * <p> * 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 <code>Version</code> object to another object. + * @brief Compare this <code>Version</code> object to another object. * * <p> * A version is considered to be <b>less than </b> 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 <code>version</code> identifier. + * @brief Return the string representation of <code>version</code> identifier. * * <p> * The format of the version string will be <code>major.minor.micro</code> @@ -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. * * <p> * 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. * * <p> * 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 1fa59e71..00000000 --- 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 <string.h> - -#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 a8321124..af8b892f 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -26,6 +26,7 @@ #include <ctype.h> #include <stdbool.h> #include <errno.h> +#include <assert.h> #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 f67bacc1..f83fe655 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) {
