]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add proper linear heap allocator
authorfincs <fincs.alt1@gmail.com>
Tue, 16 Sep 2014 22:22:30 +0000 (00:22 +0200)
committerfincs <fincs.alt1@gmail.com>
Tue, 16 Sep 2014 22:22:30 +0000 (00:22 +0200)
libctru/Makefile
libctru/source/allocator/linear.cpp [new file with mode: 0644]
libctru/source/allocator/mem_pool.cpp [new file with mode: 0644]
libctru/source/allocator/mem_pool.h [new file with mode: 0644]
libctru/source/linear.c [deleted file]

index 83aa6d0daa39086927100c4083cc629418dffe3c..c42536be10788213bd0da53f8c0348730176734d 100644 (file)
@@ -17,7 +17,7 @@ include $(DEVKITARM)/base_rules
 #---------------------------------------------------------------------------------
 TARGET         :=      ctru
 BUILD          :=      build
-SOURCES                :=      source source/services source/gpu
+SOURCES                :=      source source/services source/gpu source/allocator
 DATA           :=      data
 INCLUDES       :=      include
 
diff --git a/libctru/source/allocator/linear.cpp b/libctru/source/allocator/linear.cpp
new file mode 100644 (file)
index 0000000..a7581e8
--- /dev/null
@@ -0,0 +1,50 @@
+#include <3ds.h>
+#include "mem_pool.h"
+
+extern u32 __linear_heap, __linear_heap_size;
+
+static MemPool sLinearPool;
+
+static bool linearInit()
+{
+       auto blk = MemBlock::Create((u8*)__linear_heap, __linear_heap_size);
+       if (blk)
+       {
+               sLinearPool.AddBlock(blk);
+               return true;
+       }
+       return false;
+}
+
+void* linearAlloc(size_t size)
+{
+       // Initialize the pool if it is not ready
+       if (!sLinearPool.Ready() && !linearInit())
+               return nullptr;
+
+       // Reserve memory for MemChunk structure
+       size += 16;
+
+       // Allocate the chunk
+       MemChunk chunk;
+       if (!sLinearPool.Allocate(chunk, size, 4)) // 16-byte alignment
+               return nullptr;
+
+       // Copy the MemChunk structure and return memory
+       auto addr = chunk.addr;
+       *(MemChunk*)addr = chunk;
+       return addr + 16;
+}
+
+void* linearRealloc(void* mem, size_t size)
+{
+       // TODO
+       return NULL;
+}
+
+void linearFree(void* mem)
+{
+       // Find MemChunk structure and free the chunk
+       auto pChunk = (MemChunk*)((u8*)mem - 16);
+       sLinearPool.Deallocate(*pChunk);
+}
diff --git a/libctru/source/allocator/mem_pool.cpp b/libctru/source/allocator/mem_pool.cpp
new file mode 100644 (file)
index 0000000..93eeb4f
--- /dev/null
@@ -0,0 +1,125 @@
+#include "mem_pool.h"
+
+/*
+// This method is currently unused
+void MemPool::CoalesceLeft(MemBlock* b)
+{
+       auto curPtr = b->base;
+       for (auto p = b->prev; p; p = p->prev)
+       {
+               if ((p->base + p->size) != curPtr) break;
+               curPtr = p->base;
+               p->size += b->size;
+               DelBlock(b);
+               b = p;
+       }
+}
+*/
+
+void MemPool::CoalesceRight(MemBlock* b)
+{
+       auto curPtr = b->base + b->size;
+       auto next = b->next;
+       for (auto n = next; n; n = next)
+       {
+               next = n->next;
+               if (n->base != curPtr) break;
+               b->size += n->size;
+               curPtr += n->size;
+               DelBlock(n);
+       }
+}
+
+bool MemPool::Allocate(MemChunk& chunk, u32 size, int align)
+{
+       int alignM = (1 << align) - 1;
+       size = (size + alignM) &~ alignM; // Round the size
+       // Find the first suitable block
+       for (auto b = first; b; b = b->next)
+       {
+               auto addr = b->base;
+               u32 begWaste = (u32)addr & alignM;
+               addr += begWaste;
+               u32 bSize = b->size - begWaste;
+               if (bSize < size) continue;
+
+               // Found space!
+               chunk.addr = addr;
+               chunk.size = size;
+
+               // Resize the block
+               if (!begWaste)
+               {
+                       b->base += size;
+                       b->size -= size;
+                       if (!b->size)
+                               DelBlock(b);
+               } else
+               {
+                       auto nAddr = addr + size;
+                       auto nSize = bSize - size;
+                       b->size = begWaste;
+                       if (nSize)
+                       {
+                               // We need to add the tail chunk that wasn't used to the list
+                               auto n = MemBlock::Create(nAddr, nSize);
+                               if (n) InsertAfter(b, n);
+                               else   chunk.size += nSize; // we have no choice but to waste the space.
+                       }
+               }
+               return true;
+       }
+
+       return false;
+}
+
+void MemPool::Deallocate(const MemChunk& chunk)
+{
+       u8*  cAddr = chunk.addr;
+       auto cSize = chunk.size;
+       bool done = false;
+
+       // Try to merge the chunk somewhere into the list
+       for (auto b = first; !done && b; b = b->next)
+       {
+               auto addr = b->base;
+               if (addr > cAddr)
+               {
+                       if ((cAddr + cSize) == addr)
+                       {
+                               // Merge the chunk to the left of the block
+                               b->base = cAddr;
+                               b->size += cSize;
+                       } else
+                       {
+                               // We need to insert a new block
+                               auto c = MemBlock::Create(cAddr, cSize);
+                               if (c) InsertBefore(b, c);
+                       }
+                       done = true;
+               } else if ((b->base + b->size) == cAddr)
+               {
+                       // Coalesce to the right
+                       b->size += cSize;
+                       CoalesceRight(b);
+                       done = true;
+               }
+       }
+
+       if (!done)
+       {
+               // Either the list is empty or the chunk address is past the end
+               // address of the last block -- let's add a new block at the end
+               auto b = MemBlock::Create(cAddr, cSize);
+               if (b) AddBlock(b);
+       }
+}
+
+/*
+void MemPool::Dump(const char* title)
+{
+       printf("<%s> VRAM Pool Dump\n", title);
+       for (auto b = first; b; b = b->next)
+               printf("  - %p (%u bytes)\n", b->base, b->size);
+}
+*/
diff --git a/libctru/source/allocator/mem_pool.h b/libctru/source/allocator/mem_pool.h
new file mode 100644 (file)
index 0000000..7237f49
--- /dev/null
@@ -0,0 +1,88 @@
+#pragma once
+#include <3ds.h>
+
+struct MemChunk
+{
+       u8* addr;
+       u32 size;
+};
+
+struct MemBlock
+{
+       MemBlock *prev, *next;
+       u8* base;
+       u32 size;
+
+       static MemBlock* Create(u8* base, u32 size)
+       {
+               auto b = (MemBlock*)malloc(sizeof(MemBlock));
+               if (!b) return nullptr;
+               b->prev = nullptr;
+               b->next = nullptr;
+               b->base = base;
+               b->size = size;
+               return b;
+       }
+};
+
+struct MemPool
+{
+       MemBlock *first, *last;
+
+       bool Ready() { return first != nullptr; }
+
+       void AddBlock(MemBlock* blk)
+       {
+               blk->prev = last;
+               if (last) last->next = blk;
+               if (!first) first = blk;
+               last = blk;
+       }
+
+       void DelBlock(MemBlock* b)
+       {
+               auto prev = b->prev, &pNext = prev ? prev->next : first;
+               auto next = b->next, &nPrev = next ? next->prev : last;
+               pNext = next;
+               nPrev = prev;
+               free(b);
+       }
+
+       void InsertBefore(MemBlock* b, MemBlock* p)
+       {
+               auto prev = b->prev, &pNext = prev ? prev->next : first;
+               b->prev = p;
+               p->next = b;
+               p->prev = prev;
+               pNext = p;
+       }
+
+       void InsertAfter(MemBlock* b, MemBlock* n)
+       {
+               auto next = b->next, &nPrev = next ? next->prev : last;
+               b->next = n;
+               n->prev = b;
+               n->next = next;
+               nPrev = n;
+       }
+
+       //void CoalesceLeft(MemBlock* b);
+       void CoalesceRight(MemBlock* b);
+
+       bool Allocate(MemChunk& chunk, u32 size, int align);
+       void Deallocate(const MemChunk& chunk);
+
+       void Destroy()
+       {
+               MemBlock* next = nullptr;
+               for (auto b = first; b; b = next)
+               {
+                       next = b->next;
+                       free(b);
+               }
+               first = nullptr;
+               last = nullptr;
+       }
+
+       //void Dump(const char* title);
+};
diff --git a/libctru/source/linear.c b/libctru/source/linear.c
deleted file mode 100644 (file)
index 66eb43c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <3ds.h>
-
-extern u32 __linear_heap, __linear_heap_size;
-
-// TODO: this allocator sucks! It is not thread-safe and you cannot 'free' this memory.
-void* linearAlloc(size_t size)
-{
-       static size_t currentOffset = 0;
-       size_t free = __linear_heap_size - currentOffset;
-
-       // Enforce 16-byte alignment
-       size = (size + 15) &~ 15;
-
-       void* mem = NULL;
-       if (free >= size)
-       {
-               mem = (void*)(__linear_heap + currentOffset);
-               currentOffset += size;
-       }
-
-       return mem;
-}
-
-void* linearRealloc(void* mem, size_t size)
-{
-       return NULL; // TODO
-}
-
-void linearFree(void* mem)
-{
-       // TODO
-}