diff --git a/src/driver/allocator.hpp b/src/driver/allocator.hpp new file mode 100644 index 0000000..ab976db --- /dev/null +++ b/src/driver/allocator.hpp @@ -0,0 +1,38 @@ +#pragma once +#include "memory.hpp" + +namespace utils +{ + template + concept IsAllocator = requires(size_t size, void* ptr) + { + T().free(T().allocate(size)); + T().free(ptr); + }; + + struct AlignedAllocator + { + void* allocate(const size_t size) + { + return memory::allocate_aligned_memory(size); + } + + void free(void* ptr) + { + memory::free_aligned_memory(ptr); + } + }; + + struct NonPagedAllocator + { + void* allocate(const size_t size) + { + return memory::allocate_non_paged_memory(size); + } + + void free(void* ptr) + { + memory::free_non_paged_memory(ptr); + } + }; +} diff --git a/src/driver/list.hpp b/src/driver/list.hpp new file mode 100644 index 0000000..c40e005 --- /dev/null +++ b/src/driver/list.hpp @@ -0,0 +1,295 @@ +#pragma once +#include "allocator.hpp" +#include "exception.hpp" +#include "finally.hpp" + +namespace utils +{ + template + requires IsAllocator && IsAllocator + class list + { + struct ListEntry + { + T* entry{nullptr}; + ListEntry* next{nullptr}; + + void* this_base{nullptr}; + void* entry_base{nullptr}; + }; + + public: + using type = T; + + class iterator + { + friend list; + + public: + iterator(ListEntry* entry = nullptr) + : entry_(entry) + { + } + + T* operator*() const + { + return this->entry_->entry; + } + + T* operator->() const + { + return this->entry_->entry; + } + + bool operator==(const iterator& i) const + { + return this->entry_ == i.entry_; + } + + iterator operator++() const + { + this->entry_ = this->entry_->next; + return *this; + } + + private: + ListEntry* entry_{nullptr}; + + ListEntry* get_entry() const + { + return entry_; + } + }; + + class const_iterator + { + friend list; + + public: + const_iterator(ListEntry* entry = nullptr) + : entry_(entry) + { + } + + const T* operator*() const + { + return this->entry_->entry; + } + + const T* operator->() const + { + return this->entry_->entry; + } + + bool operator==(const const_iterator& i) const + { + return this->entry_ == i.entry_; + } + + const_iterator operator++() const + { + this->entry_ = this->entry_->next; + return *this; + } + + private: + ListEntry* entry_{nullptr}; + + ListEntry* get_entry() const + { + return entry_; + } + }; + + list() = default; + + ~list() + { + this->clear(); + } + + list(const list& obj) + : list() + { + this->operator=(obj); + } + + list(list&& obj) noexcept + : list() + { + this->operator=(std::move(obj)); + } + + list& operator=(const list& obj) + { + if (this != &obj) + { + this->clear(); + + for (const auto& i : obj) + { + this->push_back(i); + } + } + + return *this; + } + + list& operator=(list&& obj) noexcept + { + if (this != &obj) + { + this->clear(); + + this->entries_ = obj.entries_; + obj.entries_ = nullptr; + } + + return *this; + } + + T& push_back(T obj) + { + auto** inseration_point = &this->entries_; + while (*inseration_point) + { + inseration_point = &(*inseration_point)->next; + } + + auto* list_base = this->list_allocator_.allocate(sizeof(ListEntry) + alignof(ListEntry)); + auto* entry_base = this->object_allocator_.allocate(sizeof(T) + alignof(T)); + + auto* entry = align_pointer(entry_base); + auto* list_entry = align_pointer(list_base); + + list_entry->this_base = list_base; + list_entry->entry_base = entry_base; + list_entry->next = nullptr; + list_entry->entry = entry; + + *inseration_point = list_entry; + + new(entry) T(std::move(obj)); + + return *entry; + } + + T& operator[](const size_t index) + { + return this->at(index); + } + + const T& operator[](const size_t index) const + { + return this->at(index); + } + + T& at(const size_t index) + { + size_t i = 0; + for (auto& obj : *this) + { + if (++i == index) + { + return obj; + } + } + + throw std::runtime_error("Out of bounds access"); + } + + const T& at(const size_t index) const + { + size_t i = 0; + for (const auto& obj : *this) + { + if (++i == index) + { + return obj; + } + } + + throw std::runtime_error("Out of bounds access"); + } + + void clear() + { + while (this->entries_) + { + this->erase(this->begin()); + } + } + + [[nodiscard]] size_t size() const + { + size_t i = 0; + for (const auto& obj : *this) + { + ++i; + } + + return i; + } + + iterator begin() + { + return {this->entries_}; + } + + const_iterator begin() const + { + return {this->entries_}; + } + + iterator end() + { + return {}; + } + + const_iterator end() const + { + return {}; + } + + iterator erase(iterator iterator) + { + auto* list_entry = iterator.get_entry(); + auto** inseration_point = &this->entries_; + while (*inseration_point && list_entry) + { + if (*inseration_point != list_entry) + { + inseration_point = &(*inseration_point)->next; + continue; + } + + *inseration_point = list_entry->next; + + list_entry->entry->~T(); + this->object_allocator_.free(list_entry->entry_base); + this->list_allocator_.free(list_entry->this_base); + + return iterator(*inseration_point); + } + + throw std::runtime_error("Bad iterator"); + } + + private: + friend iterator; + + ObjectAllocator object_allocator_{}; + ListAllocator list_allocator_{}; + ListEntry* entries_{nullptr}; + + template + static U* align_pointer(V* pointer) + { + const auto align_bits = alignof(U) - 1; + auto ptr = reinterpret_cast(pointer); + ptr = (ptr + align_bits) & (~align_bits); + + return reinterpret_cast(ptr); + } + }; +} diff --git a/src/driver/vector.hpp b/src/driver/vector.hpp index 68563c5..8df0e80 100644 --- a/src/driver/vector.hpp +++ b/src/driver/vector.hpp @@ -1,12 +1,12 @@ #pragma once -#include "type_traits.hpp" -#include "memory.hpp" +#include "allocator.hpp" #include "exception.hpp" #include "finally.hpp" namespace utils { - template + template + requires IsAllocator class vector { public: @@ -38,9 +38,9 @@ namespace utils this->clear(); this->reserve(obj.size_); - for (size_t i = 0; i < obj.size_; ++i) + for (const auto& i : obj) { - this->push_back(obj.at(i)); + this->push_back(i); } } @@ -75,12 +75,12 @@ namespace utils auto* old_mem = this->storage_; auto* old_data = this->data(); - this->storage_ = allocate_memory_for_capacity(capacity); + this->storage_ = this->allocate_memory_for_capacity(capacity); this->capacity_ = capacity; - auto _ = utils::finally([&old_mem] + auto _ = utils::finally([&old_mem, this] { - free_memory(old_mem); + this->free_memory(old_mem); }); auto* data = this->data(); @@ -198,21 +198,41 @@ namespace utils return this->data() + this->size_; } + T* erase(T* iterator) + { + auto index = iterator - this->begin(); + if (index < 0 || static_cast(index) > this->size_) + { + throw std::runtime_error("Bad iterator"); + } + + const auto data = this->data(); + for (size_t i = index + 1; i < this->size_; ++i) + { + data[i - 1] = std::move(data[i]); + } + + data[this->size_--].~T(); + + return iterator; + } + private: + Allocator allocator_{}; void* storage_{nullptr}; size_t capacity_{0}; size_t size_{0}; - static void* allocate_memory_for_capacity(const size_t capacity) + void* allocate_memory_for_capacity(const size_t capacity) { constexpr auto alignment = alignof(T); - auto* memory = memory::allocate_non_paged_memory(capacity * sizeof(T) + alignment); + auto* memory = this->allocator_.allocate(capacity * sizeof(T) + alignment); return memory; } - static void free_memory(void* memory) + void free_memory(void* memory) { - memory::free_non_paged_memory(memory); + this->allocator_.free(memory); } template