0ad/source/lib/mem.cpp

167 lines
3.1 KiB
C++
Raw Normal View History

// malloc layer for less fragmentation, alignment, and automatic release
#include <cstdlib>
2003-11-12 15:47:38 +01:00
#include <cassert>
#include <map>
#include "types.h"
#include "mem.h"
#include "res.h"
#include "misc.h"
#include "posix.h"
2003-11-12 15:47:38 +01:00
static void heap_free(MEM* m)
{
2003-11-12 15:47:38 +01:00
free(m->org_p);
}
2003-11-12 15:47:38 +01:00
static void* heap_alloc(const size_t size, const int align, MEM* mem)
{
u8* org_p = (u8*)malloc(size+align-1);
u8* p = (u8*)round_up((uintptr_t)org_p, align);
2003-11-12 15:47:38 +01:00
mem->org_p = org_p;
return p;
}
2003-11-12 15:47:38 +01:00
//////////////////////////////////////////////////////////////////////////////
static u8* pool;
static size_t pool_pos;
static const size_t POOL_CAP = 64*MB; // TODO: user editable
2003-11-12 15:47:38 +01:00
static void pool_free(MEM* m)
{
// at end of pool? if so, 'free' it
2003-11-12 15:47:38 +01:00
if(m->ofs + m->size == pool_pos)
pool_pos -= m->size;
}
2003-11-12 15:47:38 +01:00
static void* pool_alloc(const size_t size, const uint align, MEM* mem)
{
if(!pool)
{
2003-11-12 15:47:38 +01:00
pool = (u8*)mem_alloc(size, align, MEM_HEAP);
if(!pool)
return 0;
}
ptrdiff_t ofs = (u8*)round_up((uintptr_t)pool+pool_pos, align) - pool;
if(ofs+size > POOL_CAP)
return 0;
void* p = (u8*)pool + ofs;
2003-11-12 15:47:38 +01:00
mem->size = size;
mem->ofs = ofs;
pool_pos = ofs+size;
return p;
}
2003-11-12 15:47:38 +01:00
//////////////////////////////////////////////////////////////////////////////
2003-11-12 15:47:38 +01:00
static void mmap_free(MEM* m)
{
munmap(m->p, (uint)m->size);
}
2003-11-12 15:47:38 +01:00
static void* mmap_alloc(const size_t size, const int fd, MEM* mem)
{
mem->p = mmap(0, (uint)size, PROT_READ, MAP_PRIVATE, fd, 0);
2003-11-12 15:47:38 +01:00
mem->size = size;
mem->fd = fd;
2003-11-12 15:47:38 +01:00
return mem->p;
}
2003-11-12 15:47:38 +01:00
//////////////////////////////////////////////////////////////////////////////
2003-11-12 15:47:38 +01:00
static void mem_dtor(void* p)
{
MEM* m = (MEM*)p;
if(m->type == MEM_HEAP)
heap_free(m);
else if(m->type == MEM_POOL)
pool_free(m);
else if(m->type == MEM_MAPPED)
mmap_free(m);
else
assert(0 && "mem_dtor: MEM.type invalid!");
}
int mem_free(void* p)
{
if(!p)
return 1;
Handle h = h_find((uintptr_t)p, H_MEM, 0);
if(h)
2003-11-05 17:52:41 +01:00
return h_free(h, H_MEM);
return -1;
2003-11-12 15:47:38 +01:00
}
int mem_free(Handle hm)
{
return h_free(hm, H_MEM);
}
void* mem_alloc(size_t size, const uint align, const MemType type, const int fd, Handle* ph)
{
assert(size != 0 && "mem_alloc: why is size = 0?");
// bit of a hack: the allocators require space for bookkeeping,
// but we can't allocate a handle until we know the key
// (the pointer address), which is used to find the corresponding
// handle when freeing memory.
// we fill a temp MEM, and then copy it into the handle's user data space
MEM mem;
void* p;
if(type == MEM_HEAP)
p = heap_alloc(size, align, &mem);
else if(type == MEM_POOL)
p = pool_alloc(size, align, &mem);
else if(type == MEM_MAPPED)
p = mmap_alloc(size, fd, &mem);
else
{
assert(0 && "mem_alloc: invalid type parameter");
return 0;
}
if(!p)
return 0;
MEM* pmem;
Handle h = h_alloc((uintptr_t)p, H_MEM, mem_dtor, (void**)&pmem);
2003-11-12 15:47:38 +01:00
if(!h) // failed to allocate a handle
{
mem_dtor(&mem);
return 0;
}
*pmem = mem; // copy our memory info into the handle's user data space
// caller is asking for the handle
// (freeing the memory via handle is faster than mem_free, because
// we wouldn't have to scan all handles looking for the pointer)
if(ph)
*ph = h;
return p;
}