]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/lib/font/Type1/t1malloc.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / lib / font / Type1 / t1malloc.c
diff --git a/Xserver/lib/font/Type1/t1malloc.c b/Xserver/lib/font/Type1/t1malloc.c
new file mode 100644 (file)
index 0000000..be93bda
--- /dev/null
@@ -0,0 +1,729 @@
+/* $XConsortium: t1malloc.c,v 1.5 93/09/10 10:48:21 rws 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 combine();
+static freeuncombinable();
+static unhook();
+static 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(%08x) with combine, ", addr);
+                        dumpchain();
+                }
+        }
+        else {
+                if (mallocdebug) {
+                        printf("xiFree(%08x), ", 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 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 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 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) = %08x, ", 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 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 @ %08x, 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 @ %08x, 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 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
+*/
+MemReport()
+{
+       register int i;
+       dumpchain();
+       for (i=0; i<MAXAREAS; i++)
+               reportarea(freearea[i]);
+}
+/*
+:h3.MemBytesAvail - Display Number of Bytes Now Available
+*/
+MemBytesAvail()
+{
+       printf("There are now %d bytes available\n", AvailableWords *
+                                                    sizeof(long) );
+}