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"
|
2005-08-12 19:06:53 +02:00
|
|
|
#include "h_mgr.h"
|
|
|
|
#include "mem.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <stdlib.h>
|
2005-06-28 06:06:25 +02:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
|
|
|
|
#include <map>
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
|
|
|
|
struct Mem
|
|
|
|
{
|
2004-08-09 18:44:42 +02:00
|
|
|
// initially what mem_alloc returns; can be changed via mem_assign_user
|
|
|
|
void* p;
|
|
|
|
size_t size;
|
2004-08-07 15:34:43 +02:00
|
|
|
|
2004-08-09 18:44:42 +02:00
|
|
|
// unaligned mem from allocator
|
2004-08-07 15:34:43 +02:00
|
|
|
void* raw_p;
|
|
|
|
size_t raw_size;
|
|
|
|
|
2004-08-09 18:44:42 +02:00
|
|
|
uintptr_t ctx;
|
2004-08-07 15:34:43 +02:00
|
|
|
MEM_DTOR dtor; // this allows user-specified dtors.
|
|
|
|
};
|
|
|
|
|
|
|
|
H_TYPE_DEFINE(Mem);
|
|
|
|
|
|
|
|
|
2004-08-10 18:01:04 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
static bool has_shutdown = false;
|
|
|
|
|
|
|
|
// raw pointer -> Handle
|
|
|
|
typedef std::map<void*, Handle> PtrToH;
|
|
|
|
typedef PtrToH::iterator It;
|
|
|
|
static PtrToH* _ptr_to_h;
|
|
|
|
|
|
|
|
|
|
|
|
static void ptr_to_h_shutdown()
|
|
|
|
{
|
|
|
|
has_shutdown = true;
|
|
|
|
delete _ptr_to_h;
|
|
|
|
_ptr_to_h = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// undefined NLSO init order fix
|
|
|
|
static PtrToH& get_ptr_to_h()
|
|
|
|
{
|
|
|
|
if(!_ptr_to_h)
|
|
|
|
{
|
|
|
|
if(has_shutdown)
|
|
|
|
debug_warn("mem.cpp: ptr -> handle lookup used after module shutdown");
|
|
|
|
// crash + burn
|
|
|
|
|
|
|
|
_ptr_to_h = new PtrToH;
|
|
|
|
}
|
|
|
|
return *_ptr_to_h;
|
|
|
|
}
|
|
|
|
#define ptr_to_h get_ptr_to_h()
|
|
|
|
|
|
|
|
|
2005-10-03 14:57:31 +02:00
|
|
|
// not needed by other modules - mem_get_size and mem_wrap is enough.
|
2004-08-10 18:01:04 +02:00
|
|
|
static Handle find_alloc(void* target_p, It* out_it = 0)
|
|
|
|
{
|
|
|
|
// early out optimization (don't pay for full subset check)
|
|
|
|
It 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)
|
|
|
|
{
|
|
|
|
if(out_it)
|
|
|
|
*out_it = it;
|
|
|
|
return hm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// raw_p must be in map!
|
|
|
|
static void remove_alloc(void* raw_p)
|
|
|
|
{
|
|
|
|
size_t num_removed = ptr_to_h.erase(raw_p);
|
2005-01-23 19:17:46 +01:00
|
|
|
if(num_removed != 1)
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(num_removed == 1 && "remove_alloc: not in map");
|
2004-08-10 18:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// raw_p must not already be in map!
|
|
|
|
static void set_alloc(void* raw_p, Handle hm)
|
|
|
|
{
|
|
|
|
// verify it's not already in the mapping
|
|
|
|
#ifndef NDEBUG
|
|
|
|
It it = ptr_to_h.find(raw_p);
|
|
|
|
if(it != ptr_to_h.end())
|
|
|
|
{
|
|
|
|
debug_warn("set_alloc: already in map");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ptr_to_h[raw_p] = hm;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
static void Mem_init(Mem* m, va_list args)
|
|
|
|
{
|
2005-10-12 06:35:01 +02:00
|
|
|
// these are passed to h_alloc instead of assigning in mem_wrap after a
|
|
|
|
// H_DEREF so that Mem_validate won't complain about invalid (0) values.
|
|
|
|
//
|
|
|
|
// additional bonus: by setting raw_p before reload, that and the
|
|
|
|
// dtor will be the only call site of set/remove_alloc.
|
|
|
|
m->p = va_arg(args, void*);
|
|
|
|
m->size = va_arg(args, size_t);
|
|
|
|
m->raw_p = va_arg(args, void*);
|
|
|
|
m->raw_size = va_arg(args, size_t);
|
|
|
|
m->dtor = va_arg(args, MEM_DTOR);
|
|
|
|
m->ctx = va_arg(args, uintptr_t);
|
2004-08-07 15:34:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void Mem_dtor(Mem* m)
|
|
|
|
{
|
2005-10-12 06:35:01 +02:00
|
|
|
// (reload can't fail)
|
|
|
|
|
2004-08-10 18:01:04 +02:00
|
|
|
remove_alloc(m->raw_p);
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
if(m->dtor)
|
|
|
|
m->dtor(m->raw_p, m->raw_size, m->ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
// can't alloc here, because h_alloc needs the key when called
|
|
|
|
// (key == pointer we allocate)
|
2005-08-09 18:23:19 +02:00
|
|
|
static int Mem_reload(Mem* m, const char* UNUSED(fn), Handle hm)
|
2004-08-07 15:34:43 +02:00
|
|
|
{
|
2004-08-10 18:01:04 +02:00
|
|
|
set_alloc(m->raw_p, hm);
|
2004-08-07 15:34:43 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-10-12 06:35:01 +02:00
|
|
|
static int Mem_validate(const Mem* m)
|
|
|
|
{
|
|
|
|
if(debug_is_pointer_bogus(m->p))
|
|
|
|
return -2;
|
|
|
|
if(!m->size)
|
|
|
|
return -3;
|
|
|
|
if(m->raw_p && m->raw_p > m->p)
|
|
|
|
return -4;
|
|
|
|
if(m->raw_size && m->raw_size < m->size)
|
|
|
|
return -5;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-08-09 18:44:42 +02:00
|
|
|
// "*": aligned memory returned by allocator.
|
|
|
|
// "user_*": same as above, until someones changes it via mem_assign_user
|
2004-08-07 15:34:43 +02:00
|
|
|
|
|
|
|
// allocator interface:
|
2004-08-09 18:44:42 +02:00
|
|
|
// alloc: return at least size bytes of memory (alignment done by caller)
|
2004-08-07 15:34:43 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
static void heap_free(void* raw_p, size_t UNUSED(raw_size), uintptr_t UNUSED(ctx))
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-19 15:41:49 +02:00
|
|
|
free(raw_p);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-10 00:12:02 +01:00
|
|
|
static void* heap_alloc(size_t raw_size, uintptr_t& ctx)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-19 15:41:49 +02:00
|
|
|
ctx = 0;
|
|
|
|
void* raw_p = malloc(raw_size);
|
|
|
|
return raw_p;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
/*
|
2004-03-03 00:56:51 +01:00
|
|
|
static u8* pool;
|
|
|
|
static size_t pool_pos;
|
2005-01-23 19:17:46 +01:00
|
|
|
static const size_t POOL_CAP = 8*MiB; // TODO: user editable
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
static void pool_free(void* UNUSED(raw_p), size_t raw_size, uintptr_t ctx)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-08-07 15:34:43 +02:00
|
|
|
size_t ofs = (size_t)ctx;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
// 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;
|
2004-03-03 00:56:51 +01:00
|
|
|
else
|
2004-09-19 15:41:49 +02:00
|
|
|
; // TODO: warn about lost memory in pool;
|
2004-03-03 00:56:51 +01:00
|
|
|
// suggest using a different allocator
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
static void* pool_alloc(const size_t raw_size, uintptr_t& ctx)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-08-09 18:23:19 +02:00
|
|
|
ctx = ~0U; // make sure it's invalid if we fail
|
2004-08-07 15:34:43 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!pool)
|
|
|
|
{
|
2005-01-23 19:17:46 +01:00
|
|
|
pool = (u8*)mem_alloc(POOL_CAP, 64*KiB, RES_STATIC);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!pool)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
if(pool_pos + raw_size > POOL_CAP)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
ctx = (uintptr_t)pool_pos;
|
|
|
|
pool_pos += raw_size;
|
2004-09-19 15:41:49 +02:00
|
|
|
void* raw_p = (u8*)pool + ctx;
|
|
|
|
return raw_p;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
2005-08-09 18:23:19 +02:00
|
|
|
*/
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-08-10 18:01:04 +02:00
|
|
|
int mem_free_h(Handle& hm)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-08-10 18:01:04 +02:00
|
|
|
return h_free(hm, H_Mem);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int mem_free_p(void*& p)
|
|
|
|
{
|
2004-08-15 23:48:34 +02:00
|
|
|
if(!p)
|
|
|
|
return 0;
|
|
|
|
|
2004-08-10 18:01:04 +02:00
|
|
|
Handle hm = find_alloc(p);
|
2004-03-03 00:56:51 +01:00
|
|
|
p = 0;
|
2004-08-10 18:01:04 +02:00
|
|
|
if(hm <= 0)
|
|
|
|
{
|
|
|
|
debug_warn("mem_free_p: not found in map");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return mem_free_h(hm);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-19 15:41:49 +02:00
|
|
|
// create a H_MEM handle of type MEM_USER,
|
|
|
|
// and assign it the specified memory range.
|
2005-10-03 14:57:31 +02:00
|
|
|
// if dtor is non-NULL, it is called (passing ctx) when the handle is freed.
|
|
|
|
Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-10-03 14:57:31 +02:00
|
|
|
if(!p || !size)
|
|
|
|
CHECK_ERR(ERR_INVALID_PARAM);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// we've already allocated that pointer - returns its handle
|
2004-08-09 18:44:42 +02:00
|
|
|
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;
|
|
|
|
|
2005-10-03 14:57:31 +02:00
|
|
|
// <p> wasn't allocated via mem_alloc, or we would've found its Handle.
|
|
|
|
// it is therefore some user-allocated mem and might therefore not have
|
|
|
|
// a valid <raw_p> set. since that's our search key, we set it to <p>.
|
|
|
|
if(!raw_p)
|
|
|
|
raw_p = p;
|
|
|
|
if(!raw_size)
|
|
|
|
raw_size = size;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-10-12 06:35:01 +02:00
|
|
|
hm = h_alloc(H_Mem, (const char*)p, flags|RES_KEY|RES_NO_CACHE,
|
|
|
|
p, size, raw_p, raw_size, dtor, ctx);
|
2004-03-03 00:56:51 +01:00
|
|
|
return hm;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-19 15:41:49 +02:00
|
|
|
/*
|
2004-08-07 15:34:43 +02:00
|
|
|
int mem_assign_user(Handle hm, void* user_p, size_t user_size)
|
|
|
|
{
|
|
|
|
H_DEREF(hm, Mem, m);
|
|
|
|
|
|
|
|
// security check: must be a subset of the existing buffer
|
|
|
|
// (otherwise, could reference other buffers / cause mischief)
|
2004-08-10 18:01:04 +02:00
|
|
|
char* raw_end = (char*)m->raw_p + m->raw_size;
|
2004-08-09 18:44:42 +02:00
|
|
|
char* user_end = (char*)user_p + user_size;
|
2004-08-10 18:01:04 +02:00
|
|
|
if(user_p < m->raw_p || user_end > raw_end)
|
2004-08-07 15:34:43 +02:00
|
|
|
{
|
|
|
|
debug_warn("mem_assign_user: user buffer not contained in real buffer");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2004-08-09 18:44:42 +02:00
|
|
|
m->p = user_p;
|
|
|
|
m->size = user_size;
|
2004-08-07 15:34:43 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-09-19 15:41:49 +02:00
|
|
|
*/
|
2004-08-07 15:34:43 +02:00
|
|
|
|
|
|
|
|
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)
|
2004-09-19 15:41:49 +02:00
|
|
|
*phm = ERR_NO_MEM;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-10-19 15:29:51 +02:00
|
|
|
// note: this is legitimate. vfs_load on 0-length files must return
|
|
|
|
// a valid and unique pointer to an (at least) 0-length buffer.
|
2004-03-03 00:56:51 +01:00
|
|
|
if(size == 0)
|
|
|
|
size = 1;
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
void* raw_p;
|
2004-08-09 18:44:42 +02:00
|
|
|
const size_t raw_size = size + align-1;
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
uintptr_t ctx;
|
|
|
|
MEM_DTOR dtor;
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
// 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)
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
2004-08-09 18:44:42 +02:00
|
|
|
void* p = (void*)round_up((uintptr_t)raw_p, align);
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-10-03 14:57:31 +02:00
|
|
|
Handle hm = mem_wrap(p, size, flags, raw_p, raw_size, dtor, ctx);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!hm) // failed to allocate a handle
|
|
|
|
{
|
2005-10-03 14:57:31 +02:00
|
|
|
debug_warn("mem_alloc: mem_wrap failed");
|
2004-08-09 18:44:42 +02:00
|
|
|
dtor(p, size, ctx);
|
2004-03-03 00:56:51 +01:00
|
|
|
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)
|
2004-08-09 18:44:42 +02:00
|
|
|
memset(p, 0, size);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-09 18:44:42 +02:00
|
|
|
return p;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
void* mem_get_ptr(Handle hm, size_t* user_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
|
|
|
{
|
2004-08-07 15:34:43 +02:00
|
|
|
if(user_size)
|
|
|
|
*user_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
|
|
|
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert((!m->p || m->size) && "mem_get_ptr: mem corrupted (p valid =/=> size > 0)");
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-07 15:34:43 +02:00
|
|
|
if(user_size)
|
2004-08-09 18:44:42 +02:00
|
|
|
*user_size = m->size;
|
|
|
|
return m->p;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-08 03:47:45 +02:00
|
|
|
int mem_get(Handle hm, u8** pp, size_t* psize)
|
|
|
|
{
|
|
|
|
H_DEREF(hm, Mem, m);
|
|
|
|
if(pp)
|
|
|
|
*pp = (u8*)m->p;
|
|
|
|
if(psize)
|
|
|
|
*psize = m->size;
|
|
|
|
// leave hm locked
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-19 15:41:49 +02:00
|
|
|
/*
|
2004-03-03 00:56:51 +01:00
|
|
|
ssize_t mem_size(void* p)
|
|
|
|
{
|
|
|
|
Handle hm = find_alloc(p);
|
|
|
|
H_DEREF(hm, Mem, m);
|
2004-08-09 18:44:42 +02:00
|
|
|
return (ssize_t)m->size;
|
2004-06-02 17:31:55 +02:00
|
|
|
}
|
2004-09-19 15:41:49 +02:00
|
|
|
*/
|
2005-01-07 02:13:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
void mem_shutdown()
|
|
|
|
{
|
|
|
|
ptr_to_h_shutdown();
|
2005-09-13 01:37:52 +02:00
|
|
|
}
|