diff --git a/source/lib/res/mem.cpp b/source/lib/res/mem.cpp index d0cfd29da8..415e6f31b0 100755 --- a/source/lib/res/mem.cpp +++ b/source/lib/res/mem.cpp @@ -11,27 +11,74 @@ #include -////////////////////////////////////////////////////////////////////////////// -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; - return it->second; + // 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 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,35 +247,65 @@ 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->dtor = dtor; - m->ctx = ctx; + 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; return hm; } +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; } diff --git a/source/lib/res/mem.h b/source/lib/res/mem.h index 4828150e01..d5af3825fc 100755 --- a/source/lib/res/mem.h +++ b/source/lib/res/mem.h @@ -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);