diff --git a/solutions/lab5/main.cpp b/solutions/lab5/main.cpp index e69de29..3ea3e36 100644 --- a/solutions/lab5/main.cpp +++ b/solutions/lab5/main.cpp @@ -0,0 +1,31 @@ +#include "src/solution.cpp" + +struct MyStruct { + int a; + double b; + std::string c; +}; + +int main() { + DynamicMemoryResource mr; + DynamicArray arr{&mr}; + DynamicArray structArr{&mr}; + + for (int i = 0; i < 10; ++i) { + arr.push_back(i); + } + + for (int x : arr) { + std::cout << x << " "; + } + std::cout << std::endl; + + structArr.push_back({1, 2.5, "hello"}); + structArr.push_back({2, 3.14, "world"}); + + for (const auto &s : structArr) { + std::cout << s.a << " " << s.b << " " << s.c << std::endl; + } + + return 0; +} diff --git a/solutions/lab5/src/solution.cpp b/solutions/lab5/src/solution.cpp index e69de29..16b7b79 100644 --- a/solutions/lab5/src/solution.cpp +++ b/solutions/lab5/src/solution.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include + +class DynamicMemoryResource : public std::pmr::memory_resource { +public: + void *do_allocate(std::size_t bytes, std::size_t alignment) override { + void *ptr = std::malloc(bytes); + if (!ptr) { + throw std::bad_alloc(); + } + allocatedBlocks[ptr] = bytes; + return ptr; + } + + void do_deallocate(void *p, std::size_t bytes, + std::size_t alignment) override { + auto it = allocatedBlocks.find(p); + if (it != allocatedBlocks.end()) { + assert(it->second == bytes); + allocatedBlocks.erase(it); + std::free(p); + } + } + + bool + do_is_equal(const std::pmr::memory_resource &other) const noexcept override { + return this == &other; + } + + ~DynamicMemoryResource() override { + for (auto const &[ptr, bytes] : allocatedBlocks) { + std::free(ptr); + } + } + +private: + std::map allocatedBlocks; +}; + +template > +requires std::is_default_constructible_v &&std::is_same_v< + allocator_type, std::pmr::polymorphic_allocator> class DynamicArray { +public: + explicit DynamicArray( + std::pmr::memory_resource *mr = std::pmr::get_default_resource()) + : alloc_(mr), data_(nullptr), size_(0), capacity_(0) {} + + DynamicArray(const DynamicArray &other) + : alloc_(other.alloc_), size_(other.size_), capacity_(other.capacity_) { + data_ = alloc_.allocate(capacity_); + for (size_t i = 0; i < size_; ++i) { + alloc_.construct(data_ + i, other.data_[i]); + } + } + + DynamicArray(DynamicArray &&other) noexcept + : alloc_(other.alloc_), data_(other.data_), size_(other.size_), + capacity_(other.capacity_) { + other.data_ = nullptr; + other.size_ = 0; + other.capacity_ = 0; + } + + ~DynamicArray() { + this->clear(); + alloc_.deallocate(data_, capacity_); + } + + void push_back(const T &value) { + if (size_ == capacity_) { + reserve(capacity_ == 0 ? 1 : capacity_ * 2); + } + alloc_.construct(data_ + size_, value); + ++size_; + } + + void reserve(size_t newCapacity) { + if (newCapacity > capacity_) { + T *newData = alloc_.allocate(newCapacity); + for (size_t i = 0; i < size_; ++i) { + std::allocator_traits::construct(alloc_, newData + i, + std::move(data_[i])); + std::allocator_traits::destroy(alloc_, data_ + i); + } + alloc_.deallocate(data_, capacity_); + data_ = newData; + capacity_ = newCapacity; + } + } + + void clear() { + for (size_t i = 0; i < size_; ++i) { + std::allocator_traits::destroy(alloc_, data_ + i); + } + size_ = 0; + } + + size_t size() const { return size_; } + + T &operator[](size_t index) { return data_[index]; } + const T &operator[](size_t index) const { return data_[index]; } + + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + + iterator(T *ptr) : ptr_(ptr) {} + + T &operator*() const { return *ptr_; } + T *operator->() const { return ptr_; } + iterator &operator++() { + ++ptr_; + return *this; + } + iterator operator++(int) { + iterator tmp = *this; + ++ptr_; + return tmp; + } + bool operator==(const iterator &other) const { return ptr_ == other.ptr_; } + bool operator!=(const iterator &other) const { return ptr_ != other.ptr_; } + T *get_ptr() const { return ptr_; } + + private: + T *ptr_; + }; + + iterator begin() { return iterator(data_); } + iterator end() { return iterator(data_ + size_); } + +private: + allocator_type alloc_; + T *data_; + size_t size_; + size_t capacity_; +}; diff --git a/solutions/lab5/src/solution.h b/solutions/lab5/src/solution.h deleted file mode 100644 index e69de29..0000000 diff --git a/solutions/lab5/tests/tests.cpp b/solutions/lab5/tests/tests.cpp index e69de29..5e965db 100644 --- a/solutions/lab5/tests/tests.cpp +++ b/solutions/lab5/tests/tests.cpp @@ -0,0 +1,133 @@ +#include "../src/solution.cpp" +#include "gtest/gtest.h" + +TEST(DynamicMemoryResourceTest, AllocateAndDeallocate) { + DynamicMemoryResource mr; + void *p1 = mr.allocate(100, alignof(std::max_align_t)); + ASSERT_NE(nullptr, p1); + mr.deallocate(p1, 100, alignof(std::max_align_t)); + + void *p2 = mr.allocate(200, alignof(std::max_align_t)); + ASSERT_NE(nullptr, p2); + ASSERT_NE(p1, p2); + + mr.deallocate(p2, 200, alignof(std::max_align_t)); +} + +TEST(DynamicMemoryResourceTest, DeallocateInvalidPointer) { + DynamicMemoryResource mr; + void *p = reinterpret_cast(0xDEADBEEF); + mr.deallocate(p, 100, alignof(std::max_align_t)); +} + +TEST(DynamicMemoryResourceTest, DeallocateWrongSize) { + DynamicMemoryResource mr; + void *p = mr.allocate(100, alignof(std::max_align_t)); + ASSERT_NE(nullptr, p); + ASSERT_DEATH(mr.deallocate(p, 200, alignof(std::max_align_t)), ""); + + mr.deallocate(p, 100, alignof(std::max_align_t)); +} + +TEST(DynamicArrayTest, BasicOperations) { + DynamicMemoryResource mr; + DynamicArray arr{&mr}; + + arr.push_back(1); + arr.push_back(2); + arr.push_back(3); + + ASSERT_EQ(3, arr.size()); + ASSERT_EQ(1, arr[0]); + ASSERT_EQ(2, arr[1]); + ASSERT_EQ(3, arr[2]); + + arr.clear(); + ASSERT_EQ(0, arr.size()); +} + +TEST(DynamicArrayTest, Reserve) { + DynamicMemoryResource mr; + DynamicArray arr{&mr}; + + arr.reserve(5); + ASSERT_EQ(0, arr.size()); + + arr.push_back(1); + arr.push_back(2); + + ASSERT_EQ(2, arr.size()); +} + +TEST(DynamicArrayTest, CopyConstructor) { + DynamicMemoryResource mr; + DynamicArray arr1{&mr}; + arr1.push_back(1); + arr1.push_back(2); + + DynamicArray arr2 = arr1; + + ASSERT_EQ(2, arr2.size()); + ASSERT_EQ(1, arr2[0]); + ASSERT_EQ(2, arr2[1]); + + arr1[0] = 3; + ASSERT_EQ(1, arr2[0]); +} + +TEST(DynamicArrayTest, MoveConstructor) { + DynamicMemoryResource mr; + DynamicArray arr1{&mr}; + arr1.push_back(1); + arr1.push_back(2); + + DynamicArray arr2 = std::move(arr1); + + ASSERT_EQ(2, arr2.size()); + ASSERT_EQ(1, arr2[0]); + ASSERT_EQ(2, arr2[1]); + ASSERT_EQ(0, arr1.size()); + ASSERT_EQ(nullptr, arr1.begin().get_ptr()); +} + +TEST(DynamicArrayTest, Iterator) { + DynamicMemoryResource mr; + DynamicArray arr{&mr}; + + arr.push_back(1); + arr.push_back(2); + arr.push_back(3); + + int count = 1; + for (int x : arr) { + ASSERT_EQ(count, x); + ++count; + } +} + +struct MyStruct { + int a; + double b; + std::string c; + + bool operator==(const MyStruct &other) const { + return a == other.a && b == other.b && c == other.c; + } +}; + +TEST(DynamicArrayTest, ComplexType) { + DynamicMemoryResource mr; + DynamicArray arr{&mr}; + + arr.push_back({1, 2.5, "hello"}); + arr.push_back({2, 3.14, "world"}); + + ASSERT_EQ(2, arr.size()); + ASSERT_EQ((MyStruct{1, 2.5, "hello"}), arr[0]); + ASSERT_EQ((MyStruct{2, 3.14, "world"}), arr[1]); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}