]> git.sesse.net Git - rdpsrv/blob - Xserver/lib/font/Type1/t1malloc.c
Support RDP5 logon packets.
[rdpsrv] / Xserver / lib / font / Type1 / t1malloc.c
1 /* $XConsortium: t1malloc.c,v 1.5 93/09/10 10:48:21 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
3  * All Rights Reserved
4  * Copyright Lexmark International, Inc. 1991
5  * All Rights Reserved
6  *
7  * License to use, copy, modify, and distribute this software and its
8  * documentation for any purpose and without fee is hereby granted,
9  * provided that the above copyright notice appear in all copies and that
10  * both that copyright notice and this permission notice appear in
11  * supporting documentation, and that the name of IBM or Lexmark not be
12  * used in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.
14  *
15  * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
16  * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
17  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
18  * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
19  * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
20  * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
21  * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
22  * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
23  * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
24  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
25  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
26  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
27  * THIS SOFTWARE.
28  */
29  /* MALLOC   CWEB         V0004 LOTS                                 */
30 /*
31 :h1.MALLOC - Fast Memory Allocation
32  
33 This module is meant to provide portable C-style memory allocation
34 routines (malloc/free).
35  
36 &author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
37  
38 */
39
40 #include "objects.h"    /* get #define for abort() */
41
42 static combine();
43 static freeuncombinable();
44 static unhook();
45 static dumpchain();
46 /*
47 :h3.Define NULL
48  
49 In the beginning, C compilers made no assumptions about NULL.  It was
50 even theoretically possible that NULL would not be 0.  ANSI has tied
51 this down a bit.  The following definition seems to be the most
52 popular (in terms of reducing compiler complaints), however, if your
53 compiler is unhappy about it, you can redefine it on the command line:
54 */
55 #ifndef   NULL
56 #define   NULL   0
57 #endif
58 /*
59 Of course, NULL is important because xiMalloc() is defined to return
60 NULL when out of memory.
61  
62 :h2.Data Structures Used to Manage Free Memory
63  
64 :h3.The "freeblock" Structure
65  
66 The list of available memory blocks is a doubly-linked list.  Each
67 block begins with the following structure:
68 */
69  
70 struct freeblock {
71         long size;                      /* number of 'longs' in block,
72                                            including this header */
73         struct freeblock *fore;        /* forward in doubly-linked list */
74         struct freeblock *back;        /* backward in doubly-linked list */
75 } ;
76 /*
77 In addition, each free block has a TRAILER that is simply the 'size'
78 repeated.  Thus 'size' is found at the beginning of the block and at the
79 end of the block (size-1 longs away).  'size' includes both the header
80 and the trailer.
81  
82 When a block is allocated, its 'size' is turned negative (both at the
83 beginning and at the end).  Thus, checking whether two blocks may be
84 combined is very simple.  We merely examine both neighboring blocks'
85 size to see if they are positive (and hence available for combination).
86  
87 The memory address returned to the user is therefore one "long" below the
88 size, and one extra "long" is added to the end of the block (beyond what
89 the user requested) to store the trailing size.
90  
91 :h3."firstfree" and "lastfree", the Anchors to the Free List
92  
93 "firstfree" points to the first available free block; "lastfree" points
94 to the end of the chain of available blocks.  These are linked together
95 by initialization code; see :hdref refid=addmem..
96 */
97  
98 static struct freeblock firstfree = { 0L, NULL, NULL };
99 static struct freeblock lastfree = { 0L, NULL, NULL };
100  
101 /*
102 :h3."firstcombined" and "uncombined", Keeping Track of Uncombined Blocks
103  
104 This module is designed to make the combining of adjacent free memory
105 blocks be very fast.  Nonetheless, combining blocks is naturally the
106 most expensive part of any memory system.  In an X system,
107 it is worthwhile to defer the combination for a while, because
108 frequently we will end up asking for a block of exactly the same
109 size that we recently returned and we can save ourselves some work.
110  
111 "MAXUNCOMBINED" is the maximum number of uncombined blocks that we will
112 allow at any time:
113 */
114  
115 #define   MAXUNCOMBINED  3
116  
117 /*
118 "firstcombined" is a pointer into the free list.  The uncombined blocks
119 are always at the front of the list.  "firstcombined" points to the
120 first block that has been combined.
121 */
122 static struct freeblock *firstcombined = &lastfree;
123 static short uncombined = 0; /* current number of uncombined blocks          */
124  
125 /*
126 Uncombined blocks have a negative 'size'; in this they are like
127 allocated blocks.
128  
129 We store a distinctive hex pattern in 'size' when we combine a block
130 to help us debug:
131 */
132 #define   COMBINED   0xBADBAD
133  
134 /*
135 :h3.DEBUGWORDS - Extra Memory Saved With Each Block for Debug
136  
137 We add 'DEBUGWORDS' words to each allocated block to put interesting
138 debug information:
139 */
140 #ifndef   DEBUGWORDS
141 #define   DEBUGWORDS   0
142 #endif
143  
144 /*
145 :h3.MINEXCESS - Amount of "Excess" We Would be Willing to Ignore
146  
147 When we search the free list to find memory for a user request, we
148 frequently find an area that is bigger than what the user has asked for.
149 Normally we put the remaining words (the excess) back on the free list.
150 However, if the area is just slightly bigger than what the user needs,
151 it is counter-productive to do this, as the small amount recovered tends
152 to hurt by increasing memory fragmentation rather than help by providing
153 more available memory.  "MINEXCESS" is the number of words that must be
154 recovered before we would bother to put the excess back on the free
155 list.  If there is not enough excess, we just give the user more than he
156 asked for.
157 */
158  
159 #define   MINEXCESS      (7 + DEBUGWORDS)
160  
161 /*
162 :h3.Some Flags for Debug
163 */
164  
165 long AvailableWords = 0;     /* number of words available in memory          */
166 char mallocdebug = 0;        /* a flag that enables some chatty printf's     */
167  
168 /*
169 :h3.whocalledme() - Debug for Memory Leaks
170  
171 This routine is 68000-specific; it copies the value of the application's
172 curOper variable (which is often a pointer to a character string), and
173 the first part of the stack at the time malloc was called into the
174 DEBUGWORDS area reserved with each block.
175 We use it to see who is malloc-ing memory without free-ing it.
176 */
177  
178 #if DEBUGWORDS
179  
180 static whocalledme(addr, stack)
181        long *addr;           /* address of memory block                      */
182        long *stack;          /* address of malloc's parameter on stack       */
183 {
184        register long size;   /* size of memory block                         */
185        register int i;       /* loop index                                   */
186        extern char *curOper; /* ptr to last operator (kept by appl.) */
187  
188        stack--;
189        size = - *addr;
190  
191        addr += size - 1 - DEBUGWORDS;
192        *addr++ = (long) curOper;
193        for (i=0; i < DEBUGWORDS-1; i++)
194                *addr++ = *stack++;
195 }
196 #else
197  
198 #define whocalledme(addr, stack)
199  
200 #endif
201 /*
202 :h2.xiFree() - User-Callable "Return Memory" Routine
203  
204 The actual beginning of the block is one 'long' before the address we
205 gave to the user.  The block begins and ends with '-size' in words.
206 */
207  
208 void xiFree(addr)
209         register long *addr;           /* user's memory to be returned   */
210 {
211         register long size;            /* amount of memory in this block */
212         register struct freeblock *p;  /* identical to 'addr'            */
213  
214         if (addr == NULL) {  /* common "mistake", so allow it (CHT) */
215             printf("\nxiFree(NULL)?\n");
216             return;
217         }
218  
219         size = *--addr;
220 /*
221 Make sure this address looks OK; 'size' must be less than zero (meaning
222 the block is allocated) and should be repeated at the end of the block.
223 */
224         if (size >= 0)
225                 abort("free: bad size");
226         if (addr[-1 - size] != size)
227                 abort("free: mismatched size");
228 /*
229 Now make this a 'freeblock' structure and tack it on the FRONT of the
230 free list (where uncombined blocks go):
231 */
232         AvailableWords -= size;  /* actually INCREASES AvailableWords */
233         p = (struct freeblock *) addr;
234         p->back = &firstfree;
235         (p->fore = firstfree.fore)->back = p;
236         firstfree.fore = p;
237 /*
238 If we have too many uncombined blocks, call combine() to combine one.
239 */
240         if (++uncombined > MAXUNCOMBINED) {
241                 combine();
242                 if (mallocdebug) {
243                         printf("xiFree(%08x) with combine, ", addr);
244                         dumpchain();
245                 }
246         }
247         else {
248                 if (mallocdebug) {
249                         printf("xiFree(%08x), ", addr);
250                         dumpchain();
251                 }
252         }
253  
254         return;
255 }
256  
257 /*
258 :h3.combine() - Subroutine of xiFree() to Combine Blocks
259  
260 This routine tries to combine the block just before 'firstcombined'.
261 In any event, that block will be moved to the end of the list (after
262 'firstcombined').
263 */
264  
265 static combine()
266 {
267         register struct freeblock *p;   /* block we will try to combine */
268         register long *addr;            /* identical to 'p' for 'long' access */
269         register long size;             /* size of this block  */
270         register long size2;            /* size of potential combinee */
271  
272         p = firstcombined->back;
273         if (p == &firstfree)
274                abort("why are we combining?");
275  
276         addr = (long *) p;
277         size = - p->size;
278         if (--uncombined < 0)
279                 abort("too many combine()s");
280  
281         if (addr[-1] < 0 && addr[size] < 0) {
282 /*
283 We special case the situation where no combining can be done.  Then, we
284 just mark the chain "combined" (i.e., positive size), move the
285 'firstcombined' pointer back in the chain, and return.
286 */
287                 addr[0] = addr[size - 1] = size;
288                 firstcombined = (struct freeblock *) addr;
289                 return;
290         }
291 /*
292 Otherwise, we unhook this pointer from the chain:
293 */
294         unhook(p);
295 /*
296 First we attempt to combine this with the block immediately above:
297 */
298         size2 = addr[-1];
299         if (size2 > 0) {     /* i.e., block above is free */
300                 *addr = COMBINED;  /* might help debug */
301                 addr -= size2;
302                 if (addr[0] != size2)
303                         abort("bad block above");
304                 unhook(addr);
305                 size += size2;
306         }
307 /*
308 At this point 'addr' and 'size' may be the original block, or it may be
309 the newly combined block.  Now we attempt to combine it with the block
310 below:
311 */
312         p = (struct freeblock *) (addr + size);
313         size2 = p->size;
314  
315         if (size2 > 0) {     /* i.e., block below is free                    */
316                 p->size = COMBINED;
317                 if (size2 != ((long *) p)[size2 - 1])
318                         abort("bad block below");
319                 unhook(p);
320                 size += size2;
321         }
322 /*
323 Finally we take the newly combined block and put it on the end of the
324 chain by calling the "freeuncombinable" subroutine:
325 */
326         freeuncombinable(addr, size);
327 }
328  
329 /*
330 :h3.freeuncombinable() - Free a Block That Need Not be Combined
331  
332 This block is "uncombinable" either because we have already combined
333 it with its eligible neighbors, or perhaps because we know it has
334 no neighbors.
335 */
336  
337 static freeuncombinable(addr, size)
338         register long *addr;  /* address of the block to be freed            */
339         register long size;   /* size of block in words                      */
340 {
341         register struct freeblock *p;  /* a convenient synonym for 'addr'    */
342  
343 /*
344 Mark block allocated and combined by setting its 'size' positive:
345 */
346         addr[size - 1] = addr[0] = size;
347 /*
348 Now tack the block on the end of the doubly-linked free list:
349 */
350         p = (struct freeblock *) addr;
351         p->fore = &lastfree;
352         (p->back = lastfree.back)->fore = p;
353         lastfree.back = p;
354 /*
355 If we have previously had no combined blocks, we must update
356 'firstcombined' to point to this block:
357 */
358         if (firstcombined->fore == NULL)
359                 firstcombined = p;
360 }
361  
362 /*
363 :h3.unhook() - Unhook a Block from the Doubly-linked List
364  
365 The only tricky thing here is to make sure that 'firstcombined' is
366 updated if this block happened to be the old 'firstcombined'.  (We
367 would never be unhooking 'firstfree' or 'lastfree', so we do not
368 have to worry about the end cases.)
369 */
370  
371 static unhook(p)
372         register struct freeblock *p;  /* block to unhook                    */
373 {
374         p->back->fore = p->fore;
375         p->fore->back = p->back;
376  
377         if (firstcombined == p)
378                 firstcombined = p->fore;
379 }
380 /*
381 :h2.xiMalloc() - Main User Entry Point for Getting Memory
382  
383 We have two slightly different versions of xiMalloc().  In the case
384 where we have TYPE1IMAGER and a font cache, we are prepared, when nominally
385 out of memory, to loop calling TYPE1IMAGER's GimeSpace() to release font
386 cache.
387 */
388  
389 /* The following code put in by MDC on 11/10/90 */
390  
391 #ifdef TYPE1IMAGER
392  
393 static char *malloc_local();
394  
395 char *xiMalloc(size)
396         register unsigned size;
397 {
398   char *memaddr;
399  
400   while ( (memaddr = malloc_local(size)) == NULL ) {
401     /* Ask TYPE1IMAGER to give us some of its cache back */
402     if ( I_GimeSpace() == 0 ) break; /* We are really, really, out of memory */
403   }
404  
405   return(memaddr);
406 }
407 #endif
408  
409 /*
410 Now begins the real workhorse xiMalloc() (called 'malloc_local' if
411 we are taking advantage of TYPE1IMAGER).  Its argument is an unsigned;
412 at least that lets users with 16-bit integers get a 64K chunk of
413 memory, and it is also compatible with the definition of a "size_t"
414 in most systems.
415 */
416 #ifdef TYPE1IMAGER
417 static char *malloc_local(Size)
418 #else
419 char *xiMalloc(Size)
420 #endif
421         unsigned Size;       /* number of bytes the user requested           */
422 {
423         register long size = (long)Size;  /* a working register for size     */
424         register struct freeblock *p;  /* tentative block to be returned     */
425         register long excess; /* words in excess of user request             */
426         register long *area; /* a convenient synonym for 'p'                 */
427  
428 /*
429 First, we increase 'size' to allow for the two size fields we will
430 save with the block, plus any information for debug purposes.
431 Then we ensure that the block will be large enough to hold our
432 'freeblock' information.  Finally we convert it to be in words
433 (longs), not bytes, increased to span an integral number of double
434 words, so that all memory blocks dispensed with be properly aligned.
435 */
436         size += 2*sizeof(long) + DEBUGWORDS*sizeof(long);
437         if (size < sizeof(struct freeblock) + sizeof(long))
438                size = sizeof(struct freeblock) + sizeof(long);
439         size = ((unsigned) (size + sizeof(double) - 1) / sizeof(double)) * (sizeof(double)/sizeof(long));
440  
441 /*
442 For speed, we will try first to give the user back a very recently
443 returned block--one that is on the front of the chain before
444 'firstcombined'.  These blocks still have negative sizes, and need
445 only to be "unhook"ed:
446 */
447         size = -size;
448         for (p=firstfree.fore; p != firstcombined; p=p->fore) {
449                 if (p->size == size) {
450                         unhook(p);
451                         uncombined--;
452                         if (mallocdebug) {
453                                printf("fast xiMalloc(%d) = %08x, ", size, p);
454                                dumpchain();
455                         }
456                         AvailableWords += size;  /* decreases AvailableWords */
457                         whocalledme(p, &Size);
458                         return((char *)&p->fore);
459                 }
460         }
461 /*
462 Well, if we get here, there are no uncombined blocks matching the user's
463 request.  So, we search the rest of the chain for a block that is big
464 enough.  ('size' becomes positive again):
465 */
466         size = -size;
467         for (;; p = p->fore) {
468 /*
469 If we hit the end of the chain (p->size == 0), we are probably out of
470 memory.  However, we should first try to combine any memory that has
471 not yet been combined before we give that pessimistic answer.  If
472 we succeed in combining, we can call ourselves recursively to try to
473 allocate the requested amount:
474 */
475                if (p->size == 0) {
476                        if (uncombined <= 0)
477                               return(NULL);
478                        while (firstfree.fore != firstcombined)
479                               combine();
480                        return(xiMalloc(sizeof(long) * (size - 2 - DEBUGWORDS)));
481                }
482 /*
483 Otherwise, we keep searching until we find a big enough block:
484 */
485                if (p->size >= size)
486                        break;
487         }
488 /*
489 At this point, 'p' contains a block at least as big as what the user
490 requested, so we take it off the free chain.  If it is excessively big,
491 we return the excess to the free chain:
492 */
493         unhook(p);
494         excess = p->size - size;
495         area = (long *) p;
496  
497         if (excess > MINEXCESS)
498                 freeuncombinable(area + size, excess);
499         else
500                 size = p->size;
501  
502         AvailableWords -= size;
503 /*
504 Mark first and last word of block with the negative of the size, to
505 flag that this block is allocated:
506 */
507         area[size - 1] = area[0] = - size;
508  
509         if (mallocdebug) {
510                 printf("slow xiMalloc(%d) @ %08x, ", size, area);
511                 dumpchain();
512         }
513         whocalledme(area, &Size);
514 /*
515 The address we return to the user is one 'long' BELOW the address of
516 the block.  This protects our 'size' field, so we can tell the size
517 of the block when he returns it to us with xiFree().  Also, he better not
518 touch the 'size' field at the end of the block either.  (That would be
519 nasty of him, as he would be touching memory outside of the bytes he
520 requested).
521 */
522         return((char *) (area + 1));
523 }
524  
525 /*
526 :h2 id=addmem.addmemory() - Initialize Free Memory
527  
528 This routine should be called at initialization to initialize the
529 free chain.  There is no standard way to do this in C.
530 We want the memory dispensed by malloc to be aligned on a double word
531 boundary (because some machines either require alignment, or are
532 more efficient if accesses are aligned).  Since the total size of
533 any block created by malloc is an integral number of double words,
534 all we have to do to ensure alignment is to adjust each large block
535 added to the free chain to start on an odd long-word boundary.
536 (Malloc's size field will occupy the odd long and the user's memory
537 will then begin on an even boundary.)  Since we fill in additional
538 size fields at the beginning and end of each of the large freeblocks,
539 we need only adjust the address passed to addmemory to a double word
540 boundary.
541 */
542  
543 #define   MAXAREAS   10      /* there can be this many calls to addmemory()  */
544  
545 static long *freearea[MAXAREAS] = { NULL };  /* so we can report later       */
546  
547 void addmemory(addr, size)
548         register long *addr; /* beginning of free area                       */
549         register long size;  /* number of bytes of free area                 */
550 {
551         register int i;      /* loop index variable                          */
552         register long *aaddr;  /* aligned beginning of free area             */
553  
554 #if DEBUGWORDS
555         printf("malloc has DEBUGWORDS=%d\n", DEBUGWORDS);
556 #endif
557 /*
558 First link together firstfree and lastfree if necessary:
559 */
560         if (firstfree.fore == NULL) {
561                 firstfree.fore = &lastfree;
562                 lastfree.back = &firstfree;
563         }
564 /*
565 We'll record where the area was that was given to us for later reports:
566 */
567         for (i=0; i < MAXAREAS; i++)
568                 if (freearea[i] == NULL) break;
569         if (i >= MAXAREAS)
570                 abort("too many addmemory()s");
571         aaddr = (long *) ( ((long) addr + sizeof(double) - 1) & - (long)sizeof(double) );
572         size -= (char *) aaddr - (char *) addr;
573         freearea[i] = aaddr;
574 /*
575 Convert 'size' to number of longs, and store '-size' guards at the
576 beginning and end of this area so we will not accidentally recombine the
577 first or last block:
578 */
579         size /= sizeof(long);
580  
581         AvailableWords += size - 2;
582  
583         aaddr[size - 1] = aaddr[0] = -size;
584 /*
585 Finally, call 'freeuncombinable' to put the remaining memory on the
586 free list:
587 */
588         freeuncombinable(aaddr + 1, size - 2);
589 }
590  
591 /*
592 :h3.delmemory() - Delete Memory Pool
593 */
594 void delmemory()
595 {
596        register int i;
597  
598        AvailableWords = 0;
599        firstfree.fore = &lastfree;
600        lastfree.back  = &firstfree;
601        firstcombined  = &lastfree;
602        uncombined     = 0;
603        for (i=0; i<MAXAREAS; i++)
604                freearea[i] = NULL;
605 }
606  
607 /*
608 :h2.Debug Routines
609  
610 :h3.dumpchain() - Print the Chain of Free Blocks
611 */
612  
613 static dumpchain()
614 {
615         register struct freeblock *p;  /* current free block                 */
616         register long size;  /* size of block                                */
617         register struct freeblock *back;  /* block before 'p'                */
618         register int i;      /* temp variable for counting                   */
619  
620         printf("DUMPING FAST FREE LIST:\n");
621         back = &firstfree;
622         for (p = firstfree.fore, i=uncombined; p != firstcombined;
623                                  p = p->fore) {
624                 if (--i < 0)
625                         abort("too many uncombined areas");
626                 size = p->size;
627                 printf(". . . area @ %08x, size = %ld\n", p, -size);
628                 if (size >= 0 || size != ((int *) p)[-1 - size])
629                         abort("dumpchain: bad size");
630                 if (p->back != back)
631                         abort("dumpchain: bad back");
632                 back = p;
633         }
634         printf("DUMPING COMBINED FREE LIST:\n");
635         for (; p != &lastfree; p = p->fore)  {
636                 size = p->size;
637                 printf(". . . area @ %08x, size = %d\n", p, size);
638                 if (size <= 0 || size != ((int *) p)[size - 1])
639                         abort("dumpchain: bad size");
640                 if (p->back != back)
641                         abort("dumpchain: bad back");
642                 back = p;
643         }
644         if (back != lastfree.back)
645                 abort("dumpchain: bad lastfree");
646 }
647  
648 /*
649 :h3.reportarea() - Display a Contiguous Set of Memory Blocks
650 */
651  
652 static reportarea(area)
653        register long *area;   /* start of blocks (from addmemory)            */
654 {
655        register long size;    /* size of current block                       */
656        register long wholesize;  /* size of original area                    */
657        register struct freeblock *p;  /* pointer to block                    */
658  
659        if (area == NULL)
660                return;
661        wholesize = - *area++;
662        wholesize -= 2;
663  
664        while (wholesize > 0) {
665                size = *area;
666                if (size < 0) {
667                        register int i,j;
668  
669                        size = -size;
670                        printf("Allocated %5d bytes at %08x, first words=%08x %08x\n",
671                                size * sizeof(long), area + 1, area[1], area[2]);
672 #if DEBUGWORDS
673                        printf("  ...Last operator: %s\n",
674                                (char *)area[size-DEBUGWORDS-1]);
675 #endif
676                        for (i = size - DEBUGWORDS; i < size - 2; i += 8) {
677                                printf("  ...");
678                                for (j=0; j<8; j++)
679                                        printf(" %08x", area[i+j]);
680                                printf("\n");
681                        }
682  
683                }
684                else {
685                        printf("Free %d bytes at %x\n", size * sizeof(long),
686                                area);
687                        if (size == 0)
688                                abort("zero sized memory block");
689  
690                        for (p = firstfree.fore; p != NULL; p = p->fore)
691                                if ((long *) p == area) break;
692                        if ((long *) p != area)
693                                abort("not found on forward chain");
694  
695                        for (p = lastfree.back; p != NULL; p = p->back)
696                                if ((long *) p == area) break;
697                        if ((long *) p != area)
698                                abort("not found on backward chain");
699                }
700                if (area[0] != area[size - 1])
701                        abort("unmatched check sizes");
702                area += size;
703                wholesize -= size;
704        }
705 }
706  
707 /*
708 :h3.MemReport() - Display All of Memory
709 */
710  
711 MemReport()
712 {
713        register int i;
714  
715        dumpchain();
716  
717        for (i=0; i<MAXAREAS; i++)
718                reportarea(freearea[i]);
719 }
720  
721 /*
722 :h3.MemBytesAvail - Display Number of Bytes Now Available
723 */
724  
725 MemBytesAvail()
726 {
727        printf("There are now %d bytes available\n", AvailableWords *
728                                                     sizeof(long) );
729 }