From 189ffc6af272535a79c811f02d3fe5d1378925da Mon Sep 17 00:00:00 2001 From: Jacek Pielaszkiewicz Date: Fri, 13 Jun 2014 11:09:38 +0200 Subject: [PATCH 2/7] Initial repository spec file. Change-Id: I4adf7ae50df656a822ccf5ecec7e298a493ea5e9 Signed-off-by: Jacek Pielaszkiewicz --- packaging/libConfig.manifest | 5 +++++ packaging/libConfig.spec | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 packaging/libConfig.manifest create mode 100644 packaging/libConfig.spec diff --git a/packaging/libConfig.manifest b/packaging/libConfig.manifest new file mode 100644 index 0000000..2a0cec5 --- /dev/null +++ b/packaging/libConfig.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/libConfig.spec b/packaging/libConfig.spec new file mode 100644 index 0000000..45a604f --- /dev/null +++ b/packaging/libConfig.spec @@ -0,0 +1,50 @@ +Name: libConfig +Version: 0.0.1 +Release: 0 +Source0: %{name}-%{version}.tar.gz +License: Apache-2.0 +Group: Security/Other +Summary: libConfig library. + +%description +The package provides libConfig library. + +%files +%manifest packaging/libConfig.manifest +%defattr(644,root,root,755) + +%package devel +Summary: libConfig development +Group: Development/Libraries + +%description devel +The package provides libConfig development tools and libs. + +%files devel +%defattr(644,root,root,755) + +%package tests +Summary: libConfig tests +Group: Development/Libraries + +%description tests +The package provides libConfig test tools. + +%files tests +%defattr(644,root,root,755) + +%prep +%setup -q + +%build + +%install + +%clean +rm -rf %{buildroot} + +%post + +%preun + +%postun -- 2.7.4 From 788dcdef0c508e820882f30ca23025efac5b9a68 Mon Sep 17 00:00:00 2001 From: Jacek Pielaszkiewicz Date: Tue, 17 Jun 2014 10:28:19 +0200 Subject: [PATCH 3/7] Initial library content. [Bug/Feature] Initial library content. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I0de77757b11c87099b5b0d915e30aeeff1becf61 Signed-off-by: Jacek Pielaszkiewicz --- CMakeLists.txt | 66 ++++++++++++++++++ packaging/libConfig.spec | 49 +++++++------- src/CMakeLists.txt | 35 ++++++++++ src/config/config.hpp | 59 ++++++++++++++++ src/config/exception.hpp | 44 ++++++++++++ src/config/fields.hpp | 76 +++++++++++++++++++++ src/config/from-json-visitor.hpp | 141 +++++++++++++++++++++++++++++++++++++++ src/config/is-visitable.hpp | 53 +++++++++++++++ src/config/manager.hpp | 127 +++++++++++++++++++++++++++++++++++ src/config/to-json-visitor.hpp | 122 +++++++++++++++++++++++++++++++++ src/libConfig.pc.in | 8 +++ 11 files changed, 757 insertions(+), 23 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/config/config.hpp create mode 100644 src/config/exception.hpp create mode 100644 src/config/fields.hpp create mode 100644 src/config/from-json-visitor.hpp create mode 100644 src/config/is-visitable.hpp create mode 100644 src/config/manager.hpp create mode 100644 src/config/to-json-visitor.hpp create mode 100644 src/libConfig.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2118f07 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed 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. +# +# +# @file CMakeLists.txt +# @author Jacek Pielaszkiewicz (j.pielaszkie@samsung.com) +# +CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) +PROJECT(Config) + +## pkgconfig ################################################################### +INCLUDE(FindPkgConfig) + +## default CMAKE_INSTALL_* variables ########################################### +INCLUDE(GNUInstallDirs) + +## Compiler flags, depending on the build type ################################# +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "DEBUG") +ENDIF(NOT CMAKE_BUILD_TYPE) +MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# special case for a GCC < 4.7, assume rest is fine +IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) + SET(CXX_11_STD "c++0x") +else() + SET(CXX_11_STD "c++11") +endif() + +SET(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg") +SET(CMAKE_CXX_FLAGS_PROFILING "-g -std=${CXX_11_STD} -O0 -pg") +SET(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb") +SET(CMAKE_CXX_FLAGS_DEBUG "-g -std=${CXX_11_STD} -O0 -ggdb") +SET(CMAKE_C_FLAGS_RELEASE "-g -O2 -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELEASE "-g -std=${CXX_11_STD} -O2 -DNDEBUG") +SET(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") +SET(CMAKE_CXX_FLAGS_CCOV "-g -std=${CXX_11_STD} -O2 --coverage") + +ADD_DEFINITIONS("-fPIC") # Position Independent Code +ADD_DEFINITIONS("-Werror") # Make all warnings into errors +ADD_DEFINITIONS("-Wall") # Generate all warnings +ADD_DEFINITIONS("-Wextra") # Generate even more extra warnings +ADD_DEFINITIONS("-pedantic") # Be pedantic +ADD_DEFINITIONS("-pedantic-errors") # Make pedantic warnings into errors +ADD_DEFINITIONS(-DPROGRAM_VERSION="${VERSION}") +ADD_DEFINITIONS(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") + +## Subdirectories ############################################################## +SET(SRC_FOLDER ${PROJECT_SOURCE_DIR}/src) + +IF(NOT DEFINED INCLUDE_INSTALL_DIR) + SET(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}) +ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR) + +ADD_SUBDIRECTORY(${SRC_FOLDER}) diff --git a/packaging/libConfig.spec b/packaging/libConfig.spec index 45a604f..f65c1fb 100644 --- a/packaging/libConfig.spec +++ b/packaging/libConfig.spec @@ -1,50 +1,53 @@ +%define lib_version 0.0.1 + Name: libConfig -Version: 0.0.1 +Version: %{lib_version} Release: 0 Source0: %{name}-%{version}.tar.gz License: Apache-2.0 Group: Security/Other -Summary: libConfig library. +Summary: Config library + +BuildRequires: cmake %description The package provides libConfig library. -%files -%manifest packaging/libConfig.manifest -%defattr(644,root,root,755) - %package devel -Summary: libConfig development +Summary: Development package for config library Group: Development/Libraries +Requires: boost-devel +Requires: pkgconfig(libLogger) +Requires: libjson-devel %description devel The package provides libConfig development tools and libs. %files devel %defattr(644,root,root,755) - -%package tests -Summary: libConfig tests -Group: Development/Libraries - -%description tests -The package provides libConfig test tools. - -%files tests -%defattr(644,root,root,755) +%{_includedir}/sc-tools +%{_libdir}/pkgconfig/*.pc %prep %setup -q %build +%{!?build_type:%define build_type "RELEASE"} -%install +%if %{build_type} == "DEBUG" || %{build_type} == "PROFILING" + CFLAGS="$CFLAGS -Wp,-U_FORTIFY_SOURCE" + CXXFLAGS="$CXXFLAGS -Wp,-U_FORTIFY_SOURCE" +%endif -%clean -rm -rf %{buildroot} +%cmake . \ + -D_LIB_VERSION_=%{lib_version} \ + -DVERSION=%{version} \ + -DCMAKE_BUILD_TYPE=%{build_type} -%post +make -k %{?jobs:-j%jobs} -%preun +%install +%make_install -%postun +%clean +rm -rf %{buildroot} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..437e6ca --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed 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. +# +# +# @file CMakeLists.txt +# @author Jacek Pielaszkiewicz (j.pielaszkie@samsung.com) +# +SET(PC_FILE "${SRC_FOLDER}/lib${PROJECT_NAME}.pc") + +MESSAGE(STATUS "Generating makefile for the libConfig sources...") +FILE(GLOB HEADERS "${SRC_FOLDER}/config/*.hpp") +FILE(GLOB_RECURSE SRCS "${SRC_FOLDER}" *.hpp *.cpp) + +## Setup target ################################################################ + +## Link libraries ############################################################# +INCLUDE_DIRECTORIES(${SRC_FOLDER}) +INCLUDE_DIRECTORIES(SYSTEM ${CONFIG_DEPS_INCLUDE_DIRS}) + +## Install ##################################################################### +CONFIGURE_FILE(${PC_FILE}.in "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" @ONLY) + +INSTALL(FILES ${HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/sc-tools/config") +INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" DESTINATION "/usr/lib/pkgconfig") diff --git a/src/config/config.hpp b/src/config/config.hpp new file mode 100644 index 0000000..a65c2ad --- /dev/null +++ b/src/config/config.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com) + * @brief Configuration file for the code + */ + +#ifndef CONFIG_HPP +#define CONFIG_HPP + +#ifdef __clang__ +#define CLANG_VERSION (__clang__major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#endif // __clang__ + +#if defined __GNUC__ && !defined __clang__ // clang also defines GCC versions +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +#ifdef GCC_VERSION + +#if GCC_VERSION < 40800 +// GCC 4.8 is the first where those defines are not required for +// std::this_thread::sleep_for() and ::yield(). They might exist though +// in previous versions depending on the build configuration of the GCC. +#ifndef _GLIBCXX_USE_NANOSLEEP +#define _GLIBCXX_USE_NANOSLEEP +#endif // _GLIBCXX_USE_NANOSLEEP +#ifndef _GLIBCXX_USE_SCHED_YIELD +#define _GLIBCXX_USE_SCHED_YIELD +#endif // _GLIBCXX_USE_SCHED_YIELD +#endif // GCC_VERSION < 40800 + +#if GCC_VERSION < 40700 +// Those appeared in 4.7 with full c++11 support +#define final +#define override +#define thread_local __thread // use GCC extension instead of C++11 +#endif // GCC_VERSION < 40700 + +#endif // GCC_VERSION + +#endif // CONFIG_HPP diff --git a/src/config/exception.hpp b/src/config/exception.hpp new file mode 100644 index 0000000..6489c73 --- /dev/null +++ b/src/config/exception.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Lukasz Pawelczyk + * + * Licensed 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 + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com) + * @brief Exceptions for the configuration + */ + +#ifndef CONFIG_EXCEPTION_HPP +#define CONFIG_EXCEPTION_HPP + +#include + +namespace config { + +/** + * Base class for exceptions in configuration. + * Error occured during a config file parsing, + * e.g. syntax error + */ +struct ConfigException: public std::runtime_error { + + ConfigException(const std::string& error = "") : std::runtime_error(error) {} +}; + +} // namespace config + +#endif // CONFIG_EXCEPTION_HPP diff --git a/src/config/fields.hpp b/src/config/fields.hpp new file mode 100644 index 0000000..3269856 --- /dev/null +++ b/src/config/fields.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Macros for registering configuration fields + */ + +#ifndef CONFIG_FIELDS_HPP +#define CONFIG_FIELDS_HPP + +#include +#include + +#if BOOST_PP_VARIADICS != 1 +#error variadic macros not supported +#endif + +/** + * Use this macro to register config fields. + * + * Example: + * struct Foo + * { + * std::string bar; + * std::vector tab; + * + * // SubConfigA must also register config fields + * SubConfigA sub_a; + * + * // SubConfigB must also register config fields + * std::vector tab_sub; + * + * CONFIG_REGISTER + * ( + * bar, + * tab, + * sub_a + * ) + * }; + */ +#define CONFIG_REGISTER(...) \ + template \ + void accept(Visitor v) { \ + GENERATE_ELEMENTS__(__VA_ARGS__) \ + } \ + template \ + void accept(Visitor v) const { \ + GENERATE_ELEMENTS__(__VA_ARGS__) \ + } \ + +#define GENERATE_ELEMENTS__(...) \ + BOOST_PP_LIST_FOR_EACH(GENERATE_ELEMENT__, \ + _, \ + BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)) \ + +#define GENERATE_ELEMENT__(r, _, element) \ + v.visit(#element, element); \ + +#endif // CONFIG_FIELDS_HPP diff --git a/src/config/from-json-visitor.hpp b/src/config/from-json-visitor.hpp new file mode 100644 index 0000000..a3b502d --- /dev/null +++ b/src/config/from-json-visitor.hpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief JSON visitor + */ + +#ifndef CONFIG_FROM_JSON_VISITOR_HPP +#define CONFIG_FROM_JSON_VISITOR_HPP + +#include "config/is-visitable.hpp" +#include "config/exception.hpp" + +#include +#include +#include + +namespace config { + +class FromJsonVisitor { +public: + explicit FromJsonVisitor(const std::string& jsonString) + : mObject(nullptr) + { + mObject = json_tokener_parse(jsonString.c_str()); + if (mObject == nullptr) { + throw ConfigException("Json parsing error"); + } + } + + FromJsonVisitor(const FromJsonVisitor& visitor) + : mObject(json_object_get(visitor.mObject)) + { + } + + ~FromJsonVisitor() + { + json_object_put(mObject); + } + + template + void visit(const std::string& name, T& value) + { + json_object* object = nullptr; + if (!json_object_object_get_ex(mObject, name.c_str(), &object)) { + throw ConfigException("Missing field '" + name + "' in json"); + } + fromJsonObject(object, value); + } + +private: + json_object* mObject; + + FromJsonVisitor& operator=(const FromJsonVisitor&) = delete; + + explicit FromJsonVisitor(json_object* object) + : mObject(json_object_get(object)) + { + } + + static void checkType(json_object* object, json_type type) + { + if (type != json_object_get_type(object)) { + throw ConfigException("Invalid field type"); + } + } + + static void fromJsonObject(json_object* object, int& value) + { + checkType(object, json_type_int); + std::int64_t value64 = json_object_get_int64(object); + if (value64 > INT32_MAX || value64 < INT32_MIN) { + throw ConfigException("Value out of range"); + } + value = static_cast(value64); + } + + static void fromJsonObject(json_object* object, std::int64_t& value) + { + checkType(object, json_type_int); + value = json_object_get_int64(object); + } + + static void fromJsonObject(json_object* object, bool& value) + { + checkType(object, json_type_boolean); + value = json_object_get_boolean(object); + } + + static void fromJsonObject(json_object* object, double& value) + { + checkType(object, json_type_double); + value = json_object_get_double(object); + } + + static void fromJsonObject(json_object* object, std::string& value) + { + checkType(object, json_type_string); + value = json_object_get_string(object); + } + + template + static void fromJsonObject(json_object* object, std::vector& value) + { + checkType(object, json_type_array); + int length = json_object_array_length(object); + value.resize(static_cast(length)); + for (int i = 0; i < length; ++i) { + fromJsonObject(json_object_array_get_idx(object, i), value[static_cast(i)]); + } + } + + template::value>::type> + static void fromJsonObject(json_object* object, T& value) + { + checkType(object, json_type_object); + FromJsonVisitor visitor(object); + value.accept(visitor); + } +}; + +} // namespace config + +#endif // CONFIG_FROM_JSON_VISITOR_HPP diff --git a/src/config/is-visitable.hpp b/src/config/is-visitable.hpp new file mode 100644 index 0000000..74f6036 --- /dev/null +++ b/src/config/is-visitable.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Internal configuration helper + */ + +#ifndef CONFIG_IS_VISITABLE_HPP +#define CONFIG_IS_VISITABLE_HPP + +#include + +namespace config { + +template +struct isVisitableHelper__ { + struct Visitor {}; + + template static std::true_type + test(decltype(std::declval().template accept(Visitor()))*); + + template static std::false_type + test(...); + + static constexpr bool value = std::is_same(0)), std::true_type>::value; +}; + +/** + * Helper for compile-time checking against existance of template method 'accept'. + */ +template +struct isVisitable : public std::integral_constant::value> {}; + +} // namespace config + +#endif // CONFIG_IS_VISITABLE_HPP diff --git a/src/config/manager.hpp b/src/config/manager.hpp new file mode 100644 index 0000000..902633f --- /dev/null +++ b/src/config/manager.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Configuration management functions + */ + +#ifndef CONFIG_MANAGER_HPP +#define CONFIG_MANAGER_HPP + +#include "config/to-json-visitor.hpp" +#include "config/from-json-visitor.hpp" +#include "config/is-visitable.hpp" +#include "logger/logger.hpp" + +#include +#include + + +namespace config { + +template +void loadFromString(const std::string& jsonString, Config& config) +{ + static_assert(isVisitable::value, "Use CONFIG_REGISTER macro"); + + FromJsonVisitor visitor(jsonString); + config.accept(visitor); +} + +template +std::string saveToString(const Config& config) +{ + static_assert(isVisitable::value, "Use CONFIG_REGISTER macro"); + + ToJsonVisitor visitor; + config.accept(visitor); + return visitor.toString(); +} + +static bool readFileContent(const std::string& path, std::string& result) +{ + std::ifstream file(path); + + if (!file) { + LOGD(path << ": could not open for reading"); + return false; + } + + file.seekg(0, std::ios::end); + std::streampos length = file.tellg(); + if (length < 0) { + LOGD(path << ": tellg failed"); + return false; + } + result.resize(static_cast(length)); + file.seekg(0, std::ios::beg); + + file.read(&result[0], length); + if (!file) { + LOGD(path << ": read error"); + result.clear(); + return false; + } + + return true; +} + +static bool saveFileContent(const std::string& path, const std::string& content) +{ + std::ofstream file(path); + if (!file) { + LOGD(path << ": could not open for writing"); + return false; + } + file.write(content.data(), content.size()); + if (!file) { + LOGD(path << ": could not write to"); + return false; + } + return true; +} + +template +void loadFromFile(const std::string& filename, Config& config) +{ + try { + std::string content; + if (!readFileContent(filename, content)) { + throw ConfigException("Could not load " + filename); + } + loadFromString(content, config); + } + catch(...) { + throw ConfigException("Could not load configuration."); + } +} + +template +void saveToFile(const std::string& filename, const Config& config) +{ + const std::string content = saveToString(config); + if (!saveFileContent(filename, content)) { + throw ConfigException("Could not save " + filename); + } +} + +} // namespace config + +#endif // CONFIG_MANAGER_HPP diff --git a/src/config/to-json-visitor.hpp b/src/config/to-json-visitor.hpp new file mode 100644 index 0000000..5b70eb9 --- /dev/null +++ b/src/config/to-json-visitor.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * + * Licensed 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 + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief JSON visitor + */ + +#ifndef CONFIG_TO_JSON_VISITOR_HPP +#define CONFIG_TO_JSON_VISITOR_HPP + +#include "config/is-visitable.hpp" + +#include +#include +#include + +namespace config { + +class ToJsonVisitor { + +public: + ToJsonVisitor() + : mObject(json_object_new_object()) + { + } + + ToJsonVisitor(const ToJsonVisitor& visitor) + : mObject(json_object_get(visitor.mObject)) + { + } + + ~ToJsonVisitor() + { + json_object_put(mObject); + } + + std::string toString() const + { + return json_object_to_json_string(mObject); + } + + template + void visit(const std::string& name, const T& value) + { + json_object_object_add(mObject, name.c_str(), toJsonObject(value)); + } +private: + json_object* mObject; + + ToJsonVisitor& operator=(const ToJsonVisitor&) = delete; + + json_object* detach() + { + json_object* ret = mObject; + mObject = nullptr; + return ret; + } + + static json_object* toJsonObject(int value) + { + return json_object_new_int(value); + } + + static json_object* toJsonObject(std::int64_t value) + { + return json_object_new_int64(value); + } + + static json_object* toJsonObject(bool value) + { + return json_object_new_boolean(value); + } + + static json_object* toJsonObject(double value) + { + return json_object_new_double(value); + } + + static json_object* toJsonObject(const std::string& value) + { + return json_object_new_string(value.c_str()); + } + + template + static json_object* toJsonObject(const std::vector& value) + { + json_object* array = json_object_new_array(); + for (const T& v : value) { + json_object_array_add(array, toJsonObject(v)); + } + return array; + } + + template::value>::type> + static json_object* toJsonObject(const T& value) + { + ToJsonVisitor visitor; + value.accept(visitor); + return visitor.detach(); + } +}; + +} // namespace config + +#endif // CONFIG_TO_JSON_VISITOR_HPP diff --git a/src/libConfig.pc.in b/src/libConfig.pc.in new file mode 100644 index 0000000..67f53d6 --- /dev/null +++ b/src/libConfig.pc.in @@ -0,0 +1,8 @@ +# Package Information for pkg-config + +includedir=@INCLUDE_INSTALL_DIR@ + +Name: libConfig +Description: Config library +Version: @VERSION@ +Cflags: -I${includedir}/sc_tools -- 2.7.4 From f1a8930290c1bb8784484acbab45d9f98c3204bb Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 29 Jul 2014 15:33:37 +0200 Subject: [PATCH 4/7] Fix install libdir and version in pc file [Bug/Feature] Hardcoded lib dir, no version in pc file. [Cause] N/A [Solution] N/A [Verification] Build, install Change-Id: Ic36d716917cdf0dbc69f8cd63045645bf895365f --- CMakeLists.txt | 6 ++++++ src/CMakeLists.txt | 2 +- src/libConfig.pc.in | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2118f07..1ee9397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) PROJECT(Config) +SET(_VERSION_ "0.0.1") + ## pkgconfig ################################################################### INCLUDE(FindPkgConfig) @@ -59,6 +61,10 @@ ADD_DEFINITIONS(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") ## Subdirectories ############################################################## SET(SRC_FOLDER ${PROJECT_SOURCE_DIR}/src) +IF(NOT DEFINED LIB_INSTALL_DIR) + SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}") +ENDIF(NOT DEFINED LIB_INSTALL_DIR) + IF(NOT DEFINED INCLUDE_INSTALL_DIR) SET(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}) ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 437e6ca..9c1d5c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,4 +32,4 @@ INCLUDE_DIRECTORIES(SYSTEM ${CONFIG_DEPS_INCLUDE_DIRS}) CONFIGURE_FILE(${PC_FILE}.in "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" @ONLY) INSTALL(FILES ${HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/sc-tools/config") -INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" DESTINATION "/usr/lib/pkgconfig") +INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig") diff --git a/src/libConfig.pc.in b/src/libConfig.pc.in index 67f53d6..d18f860 100644 --- a/src/libConfig.pc.in +++ b/src/libConfig.pc.in @@ -4,5 +4,5 @@ includedir=@INCLUDE_INSTALL_DIR@ Name: libConfig Description: Config library -Version: @VERSION@ +Version: @_VERSION_@ Cflags: -I${includedir}/sc_tools -- 2.7.4 From ff7e8667cd93a28498d93911191816294069f58b Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 4 Aug 2014 11:09:08 +0200 Subject: [PATCH 5/7] Dynamic configuration stored in a database [Bug/Feature] Storing configuration in SQLite3 database [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: If14be7c9e0c62c985215f904d308d2d172c71fff --- CMakeLists.txt | 5 +- packaging/libConfig.spec | 14 ++ src/CMakeLists.txt | 13 +- src/config/kvstore.cpp | 280 ++++++++++++++++++++++++++++++++++++++ src/config/kvstore.hpp | 140 +++++++++++++++++++ src/config/sqlite3/connection.cpp | 72 ++++++++++ src/config/sqlite3/connection.hpp | 65 +++++++++ src/config/sqlite3/statement.cpp | 77 +++++++++++ src/config/sqlite3/statement.hpp | 66 +++++++++ src/libConfig.pc.in | 6 +- 10 files changed, 732 insertions(+), 6 deletions(-) create mode 100644 src/config/kvstore.cpp create mode 100644 src/config/kvstore.hpp create mode 100644 src/config/sqlite3/connection.cpp create mode 100644 src/config/sqlite3/connection.hpp create mode 100644 src/config/sqlite3/statement.cpp create mode 100644 src/config/sqlite3/statement.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ee9397..c223d8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) PROJECT(Config) -SET(_VERSION_ "0.0.1") +SET(_LIB_VERSION_ "0.0.1") +SET(_LIB_SOVERSION_ "0") ## pkgconfig ################################################################### INCLUDE(FindPkgConfig) @@ -66,7 +67,7 @@ IF(NOT DEFINED LIB_INSTALL_DIR) ENDIF(NOT DEFINED LIB_INSTALL_DIR) IF(NOT DEFINED INCLUDE_INSTALL_DIR) - SET(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR) ADD_SUBDIRECTORY(${SRC_FOLDER}) diff --git a/packaging/libConfig.spec b/packaging/libConfig.spec index f65c1fb..99c1dd5 100644 --- a/packaging/libConfig.spec +++ b/packaging/libConfig.spec @@ -9,13 +9,22 @@ Group: Security/Other Summary: Config library BuildRequires: cmake +BuildRequires: pkgconfig(sqlite3) + %description The package provides libConfig library. +%files +%manifest packaging/libConfig.manifest +%defattr(644,root,root,755) +%attr(755,root,root) %{_libdir}/libConfig.so.0.0.1 +%{_libdir}/libConfig.so.0 + %package devel Summary: Development package for config library Group: Development/Libraries +Requires: libConfig = %{version}-%{release} Requires: boost-devel Requires: pkgconfig(libLogger) Requires: libjson-devel @@ -25,6 +34,7 @@ The package provides libConfig development tools and libs. %files devel %defattr(644,root,root,755) +%{_libdir}/libConfig.so %{_includedir}/sc-tools %{_libdir}/pkgconfig/*.pc @@ -51,3 +61,7 @@ make -k %{?jobs:-j%jobs} %clean rm -rf %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c1d5c8..911e71a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,17 +19,26 @@ SET(PC_FILE "${SRC_FOLDER}/lib${PROJECT_NAME}.pc") MESSAGE(STATUS "Generating makefile for the libConfig sources...") -FILE(GLOB HEADERS "${SRC_FOLDER}/config/*.hpp") -FILE(GLOB_RECURSE SRCS "${SRC_FOLDER}" *.hpp *.cpp) +FILE(GLOB HEADERS "${SRC_FOLDER}/config/*.hpp") +FILE(GLOB HEADERS_SQLITE3 "${SRC_FOLDER}/config/sqlite3/*.hpp") +FILE(GLOB_RECURSE SRCS "${SRC_FOLDER}" *.hpp *.cpp) ## Setup target ################################################################ +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS}) +SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${_LIB_SOVERSION_}) +SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY VERSION ${_LIB_VERSION_}) ## Link libraries ############################################################# +PKG_CHECK_MODULES(CONFIG_DEPS REQUIRED sqlite3) INCLUDE_DIRECTORIES(${SRC_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${CONFIG_DEPS_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${CONFIG_DEPS_LIBRARIES}) + ## Install ##################################################################### CONFIGURE_FILE(${PC_FILE}.in "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" @ONLY) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries) INSTALL(FILES ${HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/sc-tools/config") +INSTALL(FILES ${HEADERS_SQLITE3} DESTINATION "${INCLUDE_INSTALL_DIR}/sc-tools/config/sqlite3") INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig") diff --git a/src/config/kvstore.cpp b/src/config/kvstore.cpp new file mode 100644 index 0000000..f48ee6f --- /dev/null +++ b/src/config/kvstore.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Definition of a class for key-value storage in a sqlite3 database + */ + + +#include "config/kvstore.hpp" +#include "config/exception.hpp" + +#include +#include +#include +#include + +namespace config { + +namespace { + +const int AUTO_DETERM_SIZE = -1; +const int FIRST_COLUMN = 0; + +struct Transaction { + Transaction(sqlite3::Connection& connRef) + : mConnRef(connRef) + { + mConnRef.exec("BEGIN EXCLUSIVE TRANSACTION"); + } + ~Transaction() + { + mConnRef.exec("COMMIT TRANSACTION"); + } +private: + sqlite3::Connection& mConnRef; +}; + + +std::string escape(const std::string& in) +{ + const std::set toEscape({'?', '*', '[', ']'}); + + // Compute the out size + auto isEscapeChar = [&](char c) { + return toEscape.count(c) == 1; + }; + size_t numEscape = std::count_if(in.begin(), + in.end(), + isEscapeChar); + if (numEscape == 0) { + return in; + } + + // Escape characters + std::string out(in.size() + 2 * numEscape, 'x'); + for (size_t i = 0, j = 0; + i < in.size(); + ++i, ++j) { + if (isEscapeChar(in[i])) { + out[j] = '['; + ++j; + out[j] = in[i]; + ++j; + out[j] = ']'; + } else { + out[j] = in[i]; + } + } + return out; +} + +} // namespace + +KVStore::KVStore(const std::string& path) + : mConn(path) +{ + setupDb(); + prepareStatements(); +} + +KVStore::~KVStore() +{ + +} + +void KVStore::setupDb() +{ + Lock lock(mConnMtx); + const std::string setupScript = R"setupScript( + BEGIN EXCLUSIVE TRANSACTION; + + CREATE TABLE IF NOT EXISTS data ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); + + COMMIT TRANSACTION; + )setupScript"; + + mConn.exec(setupScript); +} + +void KVStore::prepareStatements() +{ + mGetValueStmt.reset( + new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? LIMIT 1")); + mGetValueListStmt.reset( + new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? ||'*' ORDER BY key")); + mGetValueCountStmt.reset( + new sqlite3::Statement(mConn, "SELECT count(key) FROM data WHERE key GLOB ? ||'*' ")); + mGetSizeStmt.reset( + new sqlite3::Statement(mConn, "SELECT count(key) FROM data")); + mSetValueStmt.reset( + new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)")); + mRemoveValuesStmt.reset( + new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' ")); + +} + +void KVStore::clear() +{ + Lock lock(mConnMtx); + Transaction transaction(mConn); + mConn.exec("DELETE FROM data"); +} + +unsigned int KVStore::size() +{ + mGetSizeStmt->reset(); + + if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + + return static_cast(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN)); +} + +unsigned int KVStore::count(const std::string& key) +{ + Lock lock(mConnMtx); + Transaction transaction(mConn); + return countInternal(key); +} + +unsigned int KVStore::countInternal(const std::string& key) +{ + mGetValueCountStmt->reset(); + + ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + + if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + + return static_cast(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN)); +} + +void KVStore::remove(const std::string& key) +{ + Lock lock(mConnMtx); + Transaction transaction(mConn); + removeInternal(key); +} + +void KVStore::removeInternal(const std::string& key) +{ + mRemoveValuesStmt->reset(); + ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); + + if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } +} + +void KVStore::set(const std::string& key, const std::string& value) +{ + Lock lock(mConnMtx); + mSetValueStmt->reset(); + + ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, 0); + + Transaction transaction(mConn); + if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } +} + +void KVStore::set(const std::string& key, const std::initializer_list& values) +{ + set(key, std::vector(values)); +} + +void KVStore::set(const std::string& key, const std::vector& values) +{ + if (values.size() > std::numeric_limits::max()) { + throw ConfigException("Too many values to insert"); + } + + Lock lock(mConnMtx); + Transaction transaction(mConn); + + removeInternal(key); + + for (unsigned int i = 0; i < values.size(); ++i) { + mSetValueStmt->reset(); + const std::string modifiedKey = key + "." + std::to_string(i);; + + ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, 0); + + if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + } +} + +std::string KVStore::get(const std::string& key) +{ + Lock lock(mConnMtx); + + mGetValueStmt->reset(); + ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + + Transaction transaction(mConn); + + int ret = ::sqlite3_step(mGetValueStmt->get()); + if (ret == SQLITE_DONE) { + throw ConfigException("No value corresponding to the key"); + } + if (ret != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + + return reinterpret_cast(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); +} + +std::vector KVStore::list(const std::string& key) +{ + Lock lock(mConnMtx); + + mGetValueListStmt->reset(); + ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + + Transaction transaction(mConn); + + unsigned int valuesSize = countInternal(key); + if (valuesSize == 0) { + throw ConfigException("No value corresponding to the key"); + } + + std::vector values(valuesSize); + for (std::string& value : values) { + if (::sqlite3_step(mGetValueListStmt->get()) != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + value = reinterpret_cast( + sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN)); + } + + return values; +} +} // namespace config diff --git a/src/config/kvstore.hpp b/src/config/kvstore.hpp new file mode 100644 index 0000000..991625b --- /dev/null +++ b/src/config/kvstore.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Declaration of a class for key-value storage in a sqlite3 database + */ + +#ifndef COMMON_CONFIG_KVSTORE_HPP +#define COMMON_CONFIG_KVSTORE_HPP + +#include "config/sqlite3/statement.hpp" +#include "config/sqlite3/connection.hpp" + +#include +#include +#include +#include +#include +#include + +namespace config { + +class KVStore { + +public: + + /** + * @param path configuration database file path + */ + KVStore(const std::string& path); + ~KVStore(); + + /** + * Clears all the stored data + */ + void clear(); + + + /** + * @return Number of all stored values + */ + unsigned int size(); + + /** + * @param key string regexp of the stored values + * + * @return Number of values corresponding to the passed key + */ + unsigned int count(const std::string& key); + + /** + * Removes values corresponding to the passed key. + * Many values may correspond to one key, so many values may + * need to be deleted + * + * @param key string regexp of the stored values + */ + void remove(const std::string& key); + + /** + * Stores a single value corresponding to the passed key + * @param key string key of the value + * @param value string value + */ + void set(const std::string& key, const std::string& value); + + /** + * Stores a vector of values. + * Generates new keys using appending a '.' and consecutive integers. + * Removes values corresponding to this key before anything is added. + * + * @param key string key of the value + * @param value string value + */ + void set(const std::string& key, const std::vector& values); + + /** + * Stores values from the list. + * + * @see KVStore::set(const std::string& key, const std::vector& values) + * @param key string key of the value + * @param values [description] + */ + void set(const std::string& key, const std::initializer_list& values); + + /** + * @param key string key of the value + * @return string value corresponding to this particular key + */ + std::string get(const std::string& key); + + /** + * @param key string key of the value + * @return vector of values corresponding to the key + */ + std::vector list(const std::string& key); + +private: + typedef std::lock_guard Lock; + + std::mutex mConnMtx; + + sqlite3::Connection mConn; + std::unique_ptr mGetValueStmt; + std::unique_ptr mGetValueCountStmt; + std::unique_ptr mGetSizeStmt; + std::unique_ptr mGetValueListStmt; + std::unique_ptr mSetValueStmt; + std::unique_ptr mRemoveValuesStmt; + + void setupDb(); + void prepareStatements(); + + void removeInternal(const std::string& key); + unsigned int countInternal(const std::string& key); + +}; + +} // namespace config + +#endif // COMMON_CONFIG_KVSTORE_HPP + + diff --git a/src/config/sqlite3/connection.cpp b/src/config/sqlite3/connection.cpp new file mode 100644 index 0000000..e5d0552 --- /dev/null +++ b/src/config/sqlite3/connection.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Definition of the class managing a sqlite3 database connection + */ + + +#include "config/sqlite3/connection.hpp" +#include "config/exception.hpp" + +namespace config { +namespace sqlite3 { + +Connection::Connection(const std::string& path) +{ + if (::sqlite3_open_v2(path.c_str(), + &mDbPtr, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL) != SQLITE_OK) { + throw ConfigException("Error opening the database: " + getErrorMessage()); + } + + if (mDbPtr == NULL) { + throw ConfigException("Error opening the database: Unable to allocate memory."); + } +} + +Connection::~Connection() +{ + if (::sqlite3_close(mDbPtr) != SQLITE_OK) { + throw ConfigException("Error during closing the database. Error: " + getErrorMessage()); + } +} + +void Connection::exec(const std::string& query) +{ + char* mess; + if (::sqlite3_exec(mDbPtr, query.c_str(), 0, 0, &mess) != SQLITE_OK) { + throw ConfigException("Error during executing statement " + std::string(mess)); + } +} + +::sqlite3* Connection::get() +{ + return mDbPtr; +} + +std::string Connection::getErrorMessage() +{ + return std::string(sqlite3_errmsg(mDbPtr)); +} + +} // namespace sqlite3 +} // namespace config diff --git a/src/config/sqlite3/connection.hpp b/src/config/sqlite3/connection.hpp new file mode 100644 index 0000000..bf53afc --- /dev/null +++ b/src/config/sqlite3/connection.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Declaration of the class managing a sqlite3 database connection + */ + +#ifndef COMMON_CONFIG_SQLITE3_CONNECTION_HPP +#define COMMON_CONFIG_SQLITE3_CONNECTION_HPP + +#include +#include + +namespace config { +namespace sqlite3 { + +struct Connection { + /** + * @param path database file path + */ + Connection(const std::string& path); + ~Connection(); + + /** + * @return pointer to the corresponding sqlite3 database object + */ + ::sqlite3* get(); + + /** + * @return last error message in the database + */ + std::string getErrorMessage(); + + /** + * Executes the query in the database. + * + * @param query query to be executed + */ + void exec(const std::string& query); + +private: + ::sqlite3* mDbPtr; +}; + +} // namespace sqlite3 +} // namespace config + +#endif // COMMON_CONFIG_SQLITE3_CONNECTION_HPP diff --git a/src/config/sqlite3/statement.cpp b/src/config/sqlite3/statement.cpp new file mode 100644 index 0000000..287cb1c --- /dev/null +++ b/src/config/sqlite3/statement.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Definition of the class managing a sqlite3 statement + */ + +#include "config/sqlite3/statement.hpp" +#include "config/exception.hpp" + + +namespace config { +namespace sqlite3 { + +Statement::Statement(sqlite3::Connection& connRef, const std::string& query) + : mConnRef(connRef) +{ + if (::sqlite3_prepare_v2(connRef.get(), + query.c_str(), + query.size(), + &mStmtPtr, + NULL) + != SQLITE_OK) { + throw ConfigException("Error during preparing statement " + + mConnRef.getErrorMessage()); + } + + if (mStmtPtr == NULL) { + throw ConfigException("Wrong query: " + query); + } +} + +Statement::Statement::~Statement() +{ + if (::sqlite3_finalize(mStmtPtr) != SQLITE_OK) { + throw ConfigException("Error during finalizing statement " + + mConnRef.getErrorMessage()); + } +} + +sqlite3_stmt* Statement::get() +{ + return mStmtPtr; +} + +void Statement::reset() +{ + if (::sqlite3_clear_bindings(mStmtPtr) != SQLITE_OK) { + throw ConfigException("Error unbinding statement: " + + mConnRef.getErrorMessage()); + } + + if (::sqlite3_reset(mStmtPtr) != SQLITE_OK) { + throw ConfigException("Error reseting statement: " + + mConnRef.getErrorMessage()); + } +} + +} // namespace sqlite3 +} // namespace config diff --git a/src/config/sqlite3/statement.hpp b/src/config/sqlite3/statement.hpp new file mode 100644 index 0000000..48a1fca --- /dev/null +++ b/src/config/sqlite3/statement.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed 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 + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Declaration of the class managing a sqlite3 statement + */ + +#ifndef COMMON_CONFIG_SQLITE3_STATEMENT_HPP +#define COMMON_CONFIG_SQLITE3_STATEMENT_HPP + +#include "config/sqlite3/connection.hpp" + +#include +#include + +namespace config { +namespace sqlite3 { + +struct Statement { + + /** + * @param connRef reference to the Connection object + * @param query query to be executed + */ + Statement(sqlite3::Connection& connRef, const std::string& query); + ~Statement(); + + /** + * @return pointer to the sqlite3 statement + */ + sqlite3_stmt* get(); + + /** + * Clears the bindings and resets the statement. + * After this the statement can be executed again + */ + void reset(); + +private: + ::sqlite3_stmt* mStmtPtr; + sqlite3::Connection& mConnRef; +}; + +} // namespace sqlite3 +} // namespace config + +#endif // COMMON_CONFIG_SQLITE3_STATEMENT_HPP + + diff --git a/src/libConfig.pc.in b/src/libConfig.pc.in index d18f860..281a5b9 100644 --- a/src/libConfig.pc.in +++ b/src/libConfig.pc.in @@ -1,8 +1,10 @@ # Package Information for pkg-config +libdir=@LIB_INSTALL_DIR@ includedir=@INCLUDE_INSTALL_DIR@ Name: libConfig Description: Config library -Version: @_VERSION_@ -Cflags: -I${includedir}/sc_tools +Version: @_LIB_VERSION_@ +Libs: -L${libdir} -lConfig +Cflags: -I${includedir}/sc-tools -- 2.7.4 From 813c5304778b3f43e3ccbe17c96d979328b21b6d Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 11 Aug 2014 12:54:12 +0200 Subject: [PATCH 6/7] Storing serializable types in KVStore [Bug/Feature] One can store many types of values in KVStore [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: If656263cd7da1c558290a0baf5e6e44f88e9b865 --- packaging/libConfig.spec | 1 - src/config/kvstore.cpp | 83 ++++++++++++++------------ src/config/kvstore.hpp | 122 ++++++++++++++++++++++++++++---------- src/config/sqlite3/connection.hpp | 1 + 4 files changed, 136 insertions(+), 71 deletions(-) diff --git a/packaging/libConfig.spec b/packaging/libConfig.spec index 99c1dd5..5219bac 100644 --- a/packaging/libConfig.spec +++ b/packaging/libConfig.spec @@ -11,7 +11,6 @@ Summary: Config library BuildRequires: cmake BuildRequires: pkgconfig(sqlite3) - %description The package provides libConfig library. diff --git a/src/config/kvstore.cpp b/src/config/kvstore.cpp index f48ee6f..cc174e4 100644 --- a/src/config/kvstore.cpp +++ b/src/config/kvstore.cpp @@ -22,13 +22,14 @@ * @brief Definition of a class for key-value storage in a sqlite3 database */ +#include "config.hpp" #include "config/kvstore.hpp" #include "config/exception.hpp" +#include #include #include -#include #include namespace config { @@ -46,12 +47,26 @@ struct Transaction { } ~Transaction() { - mConnRef.exec("COMMIT TRANSACTION"); + if (std::uncaught_exception()) { + mConnRef.exec("ROLLBACK TRANSACTION"); + } else { + mConnRef.exec("COMMIT TRANSACTION"); + } } private: sqlite3::Connection& mConnRef; }; +struct ScopedReset { + ScopedReset(std::unique_ptr& stmtPtr) + : mStmtPtr(stmtPtr) {} + ~ScopedReset() + { + mStmtPtr->reset(); + } +private: + std::unique_ptr& mStmtPtr; +}; std::string escape(const std::string& in) { @@ -85,9 +100,9 @@ std::string escape(const std::string& in) } return out; } - } // namespace + KVStore::KVStore(const std::string& path) : mConn(path) { @@ -97,24 +112,13 @@ KVStore::KVStore(const std::string& path) KVStore::~KVStore() { - } void KVStore::setupDb() { Lock lock(mConnMtx); - const std::string setupScript = R"setupScript( - BEGIN EXCLUSIVE TRANSACTION; - - CREATE TABLE IF NOT EXISTS data ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL - ); - - COMMIT TRANSACTION; - )setupScript"; - - mConn.exec(setupScript); + Transaction transaction(mConn); + mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)"); } void KVStore::prepareStatements() @@ -131,7 +135,6 @@ void KVStore::prepareStatements() new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)")); mRemoveValuesStmt.reset( new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' ")); - } void KVStore::clear() @@ -143,7 +146,8 @@ void KVStore::clear() unsigned int KVStore::size() { - mGetSizeStmt->reset(); + Lock lock(mConnMtx); + ScopedReset scopedReset(mGetSizeStmt); if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -161,9 +165,9 @@ unsigned int KVStore::count(const std::string& key) unsigned int KVStore::countInternal(const std::string& key) { - mGetValueCountStmt->reset(); + ScopedReset scopedReset(mGetValueCountStmt); - ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -181,34 +185,36 @@ void KVStore::remove(const std::string& key) void KVStore::removeInternal(const std::string& key) { - mRemoveValuesStmt->reset(); - ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); + ScopedReset scopedReset(mRemoveValuesStmt); + + ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } } -void KVStore::set(const std::string& key, const std::string& value) +void KVStore::setInternal(const std::string& key, const std::string& value) { Lock lock(mConnMtx); - mSetValueStmt->reset(); - ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); Transaction transaction(mConn); + ScopedReset scopedReset(mSetValueStmt); + if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } } -void KVStore::set(const std::string& key, const std::initializer_list& values) +void KVStore::setInternal(const std::string& key, const std::initializer_list& values) { - set(key, std::vector(values)); + setInternal(key, std::vector(values)); } -void KVStore::set(const std::string& key, const std::vector& values) +void KVStore::setInternal(const std::string& key, const std::vector& values) { if (values.size() > std::numeric_limits::max()) { throw ConfigException("Too many values to insert"); @@ -220,11 +226,11 @@ void KVStore::set(const std::string& key, const std::vector& values removeInternal(key); for (unsigned int i = 0; i < values.size(); ++i) { - mSetValueStmt->reset(); + ScopedReset scopedReset(mSetValueStmt); const std::string modifiedKey = key + "." + std::to_string(i);; - ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, 0); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -232,14 +238,14 @@ void KVStore::set(const std::string& key, const std::vector& values } } -std::string KVStore::get(const std::string& key) +std::string KVStore::getInternal(const std::string& key, std::string*) { Lock lock(mConnMtx); - mGetValueStmt->reset(); - ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); Transaction transaction(mConn); + ScopedReset scopedReset(mGetValueStmt); int ret = ::sqlite3_step(mGetValueStmt->get()); if (ret == SQLITE_DONE) { @@ -252,14 +258,14 @@ std::string KVStore::get(const std::string& key) return reinterpret_cast(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); } -std::vector KVStore::list(const std::string& key) +std::vector KVStore::getInternal(const std::string& key, std::vector*) { Lock lock(mConnMtx); - mGetValueListStmt->reset(); - ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); Transaction transaction(mConn); + ScopedReset scopedReset(mGetValueListStmt); unsigned int valuesSize = countInternal(key); if (valuesSize == 0) { @@ -277,4 +283,5 @@ std::vector KVStore::list(const std::string& key) return values; } + } // namespace config diff --git a/src/config/kvstore.hpp b/src/config/kvstore.hpp index 991625b..94258f1 100644 --- a/src/config/kvstore.hpp +++ b/src/config/kvstore.hpp @@ -25,15 +25,16 @@ #ifndef COMMON_CONFIG_KVSTORE_HPP #define COMMON_CONFIG_KVSTORE_HPP -#include "config/sqlite3/statement.hpp" #include "config/sqlite3/connection.hpp" +#include "config/sqlite3/statement.hpp" -#include -#include -#include -#include +#include #include +#include #include +#include +#include +#include namespace config { @@ -52,7 +53,6 @@ public: */ void clear(); - /** * @return Number of all stored values */ @@ -76,45 +76,49 @@ public: /** * Stores a single value corresponding to the passed key - * @param key string key of the value - * @param value string value - */ - void set(const std::string& key, const std::string& value); - - /** - * Stores a vector of values. - * Generates new keys using appending a '.' and consecutive integers. - * Removes values corresponding to this key before anything is added. * * @param key string key of the value - * @param value string value + * @param value value corresponding to the key */ - void set(const std::string& key, const std::vector& values); + template + void set(const std::string& key, const T& value) + { + return setInternal(key, value); + } /** - * Stores values from the list. + * Gets the value corresponding to the key. + * Uses stringstreams to parse. * - * @see KVStore::set(const std::string& key, const std::vector& values) - * @param key string key of the value - * @param values [description] - */ - void set(const std::string& key, const std::initializer_list& values); - - /** * @param key string key of the value - * @return string value corresponding to this particular key + * @tparam T = std::string desired type of the return value + * @return value corresponding to the key */ - std::string get(const std::string& key); + template + T get(const std::string& key) + { + return getInternal(key, static_cast(nullptr)); + } - /** - * @param key string key of the value - * @return vector of values corresponding to the key - */ - std::vector list(const std::string& key); private: typedef std::lock_guard Lock; + void setInternal(const std::string& key, const std::string& value); + void setInternal(const std::string& key, const std::initializer_list& values); + void setInternal(const std::string& key, const std::vector& values); + template + void setInternal(const std::string& key, const T& value); + template + void setInternal(const std::string& key, const std::vector& values); + + std::string getInternal(const std::string& key, std::string*); + std::vector getInternal(const std::string& key, std::vector*); + template + T getInternal(const std::string& key, T*); + template + std::vector getInternal(const std::string& key, std::vector*); + std::mutex mConnMtx; sqlite3::Connection mConn; @@ -133,6 +137,60 @@ private: }; +template +void KVStore::setInternal(const std::string& key, const T& value) +{ + std::ostringstream oss; + oss << value; + setInternal(key, oss.str()); +} + +template +void KVStore::setInternal(const std::string& key, const std::vector& values) +{ + std::vector strValues(values.size()); + + auto toString = [](const T & value) -> std::string { + std::ostringstream oss; + oss << value; + return oss.str(); + }; + std::transform(values.begin(), + values.end(), + strValues.begin(), + toString); + + setInternal(key, strValues); +} + +template +T KVStore::getInternal(const std::string& key, T*) +{ + std::istringstream ss(getInternal(key, static_cast(nullptr))); + T ret; + ss >> ret; + return ret; +} + +template +std::vector KVStore::getInternal(const std::string& key, std::vector*) +{ + std::vector strValues = getInternal(key, static_cast*>(nullptr)); + std::vector values(strValues.size()); + + auto parse = [](const std::string & strValue) -> T { + std::istringstream iss; + T value; + iss >> value; + return value; + }; + std::transform(strValues.begin(), + strValues.end(), + values.begin(), + parse); + + return values; +} } // namespace config #endif // COMMON_CONFIG_KVSTORE_HPP diff --git a/src/config/sqlite3/connection.hpp b/src/config/sqlite3/connection.hpp index bf53afc..850332a 100644 --- a/src/config/sqlite3/connection.hpp +++ b/src/config/sqlite3/connection.hpp @@ -36,6 +36,7 @@ struct Connection { * @param path database file path */ Connection(const std::string& path); + Connection(const Connection&) = delete; ~Connection(); /** -- 2.7.4 From f624d386629aa384319226d4d1e4e4788d882628 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 19 Aug 2014 10:44:38 +0200 Subject: [PATCH 7/7] Bug fix in getting vector of values form KVStore [Bug/Feature] istringstream wasn't initialized [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I61ebf4242b868e2dc6fd8adc3b69db453f80afac --- src/config/kvstore.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/kvstore.hpp b/src/config/kvstore.hpp index 94258f1..6b75c91 100644 --- a/src/config/kvstore.hpp +++ b/src/config/kvstore.hpp @@ -179,7 +179,7 @@ std::vector KVStore::getInternal(const std::string& key, std::vector*) std::vector values(strValues.size()); auto parse = [](const std::string & strValue) -> T { - std::istringstream iss; + std::istringstream iss(strValue); T value; iss >> value; return value; -- 2.7.4