mirror of
https://github.com/Yours3lf/rpi-vk-driver.git
synced 2024-11-29 11:24:14 +01:00
193 lines
5.1 KiB
C
193 lines
5.1 KiB
C
#include "ConsecutivePoolAllocator.h"
|
|
|
|
#include "CustomAssert.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
ConsecutivePoolAllocator createConsecutivePoolAllocator(char* b, unsigned bs, unsigned s)
|
|
{
|
|
assert(b); //only allocated memory
|
|
assert(bs >= sizeof(void*)); //we need to be able to store
|
|
assert(s%bs==0); //we want a size that is the exact multiple of block size
|
|
assert(s >= bs); //at least 1 element
|
|
|
|
ConsecutivePoolAllocator pa =
|
|
{
|
|
.buf = b,
|
|
.nextFreeBlock = (uint32_t*)b,
|
|
.blockSize = bs,
|
|
.size = s
|
|
};
|
|
|
|
//initialize linked list of free pointers
|
|
uint32_t* ptr = pa.nextFreeBlock;
|
|
unsigned last = s/bs - 1;
|
|
for(unsigned c = 0; c < last; ++c)
|
|
{
|
|
*ptr = (char*)ptr + bs;
|
|
ptr = (char*)ptr + bs;
|
|
}
|
|
|
|
*ptr = 0; //last element
|
|
|
|
return pa;
|
|
}
|
|
|
|
void destroyConsecutivePoolAllocator(ConsecutivePoolAllocator* pa)
|
|
{
|
|
//actual memory freeing is done by caller
|
|
pa->buf = 0;
|
|
pa->nextFreeBlock = 0;
|
|
pa->blockSize = 0;
|
|
pa->size = 0;
|
|
}
|
|
|
|
//allocate numBlocks consecutive memory
|
|
void* consecutivePoolAllocate(ConsecutivePoolAllocator* pa, uint32_t numBlocks)
|
|
{
|
|
assert(pa->buf);
|
|
|
|
CPAdebugPrint(pa);
|
|
|
|
if(!pa->nextFreeBlock)
|
|
{
|
|
return 0; //no free blocks
|
|
}
|
|
|
|
void* ret = 0;
|
|
for(uint32_t* candidate = pa->nextFreeBlock; candidate; candidate = (uint32_t*)*candidate)
|
|
{
|
|
uint32_t found = 1;
|
|
uint32_t* prevBlock = candidate;
|
|
uint32_t* blockAfterCandidate = (uint32_t*)*candidate;
|
|
//check if there are enough consecutive free blocks
|
|
for(uint32_t c = 0; c < numBlocks - 1; ++c)
|
|
{
|
|
if(blockAfterCandidate - prevBlock != pa->blockSize)
|
|
{
|
|
//signal if not consecutive (ie. diff is greater than blocksize)
|
|
found = 0;
|
|
break;
|
|
}
|
|
prevBlock = blockAfterCandidate;
|
|
blockAfterCandidate = (uint32_t*)*blockAfterCandidate;
|
|
}
|
|
|
|
//numblocks consecutive blocks found
|
|
if(found)
|
|
{
|
|
ret = candidate;
|
|
if(pa->nextFreeBlock == candidate)
|
|
{
|
|
//candidate found immediately
|
|
pa->nextFreeBlock = blockAfterCandidate;
|
|
}
|
|
else
|
|
{
|
|
//somewhere the linked list would point to candidate, we need to correct this
|
|
for(uint32_t* nextFreeBlockCandidate = pa->nextFreeBlock; nextFreeBlockCandidate; nextFreeBlockCandidate = (uint32_t*)*nextFreeBlockCandidate)
|
|
{
|
|
if((uint32_t*)*nextFreeBlockCandidate == candidate)
|
|
{
|
|
*nextFreeBlockCandidate = (uint32_t)blockAfterCandidate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//return a pointer pointing past the linked list ptr
|
|
return ret > 0 ? (char*)ret + 4 : ret;
|
|
}
|
|
|
|
//free numBlocks consecutive memory
|
|
void consecutivePoolFree(ConsecutivePoolAllocator* pa, void* p, uint32_t numBlocks)
|
|
{
|
|
assert(pa->buf);
|
|
assert(p);
|
|
|
|
p = (char*)p - 4;
|
|
|
|
if((void*)pa->nextFreeBlock > p)
|
|
{
|
|
for(uint32_t c = 0; c < numBlocks - 1; ++c)
|
|
{
|
|
//set each allocated block to form a linked list
|
|
*(uint32_t*)((char*)p + c * pa->blockSize) = (uint32_t)((char*)p + (c + 1) * pa->blockSize);
|
|
}
|
|
//set last block to point to the next free
|
|
*(uint32_t*)((char*)p + (numBlocks - 1) * pa->blockSize) = (uint32_t)pa->nextFreeBlock;
|
|
//set next free to the newly freed block
|
|
pa->nextFreeBlock = p;
|
|
return;
|
|
}
|
|
|
|
//somewhere the linked list may point after the free block (or null), we need to correct this
|
|
for(uint32_t* nextFreeBlockCandidate = pa->nextFreeBlock; nextFreeBlockCandidate; nextFreeBlockCandidate = (uint32_t*)*nextFreeBlockCandidate)
|
|
{
|
|
if((void*)*nextFreeBlockCandidate > p || !*nextFreeBlockCandidate)
|
|
{
|
|
for(uint32_t c = 0; c < numBlocks - 1; ++c)
|
|
{
|
|
//set each allocated block to form a linked list
|
|
*(uint32_t*)((char*)p + c * pa->blockSize) = (uint32_t)((char*)p + (c + 1) * pa->blockSize);
|
|
}
|
|
//set last block to point to the next free
|
|
*(uint32_t*)((char*)p + (numBlocks - 1) * pa->blockSize) = *nextFreeBlockCandidate;
|
|
|
|
*nextFreeBlockCandidate = (uint32_t)p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if there's a block free after the current block, it just allocates one more block
|
|
//else it frees current block and allocates a new one
|
|
void* consecutivePoolReAllocate(ConsecutivePoolAllocator* pa, void* currentMem, uint32_t currNumBlocks)
|
|
{
|
|
fprintf(stderr, "CPA realloc\n");
|
|
|
|
currentMem = (char*)currentMem - 4;
|
|
|
|
if(pa->nextFreeBlock == (uint32_t*)((char*)currentMem + currNumBlocks * pa->blockSize))
|
|
{
|
|
//we have one more block after current one, so just expand current
|
|
pa->nextFreeBlock = (uint32_t*)*pa->nextFreeBlock;
|
|
return currentMem;
|
|
}
|
|
else
|
|
{
|
|
void* ret = consecutivePoolAllocate(pa, currNumBlocks + 1);
|
|
char* newContents = ret;
|
|
char* oldContents = currentMem;
|
|
newContents += 4;
|
|
oldContents += 4;
|
|
memcpy(newContents, oldContents, currNumBlocks * pa->blockSize - 4);
|
|
consecutivePoolFree(pa, currentMem, currNumBlocks);
|
|
return newContents;
|
|
}
|
|
}
|
|
|
|
void CPAdebugPrint(ConsecutivePoolAllocator* pa)
|
|
{
|
|
fprintf(stderr, "\nCPA Debug Print\n");
|
|
fprintf(stderr, "pa->buf %p\n", pa->buf);
|
|
fprintf(stderr, "pa->nextFreeBlock %p\n", pa->nextFreeBlock);
|
|
|
|
fprintf(stderr, "Linear walk:\n");
|
|
for(char* ptr = pa->buf; ptr != pa->buf + pa->size; ptr += pa->blockSize)
|
|
{
|
|
fprintf(stderr, "%p: %p, ", ptr, *(uint32_t*)ptr);
|
|
}
|
|
|
|
fprintf(stderr, "\nLinked List walk:\n");
|
|
for(uint32_t* ptr = pa->nextFreeBlock; ptr; ptr = *ptr)
|
|
{
|
|
fprintf(stderr, "%p: %p, ", ptr, *ptr);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|