/*****************************************************************************\
* Copyright (c) 2002 Pelle Johansson. *
* All rights reserved. *
* *
* This file is part of the moftpd package. Use and distribution of *
* this software is governed by the terms in the file LICENCE, which *
* should have come with this package. *
\*****************************************************************************/
/* $moftpd: memory.c 1224 2004-10-28 22:42:00Z morth $ */
#include "system.h"
#include "memory.h"
#ifdef DEBUG
extern int debug;
static void DOBUG (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vsyslog (LOG_DEBUG, fmt, ap);
va_end (ap);
}
#undef DEBUG
#define DEBUG(level, msg) if (debug >= level) DOBUG msg
#define DEXPR(x) x
#else
#define DEBUG(level, msg)
#define DEXPR(x)
#endif
#define stringHashSize 31 // gcc chokes on const int
static memory_t *tMem;
static memdata_t *stringHash[stringHashSize];
static unsigned char randVals[256];
static unsigned int sweepMark;
static memdata_t *memChain, *rootChain;
void mem_init (void)
{
int fd = open ("/dev/urandom", O_RDONLY);
ssize_t j;
int i;
i = 256;
if (fd >= 0)
{
for (; i > 0; i -= j)
{
j = read (fd, randVals + 256 - i, (size_t)i);
if (j < 0)
break;
}
(void)close (fd);
}
if (i)
{
srand ((unsigned int)time (NULL));
for (; i > 0; i--)
randVals[256 - i] = random ();
}
}
void *talloc(size_t sz)
{
memory_t *mem = calloc(1, sz + sizeof(memory_t) * pNumTempLinks);
if(!mem)
return NULL;
mem[pNextTemp].link = tMem;
tMem = mem;
DEBUG (2, ("talloc: %p", mem + pNumTempLinks));
return mem + pNumTempLinks;
}
void *trealloc(void *ptr, size_t sz)
{
memory_t *mem = (memory_t*)ptr - pNumTempLinks, *res, *pmem, *nmem;
if (!ptr)
return talloc (sz);
nmem = mem[pNextTemp].link;
res = realloc(mem, sz + sizeof(memory_t) * pNumTempLinks);
if (res == mem)
return res + pNumTempLinks;
DEBUG (2, ("trealloc: %p -> %p", mem + pNumTempLinks, res + pNumTempLinks));
if(mem == tMem)
{
if(res)
tMem = res;
else
tMem = nmem;
}
else
{
for (pmem = tMem; pmem[pNextTemp].link && pmem[pNextTemp].link != mem;
pmem = pmem->link)
;
if (pmem[pNextTemp].link)
{
if(res)
pmem[pNextTemp].link = res;
else
pmem[pNextTemp].link = nmem;
}
}
if(res)
return res + pNumTempLinks;
return NULL;
}
char *tstring(const char *str)
{
char *res;
if (!str)
return NULL;
res = talloc (strlen (str) + 1);
if(res)
strcpy(res, str);
return res;
}
static void mark_memory (memdata_t *mem)
{
memory_t *cmem, *nmem;
if (mem->mark == sweepMark)
return;
mem->mark = sweepMark;
if (mem->isHashed)
return;
for (cmem = mem->firstChild.link; cmem; cmem = nmem)
{
nmem = cmem[pNextChild].link;
#ifdef MEMORY_LOCATION
if (cmem[pLocation].ptr && *cmem[pLocation].ptr != mem + 1)
{
pfree (cmem[pChild].data + 1, mem + 1);
continue;
}
#endif
mark_memory (cmem[pChild].data);
}
}
void tfree_all(void)
{
memory_t *nmem;
memdata_t *chain, *pchain;
#ifdef MEMORY_LOCATION
memory_t *mem, *pmem;
#endif
while(tMem)
{
nmem = tMem[pNextTemp].link;
DEBUG (2, ("tfree: %p", tMem + pNumTempLinks));
free (tMem);
tMem = nmem;
}
sweepMark ^= 1;
#ifdef MEMORY_LOCATION
pchain = NULL;
#endif
chain = rootChain;
while (chain)
{
#ifdef MEMORY_LOCATION
if (chain->firstParent.link)
{
pmem = NULL;
for (mem = chain->firstParent.link; mem; mem = nmem)
{
nmem = mem[pNextParent].link;
if (mem[pLocation].ptr && *mem[pLocation].ptr != chain + 1)
{
if (pmem)
pmem[pNextParent].link = nmem;
else
chain->firstParent.link = nmem;
free (pmem);
}
else
pmem = mem;
}
if (!chain->firstParent.link)
{
if (pchain)
{
pchain->next = chain->next;
DEBUG (3, ("tfree: %p->next -> %p", pchain, pchain->next));
}
else
rootChain = chain->next;
DEBUG (2, ("collected: %p", chain + 1));
pfree (chain + 1, (void*)-1);
if (pchain)
chain = pchain->next;
else
chain = memChain;
continue;
}
}
#endif
mark_memory (chain);
#ifdef MEMORY_LOCAIION
pchain = chain;
#endif
chain = chain->next;
}
pchain = NULL;
chain = memChain;
while (chain)
{
if (chain->mark != sweepMark)
{
if (pchain)
{
pchain->next = chain->next;
DEBUG (3, ("tfree: %p->next -> %p", pchain, pchain->next));
}
else
memChain = chain->next;
DEBUG (2, ("collected: %p", chain + 1));
pfree (chain + 1, (void*)-1);
if (pchain)
chain = pchain->next;
else
chain = memChain;
}
else
{
pchain = chain;
chain = chain->next;
}
}
}
void *proot(
#ifdef MEMORY_LOCATION
void **loc
#else
void
#endif
)
{
#ifdef MEMORY_LOCATION
return palloc (1, NULL, NULL, loc);
#else
return palloc(1, NULL, NULL);
#endif
}
void *palloc(size_t sz, /*@null@*/void *parent, /*@null@*/void (*deallocator)(void *ptr)
#ifdef MEMORY_LOCATION
, void **loc
#endif
)
{
memdata_t *mem = calloc (1, sz + sizeof (memdata_t));
if(!mem)
return NULL;
mem->mark = sweepMark;
mem->deallocator = deallocator;
mem->next = rootChain;
DEBUG (3, ("palloc: %p->next -> %p", mem, mem->next));
rootChain = mem;
DEBUG (2, ("palloc: %p", mem + 1));
if (!parent)
return mem + 1;
return pattach (mem + 1, parent
#ifdef MEMORY_LOCATION
, loc
#endif
);
}
void *prealloc(void *ptr, size_t sz)
{
memdata_t *mem = (memdata_t*)ptr - 1, *res;
memory_t *plink = mem->firstParent.link, *clink = mem->firstChild.link,
*nlink;
memdata_t *chain;
res = realloc (mem, sz + sizeof (memdata_t));
if (res == mem)
return res + 1;
DEBUG (2, ("prealloc: %p -> %p", mem + 1, res + 1));
if (mem == rootChain)
rootChain = res;
else if (mem == memChain)
memChain = res;
else
{
for (chain = rootChain; chain && chain->next != mem; chain = chain->next)
;
if (!chain)
for (chain = memChain; DEXPR (chain &&) chain->next != mem; chain = chain->next)
;
#ifdef DEBUG
if (!chain)
DEBUG (1, ("prealloc: %p not found in chain.", mem + 1));
#endif
chain->next = res;
DEBUG (3, ("prealloc: %p->next -> %p", chain, chain->next));
}
while(clink)
{
nlink = clink[pNextChild].link;
if(res)
{
clink[pParent].data = res;
#ifdef MEMORY_LOCATION
if (clink[pLocation].ptr)
{
void **newptr = (char*)res + ((char*)clink[pLocation].ptr - (char*)mem);
if (*newptr == clink[pChild].data + 1)
clink[pLocation].ptr = newptr;
}
#endif
}
else
pfree(clink[pChild].data + 1, ptr);
clink = nlink;
}
while(plink)
{
nlink = plink[pNextParent].link;
if(res)
{
plink[pChild].data = res;
#ifdef MEMORY_LOCATION
if (*plink[pLocation].ptr == mem + 1)
*plink[pLocation].ptr = res + 1;
#endif
}
#ifdef MEMORY_LOCATION
else if (plink[pParent])
#else
else
#endif
{
if (plink[pParent].data->firstChild.link == plink)
plink[pParent].data->firstChild.link = plink[pNextChild].link;
else
{
for (clink = plink[pParent].data->firstChild.link;
clink[pNextChild].link != plink; clink = clink[pNextChild].link)
;
clink[pNextChild].link = plink[pNextChild].link;
}
DEBUG (2, ("prealloc: unlink %p -> %p", plink[pParent], plink[pChild]));
free (plink);
}
plink = nlink;
}
if(res)
return res + 1;
return NULL;
}
void *pattach(/*@null@*/void *child, void *parent
#ifdef MEMORY_LOCATION
, /*@null@*/void **loc
#endif
)
{
memdata_t *pmem = (memdata_t*)parent - 1;
memdata_t *cmem = (memdata_t*)child - 1;
memory_t *mem;
memdata_t *chain;
if(!child)
return NULL;
#ifdef MEMORY_LOCATION
if (!parent && !loc)
return child;
#else
if (!parent)
return child;
#endif
#ifdef DEBUG
#ifdef MEMORY_LOCATION
if (parent)
#endif
{
if (pmem->isHashed)
{
DEBUG (1, ("pattach: parent %p is hashed (child %p)", parent, child));
return NULL;
}
for(mem = cmem->firstParent.link; mem; mem = mem[pNextParent].link)
{
if(mem[pParent].data == pmem)
DEBUG (2, ("pattach: %p -> %p already linked.", parent, child));
#ifdef MEMORY_LOCATION
if (!mem[pParent].data && *mem[pLocation].ptr == child)
DEBUG (2, ("pattach: %p is located root.", child));
#endif
}
}
#endif
mem = malloc((size_t)(sizeof(memory_t) * pNumLinkLinks));
if(mem)
{
DEBUG (2, ("pattach: linking %p -> %p", parent, child));
mem[pNextParent].link = cmem->firstParent.link;
mem[pNextChild].link = pmem->firstChild.link;
#ifdef MEMORY_LOCATION
if (parent)
mem[pParent].data = pmem;
else
mem[pParent].data = NULL;
#else
mem[pParent].data = pmem;
#endif
mem[pChild].data = cmem;
#ifdef MEMORY_LOCATION
mem[pLocation].ptr = loc;
if (loc)
*loc = child;
#endif
cmem->firstParent.link = mem;
pmem->firstChild.link = mem;
}
else
DEBUG (1, ("pattach: malloc failed for %p -> %p: %m", parent, child));
#ifdef MEMORY_LOCATION
if (parent)
#else
if (!mem || !mem[pNextParent].link)
#endif
{
if (cmem == rootChain)
{
DEBUG (2, ("pattach: Unrooting %p", child));
rootChain = cmem->next;
cmem->next = memChain;
DEBUG (3, ("pattach: %p->next -> %p", cmem, cmem->next));
memChain = cmem;
}
else
{
for (chain = rootChain; chain && chain->next != cmem; chain = chain->next)
;
if (chain)
{
DEBUG (2, ("pattach: Unrooting %p", child));
chain->next = cmem->next;
DEBUG (3, ("prealloc: %p->next -> %p", chain, chain->next));
cmem->next = memChain;
DEBUG (3, ("prealloc: %p->next -> %p", cmem, cmem->next));
memChain = cmem;
}
}
}
return child;
}
void *padopt(void *child, void *oldparent, void *newparent
#ifdef MEMORY_LOCATION
, void **newloc
#endif
)
{
memdata_t *opmem = (memdata_t*)oldparent - 1;
memdata_t *npmem = (memdata_t*)newparent - 1;
memdata_t *cmem = (memdata_t*)child - 1;
memory_t *mem, *ccmem = NULL, *pcmem, *ppmem;
int i;
if (!child)
return NULL;
if (!newparent || npmem->isHashed)
return child;
pcmem = NULL;
i = 0;
for(mem = cmem->firstParent.link; mem && i < 3; mem = mem[pNextParent].link)
{
if(!(i & 1))
ccmem = mem;
if(mem[pParent].data == npmem)
i |= 2;
else if(mem[pParent].data == opmem)
i |= 1;
if(!(i & 1))
pcmem = mem;
}
switch(i)
{
case 0:
DEBUG (1, ("padopt: No old link %p -> %p.", oldparent, child));
return pattach (child, newparent
#ifdef MEMORY_LOCATION
, newloc
#endif
);
case 3:
DEBUG (1, ("padopt: Both %p and %p linked to %p.", oldparent, newparent, child));
/* fallthrough */
case 1:
/* This is the one we _should_ get. */
DEBUG (2, ("padopt: %p adopting %p from %p.", newparent, child, oldparent));
if(ccmem == opmem->firstChild.link)
opmem->firstChild.link = ccmem[pNextChild].link;
else
{
for(ppmem = opmem->firstChild.link; DEXPR (ppmem[pNextChild].link &&)
ppmem[pNextChild].link != ccmem; ppmem = ppmem[pNextChild].link)
;
#ifdef DEBUG
if(!ppmem[pNextChild].link)
{
DEBUG (1, ("padopt: Couldn't find link to child in old parent."));
return child;
}
#endif
ppmem[pNextChild].link = ccmem[pNextChild].link;
}
ccmem[pNextChild].link = npmem->firstChild.link;
ccmem[pParent].data = npmem;
#ifdef MEMORY_LOCATION
ccmem[pLocation].ptr = newloc;
if (newloc)
*newloc = child;
#endif
npmem->firstChild.link = ccmem;
return child;
default:
DEBUG (1, ("padopt: %p is already parent of %p and %p isn't.", newparent, child, oldparent));
return pattach (child, newparent
#ifdef MEMORY_LOCATION
, newloc
#endif
);
}
}
void *prootify (void *ptr)
{
memdata_t *mem = (memdata_t*)ptr - 1, *pmem;
if (!ptr)
return NULL;
while (mem->firstParent.link)
{
DEBUG (2, ("prootify: %p has parent %p.", ptr, mem->firstParent.link[pParent].data + 1));
pfree (ptr, mem->firstParent.link[pParent].data + 1);
}
if (mem == memChain)
memChain = mem->next;
else
{
pmem = memChain;
while (pmem && pmem->next != mem)
pmem = pmem->next;
if (!pmem)
{
#ifdef DEBUG
if (mem == rootChain)
pmem = mem;
else
{
pmem = rootChain;
while (pmem && pmem->next != mem)
pmem = pmem->next;
}
if (pmem)
{
DEBUG (1, ("prootify: %p already root.", ptr));
}
else
DEBUG (1, ("prootify: %p not found in chain.", ptr));
#endif
return ptr;
}
pmem->next = mem->next;
DEBUG (3, ("prootify: %p->next -> %p.", pmem, pmem->next));
}
DEBUG (2, ("prootify: rooting %p.", ptr));
mem->next = rootChain;
DEBUG (3, ("prootify: %p->next -> %p.", mem, mem->next));
rootChain = mem;
return ptr;
}
void pfree(void *child, void *parent)
{
memdata_t *cmem = (memdata_t*)child - 1;
memdata_t *pmem = (memdata_t*)parent - 1;
memory_t *mem, *pcmem, *ppmem;
memhash_t *hash, *phash;
memdata_t *chain;
if(!child)
return;
pcmem = NULL;
if (parent && parent != (void*)-1)
{
#ifdef DEBUG
if (pmem->isHashed)
{
DEBUG (1, ("pfree: Parent %p is hashed (child %p).", parent, child));
return;
}
#endif
for (mem = cmem->firstParent.link; mem; mem = mem[pNextParent].link)
{
if(mem[pParent].data == pmem)
break;
pcmem = mem;
}
if (!mem)
{
#ifdef DEBUG
DEBUG (1, ("pfree: Parent %p not found in child %p.", parent, child));
ppmem = NULL;
for (mem = pmem->firstChild.link; mem; mem = mem[pNextChild].link)
{
if (mem[pChild].data == cmem)
break;
ppmem = mem;
}
if (mem)
{
DEBUG (1, ("pfree: but child found in parent."));
ppmem[pNextChild].link = mem[pNextChild].link;
free (mem);
}
#endif
return;
}
if (mem == pmem->firstChild.link)
pmem->firstChild.link = mem[pNextChild].link;
else
{
for (ppmem = pmem->firstChild.link; DEXPR (ppmem[pNextChild].link &&)
ppmem[pNextChild].link != mem; ppmem = ppmem[pNextChild].link)
;
#ifdef DEBUG
if (!ppmem[pNextChild].link)
DEBUG (1, ("pfree: Child %p not found in parent %p.", child, parent));
#endif
ppmem[pNextChild].link = mem[pNextChild].link;
}
if (pcmem)
pcmem[pNextParent].link = mem[pNextParent].link;
else
cmem->firstParent.link = mem[pNextParent].link;
DEBUG (2, ("pfree: Unlinking %p -> %p.", parent, child));
free(mem);
}
else
{
DEBUG (2, ("pfree: Really freeing %p.", child));
if (cmem->isHashed)
{
hash = cmem->firstChild.hash;
if (*hash->bucket && (*hash->bucket)->firstChild.hash == hash)
*hash->bucket = hash->next;
else
{
for (phash = (*hash->bucket)->firstChild.hash; phash->next &&
phash->next->firstChild.hash != hash;
phash = phash->next->firstChild.hash)
;
#ifdef DEBUG
if (!phash)
DEBUG (1, ("%p not found in bucket.", child));
#endif
phash->next = hash->next;
DEBUG (3, ("pfree: %p->next -> %p", phash, phash->next));
free (cmem->firstChild.hash);
}
}
else
{
while(cmem->firstChild.link)
pfree (cmem->firstChild.link[pChild].data + 1, child);
}
while ((ppmem = cmem->firstParent.link))
{
#ifdef MEMORY_LOCATION
if (!ppmem[pParent].data)
{
cmem->firstParent.link = ppmem[pNextParent].link;
free (ppmem);
}
else
#endif
pfree (child, ppmem[pParent].data + 1);
}
if (parent != (void*)-1)
{
if (cmem == rootChain)
rootChain = cmem->next;
else if (cmem == memChain)
memChain = cmem->next;
else
{
for (chain = rootChain; chain && chain->next != cmem; chain = chain->next)
;
if (!chain)
for (chain = memChain; DEXPR (chain &&) chain->next != cmem; chain = chain->next)
;
#ifdef DEBUG
if (!chain)
DEBUG (1, ("%p not found in chain.", child));
#endif
chain->next = cmem->next;
DEBUG (3, ("pfree: %p->next -> %p", chain, chain->next));
}
}
if (cmem->deallocator)
cmem->deallocator (child);
free(cmem);
}
}
void *pchild(const void *parent, const void *prevChild)
{
const memdata_t *const pmem = (memdata_t*)parent - 1;
if (!parent)
return NULL;
if(prevChild)
{
const memdata_t *const cmem = (memdata_t*)prevChild - 1;
const memory_t *mem;
/*
* Most nodes tend to have more children
* than parents, so usually it should be faster to search for the
* parent in the child's list.
*/
for(mem = cmem->firstParent.link; mem; mem = mem[pNextParent].link)
if(mem[pParent].data == pmem)
{
if(mem[pNextChild].link)
return mem[pNextChild].link[pChild].data + 1;
return NULL;
}
}
else if(pmem->firstChild.link)
return pmem->firstChild.link[pChild].data + 1;
return NULL;
}
int plinked (const void *child, const void *parent)
{
const memdata_t *pmem = (memdata_t*)parent - 1;
const memdata_t *cmem = (memdata_t*)child - 1;
const memory_t *mem;
if (!child || !parent)
return 0;
for (mem = cmem->firstParent.link; mem; mem = mem[pNextParent].link)
{
if(mem[pParent].data == pmem)
return 1;
}
return 0;
}
int pnparents (const void *child)
{
const memdata_t *cmem = (memdata_t*)child - 1;
const memory_t *mem;
int res;
if (!child)
return 0;
res = 0;
for (mem = cmem->firstParent.link; mem; mem = mem[pNextParent].link)
res++;
return res;
}
char *pstring(const char *str, void *parent)
{
int hval = 0;
const unsigned char *sp = (unsigned char*)str;
memdata_t *mem;
char *res;
if (!str)
return NULL;
while (*sp)
{
hval += randVals[(*sp + ((char*)sp - str)) & 0xFF];
sp++;
}
hval %= stringHashSize;
mem = stringHash[hval];
while (mem)
{
if (!strcmp ((char*)(mem + 1), str))
return (char*)pattach (mem + 1, parent);
mem = mem->firstChild.hash->next;
}
res = palloc ((char*)sp - str + 1, parent, NULL);
if (res)
{
strcpy (res, str);
mem = (memdata_t*)res - 1;
mem->firstChild.hash = malloc (sizeof (memhash_t));
if (mem->firstChild.hash)
{
mem->isHashed = 1;
mem->firstChild.hash->bucket = &stringHash[hval];
mem->firstChild.hash->next = stringHash[hval];
DEBUG (3, ("pstring: %p->next -> %p", mem->firstChild.hash, mem->firstChild.hash->next));
stringHash[hval] = mem;
}
}
return res;
}
syntax highlighted by Code2HTML, v. 0.9.1