Add archive as serializer
authorSangwan Kwon <sangwan.kwon@samsung.com>
Mon, 18 Nov 2019 04:32:48 +0000 (13:32 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Fri, 22 Nov 2019 01:52:17 +0000 (10:52 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
CMakeLists.txt
src/vist/archive.hpp [new file with mode: 0644]
src/vist/common/CMakeLists.txt
src/vist/common/archive.cpp [new file with mode: 0644]
src/vist/common/tests/archive.cpp [new file with mode: 0644]
src/vist/index-sequence.hpp [new file with mode: 0644]

index ff2dedf..41aeea6 100644 (file)
@@ -55,13 +55,9 @@ ADD_DEFINITIONS(-DOSQUERY_VERSION=${OSQUERY_VERSION}
                                -DOSQUERY_BUILD_VERSION=${OSQUERY_VERSION}
                                -DOSQUERY_BUILD_SDK_VERSION=${OSQUERY_VERSION})
 
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}")
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/api")
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src")
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/tools/sqlite3")
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/osquery/include")
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/vist/tsqb")
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/osquery/extensions/thrift/gen")
 INCLUDE_DIRECTORIES("/usr/local/include")
 
 ENABLE_TESTING()
diff --git a/src/vist/archive.hpp b/src/vist/archive.hpp
new file mode 100644 (file)
index 0000000..3d72296
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ *  Copyright (c) 2018-present 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    archive.hpp
+ * @author  Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief   Define archive interface for serializer and parameter-pack.
+ * @details 1. Serializer: Serialize/deserialize below types.
+ *             (fundamental types, archival object, unique_ptr, shared_ptr)
+ *          2. Parameter-pack: Pack/unpack zero or more template arguments.
+ */
+
+#pragma once
+
+#include <vist/index-sequence.hpp>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <memory>
+#include <tuple>
+
+namespace vist {
+
+class Archival;
+
+template<typename T>
+using IsFundamental = typename std::enable_if<std::is_fundamental<T>::value, int>::type;
+
+template<typename T>
+using IsArchival = typename std::enable_if<std::is_base_of<Archival, T>::value, int>::type;
+
+class Archive {
+public:
+       virtual ~Archive() = default;
+
+       template<typename Front, typename... Rest>
+       void pack(const Front& front, const Rest&... rest);
+       inline void pack(void) {}
+
+       template<typename Front, typename... Rest>
+       void unpack(Front& front, Rest&... rest);
+       inline void unpack(void) {}
+
+       template<typename... Ts>
+       void transform(std::tuple<Ts...>& tuple);
+
+       // serialize method
+       template<typename T, IsFundamental<T> = 0>
+       Archive& operator<<(const T& value);
+       template<typename T, IsArchival<T> = 0>
+       Archive& operator<<(const T& object);
+       template<typename T>
+       Archive& operator<<(const std::unique_ptr<T>& pointer);
+       template<typename T>
+       Archive& operator<<(const std::shared_ptr<T>& pointer);
+       Archive& operator<<(const std::string& value);
+       Archive& operator<<(const Archive& archive);
+
+       // deserialize method
+       template<typename T, IsFundamental<T> = 0>
+       Archive& operator>>(T& value);
+       template<typename T, IsArchival<T> = 0>
+       Archive& operator>>(T& object);
+       template<typename T>
+       Archive& operator>>(std::unique_ptr<T>& pointer);
+       template<typename T>
+       Archive& operator>>(std::shared_ptr<T>& pointer);
+       Archive& operator>>(std::string& value);
+       Archive& operator>>(Archive& archive);
+
+       unsigned char* get(void) noexcept;
+       std::size_t size(void) const noexcept;
+       void reserve(std::size_t size) noexcept;
+
+protected:
+       virtual void save(const void* bytes, std::size_t size);
+       virtual void load(void* bytes, std::size_t size);
+
+private:
+       template<typename T>
+       void transformImpl(T& tuple, EmptySequence);
+       template<typename T, std::size_t... I>
+       void transformImpl(T& tuple, IndexSequence<I...>);
+
+       std::vector<unsigned char> buffer;
+       std::size_t current = 0;
+};
+
+class Archival {
+public:
+       virtual ~Archival() = default;
+
+       virtual void pack(Archive& archive) const = 0;
+       virtual void unpack(Archive& archive) = 0;
+};
+
+template<typename Front, typename... Rest>
+void Archive::pack(const Front& front, const Rest&... rest)
+{
+       *this << front;
+       this->pack(rest...);
+}
+
+template<typename Front, typename... Rest>
+void Archive::unpack(Front& front, Rest&... rest)
+{
+       *this >> front;
+       this->unpack(rest...);
+}
+
+template<typename... Ts>
+void Archive::transform(std::tuple<Ts...>& tuple)
+{
+       constexpr auto size = std::tuple_size<std::tuple<Ts...>>::value;
+       this->transformImpl(tuple, make_index_sequence<size>());
+}
+
+template<typename T>
+void Archive::transformImpl(T& tuple, EmptySequence)
+{
+}
+
+template<typename T, std::size_t... I>
+void Archive::transformImpl(T& tuple, IndexSequence<I...>)
+{
+       this->unpack(std::get<I>(tuple)...);
+}
+
+template<typename T, IsFundamental<T>>
+Archive& Archive::operator<<(const T& value)
+{
+       this->save(reinterpret_cast<const void*>(&value), sizeof(value));
+
+       return *this;
+}
+
+template<typename T>
+Archive& Archive::operator<<(const std::unique_ptr<T>& pointer)
+{
+       return *this << *pointer;
+}
+
+template<typename T>
+Archive& Archive::operator<<(const std::shared_ptr<T>& pointer)
+{
+       return *this << *pointer;
+}
+
+template<typename T, IsArchival<T>>
+Archive& Archive::operator<<(const T& object)
+{
+       object.pack(*this);
+
+       return *this;
+}
+
+template<typename T, IsFundamental<T>>
+Archive& Archive::operator>>(T& value)
+{
+       this->load(reinterpret_cast<void*>(&value), sizeof(value));
+
+       return *this;
+}
+
+template<typename T>
+Archive& Archive::operator>>(std::unique_ptr<T>& pointer)
+{
+       if (pointer == nullptr)
+               pointer.reset(new T);
+
+       return *this >> *pointer;
+}
+
+template<typename T>
+Archive& Archive::operator>>(std::shared_ptr<T>& pointer)
+{
+       if (pointer == nullptr)
+               pointer.reset(new T);
+
+       return *this >> *pointer;
+}
+
+template<typename T, IsArchival<T>>
+Archive& Archive::operator>>(T& object)
+{
+       object.unpack(*this);
+
+       return *this;
+}
+
+} // namespace vist
index c76ae24..9f0766b 100644 (file)
@@ -16,7 +16,8 @@ PKG_CHECK_MODULES(VIST_COMMON_DEPS REQUIRED gflags klay dlog)
 
 INCLUDE_DIRECTORIES(SYSTEM . ${VIST_COMMON_DEPS_INCLUDE_DIRS})
 
-ADD_VIST_COMMON_LIBRARY(vist_common common.cpp)
+ADD_VIST_COMMON_LIBRARY(vist_common archive.cpp
+                                                                       common.cpp)
 
 FILE(GLOB COMMON_TESTS "tests/*.cpp")
 ADD_VIST_TEST(${COMMON_TESTS})
diff --git a/src/vist/common/archive.cpp b/src/vist/common/archive.cpp
new file mode 100644 (file)
index 0000000..1458309
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *  Copyright (c) 2018-present 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   archive.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief  Implementation of archive.
+ */
+
+#include <vist/archive.hpp>
+
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+
+namespace vist {
+
+Archive& Archive::operator<<(const Archive& archive)
+{
+       auto data = archive.buffer;
+       auto index = archive.current;
+       std::copy(data.begin() + index, data.end(), std::back_inserter(this->buffer));
+}
+
+Archive& Archive::operator<<(const std::string& value)
+{
+       std::size_t size = value.size();
+       this->save(reinterpret_cast<const void*>(&size), sizeof(size));
+       this->save(reinterpret_cast<const void*>(value.c_str()), value.size());
+
+       return *this;
+}
+
+Archive& Archive::operator>>(Archive& archive)
+{
+       auto data = this->buffer;
+       auto index = this->current;
+       std::copy(data.begin() + index, data.end(), std::back_inserter(archive.buffer));
+}
+
+Archive& Archive::operator>>(std::string& value)
+{
+       std::size_t size;
+       this->load(reinterpret_cast<void*>(&size), sizeof(size));
+
+       value.resize(size);
+       this->load(reinterpret_cast<void*>(&value.front()), size);
+
+       return *this;
+}
+
+unsigned char* Archive::get(void) noexcept
+{
+       return this->buffer.data();
+}
+
+std::size_t Archive::size(void) const noexcept
+{
+       return this->buffer.size();
+}
+
+void Archive::reserve(std::size_t size) noexcept
+{
+       this->buffer.reserve(size);
+}
+
+void Archive::save(const void* bytes, std::size_t size)
+{
+       auto binary = reinterpret_cast<unsigned char*>(const_cast<void*>(bytes));
+       std::vector<unsigned char> next(binary, binary + size);
+       std::copy(next.begin(), next.end(), std::back_inserter(this->buffer));
+}
+
+void Archive::load(void* bytes, std::size_t size)
+{
+       std::memcpy(bytes, reinterpret_cast<void*>(this->buffer.data() + current), size);
+       current += size;
+}
+
+} // namespace vist
diff --git a/src/vist/common/tests/archive.cpp b/src/vist/common/tests/archive.cpp
new file mode 100644 (file)
index 0000000..ab8c69b
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *  Copyright (c) 2018-present 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
+ */
+
+#include <gtest/gtest.h>
+
+#include <vist/archive.hpp>
+
+#include <memory>
+#include <limits>
+#include <string>
+#include <cassert>
+
+using namespace vist;
+
+TEST(ArchiveTests, serializer_fundamental_int)
+{
+       int input1 = std::numeric_limits<int>::lowest();
+       int input2 = std::numeric_limits<int>::max();
+
+       Archive archive;
+       archive << input1 << input2;
+
+       int output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_float)
+{
+       float input1 = std::numeric_limits<float>::lowest();
+       float input2 = std::numeric_limits<float>::max();
+
+       Archive archive;
+       archive << input1 << input2;
+
+       float output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_double)
+{
+       double input1 = std::numeric_limits<double>::lowest();
+       double input2 = std::numeric_limits<double>::max();
+
+       Archive archive;
+       archive << input1 << input2;
+
+       double output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_long_long_int)
+{
+       long long int input1 = std::numeric_limits<long long int>::lowest();
+       long long int input2 = std::numeric_limits<long long int>::max();
+
+       Archive archive;
+       archive << input1 << input2;
+
+       long long int output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_bool)
+{
+       bool input1 = true;
+       bool input2 = false;
+
+       Archive archive;
+       archive << input1 << input2;
+
+       bool output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_char)
+{
+       char input1 = 'a';
+       char input2 = 'Z';
+
+       Archive archive;
+       archive << input1 << input2;
+
+       char output1, output2;
+       archive >> output1 >> output2;
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+}
+
+TEST(ArchiveTests, serializer_fundamental_nullptr)
+{
+       std::nullptr_t input;
+
+       Archive archive;
+       archive << input;
+
+       std::nullptr_t output;
+       archive >> output;
+
+       assert(input == output);
+}
+
+TEST(ArchiveTests, serializer_fundamental_reference)
+{
+       int input = std::numeric_limits<int>::max();
+       int& refInput = input;
+
+       Archive archive;
+       archive << refInput;
+
+       int output;
+       int& refOutput = output;
+       archive >> refOutput;
+
+       EXPECT_EQ(input, output);
+       EXPECT_EQ(refInput, refOutput);
+}
+
+struct Object : public Archival {
+       void pack(Archive& archive) const override
+       {
+               archive << a << b << c;
+       }
+
+       void unpack(Archive& archive) override
+       {
+               archive >> a >> b >> c;
+       }
+
+       int a = 0;
+       bool b = true;
+       std::string c = "str";
+};
+
+TEST(ArchiveTests, archival)
+{
+       Object input;
+       input.a = 100;
+       input.b = false;
+       input.c = "archival string";
+
+       Archive archive;
+       archive << input;
+
+       Object output;
+       archive >> output;
+
+       EXPECT_EQ(input.a, output.a);
+       EXPECT_EQ(input.b, output.b);
+       EXPECT_EQ(input.c, output.c);
+}
+
+TEST(ArchiveTests, string)
+{
+       std::string input = "Archive string test";
+
+       Archive archive;
+       archive << input;
+
+       std::string output;
+       archive >> output;
+
+       EXPECT_EQ(input, output);
+}
+
+TEST(ArchiveTests, unique_ptr)
+{
+       std::unique_ptr<bool> input(new bool(true));
+
+       Archive archive;
+       archive << input;
+
+       std::unique_ptr<bool> output;
+       archive >> output;
+
+       EXPECT_EQ(*input, *output);
+}
+
+TEST(ArchiveTests, shared_ptr)
+{
+       std::shared_ptr<std::string> input = std::make_shared<std::string>();
+       *input = "Archive string test";
+
+       Archive archive;
+       archive << input;
+
+       std::shared_ptr<std::string> output;
+       archive >> output;
+
+       EXPECT_EQ(*input, *output);
+}
+
+TEST(ArchiveTests, archive)
+{
+       std::string input1 = "Archive string test1";
+       std::string input2 = "Archive string test2";
+       std::string input3 = "Archive string test3";
+
+       Archive archive1, archive2, archive3;
+       archive1 << input1;
+       archive2 << input2;
+       archive3 << input3;
+
+       archive1 << archive2;
+       archive1 >> archive3;
+
+       std::string output1;
+       std::string output2;
+       std::string output3;
+       archive3 >> output1 >> output2 >> output3;
+
+       EXPECT_EQ(output1, input3);
+       EXPECT_EQ(output2, input1);
+       EXPECT_EQ(output3, input2);
+}
+
+TEST(ArchiveTests, parameter_pack)
+{
+       int input1 = std::numeric_limits<int>::max();
+       float input2 = std::numeric_limits<float>::max();
+       double input3 = std::numeric_limits<double>::max();
+       std::string input4 = "Archive string test";
+       std::unique_ptr<bool> input5(new bool(true));
+
+       Archive archive;
+       archive.pack(input1, input2, input3, input4, input5);
+
+       int output1;
+       float output2;
+       double output3;
+       std::string output4;
+       std::unique_ptr<bool> output5;
+
+       archive.unpack(output1, output2, output3, output4, output5);
+
+       EXPECT_EQ(input1, output1);
+       EXPECT_EQ(input2, output2);
+       EXPECT_EQ(input3, output3);
+       EXPECT_EQ(input4, output4);
+       EXPECT_EQ(*input5, *output5);
+}
+
+TEST(ArchiveTests, parameter_pack_empty)
+{
+       Archive archive;
+       archive.pack();
+
+       archive.unpack();
+}
+
+TEST(ArchiveTests, parameter_pack_transform)
+{
+       int input1 = std::numeric_limits<int>::max();
+       float input2 = std::numeric_limits<float>::max();
+       double input3 = std::numeric_limits<double>::max();
+       std::string input4 = "Archive string test";
+       std::shared_ptr<bool> input5(new bool(true));
+
+       Archive archive;
+       archive.pack(input1, input2, input3, input4, input5);
+
+       int output1;
+       float output2;
+       double output3;
+       std::string raw;
+       std::string& output4 = raw;
+       std::shared_ptr<bool> output5;
+
+       auto tuple = std::make_tuple(output1, output2, output3, output4, output5);
+       archive.transform(tuple);
+
+       EXPECT_EQ(input1, std::get<0>(tuple));
+       EXPECT_EQ(input2, std::get<1>(tuple));
+       EXPECT_EQ(input3, std::get<2>(tuple));
+       EXPECT_EQ(input4, std::get<3>(tuple));
+       EXPECT_EQ(*input5, *(std::get<4>(tuple)));
+}
+
+TEST(ArchiveTests, parameter_pack_transform_empty)
+{
+       auto tuple = std::make_tuple();
+       Archive archive;
+       archive.transform(tuple);
+}
diff --git a/src/vist/index-sequence.hpp b/src/vist/index-sequence.hpp
new file mode 100644 (file)
index 0000000..170c2c9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2018-present 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   index-sequence.hpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief  Generate index sequence.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+namespace vist {
+
+struct EmptySequence {};
+
+template<std::size_t...>
+struct IndexSequence {};
+
+namespace {
+
+template<std::size_t N, std::size_t... S>
+struct SequenceExpansion : SequenceExpansion<N-1, N-1, S...> {};
+
+template<std::size_t... S>
+struct SequenceExpansion<0, S...> { using Type = IndexSequence<S...>; };
+
+template<>
+struct SequenceExpansion<0> { using Type = EmptySequence; };
+
+} // anonymous namespace
+
+template<std::size_t N>
+using make_index_sequence = typename SequenceExpansion<N>::Type;
+
+} // namespace vist