1302 lines
27 KiB
C
1302 lines
27 KiB
C
#ifndef MSC_MALLOC
|
|
|
|
#define WIN31
|
|
#define STRICT
|
|
|
|
#include <windows.h>
|
|
#include <toolhelp.h>
|
|
#include <stdio.h>
|
|
|
|
#include "mswin.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
*
|
|
* Memory allocation routines.
|
|
*
|
|
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
|
|
/*
|
|
* The plan is to allocate small blocks in the local heap and
|
|
* large blocks in the global heap. The intention is to keep
|
|
* the number of global allocations to a minimum.
|
|
*
|
|
* The boundry between small memory and large memory is
|
|
* controld by the constant SMALL_MEM_BOUNDRY. Blocks smaller
|
|
* than SMALL_MEM_BOUNDRY go into the local heap. This should
|
|
* be set large enough to capture the majority of small memory
|
|
* blocks in the local heap. But if it is too large, the local
|
|
* heap will fill up and we will end up allocating a lot of small
|
|
* blocks in the global heap.
|
|
*
|
|
* Unfortunatly, pine seems to need a large stack. With the
|
|
* stack, some global data, and the heap all cramed in to a 64K
|
|
* segment we end up with a heap that is smaller than ideal.
|
|
* This could be improved by reducing the size of the stack, or
|
|
* by moving the heap to a seperate memory block. I did a little
|
|
* work on moving the heap, but was not successful. My attepts
|
|
* can be seen in the function MemATest().
|
|
*
|
|
* Currently (7/8/94) I've set the stack to 32K (in pine/makefile.win),
|
|
* the heap to 12K, and the small memory boundry to 32 bytes.
|
|
* Statistics on pine memory allocation suggest that it would be better
|
|
* to have a 25K local heap and a 95 byte small memory boundry.
|
|
*
|
|
* Statistics on memory use can be gathered by logging memory debugging
|
|
* to a file, then running the following awk script:
|
|
*
|
|
|
|
# mem.awk
|
|
#
|
|
# Looks through log and find blocks that were allocated but not
|
|
# freed uses block id numbers, rather than addresses, since this is
|
|
# more accurate (and since this is what block ids exist!)
|
|
#
|
|
# awk may run out of memory if the logfile gets too big. If this
|
|
# happens, then another strategy will be needed...
|
|
#
|
|
|
|
BEGIN {
|
|
FS = "[ ()]+";
|
|
|
|
b[0] = 16;
|
|
b[1] = 32;
|
|
b[2] = 64;
|
|
b[3] = 96;
|
|
b[4] = 128;
|
|
b[5] = 256;
|
|
b[6] = 512;
|
|
b[7] = 1024;
|
|
b[8] = 2048;
|
|
b[9] = 4096;
|
|
b[10] = 9600;
|
|
b[11] = 20000;
|
|
b[12] = 40000;
|
|
b[13] = 80000;
|
|
b[14] = 200000000;
|
|
b[15] = 0;
|
|
bcount = 15;
|
|
for (i = 0; i < bcount; ++i)
|
|
c[i] = 0;
|
|
|
|
maxmem = 0;
|
|
maxsmallmem = 0;
|
|
|
|
allocs = 0;
|
|
frees = 0;
|
|
globalallocs = 0;
|
|
pallocs = 0;
|
|
pfrees = 0;
|
|
}
|
|
|
|
{
|
|
#print "one", $1, "two", $2, "three", $3, "four ", $4, "five ", $5;
|
|
if( $1 == "MemAlloc:" ) {
|
|
m[$5] = $0;
|
|
|
|
++allocs;
|
|
if ($9 == 1) ++globalallocs;
|
|
|
|
for (i = 0; i < bcount; ++i) {
|
|
if (b[i] > $7) {
|
|
++c[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if( $1 == "MemFree:" ) {
|
|
delete m[$5];
|
|
++frees;
|
|
}
|
|
if( $1 == "PageAlloc:" ) {
|
|
p[$5] = $0;
|
|
|
|
++pallocs;
|
|
}
|
|
else if( $1 == "PageFree:" ) {
|
|
delete p[$5];
|
|
++pfrees;
|
|
}
|
|
else if ($1 == "Memory") {
|
|
if ($6 > maxmem) maxmem = $6;
|
|
}
|
|
else if ($1 == "Small") {
|
|
if ($7 > maxsmallmem) maxsmallmem = $7;
|
|
}
|
|
}
|
|
|
|
|
|
END {
|
|
for( i in m ) {
|
|
print m[i]
|
|
}
|
|
for (i in p) {
|
|
print p[i]
|
|
}
|
|
|
|
cumulative = 0;
|
|
for (i = 0; i < bcount; ++i) {
|
|
cumulative += c[i];
|
|
printf "%9d : %5d (%5d)\n", b[i], c[i], cumulative;
|
|
}
|
|
|
|
print;
|
|
print "Max memory use: ", maxmem;
|
|
print "Max small memory use: ", maxsmallmem;
|
|
print;
|
|
print "Local allocations ", allocs - globalallocs;
|
|
print "Global allocations ", globalallocs;
|
|
print "Total allocations ", allocs;
|
|
print "Total memory frees ", frees;
|
|
print "Blocks not freed ", allocs - frees;
|
|
print;
|
|
print "Page allocations ", pallocs;
|
|
print "Page frees ", pfrees;
|
|
print "Pages not freed ", pallocs - pfrees;
|
|
}
|
|
|
|
*
|
|
* Each memory block is assigned a unique id. This is only used
|
|
* to match allocations with frees in the debug log.
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
* SEGHEAP selectes between two different implementations of the memory
|
|
* management functions. Defined and it uses a sub allocation scheme.
|
|
* Undefined and it comples a direct allocation scheme.
|
|
*
|
|
* The sub allocation scheme is greatly prefered because it greatly reduces
|
|
* the number of global memory allocations.
|
|
*/
|
|
#define SEGHEAP /* Use the sub allocation scheme. */
|
|
|
|
|
|
|
|
|
|
#define MEM_DEBUG /* Compile in memory debugging code.*/
|
|
#define MEM_DEBUG_LEVEL 8 /* Pine debug level at which memory
|
|
* debug messages will be generated.*/
|
|
#ifdef MEM_DEBUG
|
|
static int MemDebugLevel = 0; /* Doing debugging. */
|
|
static FILE *MemDebugFile = NULL; /* File to write to. */
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
#define LOCAL
|
|
#else
|
|
#define LOCAL static
|
|
#endif
|
|
|
|
|
|
#define GET_SEGMENT(x) (0xffff & ((DWORD)(x) >> 16))
|
|
#define GET_OFFSET(x) (0xffff & (DWORD)(x))
|
|
|
|
|
|
#undef malloc
|
|
#undef realloc
|
|
#undef free
|
|
|
|
|
|
void MemATest (void);
|
|
|
|
|
|
|
|
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
*
|
|
* Standard memory allcation functions.
|
|
*
|
|
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
|
|
|
|
void *
|
|
malloc (size_t size)
|
|
{
|
|
return (MemAlloc (size));
|
|
}
|
|
|
|
|
|
void __far *
|
|
_fmalloc (size_t size)
|
|
{
|
|
return (MemAlloc (size));
|
|
}
|
|
|
|
|
|
void __far *
|
|
realloc (void *memblock, size_t size)
|
|
{
|
|
return (MemRealloc (memblock, size));
|
|
}
|
|
|
|
|
|
void __far *
|
|
_frealloc (void *memblock, size_t size)
|
|
{
|
|
return (MemRealloc (memblock, size));
|
|
}
|
|
|
|
|
|
void
|
|
free (void *memblock)
|
|
{
|
|
MemFree (memblock);
|
|
}
|
|
|
|
void
|
|
_ffree (void *memblock)
|
|
{
|
|
MemFree (memblock);
|
|
}
|
|
|
|
|
|
/*
|
|
* Turn on memory debugging and specify file to write to.
|
|
*/
|
|
void
|
|
MemDebug (int debug, FILE *debugFile)
|
|
{
|
|
#ifdef MEM_DEBUG
|
|
if (debugFile == NULL) {
|
|
MemDebugLevel = 0;
|
|
MemDebugFile = NULL;
|
|
}
|
|
else {
|
|
MemDebugLevel = debug;
|
|
MemDebugFile = debugFile;
|
|
fprintf (MemDebugFile, "Memory Debuging set on\n");
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SEGHEAP
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
*
|
|
* SEGHEAP Memory allocation routines.
|
|
*
|
|
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
|
|
/*
|
|
* This implementation allocates memory in pages then sub alloates from the
|
|
* pages. This greatly reduces the number of global memory blocks allocated.
|
|
* This code originally written by Stephen Chung and posted to a Usenet news
|
|
* group. I've modified them for use in Pine. The author says:
|
|
*
|
|
*
|
|
* Copyright (C) Stephen Chung, 1991-1992. All rights reserved.
|
|
*
|
|
* Afterwords
|
|
* ----------
|
|
*
|
|
* Theoretically, you are required to obtain special approval from me (because
|
|
* I copyrighted these routines) if you want to use them in your programs.
|
|
* However, I usually don't really care if you are not using these routines in
|
|
* a commercial, shareware etc. product.
|
|
*
|
|
* Any questions and/or bug fixes, please send email to:
|
|
*
|
|
* Stephen Chung stephenc@cunixf.cc.columbia.edu
|
|
*
|
|
* If it bounces, then try schung@cogsci.Berkeley.EDU
|
|
*
|
|
* Have fun!
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* The folloing control debugging code for testing out of memory conditions.
|
|
* there are to test code.
|
|
*
|
|
* MEM_ALLOC_LIMT
|
|
* Setting this to anything other than zero will limit memory allocation
|
|
* to (rougly) that many bytes.
|
|
*
|
|
* MEM_FAIL_SOON
|
|
* Compiles in a function which will cause the memory allocation to fail
|
|
* soon after that function is called. this can be used to test
|
|
* mem alloc failurs in specific code segments.
|
|
*/
|
|
#define MEM_ALLOC_LIMIT 0
|
|
#define MEM_FAIL_SOON 0
|
|
|
|
|
|
#if MEM_FAIL_SOON
|
|
static long MemFailSoonLimit = 0;
|
|
#endif
|
|
|
|
|
|
|
|
#define MAGIC 0x42022667
|
|
#define MAGIC2 0x56743296
|
|
|
|
typedef struct MemoryStruct {
|
|
long int magic;
|
|
void far *page;
|
|
WORD id;
|
|
MemSize size;
|
|
BOOL allocated;
|
|
struct MemoryStruct far *next, far *prev;
|
|
long int magic2;
|
|
} MEMHEADER;
|
|
|
|
typedef struct PageHeaderStruct {
|
|
long int magic;
|
|
HANDLE handle;
|
|
WORD id;
|
|
MemSize size;
|
|
MemSize used;
|
|
MemSize overhead;
|
|
MEMHEADER far *data, far *empty;
|
|
struct PageHeaderStruct far *next, far *prev;
|
|
long int magic2;
|
|
} MEMPAGEHEADER;
|
|
|
|
typedef struct {
|
|
MEMPAGEHEADER far *pages;
|
|
int nr_pages;
|
|
} MAINMEMHEADER;
|
|
|
|
#define PAGESIZE (6 * 1024)
|
|
#define USEABLESIZE (PAGESIZE - sizeof(MEMPAGEHEADER) - sizeof(MEMHEADER))
|
|
|
|
|
|
LOCAL MAINMEMHEADER MemHeader = { NULL, 0 };
|
|
LOCAL WORD MemID = 0; /* Keep track of ID. */
|
|
LOCAL WORD PageID = 0;
|
|
LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
|
|
LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
|
|
LOCAL unsigned long PageMemInUse = 0;
|
|
LOCAL unsigned long PageMemInUseMax = 0;
|
|
|
|
|
|
|
|
static MEMPAGEHEADER far *
|
|
AddPage(MemSize n)
|
|
{
|
|
void far *cp;
|
|
MEMHEADER far *mp;
|
|
MEMPAGEHEADER far *p;
|
|
HANDLE handle = NULL;
|
|
|
|
|
|
#if MEM_ALLOC_LIMIT
|
|
if (n + PageMemInUse > MEM_ALLOC_LIMIT) {
|
|
MessageBox (NULL, "PageAlloc: Above preset limit, allocation fails",
|
|
"Out Of Memory", MB_ICONSTOP | MB_OK);
|
|
return (NULL);
|
|
}
|
|
#endif
|
|
|
|
handle = GlobalAlloc(GHND, n);
|
|
if (handle == NULL) {
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= 1)
|
|
fprintf (MemDebugFile, "***\n*** Out of memory: allocating %d bytes\n***\n", n);
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
if (MemHeader.pages == NULL || MemHeader.nr_pages <= 0) {
|
|
p = MemHeader.pages = (MEMPAGEHEADER far *) GlobalLock(handle);
|
|
p->prev = NULL;
|
|
} else {
|
|
for (p = MemHeader.pages; p->next != NULL; p = p->next);
|
|
p->next = (MEMPAGEHEADER far *) GlobalLock(handle);
|
|
p->next->prev = p;
|
|
p = p->next;
|
|
}
|
|
|
|
p->magic = MAGIC;
|
|
p->handle = handle;
|
|
p->next = NULL;
|
|
p->id = PageID++;
|
|
p->size = n;
|
|
p->used = 0;
|
|
p->overhead = sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER);
|
|
p->magic2 = MAGIC2;
|
|
|
|
cp = ((char far *) p) + sizeof(MEMPAGEHEADER);
|
|
mp = (MEMHEADER far *) cp;
|
|
|
|
p->data = p->empty = mp;
|
|
|
|
mp->magic = 0L;
|
|
mp->magic2 = 0L;
|
|
mp->allocated = FALSE;
|
|
mp->page = p;
|
|
mp->size = p->size - p->overhead;
|
|
mp->next = mp->prev = NULL;
|
|
|
|
MemHeader.nr_pages++;
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "PageAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
|
|
p, p->id, (long)n, 1);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
PageMemInUse += n;
|
|
if (PageMemInUse > PageMemInUseMax)
|
|
PageMemInUseMax = PageMemInUse;
|
|
|
|
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
DeletePage (MEMPAGEHEADER far *p)
|
|
{
|
|
#ifdef MEM_DEBUG
|
|
/* Deubgging info... */
|
|
if (MemDebugLevel >= 4) {
|
|
if (PageMemInUse == PageMemInUseMax)
|
|
fprintf (MemDebugFile, "Page usage is up to %lu\n", PageMemInUseMax);
|
|
}
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "PageFree: addr(%lx) id(%u)\n",
|
|
p, p->id);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
PageMemInUse -= p->size;
|
|
|
|
|
|
if (p->next == NULL && p->prev == NULL) {
|
|
MemHeader.pages = NULL;
|
|
MemHeader.nr_pages = 0;
|
|
GlobalUnlock (p->handle);
|
|
GlobalFree (p->handle);
|
|
} else {
|
|
if (p == MemHeader.pages) MemHeader.pages = p->next;
|
|
MemHeader.nr_pages--;
|
|
|
|
if (p->prev != NULL) p->prev->next = p->next;
|
|
if (p->next != NULL) p->next->prev = p->prev;
|
|
|
|
GlobalUnlock (p->handle);
|
|
GlobalFree (p->handle);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Segmented heap memory allocation.
|
|
*/
|
|
|
|
MemPtr
|
|
_MemAlloc (MemSize n, char __far * file, int line)
|
|
{
|
|
MEMPAGEHEADER far *p;
|
|
MEMHEADER far *mp;
|
|
char far *cp;
|
|
|
|
if (n >= 65535) {
|
|
assert (n < 65535);
|
|
goto AllocFail;
|
|
}
|
|
|
|
#if MEM_FAIL_SOON
|
|
if (MemFailSoonLimit != 0 && MemFailSoonLimit < MemInUse + n) {
|
|
MessageBox (NULL, "MemAlloc: Told to fail here, allocation fails",
|
|
"Out Of Memory", MB_ICONSTOP | MB_OK);
|
|
return (NULL);
|
|
}
|
|
#endif
|
|
|
|
/* Larger than page size? */
|
|
|
|
if (n > USEABLESIZE) {
|
|
p = AddPage(n + sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER));
|
|
if (p == NULL)
|
|
goto AllocFail;
|
|
|
|
mp = p->data;
|
|
mp->magic = MAGIC;
|
|
mp->magic2 = MAGIC2;
|
|
mp->id = MemID++;
|
|
mp->allocated = TRUE;
|
|
|
|
|
|
p->used = n;
|
|
p->empty = NULL;
|
|
|
|
cp = ((char far *) mp) + sizeof(MEMHEADER);
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
|
|
cp, mp->id, (long)n, 0);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
MemInUse += n;
|
|
if (MemInUse > MemInUseMax)
|
|
MemInUseMax = MemInUse;
|
|
return ((MemPtr) cp);
|
|
}
|
|
|
|
|
|
/* Search for the hole */
|
|
|
|
for (p = MemHeader.pages; p != NULL; p = p->next) {
|
|
/* Scan the chains */
|
|
if (p->size - p->used - p->overhead <= 0) continue;
|
|
if (p->empty == NULL) continue;
|
|
|
|
for (mp = p->empty; mp != NULL; mp = mp->next) {
|
|
if (!mp->allocated && mp->size >= n) break;
|
|
}
|
|
|
|
if (mp != NULL) break;
|
|
}
|
|
|
|
/* New page needed? */
|
|
|
|
if (p == NULL) {
|
|
p = AddPage(PAGESIZE);
|
|
if (p == NULL)
|
|
goto AllocFail;
|
|
mp = p->data;
|
|
}
|
|
|
|
/* Do we need to break it up? */
|
|
|
|
if (mp->size - n > sizeof(MEMHEADER)) {
|
|
MEMHEADER far *mp2;
|
|
|
|
cp = ((char far *) mp) + n + sizeof(MEMHEADER);
|
|
mp2 = (MEMHEADER far *) cp;
|
|
|
|
mp2->magic = 0L;
|
|
mp2->magic2 = 0L;
|
|
mp2->allocated = FALSE;
|
|
mp2->page = p;
|
|
mp2->size = mp->size - n - sizeof(MEMHEADER);
|
|
|
|
mp2->next = mp->next;
|
|
mp2->prev = mp;
|
|
if (mp->next != NULL) mp->next->prev = mp2;
|
|
mp->next = mp2;
|
|
|
|
|
|
p->overhead += sizeof(MEMHEADER);
|
|
|
|
mp->size = n;
|
|
}
|
|
|
|
mp->magic = MAGIC;
|
|
mp->magic2 = MAGIC2;
|
|
mp->allocated = TRUE;
|
|
mp->id = MemID++;
|
|
|
|
p->used += n;
|
|
cp = ((char far *) mp) + sizeof(MEMHEADER);
|
|
|
|
|
|
/* Debugging info... */
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
|
|
cp, mp->id, (long)n, 0);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
MemInUse += n;
|
|
if (MemInUse > MemInUseMax)
|
|
MemInUseMax = MemInUse;
|
|
|
|
|
|
/* Search for the next empty hole */
|
|
|
|
for (; mp != NULL; mp = mp->next) {
|
|
if (!mp->allocated && mp->size > 0) break;
|
|
}
|
|
|
|
p->empty = mp;
|
|
|
|
return ((MemPtr) cp);
|
|
|
|
|
|
AllocFail:
|
|
#if 0
|
|
assert (FALSE /* Memory allocation failed! */);*/
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Segmented heap memory free.
|
|
*/
|
|
int
|
|
_MemFree (MemPtr vp, char __far *file, int line)
|
|
{
|
|
MEMPAGEHEADER far *p;
|
|
MEMHEADER far *mp, far *mp2;
|
|
char far *cp;
|
|
|
|
if (vp == NULL)
|
|
return (0);
|
|
|
|
|
|
cp = ((char far *) vp) - sizeof(MEMHEADER);
|
|
mp = (MEMHEADER far *) cp;
|
|
|
|
if (mp->magic != MAGIC || mp->magic2 != MAGIC2|| !mp->allocated) {
|
|
assert (mp->magic == MAGIC);
|
|
assert (mp->magic2 == MAGIC2);
|
|
assert (mp->allocated);
|
|
return (-1);
|
|
}
|
|
|
|
#ifdef MEM_DEBUG
|
|
/* Deubgging info... */
|
|
if (MemDebugLevel >= 4) {
|
|
if (MemInUse == MemInUseMax)
|
|
fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
|
|
}
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n", vp, mp->id);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
MemInUse -= mp->size;
|
|
|
|
|
|
p = (MEMPAGEHEADER far *) mp->page;
|
|
p->used -= mp->size;
|
|
|
|
mp->magic = 0L;
|
|
mp->magic2 = 0L;
|
|
mp->allocated = FALSE;
|
|
|
|
/* Merge? */
|
|
|
|
mp2 = mp->prev;
|
|
|
|
if (mp2 != NULL && !mp2->allocated) {
|
|
mp2->next = mp->next;
|
|
if (mp->next != NULL) mp->next->prev = mp2;
|
|
mp2->size += mp->size + sizeof(MEMHEADER);
|
|
|
|
p->overhead -= sizeof(MEMHEADER);
|
|
|
|
mp = mp2;
|
|
}
|
|
|
|
mp2 = mp->next;
|
|
|
|
if (mp2 != NULL && !mp2->allocated) {
|
|
mp->next = mp2->next;
|
|
if (mp2->next != NULL)
|
|
mp2->next->prev = mp;
|
|
|
|
mp->size += mp2->size + sizeof(MEMHEADER);
|
|
|
|
p->overhead -= sizeof(MEMHEADER);
|
|
}
|
|
|
|
if (mp->prev == NULL && mp->next == NULL) {
|
|
DeletePage(p);
|
|
} else {
|
|
if (p->empty == NULL || mp < p->empty) p->empty = mp;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
|
|
MemPtr
|
|
_MemRealloc (MemPtr p, MemSize n, char __far *file, int line)
|
|
{
|
|
MEMHEADER far *mp;
|
|
char far *cp;
|
|
|
|
if (p != NULL) {
|
|
/* Block already large enough? */
|
|
cp = ((char far *) p) - sizeof(MEMHEADER);
|
|
mp = (MEMHEADER far *) cp;
|
|
|
|
if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
|
|
assert (mp->magic == MAGIC);
|
|
assert (mp->magic2 == MAGIC2);
|
|
return (p);
|
|
}
|
|
|
|
if (mp->size >= n) return (p); /* No need to do anything */
|
|
}
|
|
/* Else swap to another block */
|
|
|
|
cp = MemAlloc (n);
|
|
if (cp == NULL)
|
|
return (NULL);
|
|
|
|
if (p != NULL) {
|
|
_fmemcpy(cp, p, (size_t)((mp->size >= n) ? n : mp->size));
|
|
MemFree (p);
|
|
}
|
|
|
|
return ((void far *) cp);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
MemFailSoon (MemSize n)
|
|
{
|
|
#if MEM_FAIL_SOON
|
|
MemFailSoonLimit = MemInUse + n;
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= 1) {
|
|
fprintf (MemDebugFile,
|
|
"MemFailSoon: Fail when allocation increases by %ld (Max %ld)\n",
|
|
n, MemFailSoonLimit);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MemSize
|
|
MemBlkSize (MemPtr p)
|
|
{
|
|
MEMHEADER far *mp;
|
|
char far *cp;
|
|
|
|
if (p == NULL) return (0);
|
|
cp = ((char far *) p) - sizeof(MEMHEADER);
|
|
|
|
mp = (MEMHEADER far *) cp;
|
|
|
|
if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
|
|
assert (mp->magic == MAGIC);
|
|
assert (mp->magic2 == MAGIC2);
|
|
return (0);
|
|
}
|
|
|
|
return (mp->size);
|
|
}
|
|
|
|
|
|
#if 0
|
|
MemPtr
|
|
MemDup (void far *p)
|
|
{
|
|
unsigned int len;
|
|
void far *p1;
|
|
|
|
len = MgetBlkSize (p);
|
|
p1 = MemAlloc (len);
|
|
if (p1 != NULL)
|
|
_fmemcpy(p1, p, len);
|
|
|
|
return (p1);
|
|
}
|
|
|
|
|
|
void
|
|
MemoryStatistics (long int *allocated, long int *used, long int *overhead)
|
|
{
|
|
MEMPAGEHEADER far *p;
|
|
|
|
*allocated = *used = *overhead = 0L;
|
|
|
|
for (p = MemHeader.pages; p != NULL; p = p->next) {
|
|
*allocated += p->size;
|
|
*used += p->used;
|
|
*overhead += p->overhead;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
MemFreeAll (void)
|
|
{
|
|
MEMPAGEHEADER far *p, far *p1;
|
|
|
|
for (p = MemHeader.pages; p != NULL; ) {
|
|
p1 = p->next;
|
|
GlobalUnlock (p->handle);
|
|
GlobalFree (p->handle);
|
|
p = p1;
|
|
}
|
|
|
|
MemHeader.pages = NULL;
|
|
MemHeader.nr_pages = 0;
|
|
}
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
/* For debugging purposes... not very pretty */
|
|
|
|
void PrintMemoryChains(void)
|
|
{
|
|
MEMPAGEHEADER far *p;
|
|
MEMHEADER far *mp;
|
|
char far *cp;
|
|
char buffer[100];
|
|
|
|
/* Block already large enough? */
|
|
|
|
|
|
for (p = MemHeader.pages; p != NULL; p = p->next) {
|
|
for (mp = p->data; mp != NULL; mp = mp->next) {
|
|
fprintf (MemDebugFile, "%Fp | %u | %s", mp, mp->size, mp->allocated ? "Alloc" : "Free");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
|
|
#else /* !SEGHEAP. Old version, not used. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
*
|
|
* Direct memory allocation
|
|
*
|
|
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
|
|
/*
|
|
* This following implementation allocates large memory blocks directly
|
|
* from global memory and small memory blocks from the local heap. The
|
|
* problem with this method is that pine's local heap is quite small
|
|
* so most of the memory ends up comming from the global heap.
|
|
*/
|
|
|
|
|
|
#define GUARD_LOW0 0xbbbb
|
|
#define GUARD_LOW 0x9999
|
|
#define GUARD_HIGH 0xaaaaaaaa
|
|
|
|
#define SMALL_MEM_BOUNDRY 32
|
|
|
|
#define HEAP_SIZE 32000
|
|
|
|
|
|
/* Memory block header. Placed at beginning of each allocated block. */
|
|
typedef struct { /*size len */
|
|
WORD guard0; /* 00 - 02 */
|
|
HGLOBAL handle; /* 02 - 02 */
|
|
short globalBlk; /* 04 - 02 */
|
|
MemSize size; /* 06 - 04 */
|
|
WORD id; /* 0A - 02 */
|
|
WORD guard1; /* 0C - 02 */
|
|
} MemBlk; /* Total size: 0E */
|
|
|
|
typedef MemBlk __far * MemBlkPtr;
|
|
|
|
|
|
/* Memory high guard tailer. Placed at end of each allocated block. */
|
|
typedef struct {
|
|
unsigned long guard1;
|
|
} MemHighGuard;
|
|
|
|
typedef MemHighGuard __far *MemHighGuardPtr;
|
|
|
|
|
|
/*
|
|
* Memory allocation globals.
|
|
*/
|
|
LOCAL WORD MemID = 0; /* Keep track of ID. */
|
|
LOCAL unsigned long MemLocalFails = 0;
|
|
LOCAL BOOL MemLocalFull = FALSE; /* True when local heap full*/
|
|
#ifdef MEM_DEBUG
|
|
LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
|
|
LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
|
|
LOCAL unsigned long SmallMemInUse = 0;
|
|
LOCAL unsigned long SmallMemInUseMax = 0;
|
|
#endif /* MEM_DEBUG */
|
|
|
|
|
|
|
|
/*
|
|
* Allocate a memory block.
|
|
* The file and line indicate where we are called from (for debugging)
|
|
* but in pine these mostly point to a bottel neck routine and are
|
|
* useless.
|
|
*/
|
|
MemPtr
|
|
_MemAlloc (MemSize size, char __far * file, int line)
|
|
{
|
|
MemBlkPtr pBlk;
|
|
MemHighGuardPtr pHG;
|
|
HGLOBAL hBlk;
|
|
HLOCAL hLBlk;
|
|
UINT totalSize;
|
|
BYTE __far * pb;
|
|
|
|
assert (size <= MEM_BLOCK_SIZE_MAX);
|
|
|
|
|
|
/*
|
|
* Calculate total size we need to allocate.
|
|
*/
|
|
totalSize = (UINT)size + sizeof (MemBlk) + sizeof (MemHighGuard);
|
|
|
|
|
|
pBlk = NULL;
|
|
|
|
/*
|
|
* If it's a small block and the local heap is not full, try
|
|
* allocating from the local heap.
|
|
*/
|
|
if (size <= SMALL_MEM_BOUNDRY && !MemLocalFull) {
|
|
|
|
/* Allocate block from local storage. */
|
|
hLBlk = LocalAlloc (LMEM_MOVEABLE, totalSize);
|
|
if (hLBlk != NULL) {
|
|
|
|
/* Lock block and dereference. */
|
|
pBlk = (MemBlkPtr) LocalLock (hLBlk);
|
|
if (pBlk != NULL) {
|
|
pBlk->handle = hLBlk;
|
|
pBlk->globalBlk = FALSE;
|
|
}
|
|
else
|
|
LocalFree (hLBlk);
|
|
}
|
|
else {
|
|
++MemLocalFails;
|
|
MemLocalFull = TRUE;
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL)
|
|
fprintf (MemDebugFile, "Local Memory alloc failed, %lu fails, %lu bytes in use\n",
|
|
MemLocalFails, SmallMemInUse);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* If it is a large block, or local alloc failed, we allocate from
|
|
* global space.
|
|
*/
|
|
if (pBlk == NULL) {
|
|
|
|
/* Allocate block from global storage. */
|
|
hBlk = GlobalAlloc (GMEM_MOVEABLE, totalSize);
|
|
if (hBlk == NULL)
|
|
return (NULL);
|
|
|
|
|
|
/* Lock block and dereference. */
|
|
pBlk = (MemBlkPtr) GlobalLock (hBlk);
|
|
if (pBlk == NULL) {
|
|
GlobalFree (hBlk);
|
|
return (NULL);
|
|
}
|
|
pBlk->handle = hBlk;
|
|
pBlk->globalBlk = TRUE;
|
|
}
|
|
|
|
|
|
|
|
/* Fill rest of header. */
|
|
pBlk->guard0 = GUARD_LOW0;
|
|
pBlk->size = size;
|
|
pBlk->id = ++MemID;
|
|
pBlk->guard1 = GUARD_LOW;
|
|
|
|
|
|
/* Find address that will be returned to caller. */
|
|
pb = (BYTE __far *) (pBlk + 1);
|
|
|
|
|
|
/* Find high guard and fill. */
|
|
pHG = (MemHighGuardPtr) (pb + size);
|
|
pHG->guard1 = GUARD_HIGH;
|
|
|
|
|
|
/* Debugging info... */
|
|
#ifdef MEM_DEBUG
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
if( !file ) file = "??";
|
|
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
|
|
pb, pBlk->id, (long)size, pBlk->globalBlk);
|
|
fflush (MemDebugFile);
|
|
}
|
|
MemInUse += totalSize;
|
|
if (MemInUse > MemInUseMax)
|
|
MemInUseMax = MemInUse;
|
|
if (size <= SMALL_MEM_BOUNDRY) {
|
|
SmallMemInUse += totalSize;
|
|
if (SmallMemInUse > SmallMemInUseMax)
|
|
SmallMemInUseMax = SmallMemInUse;
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
|
|
return ((MemPtr) pb);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Free a block.
|
|
*/
|
|
int
|
|
_MemFree (MemPtr block, char __far *file, int line)
|
|
{
|
|
MemBlkPtr pBlk;
|
|
MemHighGuardPtr pHG;
|
|
HGLOBAL hBlk;
|
|
HLOCAL hLBlk;
|
|
BOOL brc;
|
|
UINT totalSize;
|
|
|
|
if (block == NULL)
|
|
return (0);
|
|
|
|
|
|
/* Find header and high guard and check them. */
|
|
pBlk = ((MemBlkPtr)block) - 1;
|
|
pHG = (MemHighGuardPtr) ((char __far *)block + pBlk->size);
|
|
|
|
totalSize = pBlk->size + sizeof (MemBlk) + sizeof (MemHighGuard);
|
|
|
|
/* If these changed them someone wrote where the should not have. */
|
|
assert (pBlk->guard0 == GUARD_LOW0);
|
|
assert (pBlk->guard1 == GUARD_LOW);
|
|
assert (pHG->guard1 == GUARD_HIGH);
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
/* Deubgging info... */
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
if (pBlk->size <= SMALL_MEM_BOUNDRY &&
|
|
SmallMemInUse == SmallMemInUseMax)
|
|
fprintf (MemDebugFile, "Small memory usage is up to %lu\n", SmallMemInUseMax);
|
|
if (MemInUse == MemInUseMax)
|
|
fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
|
|
}
|
|
MemInUse -= totalSize;
|
|
if (pBlk->size <= SMALL_MEM_BOUNDRY)
|
|
SmallMemInUse -= totalSize;
|
|
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
|
|
fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n",
|
|
block, pBlk->id);
|
|
fflush (MemDebugFile);
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
|
|
|
|
/*
|
|
* Header indicates which block it came from
|
|
*/
|
|
if (!pBlk->globalBlk) {
|
|
/* Unlock block */
|
|
hLBlk = pBlk->handle;
|
|
brc = LocalUnlock (hLBlk);
|
|
assert (!brc);
|
|
|
|
/* And free block. */
|
|
hLBlk = LocalFree (hLBlk);
|
|
assert (hLBlk == NULL);
|
|
MemLocalFull = FALSE;
|
|
}
|
|
else {
|
|
/* Unlock block */
|
|
hBlk = pBlk->handle;
|
|
brc = GlobalUnlock (hBlk);
|
|
assert (!brc);
|
|
|
|
/* And free block. */
|
|
hBlk = GlobalFree (hBlk);
|
|
assert (hBlk == NULL);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Reallocate a memory block. Simplistic approach.
|
|
*/
|
|
MemPtr
|
|
_MemRealloc (MemPtr block, MemSize size, char __far * file, int line)
|
|
{
|
|
MemPtr newBlock;
|
|
|
|
|
|
newBlock = MemAlloc (size);
|
|
if (newBlock == NULL)
|
|
return (NULL);
|
|
|
|
if (block != NULL) {
|
|
_fmemcpy (newBlock, block , (size_t)MIN (size, MemBlkSize (block)));
|
|
MemFree (block);
|
|
}
|
|
|
|
return (newBlock);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Return the size of a memory block
|
|
*/
|
|
MemSize
|
|
MemBlkSize (MemPtr block)
|
|
{
|
|
MemBlkPtr pBlk;
|
|
|
|
if (block == NULL) return (0);
|
|
pBlk = ((MemBlkPtr)block) - 1;
|
|
assert (pBlk->guard1 == GUARD_LOW);
|
|
|
|
return (pBlk->size);
|
|
}
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
struct testblock {
|
|
struct testblock __far * prev;
|
|
HLOCAL h;
|
|
};
|
|
|
|
|
|
|
|
void
|
|
MemATest (void)
|
|
{
|
|
void __near *n;
|
|
struct testblock __far *p;
|
|
struct testblock __far *pnew;
|
|
HLOCAL hl;
|
|
int bcount;
|
|
UINT segment, start, end;
|
|
void __far *f;
|
|
HGLOBAL hg;
|
|
DWORD dw;
|
|
LOCALINFO li;
|
|
UINT DataSeg;
|
|
|
|
#if 0
|
|
hg = GlobalAlloc (GMEM_FIXED, HEAP_SIZE); /* Allocate global block */
|
|
if (hg == NULL)
|
|
return;
|
|
f = GlobalLock (hg); /* Lock and get pointer. */
|
|
if (f == NULL)
|
|
goto Fail1;
|
|
segment = (UINT) GET_SEGMENT (f); /* Get segment and offsets. */
|
|
start = (UINT) GET_OFFSET (f);
|
|
end = start + HEAP_SIZE - 1;
|
|
start += 16;
|
|
if (!LocalInit (segment, start, end)) /* Init it as the local heap*/
|
|
goto Fail2;
|
|
#endif
|
|
#if 0
|
|
__asm MOV DataSeg,DS; /* Get current DS. */
|
|
__asm MOV DS,segment; /* Set DS to new heap. */
|
|
hl = LocalAlloc (0, SMALL_MEM_BOUNDRY); /* Now allocate something. */
|
|
__asm MOV DS,DataSeg; /* Restore DS. */
|
|
if (hl == NULL)
|
|
return;
|
|
n = LocalLock (hl); /* Find where it is. */
|
|
f = (void __far *)n;
|
|
segment = GET_SEGMENT(f); /* What Segment. */
|
|
dw = GlobalHandle (segment);
|
|
hg = (HGLOBAL) (dw & 0xffff);
|
|
if (hg == NULL)
|
|
return;
|
|
|
|
li.dwSize = sizeof (li); /* What size. */
|
|
if (!LocalInfo (&li, hg))
|
|
return;
|
|
|
|
dw = GlobalSize (hg);
|
|
f = GlobalLock (hg);
|
|
GlobalUnlock (hg);
|
|
|
|
LocalUnlock (hl);
|
|
LocalFree (hl);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
p = NULL;
|
|
pnew = NULL;
|
|
bcount = 0;
|
|
|
|
do {
|
|
hl = LocalAlloc (0, SMALL_MEM_BOUNDRY);
|
|
if (hl != NULL) {
|
|
++bcount;
|
|
n = LocalLock (hl);
|
|
pnew = (struct testblock __far*) n;
|
|
pnew->h = hl;
|
|
pnew->prev = p;
|
|
p = pnew;
|
|
}
|
|
} while (hl != NULL);
|
|
|
|
|
|
if (MemDebugFile != NULL)
|
|
fprintf (MemDebugFile, "Allocated %d blocks of size %d\n",
|
|
bcount, SMALL_MEM_BOUNDRY);
|
|
|
|
while (p != NULL) {
|
|
pnew = p->prev;
|
|
hl = p->h;
|
|
LocalUnlock (hl);
|
|
LocalFree (hl);
|
|
p = pnew;
|
|
}
|
|
fflush (MemDebugFile);
|
|
#if 0
|
|
Fail2: GlobalUnlock (hg);
|
|
Fail1: GlobalFree (hg);
|
|
#endif
|
|
return;
|
|
}
|
|
#endif /* MEM_DEBUG */
|
|
|
|
#endif /* ifdef SEGHEAP */
|
|
|
|
#endif /* MSC_MALLOC */
|