1
0
forked from 0ad/0ad

add support for lying about a memory handle's actual allocation (useful for the file code, which allocates extra room for padding yet wants to return the allocation's handle)

This was SVN commit r932.
This commit is contained in:
janwas 2004-08-07 13:34:43 +00:00
parent 58ae20116a
commit 09cf9dce75
2 changed files with 172 additions and 100 deletions

View File

@ -11,27 +11,74 @@
#include <map>
//////////////////////////////////////////////////////////////////////////////
static void heap_free(void* const p, const size_t size, const uintptr_t ctx)
struct Mem
{
UNUSED(p);
UNUSED(size);
// what is reported by mem_get_ptr / mem_size
void* user_p;
size_t user_size;
void* org_p = (void*)ctx;
free(org_p);
// actual allocation params (set by allocator)
void* raw_p;
size_t raw_size;
uintptr_t ctx;
MEM_DTOR dtor; // this allows user-specified dtors.
};
H_TYPE_DEFINE(Mem);
static void Mem_init(Mem* m, va_list args)
{
UNUSED(m);
UNUSED(args);
}
static void* heap_alloc(const size_t size, const size_t align, uintptr_t& ctx, MEM_DTOR& dtor)
static void Mem_dtor(Mem* m)
{
u8* org_p = (u8*)malloc(size+align-1);
u8* p = (u8*)round_up((uintptr_t)org_p, align);
if(m->dtor)
m->dtor(m->raw_p, m->raw_size, m->ctx);
}
ctx = (uintptr_t)org_p;
dtor = heap_free;
return p;
// 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*/, Handle /*h*/)
{
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// "raw_*": memory requested from allocator (+ padding for alignment
// requested by mem_alloc)
// "user_*": same as raw, until someones changes it via {?}
// allocator interface:
// alloc: return at least raw_size bytes of memory (alignment done by caller)
//////////////////////////////////////////////////////////////////////////////
static void heap_free(void* const raw_p, const size_t raw_size, const uintptr_t ctx)
{
UNUSED(raw_p);
UNUSED(raw_size);
void* heap_p = (void*)ctx;
free(heap_p);
}
static void* heap_alloc(const size_t raw_size, uintptr_t& ctx)
{
void* heap_p = malloc(raw_size);
ctx = (uintptr_t)heap_p;
return heap_p;
}
@ -43,42 +90,44 @@ 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)
static void pool_free(void* const raw_p, const size_t raw_size, const uintptr_t ctx)
{
UNUSED(p);
UNUSED(raw_p);
size_t ofs = ctx;
size_t ofs = (size_t)ctx;
// at end of pool? if so, 'free' it
if(ofs + size == pool_pos)
pool_pos -= size;
// at or beyond current next-alloc position: invalid
if(ofs >= pool_pos)
debug_warn("pool_free: invalid ctx, beyond end of pool");
// at end of pool; 'free' it by moving pos back
else if(ofs + raw_size == pool_pos)
pool_pos = ofs;
else
; // TODO: warn about 'leaked' memory;
// suggest using a different allocator
}
static void* pool_alloc(const size_t size, const size_t align, uintptr_t& ctx, MEM_DTOR& dtor)
static void* pool_alloc(const size_t raw_size, uintptr_t& ctx)
{
ctx = ~0; // make sure it's invalid if we fail
if(!pool)
{
pool = (u8*)mem_alloc(size, align, 0);
pool = (u8*)mem_alloc(POOL_CAP, 64*KB, RES_STATIC);
if(!pool)
return 0;
}
ptrdiff_t ofs = round_up(pool_pos, align);
ctx = (uintptr_t)ofs;
dtor = pool_free;
if(ofs+size > POOL_CAP)
if(pool_pos + raw_size > POOL_CAP)
{
debug_warn("pool_alloc: not enough memory in pool");
return 0;
}
pool_pos = ofs+size;
return (u8*)pool + ofs;
ctx = (uintptr_t)pool_pos;
pool_pos += raw_size;
return (u8*)pool + ctx;
}
@ -118,12 +167,35 @@ static PtrToH& get_ptr_to_h()
// not needed by other modules - mem_get_size and mem_assign is enough.
static Handle find_alloc(void* p)
static Handle find_alloc(void* target_p)
{
PtrToH::const_iterator it = ptr_to_h.find(p);
if(it == ptr_to_h.end())
return 0;
// early out optimization (don't pay for full subset check)
PtrToH::const_iterator it = ptr_to_h.find(target_p);
if(it != ptr_to_h.end())
return it->second;
// not found; now check if target_p is within one of the mem ranges
for(it = ptr_to_h.begin(); it != ptr_to_h.end(); ++it)
{
std::pair<void*, Handle> item = *it;
void* p = item.first;
Handle hm = item.second;
// not before this alloc's p; could be it. now do range check.
if(target_p >= p)
{
Mem* m = (Mem*)h_user_data(hm, H_Mem);
if(m)
{
// found it within this mem range.
if(target_p <= (char*)m->raw_p + m->raw_size)
return hm;
}
}
}
// not found
return 0;
}
@ -151,46 +223,8 @@ static void set_alloc(void* p, Handle 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)
{
UNUSED(m);
UNUSED(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, Handle h)
{
UNUSED(m);
UNUSED(fn);
UNUSED(h);
return 0;
}
int mem_free_p(void*& p)
@ -213,28 +247,30 @@ int mem_free_h(Handle& hm)
}
Handle mem_assign(void* p, size_t size, uint flags /* = 0 */, MEM_DTOR dtor /* = 0 */, uintptr_t ctx /* = 0 */)
Handle mem_assign(void* raw_p, size_t raw_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);
Handle hm = find_alloc(raw_p);
if(hm > 0)
return hm;
if(!p || !size)
if(!raw_p || !raw_size)
{
debug_warn("mem_assign: invalid p or size");
return 0;
}
hm = h_alloc(H_Mem, (const char*)p, flags | RES_KEY);
hm = h_alloc(H_Mem, (const char*)raw_p, flags | RES_KEY);
if(!hm)
return 0;
set_alloc(p, hm);
set_alloc(raw_p, hm);
H_DEREF(hm, Mem, m);
m->p = p;
m->size = size;
m->raw_p = raw_p;
m->raw_size = raw_size;
m->user_p = raw_p;
m->user_size = raw_size;
m->dtor = dtor;
m->ctx = ctx;
@ -242,6 +278,34 @@ Handle mem_assign(void* p, size_t size, uint flags /* = 0 */, MEM_DTOR dtor /* =
}
int mem_assign_user(Handle hm, void* user_p, size_t user_size)
{
H_DEREF(hm, Mem, m);
// make sure it's not already been assigned
// (doesn't make sense, probably logic error)
if(m->user_p != m->raw_p || m->user_size != m->raw_size)
{
debug_warn("mem_assign_user: already user_assign-ed");
return -1;
}
// security check: must be a subset of the existing buffer
// (otherwise, could reference other buffers / cause mischief)
char* raw_end = (char*)m->raw_p + m->raw_size;
char* user_end = (char*)m->user_p + m->user_size;
if(user_p < m->raw_p || user_end > raw_end)
{
debug_warn("mem_assign_user: user buffer not contained in real buffer");
return -EINVAL;
}
m->user_p = user_p;
m->user_size = user_size;
return 0;
}
void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
{
if(phm)
@ -260,24 +324,30 @@ void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
flags = res_cur_scope;
// otherwise, assume global scope
int scope = flags & RES_SCOPE_MASK;
size_t raw_size = (size_t)round_up(size, align);
// filled in by allocators
void* raw_p;
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)
// if(scope == RES_TEMP)
// {
// raw_p = pool_alloc(raw_size, ctx);
// dtor = pool_free;
// }
// else
{
raw_p = heap_alloc(raw_size, ctx);
dtor = heap_free;
}
if(!raw_p)
return 0;
Handle hm = mem_assign(p, size, scope, dtor, ctx);
Handle hm = mem_assign(raw_p, raw_size, flags, dtor, ctx);
if(!hm) // failed to allocate a handle
{
dtor(p, size, ctx);
dtor(raw_p, raw_size, ctx);
return 0;
}
@ -291,27 +361,27 @@ void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
*phm = hm;
if(flags & MEM_ZERO)
memset(p, 0, size);
memset(raw_p, 0, raw_size);
return p;
return raw_p;
}
void* mem_get_ptr(Handle hm, size_t* size /* = 0 */)
void* mem_get_ptr(Handle hm, size_t* user_size /* = 0 */)
{
Mem* m = H_USER_DATA(hm, Mem);
if(!m)
{
if(size)
*size = 0;
if(user_size)
*user_size = 0;
return 0;
}
assert((!m->p || m->size) && "mem_get_ptr: mem corrupted (p valid =/=> size > 0)");
assert((!m->user_p || m->user_size) && "mem_get_ptr: mem corrupted (p valid =/=> size > 0)");
if(size)
*size = m->size;
return m->p;
if(user_size)
*user_size = m->user_size;
return m->user_p;
}
@ -319,5 +389,5 @@ ssize_t mem_size(void* p)
{
Handle hm = find_alloc(p);
H_DEREF(hm, Mem, m);
return (ssize_t)m->size;
return (ssize_t)m->user_size;
}

View File

@ -31,6 +31,8 @@ extern int mem_free_h(Handle& hm);
// dtor is called when the handle is freed, if non-NULL.
extern Handle mem_assign(void* p, size_t size, uint flags = 0, MEM_DTOR dtor = 0, uintptr_t ctx = 0);
extern int mem_assign_user(Handle hm, void* user_p, size_t user_size);
// returns 0 if the handle is invalid
extern void* mem_get_ptr(Handle h, size_t* size = 0);