diff --git a/module-1/homework/Allocator/CMakeLists.txt b/module-1/homework/Allocator/CMakeLists.txt index 34c00966..182a2d5d 100644 --- a/module-1/homework/Allocator/CMakeLists.txt +++ b/module-1/homework/Allocator/CMakeLists.txt @@ -4,6 +4,8 @@ include(GoogleTest) project("runner") +set(CMAKE_CXX_STANDARD 14) + configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . diff --git a/module-1/homework/Allocator/src/allocator/allocator.h b/module-1/homework/Allocator/src/allocator/allocator.h index 827256c5..13037d47 100644 --- a/module-1/homework/Allocator/src/allocator/allocator.h +++ b/module-1/homework/Allocator/src/allocator/allocator.h @@ -1,41 +1,119 @@ #include #include +#include template class CustomAllocator { - public: - - using value_type = T; - // Your code goes here - - CustomAllocator(); - CustomAllocator(const CustomAllocator& other) noexcept; - ~CustomAllocator(); - - template - CustomAllocator(const CustomAllocator& other) noexcept; - - T* allocate(std::size_t n); - void deallocate(T* p, std::size_t n); - template - void construct(pointer p, Args&&... args); - void destroy(pointer p); - - template - friend bool operator==(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept; - template - friend bool operator!=(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept; - - private: - // Your code goes here +public: + + template + struct rebind { + using other = CustomAllocator; + }; + + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using size_type = std::size_t; + using pointer_difference = std::ptrdiff_t; + using propagate_on_container_copy_assignment = std::false_type; + using propagate_on_container_move_assignment = std::false_type; + using propogate_on_container_swap = std::false_type; + using is_always_equal = std::false_type; + + + CustomAllocator(); + CustomAllocator(const CustomAllocator& other) noexcept; + ~CustomAllocator(); + + template + explicit CustomAllocator(const CustomAllocator& other) noexcept; + + T* allocate(std::size_t n); + void deallocate(T* p, std::size_t n); + template + void construct(pointer p, Args&&... args); + void destroy(pointer p); + + template + friend bool operator==(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept; + template + friend bool operator!=(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept; + +private: + void* arena_; + int* arena_offset_; + int* curr_arena_ref_count_; }; template bool operator==(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept { - // Your code goes here + return lhs.arena_ == rhs.arena_; } template bool operator!=(const CustomAllocator& lhs, const CustomAllocator& rhs) noexcept { - // Your code goes here -} \ No newline at end of file + return !(lhs == rhs); +} + +template +CustomAllocator::CustomAllocator() { + arena_ = ::operator new(40000 * sizeof(T)); + arena_offset_ = new int(0); + curr_arena_ref_count_ = new int(1); +} + +template +CustomAllocator::CustomAllocator(const CustomAllocator& other) noexcept : + arena_(other.arena_), + curr_arena_ref_count_(other.curr_arena_ref_count_), + arena_offset_(other.arena_offset_) +{ + (*curr_arena_ref_count_)++; +} + +template +template +CustomAllocator::CustomAllocator(const CustomAllocator& other) noexcept : + arena_(other.arena_), + curr_arena_ref_count_(other.curr_arena_ref_count_), + arena_offset_(other.arena_offset_) +{ + (*curr_arena_ref_count_)++; +} + +template +CustomAllocator::~CustomAllocator() { + (*curr_arena_ref_count_)--; + if (*curr_arena_ref_count_ == 0) { + ::operator delete(arena_); + delete curr_arena_ref_count_; + delete arena_offset_; + } +} + +template +T* CustomAllocator::allocate(std::size_t n) { + int offset = *arena_offset_; + + *arena_offset_ += n; + return static_cast(arena_) + offset; +} + +template +void CustomAllocator::deallocate(T* p, std::size_t n) {} + +template +template +void CustomAllocator::construct(CustomAllocator::pointer p, Args&& ... args) { + ::new((void*)p) T(std::forward(args)...); +} + +template +void CustomAllocator::destroy(CustomAllocator::pointer p) { + p->~T(); +} + + diff --git a/module-1/homework/Allocator/src/list/list.h b/module-1/homework/Allocator/src/list/list.h index a0affdef..6c90b2a2 100644 --- a/module-1/homework/Allocator/src/list/list.h +++ b/module-1/homework/Allocator/src/list/list.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -8,21 +7,31 @@ namespace task { template> class list { - public: - + class iterator; +public: + using value_type = T; + using allocator_type = Allocator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using iterator = iterator; + using const_iterator = const iterator; + // Your code goes here // Special member functions list(); - list(const list& other) { - // Your code goes here - } + list(const list& other); + list(const list& other, const Allocator& alloc); - - list(list&& other) : lst(std::move(other.lst)); + + list(list&& other) noexcept; list(list&& other, const Allocator& alloc); ~list(); @@ -38,16 +47,16 @@ class list { const_reference back() const; // Iterators - iterator begin() noexcept; - const_iterator begin() const noexcept; + iterator begin() noexcept { return iterator(NIL->getNext()); } + const_iterator begin() const noexcept { return iterator(NIL->getNext()); } - iterator end() noexcept; - const_iterator end() const noexcept; + iterator end() noexcept { return iterator(NIL); } + const_iterator end() const noexcept { return iterator(NIL); } // Capacity - bool empty() const noexcept; - size_type size() const noexcept; - size_type max_size() const noexcept; + bool empty() const noexcept { return size_ == 0; } + size_type size() const noexcept { return size_; } +// size_type max_size() const noexcept; // Modifiers void clear(); @@ -71,10 +80,510 @@ class list { void unique(); void sort(); - allocator_type get_allocator() const noexcept; + allocator_type get_allocator() const noexcept { + return allocator_type(alloc_); + } private: - // Your code goes here + class Node { + public: + + Node(): value(), next(nullptr), prev(nullptr) {} + + explicit Node(value_type value, Node* next = nullptr, Node* prev = nullptr) { + this->value = value; + this->next = next; + this->prev = prev; + } + + value_type getValue() const { return value; } + + reference getValueRef() { return value; } + + const_reference getValueRef() const { return value;} + + const Node* getPrev() const { return prev; } + + Node* getPrev() { return prev; } + + const Node* getNext() const { return next; } + + Node* getNext() { return next; } + + void setNext(Node* newNext) { this->next = newNext; } + + void setPrev(Node* newPrev) { this->prev = newPrev; } + + ~Node() { + this->next = nullptr; + this->prev = nullptr; + } + + private: + value_type value; + Node* prev; + Node* next; + }; +public: + class iterator { + friend class std::iterator_traits; + public: + typedef T value_type; + typedef iterator self_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef T* pointer; + typedef T& reference; + typedef std::ptrdiff_t difference_type; + + explicit iterator(Node* ptr): ptr(ptr) {} + + iterator& operator++() { + ptr = ptr->getNext(); + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + ++(*this); + return copy; + } + + iterator& operator--() { + ptr = ptr->getPrev(); + return *this; + } + + iterator operator--(int) { + iterator copy(*this); + --(*this); + return copy; + } + + bool operator==(const iterator& rhs) { return ptr == rhs.ptr; } + bool operator!=(const iterator& rhs) { return ptr != rhs.ptr; } + + reference operator*() { return ptr->getValueRef(); } + T* operator->() { return &ptr->getValueRef(); } + private: + Node* ptr = nullptr; + }; + +private: + typedef typename std::allocator_traits::template rebind_alloc node_allocator; + typedef typename std::allocator_traits node_alloc_traits; + + + node_allocator alloc_; + + Node* NIL; + + size_t size_ = 0; + + static list merge(const list& left, const list& right);//begin2 == end1 + static std::pair bisect(const list& source); + static list sorted(const list& source); + + void copyValues(iterator beg, iterator end); }; -} \ No newline at end of file +} + +template +task::list::list(): NIL(alloc_.allocate(1)) { + size_ = 0; + alloc_.construct(NIL); + NIL->setNext(NIL); + NIL->setPrev(NIL); +} + +template +task::list::list(const task::list& other): + alloc_(node_alloc_traits::select_on_container_copy_construction(other.alloc_)), + NIL(alloc_.allocate(1)) +{ + size_ = 0; + alloc_.construct(NIL); + NIL->setNext(NIL); + NIL->setPrev(NIL); + + this->copyValues(other.begin(), other.end()); +} + +template +task::list::list(const task::list& other, const Allocator& alloc): alloc_(alloc), NIL(alloc.allocate(1)) { + alloc.construct(NIL); + NIL->setPrev(NIL); + NIL->setNext(NIL); + size_ = 0; + + this->copyValues(other.begin(), other.end()); +} + +template +task::list::~list() { + if (NIL == nullptr) { + return; + } + Node* curr = NIL->getNext(); + while (curr && curr->getNext() != NIL) { + Node* to_delete = curr; + curr = curr->getNext(); + alloc_.destroy(to_delete); + alloc_.deallocate(to_delete, 1); + } + alloc_.destroy(NIL); + alloc_.deallocate(NIL, 1); +} + +template +task::list& task::list::operator=(const task::list& other) { + if (this != &other) { + if (node_alloc_traits::propagate_on_container_copy_assignment::value) { + if (alloc_ != other.alloc_) { + clear(); + } + alloc_ = other.alloc_; + + } + clear(); + Node* other_head = other.NIL->getNext(); + for (int i = 0; i < other.size(); ++i) { + this->push_back(other_head->getValue()); + other_head = other_head->getNext(); + } + size_ = other.size(); + } + return *this; +} + +template +void task::list::copyValues(iterator beg, iterator end) { + for (auto it = beg; it != end; it++) { + push_back(*it); + } +} + +template +void task::list::clear() { + while (size()) { + pop_back(); + } +} + +template +void task::list::push_back(const T& value) { + Node* curr_last = NIL->getPrev(); + Node* begin = NIL; + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, value); + + new_node->setNext(begin); + new_node->setPrev(curr_last); + begin->setPrev(new_node); + curr_last->setNext(new_node); + ++size_; +} + +template +void task::list::pop_back() { + Node* last = NIL->getPrev(); + if (last == NIL) { + throw std::runtime_error("Pop back in empty list"); + } + + Node* prevLast = last->getPrev(); + prevLast->setNext(NIL); + NIL->setPrev(prevLast); + alloc_.destroy(last); + alloc_.deallocate(last, 1); + --size_; +} + +template +void task::list::push_front(const T& value) { + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, value); + Node* curr_front = NIL->getNext(); + NIL->setNext(new_node); + new_node->setPrev(NIL); + + new_node->setNext(curr_front); + curr_front->setPrev(new_node); + size_++; +} + +template +void task::list::pop_front() { + if (NIL->getNext() == NIL) { + throw std::runtime_error("Pop front in empty list"); + } + Node* curr_front = NIL->getNext(); + Node* second = curr_front->getNext(); + NIL->setNext(second); + second->setPrev(NIL); + alloc_.destroy(curr_front); + alloc_.deallocate(curr_front, 1); + size_--; +} + +template +void task::list::resize(size_t count) { + while (size_ > count) { + pop_back(); + } + while (size_ < count) { + push_back({}); + } + size_ = count; +} + +template +void task::list::remove(const T& value) { + Node* curr = NIL->getNext(); + std::vector to_delete; + while (curr != NIL) { + if (curr->getValueRef() == value) { + to_delete.push_back(curr); + Node* prev = curr->getPrev(); + prev->setNext(curr->getNext()); + curr->getNext()->setPrev(prev); + curr = curr->getNext(); + size_--; + } else { + curr = curr->getNext(); + } + } + for (auto& el : to_delete) { + alloc_.destroy(el); + alloc_.deallocate(el, 1); + } +} + +template +void task::list::unique() { + Node* curr = NIL->getNext()->getNext(); + + while (curr->getPrev() != NIL) { + Node* prev = curr->getPrev(); + if (prev != NIL && prev->getValueRef() == curr->getValueRef()) { + Node* next = curr->getNext(); + prev->setNext(next); + next->setPrev(prev); + size_--; + Node* to_delete = curr; + curr = curr->getNext(); + + alloc_.destroy(to_delete); + alloc_.deallocate(to_delete, 1); + } else { + curr = curr->getNext(); + } + } +} + +template +void task::list::push_back(T&& value) { + Node* curr_last = NIL->getPrev(); + Node* begin = NIL; + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, std::forward(value)); + + new_node->setNext(begin); + new_node->setPrev(curr_last); + begin->setPrev(new_node); + curr_last->setNext(new_node); + ++size_; +} + +template +template +void task::list::emplace_back(Args&& ... args) { + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, std::forward(args)...); + Node* curr_front = NIL->getNext(); + NIL->setNext(new_node); + new_node->setPrev(NIL); + + new_node->setNext(curr_front); + curr_front->setPrev(new_node); + size_++; +} + +template +void task::list::push_front(T&& value) { + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, std::forward(value)); + Node* curr_front = NIL->getNext(); + NIL->setNext(new_node); + new_node->setPrev(NIL); + + new_node->setNext(curr_front); + curr_front->setPrev(new_node); + size_++; +} + +template +template +void task::list::emplace_front(Args&& ... args) { + Node* new_node = static_cast(alloc_.allocate(1)); + alloc_.construct(new_node, std::forward(args)...); + Node* curr_front = NIL->getNext(); + NIL->setNext(new_node); + new_node->setPrev(NIL); + + new_node->setNext(curr_front); + curr_front->setPrev(new_node); + size_++; +} + +template +task::list task::list::merge(const task::list& left, const task::list& right) { + task::list result; + + const Node* curr_left = left.NIL->getNext(); + const Node* curr_right = right.NIL->getNext(); + + while (curr_left != left.NIL && curr_right != right.NIL) { + if (curr_left->getValueRef() < curr_right->getValueRef()) { + result.push_back(std::move(curr_left->getValueRef())); + curr_left = curr_left->getNext(); + } else { + result.push_back(std::move(curr_right->getValueRef())); + curr_right = curr_right->getNext(); + } + } + + while (curr_left != left.NIL) { + result.push_back(std::move(curr_left->getValueRef())); + curr_left = curr_left->getNext(); + } + + while (curr_right != right.NIL) { + result.push_back(std::move(curr_right->getValueRef())); + curr_right = curr_right->getNext(); + } + + return result; +} + +template +std::pair*, task::list*> task::list::bisect(const task::list& source) { + int size = source.size(); + auto* left = new task::list(); + auto* right = new task::list(); + + Node* curr = source.NIL->getNext(); + + for (int i = 0; i < size / 2; ++i, curr = curr->getNext()) { + left->push_back(std::move(curr->getValueRef())); + } + + for (int i = size / 2; i < size; ++i, curr = curr->getNext()) { + right->push_back(std::move(curr->getValueRef())); + } + + return {left, right}; +} + +template +task::list task::list::sorted(const task::list& source) { + if (source.size() > 1) { + auto res = task::list::bisect(source); + return task::list::merge(task::list::sorted(*res.first), task::list::sorted(*res.second)); + } + return source; +} + +template +task::list::list(task::list&& other, const Allocator& alloc) : + alloc_(alloc), + NIL(other.NIL) +{ + other.NIL = nullptr; +} + +template +task::list::list(task::list&& other) noexcept : + alloc_(std::move(other.alloc_)), + NIL(other.NIL) +{ + other.NIL = nullptr; +} + +template +task::list& task::list::operator=(task::list&& other) noexcept { + clear(); + if (node_alloc_traits::propagate_on_container_move_assignment::value) { + alloc_ = std::move(other.alloc_); + NIL = other.NIL; + other.NIL = nullptr; + } else if (node_alloc_traits::is_always_equal::value || alloc_ == other.alloc_) { + NIL = other.NIL; + other.NIL = nullptr; + } else { + for (auto it = other.begin(); it != other.end(); ++it) { + push_back(std::move(*it)); + } + } + + return *this; +} + +template +typename task::list::reference task::list::front() { + if (!size()) { + throw std::runtime_error("get front in empty list"); + } + + return NIL->getNext()->getValueRef(); +} + +template +typename task::list::const_reference task::list::front() const { + if (!size()) { + throw std::runtime_error("get front in empty list"); + } + + return NIL->getNext()->getValueRef(); +} + +template +typename task::list::reference task::list::back() { + if (!size()) { + throw std::runtime_error("get back in empty list"); + } + + return NIL->getPrev()->getValueRef(); +} + +template +typename task::list::const_reference task::list::back() const { + if (!size()) { + throw std::runtime_error("get back in empty list"); + } + + return NIL->getPrev()->getValueRef(); +} + +template +void task::list::sort() { + *this = sorted(*this); +} + +template +void task::list::swap(task::list& other) noexcept { + if (!node_alloc_traits::propagate_on_container_swap::value && alloc_ != other.alloc_) { + throw std::runtime_error("Swap with different allocators"); + } + if (node_alloc_traits::propagate_on_container_swap::value) { + std::swap(alloc_, other.alloc_); + } + std::swap(NIL, other.NIL); +} + + + + + + + diff --git a/module-1/homework/Allocator/tests.cpp b/module-1/homework/Allocator/tests.cpp index 4d88d43a..ba43eae3 100644 --- a/module-1/homework/Allocator/tests.cpp +++ b/module-1/homework/Allocator/tests.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -7,6 +8,7 @@ #include "src/list/list.h" #include "gtest/gtest.h" +//#define CustomAllocator std::allocator TEST(CopyAssignment, Test) { task::list> actual; @@ -30,7 +32,7 @@ TEST(MoveAssignment, Test1) { l2 = std::move(l1); l3.push_back("hello"); l4 = std::move(l3); - + ASSERT_TRUE(std::equal(l1.begin(), l1.end(), l3.begin(), l3.end())); ASSERT_TRUE(std::equal(l2.begin(), l2.end(), l4.begin(), l4.end())); } @@ -38,7 +40,7 @@ TEST(MoveAssignment, Test1) { TEST(Front, Test1) { task::list> actual; std::list> expected; - + actual.push_back("hello"); expected.push_back("hello"); @@ -56,7 +58,7 @@ TEST(Front, Test1) { TEST(Clear, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.push_back("hello"); expected.push_back("hello"); @@ -69,16 +71,15 @@ TEST(Clear, Test1) { TEST(Swap, Test1) { task::list> actual; + task::list> actual2(actual); std::list> expected; - + std::list> expected2(expected); + for (std::size_t i = 0; i < 10; i++) { actual.push_back("hello"); expected.push_back("hello"); } - task::list> actual2; - std::list> expected2; - for (std::size_t i = 0; i < 10; i++) { actual2.push_back("world"); expected2.push_back("world"); @@ -94,18 +95,19 @@ TEST(PushBack, Test) { std::vector expected_v(10, "hello"); task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.push_back(std::move(actual_v[i])); expected.push_back(std::move(expected_v[i])); } + ASSERT_TRUE(std::equal(actual.begin(), actual.end(), expected.begin(), expected.end())); } TEST(EmplaceBack, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.emplace_back("hello"); expected.emplace_back("hello"); @@ -116,7 +118,7 @@ TEST(EmplaceBack, Test1) { TEST(PopBack, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.emplace_back("hello"); expected.emplace_back("hello"); @@ -134,7 +136,7 @@ TEST(PushFront, Test1) { std::vector expected_v(10, "hello"); task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.push_front(actual_v[i]); expected.push_front(expected_v[i]); @@ -147,18 +149,18 @@ TEST(PushFront, Test2) { std::vector expected_v(10, "hello"); task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.push_front(std::move(actual_v[i])); expected.push_front(std::move(expected_v[i])); } ASSERT_TRUE(std::equal(actual.begin(), actual.end(), expected.begin(), expected.end())); -} +} TEST(EmplaceFront, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.emplace_front("hello"); expected.emplace_front("hello"); @@ -169,7 +171,7 @@ TEST(EmplaceFront, Test1) { TEST(PopFront, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 10; i++) { actual.emplace_back("hello"); expected.emplace_back("hello"); @@ -209,6 +211,11 @@ TEST(Unique, Test1) { std::list> expected; expected.push_back("hello"); expected.push_back("world"); + + for (auto it = actual.begin(); it != actual.end(); ++it) { + std::cout << *it << ' '; + } + ASSERT_TRUE(std::equal(actual.begin(), actual.end(), expected.begin(), expected.end())); } @@ -232,7 +239,7 @@ TEST(Sort, Test1) { TEST(Mixed, Test1) { task::list> actual; std::list> expected; - + for (std::size_t i = 0; i < 5; i++) { actual.push_back("hello"); expected.push_back("hello");