If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle
See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// lib_heap.c -- library heap management
#include "library.h"
/*
Provide a simple, portable means of checking for memory leaks and bounding
errors in the code.
*/
typedef struct memhdr_s {
struct memhdr_s *prev;
struct memhdr_s *next;
int bytes;
byte tag;
byte zone[3]; // catch bounding errors
} memhdr_t;
typedef struct {
memhdr_t *prev; // must match the beginning of memhdr_t
memhdr_t *next;
} memqueue_t;
memqueue_t mem_head = { (memhdr_t *)&mem_head, (memhdr_t *)&mem_head };
/*
==============
L_Malloc
Allocate the given amount of memory
==============
*/
void *L_Malloc(int bytes, int tag)
{
memhdr_t *hdr;
void *block;
if (!bytes)
return 0;
hdr = (memhdr_t *)malloc(bytes + sizeof(memhdr_t) + 4);
if (!hdr)
throw LError("Out of memory");
block = hdr + 1;
hdr->prev = (memhdr_t *)&mem_head;
hdr->next = mem_head.next;
hdr->next->prev = hdr->prev->next = hdr;
hdr->bytes = bytes;
hdr->tag = tag;
memset(hdr->zone, 0xCC, sizeof(hdr->zone));
memset((byte *)block + bytes, 0xCC, 4);
return block;
}
/*
==============
L_Strdup
==============
*/
char *L_Strdup(const char *string, int tag)
{
int len;
char *dest;
len = strlen(string) + 1;
dest = (char *)L_Malloc(len, tag);
memcpy(dest, string, len);
return dest;
}
/*
==============
L_Free
Free a block that was allocated by L_Malloc
==============
*/
void L_Free(void *block)
{
memhdr_t *hdr = ((memhdr_t *)block) - 1;
// check the protection zones
if (memnchr(hdr->zone, 0xCC, sizeof(hdr->zone)) ||
memnchr((byte *)block + hdr->bytes, 0xCC, 4))
throw LError("Com_Free: Bounding error for block at %08x (%i bytes, tagged %i)",
block, hdr->bytes, hdr->tag);
// unlink
hdr->prev->next = hdr->next;
hdr->next->prev = hdr->prev;
free(hdr);
}
/*
==============
L_Realloc
Reallocate the given memory block. Both block and bytes can be 0.
tag won't override the tag if the block already exists.
==============
*/
void *L_Realloc(void *block, int bytes, int tag)
{
memhdr_t *hdr;
if (!block)
return L_Malloc(bytes, tag);
hdr = ((memhdr_t *)block) - 1;
// check the protection zones
if (memnchr(hdr->zone, 0xCC, sizeof(hdr->zone)) ||
memnchr((byte *)block + hdr->bytes, 0xCC, 4))
throw LError("Com_Realloc: Bounding error for block at %08x (%i bytes, tagged %i)",
block, hdr->bytes, hdr->tag);
// if new size is 0, just free the block
if (!bytes) {
hdr->prev->next = hdr->next;
hdr->next->prev = hdr->prev;
free(hdr);
return 0;
}
hdr = (memhdr_t *)realloc(hdr, sizeof(memhdr_t) + bytes + 4);
block = hdr + 1;
// re-link, memory might have been moved
hdr->prev->next = hdr;
hdr->next->prev = hdr;
hdr->bytes = bytes;
memset((byte *)block + bytes, 0xCC, 4);
return block;
}
/*
==============
L_TagFree
Check for blocks with certain tags. Complain (and free) if any were found.
This should help to find memory leaks.
==============
*/
void L_TagFree(int start, int end)
{
memhdr_t *next = (memhdr_t *)&mem_head;
memhdr_t *hdr;
bool leak = false;
next = next->next;
while(next != (memhdr_t *)&mem_head) {
hdr = next;
next = next->next; // whoaaa....
if (start < 0 || (hdr->tag >= start && (end < 0 || hdr->tag <= end))) {
L_Printf("Memory leak at %08x (%i bytes, tagged %i)\n",
hdr+1, hdr->bytes, hdr->tag);
L_Free(hdr+1);
leak = true;
}
}
if (leak)
throw LError("Memory leaks found!");
}