1
0
forked from 0ad/0ad
0ad/source/lib/res/mem.cpp
2004-03-02 23:56:51 +00:00

290 lines
5.0 KiB
C++
Executable File

// malloc layer for less fragmentation, alignment, and automatic release
#include <cstdlib>
#include <cassert>
#include "lib.h"
#include "types.h"
#include "mem.h"
#include "h_mgr.h"
#include "misc.h"
#include <map>
//////////////////////////////////////////////////////////////////////////////
static void heap_free(void* const p, const size_t size, const uintptr_t ctx)
{
void* org_p = (void*)ctx;
free(org_p);
}
static void* heap_alloc(const size_t size, const int align, uintptr_t& ctx, MEM_DTOR& dtor)
{
u8* org_p = (u8*)malloc(size+align-1);
u8* p = (u8*)round_up((uintptr_t)org_p, align);
ctx = (uintptr_t)org_p;
dtor = heap_free;
return p;
}
//////////////////////////////////////////////////////////////////////////////
static u8* pool;
static size_t pool_pos;
static const size_t POOL_CAP = 8*MB; // TODO: user editable
static void pool_free(void* const p, const size_t size, const uintptr_t ctx)
{
size_t ofs = ctx;
// at end of pool? if so, 'free' it
if(ofs + size == pool_pos)
pool_pos -= size;
else
; // TODO: warn about 'leaked' memory;
// suggest using a different allocator
}
static void* pool_alloc(const size_t size, const uint align, uintptr_t& ctx, MEM_DTOR& dtor)
{
if(!pool)
{
pool = (u8*)mem_alloc(size, align, 0);
if(!pool)
return 0;
}
ptrdiff_t ofs = round_up(pool_pos, align);
ctx = (uintptr_t)ofs;
dtor = pool_free;
if(ofs+size > POOL_CAP)
{
assert(0 && "pool_alloc: not enough memory in pool");
return 0;
}
pool_pos = ofs+size;
return (u8*)pool + ofs;
}
//////////////////////////////////////////////////////////////////////////////
typedef std::map<void*, Handle> PtrToH;
// undefined NLSO init order fix
static PtrToH& _ptr_to_h()
{
static PtrToH ptr_to_h_;
return ptr_to_h_;
}
#define ptr_to_h _ptr_to_h()
// not needed by other modules - mem_get_size and mem_assign is enough.
static Handle find_alloc(void* p)
{
PtrToH::const_iterator it = ptr_to_h.find(p);
if(it == ptr_to_h.end())
return 0;
return it->second;
}
// returns the handle that will be removed, for mem_free convenience.
// makes sure p is in the mapping.
static Handle remove_alloc(void* p)
{
PtrToH::iterator it = ptr_to_h.find(p);
if(it == ptr_to_h.end())
{
assert("remove_alloc: pointer not in map");
return 0;
}
Handle hm = it->second;
ptr_to_h.erase(it);
return hm;
}
// p must not alread be in mapping!
static void set_alloc(void* p, Handle hm)
{
ptr_to_h[p] = hm;
}
struct Mem
{
void* p;
size_t size;
// allocator specific
uintptr_t ctx;
MEM_DTOR dtor; // this allows user-specified dtors.
// alternative: switch(mem->type) in mem_dtor
};
H_TYPE_DEFINE(Mem)
static void Mem_init(Mem* m, va_list args)
{
}
static void Mem_dtor(Mem* m)
{
if(m->dtor)
m->dtor(m->p, m->size, m->ctx);
}
// can't alloc here, because h_alloc needs the key when called
// (key == pointer we allocate)
static int Mem_reload(Mem* m, const char* fn)
{
return 0;
}
int mem_free_p(void*& p)
{
if(!p)
return ERR_INVALID_PARAM;
Handle hm = remove_alloc(p);
p = 0;
return h_free(hm, H_Mem);
}
int mem_free_h(Handle& hm)
{
void* p = mem_get_ptr(hm);
hm = 0;
return mem_free_p(p);
}
Handle mem_assign(void* p, size_t size, uint flags /* = 0 */, MEM_DTOR dtor /* = 0 */, uintptr_t ctx /* = 0 */)
{
// we've already allocated that pointer - returns its handle
Handle hm = find_alloc(p);
if(hm)
return hm;
if(!p || !size)
{
assert(0 && "mem_assign: invalid p or size");
return 0;
}
hm = h_alloc(H_Mem, (const char*)p, flags | RES_KEY);
if(!hm)
return 0;
set_alloc(p, hm);
H_DEREF(hm, Mem, m);
m->p = p;
m->size = size;
m->dtor = dtor;
m->ctx = ctx;
return hm;
}
void* mem_alloc(size_t size, const uint align, uint flags, Handle* phm)
{
if(phm)
*phm = 0;
if(size == 0)
{
assert(0 && "mem_alloc: why is size = 0?");
size = 1;
}
// no scope indicated
if(!flags)
// in a handle _reload function - default to its scope
if(res_cur_scope)
flags = res_cur_scope;
// otherwise, assume global scope
int scope = flags & RES_SCOPE_MASK;
// filled in by allocators
uintptr_t ctx;
MEM_DTOR dtor;
void* p = 0;
if(scope == RES_TEMP)
p = pool_alloc(size, align, ctx, dtor);
else
p = heap_alloc(size, align, ctx, dtor);
if(!p)
return 0;
Handle hm = mem_assign(p, size, scope, dtor, ctx);
if(!hm) // failed to allocate a handle
{
dtor(p, size, ctx);
return 0;
}
// check if pointer was already allocated?
// 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(phm)
*phm = hm;
if(flags & MEM_ZERO)
memset(p, 0, size);
return p;
}
void* mem_get_ptr(Handle hm, size_t* size)
{
Mem* m = H_USER_DATA(hm, Mem);
if(!m)
return 0;
assert((!m->p || m->size) && "mem_get_ptr: mem corrupted (p valid =/=> size > 0)");
if(size)
*size = m->size;
return m->p;
}
ssize_t mem_size(void* p)
{
Handle hm = find_alloc(p);
H_DEREF(hm, Mem, m);
return (ssize_t)m->size;
}