|
@@ -105,4 +105,227 @@ struct FreeListA {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+struct LinkedListNode{
|
|
|
|
|
+ LinkedListNode* prev;
|
|
|
|
|
+ LinkedListNode* next;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+template<typename T>
|
|
|
|
|
+struct DoubleLinkedList{
|
|
|
|
|
+ static_assert(std::is_base_of_v<LinkedListNode, T>);
|
|
|
|
|
+ int _size;
|
|
|
|
|
+ LinkedListNode head;
|
|
|
|
|
+ LinkedListNode tail;
|
|
|
|
|
+
|
|
|
|
|
+ DoubleLinkedList(): _size(0){
|
|
|
|
|
+ head.prev = nullptr;
|
|
|
|
|
+ head.next = &tail;
|
|
|
|
|
+ tail.prev = &head;
|
|
|
|
|
+ tail.next = nullptr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void push_back(T* node){
|
|
|
|
|
+ node->prev = tail.prev;
|
|
|
|
|
+ node->next = &tail;
|
|
|
|
|
+ tail.prev->next = node;
|
|
|
|
|
+ tail.prev = node;
|
|
|
|
|
+ _size++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void push_front(T* node){
|
|
|
|
|
+ node->prev = &head;
|
|
|
|
|
+ node->next = head.next;
|
|
|
|
|
+ head.next->prev = node;
|
|
|
|
|
+ head.next = node;
|
|
|
|
|
+ _size++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void pop_back(){
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("DoubleLinkedList::pop_back() called on empty list");
|
|
|
|
|
+#endif
|
|
|
|
|
+ tail.prev->prev->next = &tail;
|
|
|
|
|
+ tail.prev = tail.prev->prev;
|
|
|
|
|
+ _size--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void pop_front(){
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("DoubleLinkedList::pop_front() called on empty list");
|
|
|
|
|
+#endif
|
|
|
|
|
+ head.next->next->prev = &head;
|
|
|
|
|
+ head.next = head.next->next;
|
|
|
|
|
+ _size--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T* back() const {
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("DoubleLinkedList::back() called on empty list");
|
|
|
|
|
+#endif
|
|
|
|
|
+ return static_cast<T*>(tail.prev);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T* front() const {
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("DoubleLinkedList::front() called on empty list");
|
|
|
|
|
+#endif
|
|
|
|
|
+ return static_cast<T*>(head.next);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void erase(T* node){
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("DoubleLinkedList::erase() called on empty list");
|
|
|
|
|
+ LinkedListNode* n = head.next;
|
|
|
|
|
+ while(n != &tail){
|
|
|
|
|
+ if(n == node) break;
|
|
|
|
|
+ n = n->next;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(n != node) throw std::runtime_error("DoubleLinkedList::erase() called on node not in the list");
|
|
|
|
|
+#endif
|
|
|
|
|
+ node->prev->next = node->next;
|
|
|
|
|
+ node->next->prev = node->prev;
|
|
|
|
|
+ _size--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void move_all_back(DoubleLinkedList<T>& other){
|
|
|
|
|
+ if(other.empty()) return;
|
|
|
|
|
+ other.tail.prev->next = &tail;
|
|
|
|
|
+ tail.prev->next = other.head.next;
|
|
|
|
|
+ other.head.next->prev = tail.prev;
|
|
|
|
|
+ tail.prev = other.tail.prev;
|
|
|
|
|
+ _size += other._size;
|
|
|
|
|
+ other.head.next = &other.tail;
|
|
|
|
|
+ other.tail.prev = &other.head;
|
|
|
|
|
+ other._size = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool empty() const {
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(size() == 0){
|
|
|
|
|
+ if(head.next != &tail || tail.prev != &head){
|
|
|
|
|
+ throw std::runtime_error("DoubleLinkedList::size() returned 0 but the list is not empty");
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+ return _size == 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int size() const { return _size; }
|
|
|
|
|
+
|
|
|
|
|
+ void apply(std::function<void(T*)> func){
|
|
|
|
|
+ LinkedListNode* p = head.next;
|
|
|
|
|
+ while(p != &tail){
|
|
|
|
|
+ LinkedListNode* next = p->next;
|
|
|
|
|
+ func(static_cast<T*>(p));
|
|
|
|
|
+ p = next;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+template<int __BlockSize=128>
|
|
|
|
|
+struct MemoryPool{
|
|
|
|
|
+ static const size_t __MaxBlocks = 256*1024 / __BlockSize;
|
|
|
|
|
+ struct Block{
|
|
|
|
|
+ void* arena;
|
|
|
|
|
+ char data[__BlockSize];
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ struct Arena: LinkedListNode{
|
|
|
|
|
+ Block _blocks[__MaxBlocks];
|
|
|
|
|
+ Block* _free_list[__MaxBlocks];
|
|
|
|
|
+ int _free_list_size;
|
|
|
|
|
+
|
|
|
|
|
+ Arena(): _free_list_size(__MaxBlocks) {
|
|
|
|
|
+ for(int i=0; i<__MaxBlocks; i++){
|
|
|
|
|
+ _blocks[i].arena = this;
|
|
|
|
|
+ _free_list[i] = &_blocks[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool empty() const { return _free_list_size == 0; }
|
|
|
|
|
+ bool full() const { return _free_list_size == __MaxBlocks; }
|
|
|
|
|
+
|
|
|
|
|
+ Block* alloc(){
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(empty()) throw std::runtime_error("Arena::alloc() called on empty arena");
|
|
|
|
|
+#endif
|
|
|
|
|
+ _free_list_size--;
|
|
|
|
|
+ return _free_list[_free_list_size];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void dealloc(Block* block){
|
|
|
|
|
+#if DEBUG_MEMORY_POOL
|
|
|
|
|
+ if(full()) throw std::runtime_error("Arena::dealloc() called on full arena");
|
|
|
|
|
+#endif
|
|
|
|
|
+ _free_list[_free_list_size] = block;
|
|
|
|
|
+ _free_list_size++;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ DoubleLinkedList<Arena> _arenas;
|
|
|
|
|
+ DoubleLinkedList<Arena> _empty_arenas;
|
|
|
|
|
+ DoubleLinkedList<Arena> _full_arenas;
|
|
|
|
|
+
|
|
|
|
|
+ template<typename __T>
|
|
|
|
|
+ void* alloc() { return alloc(sizeof(__T)); }
|
|
|
|
|
+
|
|
|
|
|
+ void* alloc(size_t size){
|
|
|
|
|
+ if(size > __BlockSize){
|
|
|
|
|
+ void* p = malloc(sizeof(void*) + size);
|
|
|
|
|
+ memset(p, 0, sizeof(void*));
|
|
|
|
|
+ return (char*)p + sizeof(void*);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(_arenas.empty()){
|
|
|
|
|
+ if(_full_arenas.empty()){
|
|
|
|
|
+ _arenas.push_back(new Arena());
|
|
|
|
|
+ }else{
|
|
|
|
|
+ _arenas.move_all_back(_full_arenas);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Arena* arena = _arenas.back();
|
|
|
|
|
+ void* p = arena->alloc()->data;
|
|
|
|
|
+ if(arena->empty()){
|
|
|
|
|
+ _arenas.pop_back();
|
|
|
|
|
+ _empty_arenas.push_back(arena);
|
|
|
|
|
+ }
|
|
|
|
|
+ return p;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void dealloc(void* p){
|
|
|
|
|
+ Block* block = (Block*)((char*)p - sizeof(void*));
|
|
|
|
|
+ if(block->arena == nullptr){
|
|
|
|
|
+ free(block);
|
|
|
|
|
+ }else{
|
|
|
|
|
+ Arena* arena = (Arena*)block->arena;
|
|
|
|
|
+ if(arena->empty()){
|
|
|
|
|
+ _empty_arenas.erase(arena);
|
|
|
|
|
+ _arenas.push_front(arena);
|
|
|
|
|
+ arena->dealloc(block);
|
|
|
|
|
+ }else{
|
|
|
|
|
+ arena->dealloc(block);
|
|
|
|
|
+ if(arena->full()){ // && _arenas.size() > 2
|
|
|
|
|
+ _arenas.erase(arena);
|
|
|
|
|
+ if(_full_arenas.size() < 8){
|
|
|
|
|
+ _full_arenas.push_back(arena);
|
|
|
|
|
+ }else{
|
|
|
|
|
+ delete arena;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ~MemoryPool(){
|
|
|
|
|
+ // std::cout << _arenas.size() << std::endl;
|
|
|
|
|
+ // std::cout << _empty_arenas.size() << std::endl;
|
|
|
|
|
+ // std::cout << _full_arenas.size() << std::endl;
|
|
|
|
|
+ _arenas.apply([](Arena* arena){ delete arena; });
|
|
|
|
|
+ _empty_arenas.apply([](Arena* arena){ delete arena; });
|
|
|
|
|
+ _full_arenas.apply([](Arena* arena){ delete arena; });
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
}; // namespace pkpy
|
|
}; // namespace pkpy
|