diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:40 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:40 +0000 |
commit | 153e8da44452905ae04a0e20ad0d85f40399b4ca (patch) | |
tree | b4e614de58d4b596dab3dcc7fd4054ec96db592a /src/Type1/t1malloc.c | |
download | xorg-lib-libXfont-153e8da44452905ae04a0e20ad0d85f40399b4ca.tar.gz |
R6.6 is the Xorg base-lineXORG-MAIN
Diffstat (limited to 'src/Type1/t1malloc.c')
-rw-r--r-- | src/Type1/t1malloc.c | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/src/Type1/t1malloc.c b/src/Type1/t1malloc.c new file mode 100644 index 0000000..5028c8c --- /dev/null +++ b/src/Type1/t1malloc.c @@ -0,0 +1,735 @@ +/* $Xorg: t1malloc.c,v 1.3 2000/08/17 19:46:34 cpqbld Exp $ */ +/* Copyright International Business Machines, Corp. 1991 + * All Rights Reserved + * Copyright Lexmark International, Inc. 1991 + * All Rights Reserved + * + * License to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of IBM or Lexmark not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF + * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, + * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. THE ENTIRE RISK AS TO THE + * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT + * OR MAINTAIN, BELONGS TO THE LICENSEE. SHOULD ANY PORTION OF THE + * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE + * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION. IN NO EVENT SHALL + * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + /* MALLOC CWEB V0004 LOTS */ +/* +:h1.MALLOC - Fast Memory Allocation + +This module is meant to provide portable C-style memory allocation +routines (malloc/free). + +&author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com) + +*/ + +#include "objects.h" /* get #define for abort() */ + +static void combine(); +static void freeuncombinable(); +static void unhook(); +static void dumpchain(); + +/* +:h3.Define NULL + +In the beginning, C compilers made no assumptions about NULL. It was +even theoretically possible that NULL would not be 0. ANSI has tied +this down a bit. The following definition seems to be the most +popular (in terms of reducing compiler complaints), however, if your +compiler is unhappy about it, you can redefine it on the command line: +*/ +#ifndef NULL +#define NULL 0 +#endif +/* +Of course, NULL is important because xiMalloc() is defined to return +NULL when out of memory. + +:h2.Data Structures Used to Manage Free Memory + +:h3.The "freeblock" Structure + +The list of available memory blocks is a doubly-linked list. Each +block begins with the following structure: +*/ + +struct freeblock { + long size; /* number of 'longs' in block, + including this header */ + struct freeblock *fore; /* forward in doubly-linked list */ + struct freeblock *back; /* backward in doubly-linked list */ +} ; +/* +In addition, each free block has a TRAILER that is simply the 'size' +repeated. Thus 'size' is found at the beginning of the block and at the +end of the block (size-1 longs away). 'size' includes both the header +and the trailer. + +When a block is allocated, its 'size' is turned negative (both at the +beginning and at the end). Thus, checking whether two blocks may be +combined is very simple. We merely examine both neighboring blocks' +size to see if they are positive (and hence available for combination). + +The memory address returned to the user is therefore one "long" below the +size, and one extra "long" is added to the end of the block (beyond what +the user requested) to store the trailing size. + +:h3."firstfree" and "lastfree", the Anchors to the Free List + +"firstfree" points to the first available free block; "lastfree" points +to the end of the chain of available blocks. These are linked together +by initialization code; see :hdref refid=addmem.. +*/ + +static struct freeblock firstfree = { 0L, NULL, NULL }; +static struct freeblock lastfree = { 0L, NULL, NULL }; + +/* +:h3."firstcombined" and "uncombined", Keeping Track of Uncombined Blocks + +This module is designed to make the combining of adjacent free memory +blocks be very fast. Nonetheless, combining blocks is naturally the +most expensive part of any memory system. In an X system, +it is worthwhile to defer the combination for a while, because +frequently we will end up asking for a block of exactly the same +size that we recently returned and we can save ourselves some work. + +"MAXUNCOMBINED" is the maximum number of uncombined blocks that we will +allow at any time: +*/ + +#define MAXUNCOMBINED 3 + +/* +"firstcombined" is a pointer into the free list. The uncombined blocks +are always at the front of the list. "firstcombined" points to the +first block that has been combined. +*/ +static struct freeblock *firstcombined = &lastfree; +static short uncombined = 0; /* current number of uncombined blocks */ + +/* +Uncombined blocks have a negative 'size'; in this they are like +allocated blocks. + +We store a distinctive hex pattern in 'size' when we combine a block +to help us debug: +*/ +#define COMBINED 0xBADBAD + +/* +:h3.DEBUGWORDS - Extra Memory Saved With Each Block for Debug + +We add 'DEBUGWORDS' words to each allocated block to put interesting +debug information: +*/ +#ifndef DEBUGWORDS +#define DEBUGWORDS 0 +#endif + +/* +:h3.MINEXCESS - Amount of "Excess" We Would be Willing to Ignore + +When we search the free list to find memory for a user request, we +frequently find an area that is bigger than what the user has asked for. +Normally we put the remaining words (the excess) back on the free list. +However, if the area is just slightly bigger than what the user needs, +it is counter-productive to do this, as the small amount recovered tends +to hurt by increasing memory fragmentation rather than help by providing +more available memory. "MINEXCESS" is the number of words that must be +recovered before we would bother to put the excess back on the free +list. If there is not enough excess, we just give the user more than he +asked for. +*/ + +#define MINEXCESS (7 + DEBUGWORDS) + +/* +:h3.Some Flags for Debug +*/ + +long AvailableWords = 0; /* number of words available in memory */ +char mallocdebug = 0; /* a flag that enables some chatty printf's */ + +/* +:h3.whocalledme() - Debug for Memory Leaks + +This routine is 68000-specific; it copies the value of the application's +curOper variable (which is often a pointer to a character string), and +the first part of the stack at the time malloc was called into the +DEBUGWORDS area reserved with each block. +We use it to see who is malloc-ing memory without free-ing it. +*/ + +#if DEBUGWORDS + +static whocalledme(addr, stack) + long *addr; /* address of memory block */ + long *stack; /* address of malloc's parameter on stack */ +{ + register long size; /* size of memory block */ + register int i; /* loop index */ + extern char *curOper; /* ptr to last operator (kept by appl.) */ + + stack--; + size = - *addr; + + addr += size - 1 - DEBUGWORDS; + *addr++ = (long) curOper; + for (i=0; i < DEBUGWORDS-1; i++) + *addr++ = *stack++; +} +#else + +#define whocalledme(addr, stack) + +#endif +/* +:h2.xiFree() - User-Callable "Return Memory" Routine + +The actual beginning of the block is one 'long' before the address we +gave to the user. The block begins and ends with '-size' in words. +*/ + +void xiFree(addr) + register long *addr; /* user's memory to be returned */ +{ + register long size; /* amount of memory in this block */ + register struct freeblock *p; /* identical to 'addr' */ + + if (addr == NULL) { /* common "mistake", so allow it (CHT) */ + printf("\nxiFree(NULL)?\n"); + return; + } + + size = *--addr; +/* +Make sure this address looks OK; 'size' must be less than zero (meaning +the block is allocated) and should be repeated at the end of the block. +*/ + if (size >= 0) + abort("free: bad size"); + if (addr[-1 - size] != size) + abort("free: mismatched size"); +/* +Now make this a 'freeblock' structure and tack it on the FRONT of the +free list (where uncombined blocks go): +*/ + AvailableWords -= size; /* actually INCREASES AvailableWords */ + p = (struct freeblock *) addr; + p->back = &firstfree; + (p->fore = firstfree.fore)->back = p; + firstfree.fore = p; +/* +If we have too many uncombined blocks, call combine() to combine one. +*/ + if (++uncombined > MAXUNCOMBINED) { + combine(); + if (mallocdebug) { + printf("xiFree(%p) with combine, ", addr); + dumpchain(); + } + } + else { + if (mallocdebug) { + printf("xiFree(%p), ", addr); + dumpchain(); + } + } + + return; +} + +/* +:h3.combine() - Subroutine of xiFree() to Combine Blocks + +This routine tries to combine the block just before 'firstcombined'. +In any event, that block will be moved to the end of the list (after +'firstcombined'). +*/ + +static void +combine() +{ + register struct freeblock *p; /* block we will try to combine */ + register long *addr; /* identical to 'p' for 'long' access */ + register long size; /* size of this block */ + register long size2; /* size of potential combinee */ + + p = firstcombined->back; + if (p == &firstfree) + abort("why are we combining?"); + + addr = (long *) p; + size = - p->size; + if (--uncombined < 0) + abort("too many combine()s"); + + if (addr[-1] < 0 && addr[size] < 0) { +/* +We special case the situation where no combining can be done. Then, we +just mark the chain "combined" (i.e., positive size), move the +'firstcombined' pointer back in the chain, and return. +*/ + addr[0] = addr[size - 1] = size; + firstcombined = (struct freeblock *) addr; + return; + } +/* +Otherwise, we unhook this pointer from the chain: +*/ + unhook(p); +/* +First we attempt to combine this with the block immediately above: +*/ + size2 = addr[-1]; + if (size2 > 0) { /* i.e., block above is free */ + *addr = COMBINED; /* might help debug */ + addr -= size2; + if (addr[0] != size2) + abort("bad block above"); + unhook(addr); + size += size2; + } +/* +At this point 'addr' and 'size' may be the original block, or it may be +the newly combined block. Now we attempt to combine it with the block +below: +*/ + p = (struct freeblock *) (addr + size); + size2 = p->size; + + if (size2 > 0) { /* i.e., block below is free */ + p->size = COMBINED; + if (size2 != ((long *) p)[size2 - 1]) + abort("bad block below"); + unhook(p); + size += size2; + } +/* +Finally we take the newly combined block and put it on the end of the +chain by calling the "freeuncombinable" subroutine: +*/ + freeuncombinable(addr, size); +} + +/* +:h3.freeuncombinable() - Free a Block That Need Not be Combined + +This block is "uncombinable" either because we have already combined +it with its eligible neighbors, or perhaps because we know it has +no neighbors. +*/ + +static void +freeuncombinable(addr, size) + register long *addr; /* address of the block to be freed */ + register long size; /* size of block in words */ +{ + register struct freeblock *p; /* a convenient synonym for 'addr' */ + +/* +Mark block allocated and combined by setting its 'size' positive: +*/ + addr[size - 1] = addr[0] = size; +/* +Now tack the block on the end of the doubly-linked free list: +*/ + p = (struct freeblock *) addr; + p->fore = &lastfree; + (p->back = lastfree.back)->fore = p; + lastfree.back = p; +/* +If we have previously had no combined blocks, we must update +'firstcombined' to point to this block: +*/ + if (firstcombined->fore == NULL) + firstcombined = p; +} + +/* +:h3.unhook() - Unhook a Block from the Doubly-linked List + +The only tricky thing here is to make sure that 'firstcombined' is +updated if this block happened to be the old 'firstcombined'. (We +would never be unhooking 'firstfree' or 'lastfree', so we do not +have to worry about the end cases.) +*/ + +static void +unhook(p) + register struct freeblock *p; /* block to unhook */ +{ + p->back->fore = p->fore; + p->fore->back = p->back; + + if (firstcombined == p) + firstcombined = p->fore; +} +/* +:h2.xiMalloc() - Main User Entry Point for Getting Memory + +We have two slightly different versions of xiMalloc(). In the case +where we have TYPE1IMAGER and a font cache, we are prepared, when nominally +out of memory, to loop calling TYPE1IMAGER's GimeSpace() to release font +cache. +*/ + +/* The following code put in by MDC on 11/10/90 */ + +#ifdef TYPE1IMAGER + +static char *malloc_local(); + +char *xiMalloc(size) + register unsigned size; +{ + char *memaddr; + + while ( (memaddr = malloc_local(size)) == NULL ) { + /* Ask TYPE1IMAGER to give us some of its cache back */ + if ( I_GimeSpace() == 0 ) break; /* We are really, really, out of memory */ + } + + return(memaddr); +} +#endif + +/* +Now begins the real workhorse xiMalloc() (called 'malloc_local' if +we are taking advantage of TYPE1IMAGER). Its argument is an unsigned; +at least that lets users with 16-bit integers get a 64K chunk of +memory, and it is also compatible with the definition of a "size_t" +in most systems. +*/ +#ifdef TYPE1IMAGER +static char *malloc_local(Size) +#else +char *xiMalloc(Size) +#endif + unsigned Size; /* number of bytes the user requested */ +{ + register long size = (long)Size; /* a working register for size */ + register struct freeblock *p; /* tentative block to be returned */ + register long excess; /* words in excess of user request */ + register long *area; /* a convenient synonym for 'p' */ + +/* +First, we increase 'size' to allow for the two size fields we will +save with the block, plus any information for debug purposes. +Then we ensure that the block will be large enough to hold our +'freeblock' information. Finally we convert it to be in words +(longs), not bytes, increased to span an integral number of double +words, so that all memory blocks dispensed with be properly aligned. +*/ + size += 2*sizeof(long) + DEBUGWORDS*sizeof(long); + if (size < sizeof(struct freeblock) + sizeof(long)) + size = sizeof(struct freeblock) + sizeof(long); + size = ((unsigned) (size + sizeof(double) - 1) / sizeof(double)) * (sizeof(double)/sizeof(long)); + +/* +For speed, we will try first to give the user back a very recently +returned block--one that is on the front of the chain before +'firstcombined'. These blocks still have negative sizes, and need +only to be "unhook"ed: +*/ + size = -size; + for (p=firstfree.fore; p != firstcombined; p=p->fore) { + if (p->size == size) { + unhook(p); + uncombined--; + if (mallocdebug) { + printf("fast xiMalloc(%d) = %p, ", size, p); + dumpchain(); + } + AvailableWords += size; /* decreases AvailableWords */ + whocalledme(p, &Size); + return((char *)&p->fore); + } + } +/* +Well, if we get here, there are no uncombined blocks matching the user's +request. So, we search the rest of the chain for a block that is big +enough. ('size' becomes positive again): +*/ + size = -size; + for (;; p = p->fore) { +/* +If we hit the end of the chain (p->size == 0), we are probably out of +memory. However, we should first try to combine any memory that has +not yet been combined before we give that pessimistic answer. If +we succeed in combining, we can call ourselves recursively to try to +allocate the requested amount: +*/ + if (p->size == 0) { + if (uncombined <= 0) + return(NULL); + while (firstfree.fore != firstcombined) + combine(); + return(xiMalloc(sizeof(long) * (size - 2 - DEBUGWORDS))); + } +/* +Otherwise, we keep searching until we find a big enough block: +*/ + if (p->size >= size) + break; + } +/* +At this point, 'p' contains a block at least as big as what the user +requested, so we take it off the free chain. If it is excessively big, +we return the excess to the free chain: +*/ + unhook(p); + excess = p->size - size; + area = (long *) p; + + if (excess > MINEXCESS) + freeuncombinable(area + size, excess); + else + size = p->size; + + AvailableWords -= size; +/* +Mark first and last word of block with the negative of the size, to +flag that this block is allocated: +*/ + area[size - 1] = area[0] = - size; + + if (mallocdebug) { + printf("slow xiMalloc(%d) @ %08x, ", size, area); + dumpchain(); + } + whocalledme(area, &Size); +/* +The address we return to the user is one 'long' BELOW the address of +the block. This protects our 'size' field, so we can tell the size +of the block when he returns it to us with xiFree(). Also, he better not +touch the 'size' field at the end of the block either. (That would be +nasty of him, as he would be touching memory outside of the bytes he +requested). +*/ + return((char *) (area + 1)); +} + +/* +:h2 id=addmem.addmemory() - Initialize Free Memory + +This routine should be called at initialization to initialize the +free chain. There is no standard way to do this in C. +We want the memory dispensed by malloc to be aligned on a double word +boundary (because some machines either require alignment, or are +more efficient if accesses are aligned). Since the total size of +any block created by malloc is an integral number of double words, +all we have to do to ensure alignment is to adjust each large block +added to the free chain to start on an odd long-word boundary. +(Malloc's size field will occupy the odd long and the user's memory +will then begin on an even boundary.) Since we fill in additional +size fields at the beginning and end of each of the large freeblocks, +we need only adjust the address passed to addmemory to a double word +boundary. +*/ + +#define MAXAREAS 10 /* there can be this many calls to addmemory() */ + +static long *freearea[MAXAREAS] = { NULL }; /* so we can report later */ + +void addmemory(addr, size) + register long *addr; /* beginning of free area */ + register long size; /* number of bytes of free area */ +{ + register int i; /* loop index variable */ + register long *aaddr; /* aligned beginning of free area */ + +#if DEBUGWORDS + printf("malloc has DEBUGWORDS=%d\n", DEBUGWORDS); +#endif +/* +First link together firstfree and lastfree if necessary: +*/ + if (firstfree.fore == NULL) { + firstfree.fore = &lastfree; + lastfree.back = &firstfree; + } +/* +We'll record where the area was that was given to us for later reports: +*/ + for (i=0; i < MAXAREAS; i++) + if (freearea[i] == NULL) break; + if (i >= MAXAREAS) + abort("too many addmemory()s"); + aaddr = (long *) ( ((long) addr + sizeof(double) - 1) & - (long)sizeof(double) ); + size -= (char *) aaddr - (char *) addr; + freearea[i] = aaddr; +/* +Convert 'size' to number of longs, and store '-size' guards at the +beginning and end of this area so we will not accidentally recombine the +first or last block: +*/ + size /= sizeof(long); + + AvailableWords += size - 2; + + aaddr[size - 1] = aaddr[0] = -size; +/* +Finally, call 'freeuncombinable' to put the remaining memory on the +free list: +*/ + freeuncombinable(aaddr + 1, size - 2); +} + +/* +:h3.delmemory() - Delete Memory Pool +*/ +void delmemory() +{ + register int i; + + AvailableWords = 0; + firstfree.fore = &lastfree; + lastfree.back = &firstfree; + firstcombined = &lastfree; + uncombined = 0; + for (i=0; i<MAXAREAS; i++) + freearea[i] = NULL; +} + +/* +:h2.Debug Routines + +:h3.dumpchain() - Print the Chain of Free Blocks +*/ + +static void +dumpchain() +{ + register struct freeblock *p; /* current free block */ + register long size; /* size of block */ + register struct freeblock *back; /* block before 'p' */ + register int i; /* temp variable for counting */ + + printf("DUMPING FAST FREE LIST:\n"); + back = &firstfree; + for (p = firstfree.fore, i=uncombined; p != firstcombined; + p = p->fore) { + if (--i < 0) + abort("too many uncombined areas"); + size = p->size; + printf(". . . area @ %p, size = %ld\n", p, -size); + if (size >= 0 || size != ((int *) p)[-1 - size]) + abort("dumpchain: bad size"); + if (p->back != back) + abort("dumpchain: bad back"); + back = p; + } + printf("DUMPING COMBINED FREE LIST:\n"); + for (; p != &lastfree; p = p->fore) { + size = p->size; + printf(". . . area @ %p, size = %d\n", p, size); + if (size <= 0 || size != ((int *) p)[size - 1]) + abort("dumpchain: bad size"); + if (p->back != back) + abort("dumpchain: bad back"); + back = p; + } + if (back != lastfree.back) + abort("dumpchain: bad lastfree"); +} + +/* +:h3.reportarea() - Display a Contiguous Set of Memory Blocks +*/ + +static void +reportarea(area) + register long *area; /* start of blocks (from addmemory) */ +{ + register long size; /* size of current block */ + register long wholesize; /* size of original area */ + register struct freeblock *p; /* pointer to block */ + + if (area == NULL) + return; + wholesize = - *area++; + wholesize -= 2; + + while (wholesize > 0) { + size = *area; + if (size < 0) { + register int i,j; + + size = -size; + printf("Allocated %5d bytes at %08x, first words=%08x %08x\n", + size * sizeof(long), area + 1, area[1], area[2]); +#if DEBUGWORDS + printf(" ...Last operator: %s\n", + (char *)area[size-DEBUGWORDS-1]); +#endif + for (i = size - DEBUGWORDS; i < size - 2; i += 8) { + printf(" ..."); + for (j=0; j<8; j++) + printf(" %08x", area[i+j]); + printf("\n"); + } + + } + else { + printf("Free %d bytes at %x\n", size * sizeof(long), + area); + if (size == 0) + abort("zero sized memory block"); + + for (p = firstfree.fore; p != NULL; p = p->fore) + if ((long *) p == area) break; + if ((long *) p != area) + abort("not found on forward chain"); + + for (p = lastfree.back; p != NULL; p = p->back) + if ((long *) p == area) break; + if ((long *) p != area) + abort("not found on backward chain"); + } + if (area[0] != area[size - 1]) + abort("unmatched check sizes"); + area += size; + wholesize -= size; + } +} + +/* +:h3.MemReport() - Display All of Memory +*/ + +void MemReport() +{ + register int i; + + dumpchain(); + + for (i=0; i<MAXAREAS; i++) + reportarea(freearea[i]); +} + +/* +:h3.MemBytesAvail - Display Number of Bytes Now Available +*/ + +void MemBytesAvail() +{ + printf("There are now %d bytes available\n", AvailableWords * + sizeof(long) ); +} |