/*
* LIB/ZALLOC.C - mostly self contained zero-overhead memory pool/allocation
* subsystem.
*
* This subsystem implements memory pools and memory allocation
* routines. It uses pagealloc() for a low level allocator and
* malloc() for the MemPool structures. The idea is to (a) prevent
* fragmentation and (b) allow pools to be unmapped from the VM space
* when no longer needed.
*
* Pools are managed via a linked list of 'free' areas. Allocating
* memory creates holes in the freelist, freeing memory fills them.
* Since the freelist consists only of free memory areas, it is possible
* to allocate all the memory in a pool without incuring any structural
* overhead.
*
* The system works best when allocating similarly-sized chunks of
* memory.
*
* (c)Copyright 1997, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*/
#include "defs.h"
#define POOLSIZE 65536
#define MEMNODE_SIZE_MASK ((sizeof(char *) == 4) ? 7 : 15)
Prototype void *nzalloc(MemPool **mpool, int bytes);
Prototype void *zalloc(MemPool **mpool, int bytes);
Prototype char *zallocStr(MemPool **pmp, const char *s);
Prototype char *zappendStr(MemPool **pmp, char **pbuf, const char *s1, const char *s2);
Prototype char *zallocStrTrim(MemPool **pmp, const char *s, int l);
Prototype void zfree(MemPool **mpool, void *ptr, int bytes);
Prototype void zfreeStr(MemPool **pmp, char **ps);
Prototype void allocPool(MemPool **mpool, int bytes);
Prototype void freePool(MemPool **mpool);
MemPool *initPool(int bytes);
void *
nzalloc(MemPool **pmp, int bytes)
{
MemPool *mp;
/* 8 or 16-byte alignment required, depending on the pointer size */
bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
while ((mp = *pmp) != NULL) {
if (bytes <= mp->mp_Size - mp->mp_Used) {
MemNode **pmn;
MemNode *mn;
for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) {
if (bytes <= mn->mr_Bytes) {
/*
* Cut a chunk of memory out of the beginning of this
* block and fixup the link appropriately.
*/
char *ptr = (char *)mn;
if (mn->mr_Bytes == bytes) {
*pmn = mn->mr_Next;
} else {
mn = (MemNode *)((char *)mn + bytes);
mn->mr_Next = ((MemNode *)ptr)->mr_Next;
mn->mr_Bytes = ((MemNode *)ptr)->mr_Bytes - bytes;
*pmn = mn;
}
mp->mp_Used += bytes;
return(ptr);
}
}
}
pmp = &mp->mp_Next;
}
/*
* Failed to locate sufficient memory, allocate another
* pool.
*/
allocPool(pmp, ((bytes < POOLSIZE) ? POOLSIZE : bytes));
return(zalloc(pmp, bytes));
}
void *
zalloc(MemPool **pmp, int bytes)
{
void *ptr = nzalloc(pmp, bytes);
bzero(ptr, bytes);
return(ptr);
}
char *
zallocStr(MemPool **pmp, const char *s)
{
char *r = zalloc(pmp, strlen(s) + 1);
strcpy(r, s);
return(r);
}
char *
zappendStr(MemPool **pmp, char **pbuf, const char *s1, const char *s2)
{
int l = ((s1) ? strlen(s1) : 0) + ((s2) ? strlen(s2) : 0);
int o = 0;
char *ptr = NULL;
if (*pbuf) {
o = strlen(*pbuf);
ptr = zalloc(pmp, l + o + 1);
strcpy(ptr, *pbuf);
zfree(pmp, *pbuf, o + 1);
*pbuf = NULL;
} else {
ptr = zalloc(pmp, l + 1);
}
ptr[o] = 0;
if (s1)
strcat(ptr + o, s1);
if (s2)
strcat(ptr + o, s2);
*pbuf = ptr;
return(ptr);
}
char *
zallocStrTrim(MemPool **pmp, const char *s, int l)
{
char *r;
while (l && (*s == ' ' || *s == '\t')) {
++s;
--l;
}
--l;
while (l >= 0 &&
(s[l] == '\r' || s[l] == '\n' || s[l] == ' ' || s[l] == '\t')
) {
--l;
}
++l;
r = zalloc(pmp, l + 1);
bcopy(s, r, l);
r[l] = 0;
return(r);
}
void
zfree(MemPool **pmp, void *ptr, int bytes)
{
MemPool *mp;
/* 8 or 16-byte alignment required, depending on the pointer size */
bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
while ((mp = *pmp) != NULL) {
if ((char *)ptr >= (char *)mp->mp_Base && (char *)ptr < (char *)mp->mp_Base + mp->mp_Size) {
MemNode **pmn;
MemNode *mn;
mp->mp_Used -= bytes;
for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) {
/*
* If area between last node and current node
* - check range
* - check merge with next area
* - check merge with previous area
*/
if ((char *)ptr <= (char *)mn) {
/*
* range check
*/
if ((char *)ptr + bytes > (char *)mn) {
logit(LOG_CRIT, "zfree(%08lx,%d) failed1, corrupt memlist", (long)ptr, bytes);
exit(1);
}
/*
* merge against next area or create independant area
*/
if ((char *)ptr + bytes == (char *)mn) {
((MemNode *)ptr)->mr_Next = mn->mr_Next;
((MemNode *)ptr)->mr_Bytes= bytes + mn->mr_Bytes;
} else {
((MemNode *)ptr)->mr_Next = mn;
((MemNode *)ptr)->mr_Bytes= bytes;
}
*pmn = mn = (MemNode *)ptr;
/*
* merge against previous area (if there is a previous
* area).
*/
if (pmn != &mp->mp_First) {
if ((char *)pmn + ((MemNode *)pmn)->mr_Bytes == (char *)ptr) {
((MemNode *)pmn)->mr_Next = mn->mr_Next;
((MemNode *)pmn)->mr_Bytes += mn->mr_Bytes;
}
}
return;
}
if ((char *)ptr < (char *)mn + mn->mr_Bytes) {
logit(LOG_CRIT, "zfree(%08lx,%d) failed2, corrupt memlist", (long)ptr, bytes);
exit(1);
}
}
/*
* We are beyond the last MemNode, append new MemNode. Merge against
* previous area if possible.
*/
((MemNode *)ptr)->mr_Next = NULL;
((MemNode *)ptr)->mr_Bytes = bytes;
*pmn = mn = (MemNode *)ptr;
return;
}
pmp = &mp->mp_Next;
}
logit(LOG_CRIT, "zfree(%08lx,%d) failed3, corrupt memlist", (long)ptr, bytes);
exit(1);
}
void
zfreeStr(MemPool **pmp, char **ps)
{
if (*ps) {
zfree(pmp, *ps, strlen(*ps) + 1);
*ps = NULL;
}
}
void
allocPool(MemPool **pmp, int bytes)
{
MemPool *mp = calloc(sizeof(MemPool), 1);
mp->mp_Next = *pmp;
mp->mp_Base = pagealloc(&mp->mp_Size, bytes);
mp->mp_First = mp->mp_Base;
mp->mp_First->mr_Next = NULL;
mp->mp_First->mr_Bytes = mp->mp_Size;
*pmp = mp;
}
void
freePool(MemPool **pmp)
{
MemPool *mp;
while ((mp = *pmp) != NULL) {
*pmp = mp->mp_Next;
pagefree(mp->mp_Base, mp->mp_Size);
free(mp);
}
}
syntax highlighted by Code2HTML, v. 0.9.1