2004-03-03 00:56:51 +01:00
|
|
|
// malloc layer for less fragmentation, alignment, and automatic release
|
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
#include "lib.h"
|
2004-06-01 19:34:12 +02:00
|
|
|
#include "res.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
#include "h_mgr.h"
|
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <map>
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
static void heap_free(void* const p, const size_t size, const uintptr_t ctx)
|
|
|
|
{
|
2004-06-11 04:14:18 +02:00
|
|
|
UNUSED(p);
|
|
|
|
UNUSED(size);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
void* org_p = (void*)ctx;
|
|
|
|
free(org_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
static void* heap_alloc(const size_t size, const size_t align, uintptr_t& ctx, MEM_DTOR& dtor)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2004-06-11 04:14:18 +02:00
|
|
|
UNUSED(p);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
static void* pool_alloc(const size_t size, const size_t align, uintptr_t& ctx, MEM_DTOR& dtor)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2004-05-08 03:11:51 +02:00
|
|
|
debug_warn("pool_alloc: not enough memory in pool");
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool_pos = ofs+size;
|
|
|
|
return (u8*)pool + ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
static bool has_shutdown = false;
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
typedef std::map<void*, Handle> PtrToH;
|
2004-05-06 19:14:30 +02:00
|
|
|
static PtrToH* _ptr_to_h;
|
|
|
|
|
|
|
|
|
|
|
|
static void ptr_to_h_shutdown()
|
|
|
|
{
|
|
|
|
has_shutdown = true;
|
|
|
|
delete _ptr_to_h;
|
2004-05-14 23:20:23 +02:00
|
|
|
_ptr_to_h = 0;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
// undefined NLSO init order fix
|
2004-05-06 19:14:30 +02:00
|
|
|
static PtrToH& get_ptr_to_h()
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-06 19:14:30 +02:00
|
|
|
if(!_ptr_to_h)
|
|
|
|
{
|
|
|
|
if(has_shutdown)
|
2004-05-08 03:11:51 +02:00
|
|
|
debug_warn("mem.cpp: ptr -> handle lookup used after module shutdown");
|
2004-05-06 19:14:30 +02:00
|
|
|
// crash + burn
|
|
|
|
|
|
|
|
_ptr_to_h = new PtrToH;
|
|
|
|
|
|
|
|
atexit2(ptr_to_h_shutdown);
|
|
|
|
}
|
|
|
|
return *_ptr_to_h;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
2004-05-06 19:14:30 +02:00
|
|
|
#define ptr_to_h get_ptr_to_h()
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
{
|
2004-05-08 03:11:51 +02:00
|
|
|
debug_warn("remove_alloc: pointer not in map");
|
2004-03-03 00:56:51 +01:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2004-06-11 04:14:18 +02:00
|
|
|
H_TYPE_DEFINE(Mem);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
static void Mem_init(Mem* m, va_list args)
|
|
|
|
{
|
2004-06-11 04:14:18 +02:00
|
|
|
UNUSED(m);
|
|
|
|
UNUSED(args);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2004-06-11 04:14:18 +02:00
|
|
|
UNUSED(m);
|
|
|
|
UNUSED(fn);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
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);
|
2004-06-01 19:34:12 +02:00
|
|
|
if(hm > 0)
|
2004-03-03 00:56:51 +01:00
|
|
|
return hm;
|
|
|
|
|
|
|
|
if(!p || !size)
|
|
|
|
{
|
2004-05-08 03:11:51 +02:00
|
|
|
debug_warn("mem_assign: invalid p or size");
|
2004-03-03 00:56:51 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
if(phm)
|
|
|
|
*phm = 0;
|
|
|
|
|
|
|
|
if(size == 0)
|
|
|
|
{
|
2004-05-08 03:11:51 +02:00
|
|
|
debug_warn("mem_alloc: why is size = 0?");
|
2004-03-03 00:56:51 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
void* mem_get_ptr(Handle hm, size_t* size /* = 0 */)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
Mem* m = H_USER_DATA(hm, Mem);
|
|
|
|
if(!m)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
|
|
|
if(size)
|
|
|
|
*size = 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
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;
|
2004-06-02 17:31:55 +02:00
|
|
|
}
|