diff --git a/CMakeLists.txt b/CMakeLists.txt index 598c869..515040d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ option(ENABLE_TESTING "Build small (unit) tests" ON) # ================ Project ======================== # Project name and a few useful settings -project(ip_filter +project(allocator VERSION 0.0.$ENV{TRAVIS_BUILD_NUMBER} - DESCRIPTION "OTUS c++ homeworks: ip_filter" + DESCRIPTION "OTUS c++ homeworks: allocator" LANGUAGES CXX ) diff --git a/apps/Allocator/CMakeLists.txt b/apps/Allocator/CMakeLists.txt new file mode 100644 index 0000000..24c08e4 --- /dev/null +++ b/apps/Allocator/CMakeLists.txt @@ -0,0 +1,4 @@ +add_and_install_project_app(allocator + DEPEND + lib_Allocator +) \ No newline at end of file diff --git a/apps/Allocator/src/main_Allocator.cpp b/apps/Allocator/src/main_Allocator.cpp new file mode 100644 index 0000000..4c0e125 --- /dev/null +++ b/apps/Allocator/src/main_Allocator.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include +#include + + +int main() { + size_t factorial = 1; + + // map with std::allocator + std::map std_m; + for (size_t i = 0; i < 10; ++i) { + std_m[i] = factorial; + factorial *= (i+1); + } + for (auto it = std_m.begin(); it != std_m.end(); ++it) { + std::cout << it->first << " " << it->second << std::endl; + } + + // map with custom allocator + factorial = 1; + std::map, + Allocator::Allocator, 10>> m; + for (size_t i = 0; i < 10; ++i) { + m[i] = factorial; + factorial *= (i+1); + } + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << " " << it->second << std::endl; + } + + // custom container with std::allocator + Allocator::List std_l; + for (int i = 0; i < 10; ++i) { + std_l.push_back(i); + } + for (auto it = std_l.begin(); it != std_l.end(); ++it) { + std::cout << *it << " "; + } + std::cout << std::endl; + + // custom container with custom allocator + Allocator::List> l; + for (int i = 0; i < 10; ++i) { + l.push_back(i); + } + for (auto it = l.begin(); it != l.end(); ++it) { + std::cout << *it << " "; + } + std::cout << std::endl; + + + + return 0; +} diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6404fab..1c56ec0 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,2 +1,3 @@ # add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) \ No newline at end of file +# add_subdirectory(IpFilter) +add_subdirectory(Allocator) \ No newline at end of file diff --git a/apps/HelloWorld/CMakeLists.txt b/apps/HelloWorld/CMakeLists.txt index f2b2a63..e09d08c 100644 --- a/apps/HelloWorld/CMakeLists.txt +++ b/apps/HelloWorld/CMakeLists.txt @@ -1,4 +1,4 @@ add_and_install_project_app(helloworld DEPEND - HelloWorld + lib_HelloWorld ) \ No newline at end of file diff --git a/apps/IpFilter/CMakeLists.txt b/apps/IpFilter/CMakeLists.txt index 28a3f08..26eaed2 100644 --- a/apps/IpFilter/CMakeLists.txt +++ b/apps/IpFilter/CMakeLists.txt @@ -1,4 +1,4 @@ add_and_install_project_app(ip_filter DEPEND - IpFilter + lib_IpFilter ) \ No newline at end of file diff --git a/cmake/project_target.cmake b/cmake/project_target.cmake index 1fb0e00..d2ecc16 100644 --- a/cmake/project_target.cmake +++ b/cmake/project_target.cmake @@ -1,6 +1,7 @@ function(add_project_library) get_filename_component(lib_name "${CMAKE_CURRENT_SOURCE_DIR}" NAME) - set(lib_name lib${lib_name}) + set(lib_name lib_${lib_name}) + glob_target_files(${lib_name} TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) @@ -50,7 +51,7 @@ function(add_project_test) ) target_link_libraries(${test_name} - lib${target_name} + lib_${target_name} ${Boost_LIBRARIES} ) @@ -78,7 +79,7 @@ function(add_and_install_project_app app_name) set(app_lib_dependency "") foreach(link_lib ${add_project_app_DEPEND}) - LIST(APPEND app_lib_dependency lib${link_lib}) + LIST(APPEND app_lib_dependency ${link_lib}) endforeach() target_link_libraries(${app_name} diff --git a/library/Allocator/CMakeLists.txt b/library/Allocator/CMakeLists.txt new file mode 100644 index 0000000..a8d262f --- /dev/null +++ b/library/Allocator/CMakeLists.txt @@ -0,0 +1,3 @@ +add_project_library() + +add_subdirectory(test) \ No newline at end of file diff --git a/library/Allocator/include/Allocator/Allocator.h b/library/Allocator/include/Allocator/Allocator.h new file mode 100644 index 0000000..696d179 --- /dev/null +++ b/library/Allocator/include/Allocator/Allocator.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include + +namespace Allocator { + +//-------------------------------------- +#pragma mark Allocator declaration +//-------------------------------------- + +template +class Allocator{ +public: + using value_type = T; + using pointer = T*; + Allocator(); + + template + struct rebind; + + pointer allocate(std::size_t n = 1); + void deallocate(pointer p, std::size_t n); + template + void construct(U *p, Args&&... args); + void destroy(pointer p); + + ~Allocator(); + + bool operator==(const Allocator& other); + bool operator!=(const Allocator& other); + +private: + T* FindAvailableChunk(std::size_t n); + + const size_t max_allocatable_objects; + bool *m_free_chunks = nullptr; + T *m_area = nullptr; +}; + +//-------------------------------------- +#pragma mark Implementation +//-------------------------------------- + +template +Allocator::Allocator(): max_allocatable_objects(N) {} + +template +template +struct Allocator::rebind{ + using other = Allocator; +}; + +template +T* Allocator::allocate(std::size_t n) { + if (nullptr == m_area) { + m_area = static_cast (::operator new (max_allocatable_objects*sizeof(T))); + if (nullptr == m_area) { + throw std::bad_alloc(); + } + m_free_chunks = new bool[max_allocatable_objects]; + if (m_free_chunks == nullptr) { + throw std::bad_alloc(); + } + std::fill_n(m_free_chunks, max_allocatable_objects, true); + } + if (n > max_allocatable_objects) + throw std::bad_alloc{}; + auto p = FindAvailableChunk(n); + return p; +} + +template +void Allocator::deallocate(T *p, std::size_t n) { + int pos = (p-m_area); + if (pos < 0 || pos + n > max_allocatable_objects) + throw std::bad_alloc{}; + while (n >0) { + m_free_chunks[pos] = false; + ++pos; + --n; + } +} + +template +template +void Allocator::construct(U *p, Args&&... args) { + new(p) U(std::forward(args)...); +} + +template +void Allocator::destroy(T *p) { + p->~T(); +} + +template +Allocator::~Allocator() { + ::operator delete(m_area); + delete [] m_free_chunks; +} + +template +bool Allocator::operator==(const Allocator& other) { + return true; +} + +template +bool Allocator::operator!=(const Allocator& other) { + return !((*this)==other); +} + +template +T* Allocator::FindAvailableChunk(std::size_t n) { + size_t idx = 0; + bool stop = false; + while (!stop && idx + n < max_allocatable_objects) { + while (!m_free_chunks[idx] && idx + n < max_allocatable_objects) { + ++idx; + } + bool s = true; + for (int i = 0; i < n; ++i) { + s &= m_free_chunks[idx+i]; + } + stop |= s; + } + if (idx+n > max_allocatable_objects) + throw std::bad_alloc{}; + else{ + for (int i = 0; i < n; ++i){ + m_free_chunks[idx+i] = false; + } + return m_area + idx; + } +} + +} // namespace Allocator \ No newline at end of file diff --git a/library/Allocator/include/Allocator/List.h b/library/Allocator/include/Allocator/List.h new file mode 100644 index 0000000..6c445cd --- /dev/null +++ b/library/Allocator/include/Allocator/List.h @@ -0,0 +1,88 @@ +#pragma once + +#include + +namespace Allocator { + +//------------------------------------------------------------ +#pragma mark List Declaration +//------------------------------------------------------------ + +template > +class List +{ +public: + using value_type = typename std::allocator_traits::value_type; +private: + struct Node { + value_type data = value_type(); + Node *next = nullptr; + }; + + using NodeAllocator = typename Allocator::template rebind::other; + +public: + struct iterator + { + Node* m_ptr; + + iterator(Node* ptr) : m_ptr(ptr) {} + + iterator operator++() { + m_ptr = m_ptr->next; + + return *this; } + T& operator*() { return m_ptr->data; } + bool operator==(const iterator& rhs) { return m_ptr == rhs.m_ptr; } + bool operator!=(const iterator& rhs) { return m_ptr != rhs.m_ptr; } + }; + + List(); + + auto begin() -> iterator { return iterator(m_dummy_head->next); } + auto end() -> iterator { return iterator(m_tail->next); } + auto begin() const -> const iterator { return iterator(m_dummy_head->next); } + auto end() const -> const iterator { return iterator(m_tail->next); } + void push_back(const value_type &t); + ~List(); + +private: + Node *m_dummy_head = nullptr, *m_tail = nullptr; + NodeAllocator m_node_allocator; +}; + +//------------------------------------------------------------ +#pragma mark List Implementation +//------------------------------------------------------------ + +template +List::List(): m_dummy_head() { + m_dummy_head = m_node_allocator.allocate(1); + m_node_allocator.construct(m_dummy_head, Node{}); + m_tail = m_dummy_head; +} + +template +void List::push_back(const value_type &t) { + Node *node = m_node_allocator.allocate(1); + m_node_allocator.construct(node, Node{t, nullptr}); + m_tail->next = node; + m_tail = node; +} + +template +List::~List() +{ + auto node = m_dummy_head->next; + while (node != nullptr) + { + auto next_node = node->next; + m_node_allocator.deallocate(node, 1); + m_node_allocator.destroy(node); + node = next_node; + } + m_node_allocator.deallocate(m_dummy_head, 1); + m_node_allocator.destroy(m_dummy_head); +} + +} // namespace slist \ No newline at end of file diff --git a/library/Allocator/src/Allocator.cpp b/library/Allocator/src/Allocator.cpp new file mode 100644 index 0000000..1687097 --- /dev/null +++ b/library/Allocator/src/Allocator.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/library/Allocator/src/List.cpp b/library/Allocator/src/List.cpp new file mode 100644 index 0000000..1a10f5f --- /dev/null +++ b/library/Allocator/src/List.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/library/Allocator/test/CMakeLists.txt b/library/Allocator/test/CMakeLists.txt new file mode 100644 index 0000000..b6826b6 --- /dev/null +++ b/library/Allocator/test/CMakeLists.txt @@ -0,0 +1 @@ +add_project_test() \ No newline at end of file diff --git a/library/Allocator/test/src/test_Allocator.cpp b/library/Allocator/test/src/test_Allocator.cpp new file mode 100644 index 0000000..1d2b7c8 --- /dev/null +++ b/library/Allocator/test/src/test_Allocator.cpp @@ -0,0 +1,62 @@ +#define BOOST_TEST_MODULE Allocator_test_module + +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(Allocator_test_suite) + +BOOST_AUTO_TEST_CASE(Allocator_stdVectorInt) +{ + constexpr size_t N = 16; + std::vector> vec; + for (size_t i = 0; i < N/2; ++i) { + vec.push_back(42); + } + BOOST_CHECK(std::all_of(vec.begin(), vec.end(), + [](int a){ return a == 42;})); +} + +BOOST_AUTO_TEST_CASE(Allocator_stdMapInt) +{ + constexpr size_t N = 10; + std::map, + Allocator::Allocator, N>> mm; + for (size_t i = 0; i < N; ++i) { + mm[i] = i+42; + } + for (int i = 0; i < N; ++i) { + BOOST_CHECK(mm.at(i) == (i+42)); + } +} + +BOOST_AUTO_TEST_CASE(List_stdAllocator) +{ + Allocator::List l; + constexpr int N = 10; + for (int i = 0; i < N; ++i) { + l.push_back(i); + } + int i = 0; + for (auto it = l.begin(); it != l.end(); ++it, ++i) { + BOOST_CHECK(*it == i); + } +} + +BOOST_AUTO_TEST_CASE(List_CustomAllocator) +{ + constexpr size_t N = 9; + Allocator::List> l; + for (int i = 0; i < N; ++i) { + l.push_back(i); + } + int i = 0; + for (auto it = l.begin(); it != l.end(); ++it, ++i) { + BOOST_CHECK(*it == i); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 6404fab..1c56ec0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -1,2 +1,3 @@ # add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) \ No newline at end of file +# add_subdirectory(IpFilter) +add_subdirectory(Allocator) \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index fbb59cc..1cc0392 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,11 +1,11 @@ cmake . # build lib -cmake --build . --target libIpFilter +cmake --build . --target lib_Allocator # build test for lib -cmake --build . --target test_IpFilter +cmake --build . --target test_Allocator # run test -ctest test_IpFilter +ctest test_Allocator # build and install app -cmake --build . --target ip_filter +cmake --build . --target allocator # generate deb package cpack