home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume1 / 8706 / 25 < prev    next >
Encoding:
Text File  |  1993-09-02  |  15.4 KB  |  643 lines

  1. Article 62 of comp.sources.misc:
  2. Relay-Version: version B 2.10.3 alpha 5/22/85; site osu-eddie.UUCP
  3. Path: osu-eddie!cbosgd!ucbvax!ucbcad!ames!oliveb!pyramid!uccba!hal!ncoast!allbery
  4. From: kent@ncoast.UUCP (Kent Williams)
  5. Newsgroups: comp.sources.misc
  6. Subject: Replacement malloc for Microsoft C 4.0
  7. Message-ID: <2647@ncoast.UUCP>
  8. Date: 16 Jun 87 20:04:49 GMT
  9. Date-Received: 17 Jun 87 12:33:52 GMT
  10. Sender: allbery@ncoast.UUCP
  11. Distribution: comp
  12. Organization: Cleveland Public Access UN*X, Cleveland, OH
  13. Lines: 623
  14. Keywords: malloc MSC
  15. Approved: allbery@ncoast.UUCP
  16. X-Archive: comp.sources.misc/8706/25
  17.  
  18. Here is a replacement for the standard malloc, realloc, calloc, free,
  19. sbrk, and brk for Microsoft C 4.0.
  20.  
  21. It was written to satisfy the need for a 'debugging malloc' in the MSC4.0
  22. environment on the PC.
  23.  
  24. It can be used in production code with impunity, though it isn't as
  25. efficient space-wise as the standard set of routines.
  26.  
  27. The core routines have been tested in all memory models - but as noted,
  28. there will need to be some work on the debug code to make it function
  29. properly in other than small code, small data.
  30.  
  31. -----------------------------CUT HERE----------------------------------------
  32. /*
  33. ** Alloc.c - replacement memory allocator for Microsoft C 4.0
  34. **
  35. ** malloc, free, calloc, realloc
  36. ** Implemented in a totally paranoid fashion - if anyone walks on the
  37. ** heap, you know about it immediately.
  38. ** If you define TEST, you get some debug code compiled in.
  39. ** If you define MALLOCTEST, you get a little malloc debugger.
  40. ** If you undefine NOSTDIO, you get standard I/O rather than my oddball
  41. ** stuff for i/o.
  42. **
  43. ** Alas, the debugging code here assumes small code, small data - though
  44. ** the malloc'er itself works fine in larger models.  The things that
  45. ** would have to change is 1) some of the code in the memtest routine.
  46. ** 2) the code for returnaddress. 3) The calls to brkctl.
  47. **
  48. ** This is provided with no warranty at all by:
  49. ** Kent Williams
  50. ** 722 Rundell
  51. ** Iowa City, IA 52240
  52. ** {...cwruecmp}!ncoast!kent
  53. **
  54. */
  55.  
  56. #define NOSTDIO
  57. #define NULL 0
  58.  
  59. /*
  60. ** low level breaker.
  61. ** It is documented in Xenix documentation as
  62. ** char far *brkctl(command,increment,ptr)
  63. **    int command; long increment; char far *ptr;
  64. **
  65. ** where command is one of:
  66. ** #define BR_ARGSEG    0001    specified segment
  67. ** #define BR_NEWSEG    0002    new segment
  68. ** #define BR_IMPSEG    0003    implied segment - last data segment
  69. ** #define BR_FREESEG    0004    free the specified segment
  70. ** #define BR_HUGE      0100    do the specified command in the huge context.
  71. **
  72. ** In small model, command should always be BR_ARGSEG, and
  73. ** ptr should be the address of something in the data segment.
  74. ** In large model, you're on your own, though I think you could probably
  75. ** call with BR_ARGSEG and the default data segment the first time, and
  76. ** BR_IMPSEG on all subsequent calls.
  77. **
  78. ** Returns either a far ptr to allocated memory, or -1 if there's an error.
  79. */
  80. extern char far *brkctl(int,long,char far *);
  81.  
  82. /*
  83. ** Mystery variable that keeps the library malloc from getting pulled in.
  84. ** It SEEMS to be just the offset of the start of the heap ... so that's
  85. ** what I set it up to.  NOTE : I figured this all out by disassembling the
  86. ** dynamic memory allocation routines - If they change the library, all
  87. ** bets are off.
  88. */
  89. char *_asegds = NULL;
  90.  
  91. /*
  92. ** I don't use stdio.  If you do, you should undefine NOSTDIO
  93. */
  94. #ifdef NOSTDIO
  95.     extern char *getline(char *);
  96. #    define stdin 0
  97. #    define stdout 1
  98. #    define stderr 2
  99. #else
  100. #    include <stdio.h>
  101. #    define getline(x)    gets(x)
  102. #endif
  103.  
  104. /* constants */
  105. #define MAGIC_BUSY 0x1234
  106. #define MAGIC_FREE 0x8765
  107.  
  108. #ifdef TEST
  109. /*
  110. ** types used for 'return-stamping' memory allocation blocks.
  111. */
  112. typedef void (*funcptr)(void);
  113. extern funcptr returnaddress(void);
  114. #endif
  115.  
  116. /* structures */
  117. typedef struct _qqq
  118. {
  119.     struct _qqq *next,*last;
  120.     unsigned size;
  121.     unsigned magic;
  122. #ifdef TEST
  123.     /*
  124.     ** stamp memory blocks with the address of who created/destroyed them.
  125.     */
  126.     funcptr returnadd;
  127. #endif    /* TEST */
  128. } mblock;
  129.  
  130. #ifdef TEST
  131. /*
  132. ** If you undef NOSTDIO above, then we assume you're not running in
  133. ** my environment, and need the following routine.
  134. */
  135. #ifdef NOSTDIO
  136. /*
  137. ** funky subtrefuge to pick up the return address
  138. ** essentialy a replacement for:
  139.     public    _returnaddress
  140. _returnaddress    proc    near
  141.     mov    ax,2[bp]
  142.     sub    ax,3
  143.     ret
  144. _returnaddress    endp
  145. **
  146. */
  147. funcptr returnaddress(x)
  148. unsigned x;
  149. {
  150.     register unsigned *xp;
  151.     union { unsigned up; funcptr fp; } rval;
  152.     /*
  153.     **    pick up previous frame pointer.
  154.     **  This only works for small code, small data, though
  155.     **  it shouldn't be too hard to figure out other models.
  156.     */
  157.     xp = &x;    /* x is at 4[bp] */
  158.     xp -= 2;    /* old frame pointer is at 0[bp] */
  159.     xp = (unsigned *)(*xp);
  160.     xp += 1;    /* return address is at 2[oldbp] */
  161.     rval.up = *xp - 3;
  162.     return rval.fp;
  163. }
  164. #endif    /* NOSTDIO */
  165.  
  166. /*
  167. ** mark a block with a return address.  Useful when 'envelope'
  168. ** functions are used to call the actual allocation routines.
  169. */
  170. void mark_block(ptr,address)
  171. register mblock *ptr;
  172. funcptr address;
  173. {
  174.     (--ptr)->returnadd = address;
  175. }
  176. #endif    /* TEST */
  177.  
  178. /*
  179. ** QUANTUM is the minimum size blob requested from brkctl.
  180. */
  181. #define QUANTUM ((long)((4096+sizeof(mblock)-1)/sizeof(mblock)))
  182.  
  183. /*
  184. ** queueing macros
  185. */
  186. #define enqueue(q,i)    q_insert((q)->last,i)
  187. #define dequeue(q)        (q_remove( (q) ->next))
  188.  
  189. #define q_empty(q)         ((q)->next==(q)&&(q)->last==(q))
  190.  
  191. /*
  192. ** externals
  193. */
  194. #include <ctype.h>
  195. #include <string.h>
  196.  
  197. /*
  198. ** q_insert - insert item in the queue designated by head
  199. ** returns : Nothing
  200. */
  201. static void q_insert(head,item)
  202. register mblock *head,*item;
  203. {
  204.     item->last = head;
  205.     item->next = head->next;
  206.     head->next->last = item;
  207.     head->next = item;
  208. }
  209.  
  210. /*
  211. ** q_remove - remove an item from a queue.
  212. ** returns NULL if queue is empty
  213. */
  214. static mblock *q_remove(item)
  215. register mblock *item;
  216. {
  217.     if(item->next == item && item->last == item)
  218.         return (mblock *)NULL;
  219.     item->next->last = item->last;
  220.     item->last->next = item->next;
  221.     return item;
  222. }
  223.  
  224. mblock arena = { NULL,NULL,0,0 };
  225.  
  226.  
  227. /* 
  228. ** check_malloc - make sure everything is kosher with a particular
  229. ** memory block
  230. */
  231. int check_malloc(item)
  232. register mblock *item;
  233. {
  234.     if(item->next->last != item)
  235.         return -1;
  236.     if(item->last->next != item)
  237.         return -1;
  238.     if(item->magic != MAGIC_BUSY && item->magic != MAGIC_FREE)
  239.         return -1;
  240.     return 0;
  241. }
  242.  
  243.  
  244. /*
  245. ** free an allocated memory block
  246. */
  247. int free(item)
  248. register mblock *item;
  249. {
  250. #ifdef TEST
  251.     int reason;
  252.     static char *reasons[] = {
  253.         "Not initialized",
  254.         "Bad pointer or chain corrupted",
  255.         "Already freed",
  256.     };
  257. #endif
  258.     /* check for initialization */
  259.     if(arena.next == NULL) {
  260.         arena.next = arena.last = &arena;
  261.         arena.magic = MAGIC_BUSY;
  262.         arena.size = 0;
  263.         /*
  264.         ** if there isn't anything allocated, freeing anything is an error.
  265.         */
  266. #ifdef TEST
  267.     {
  268.         reason = 0;
  269.         goto bad_free;
  270.     }
  271. #else
  272.         return -1;
  273. #endif
  274.     }
  275.     
  276.     /* cheat by decrementing the pointer to access the magic block */
  277.     --item;
  278.  
  279.     /* if this is a gypsy block, return error */
  280.     if(check_malloc(item))
  281. #ifdef TEST
  282.     {
  283.         reason = 1;
  284.         goto bad_free;
  285.     }
  286. #else
  287.         return -1;
  288. #endif
  289.     if(item->magic != MAGIC_BUSY)
  290. #ifdef TEST
  291.     {
  292.         reason = 2;
  293.     bad_free:
  294.         fprintf(stderr,"Bad call to free from %04x, reason = %s\n",
  295.             returnaddress(),reasons[reason]);
  296.         myexit(1);
  297.     }
  298. #else
  299.         return -1;
  300. #endif
  301.  
  302.     /* if the item being freed isn't an empty list (i.e. not
  303.     ** linked into the allocation list), then try and merge it
  304.     ** with contiguous blocks
  305.     */
  306.     if(item->next != item) {
  307.         /* mark block as free (invariant) */
  308.         item->magic = MAGIC_FREE;
  309.         /* check to see if next block is free, and contiguous with the
  310.         ** current block
  311.         */
  312.         if((item->next->magic == MAGIC_FREE) &&
  313.             (item->next == (item + item->size))) {
  314.             /* add in size of next block */
  315.             item->size += item->next->size;
  316.             /* remove the next block from the queue */
  317.             (void)q_remove(item->next);
  318.         }
  319.         /* check to see if last block is free and contiguous with the current
  320.         ** block
  321.         */
  322.         if((item->last->magic == MAGIC_FREE) &&
  323.             (item == (item->last + item->last->size))) {
  324.             item->last->size += item->size;
  325.             (void)q_remove(item);
  326.         }
  327. #ifdef TEST
  328.         item->returnadd = returnaddress();
  329. #endif
  330.         return 0;
  331.     }
  332.     /* we are freeing something that's never been allocated */
  333.     item->magic = MAGIC_FREE;
  334.     enqueue(&arena,item);    /* just enqueue the block */
  335.     return 0;
  336. }
  337.  
  338. void *malloc(size)
  339. unsigned size;
  340. {
  341.     register mblock *current;
  342.     long lmsize;
  343.     /* check for initialization */
  344.     if(arena.next == NULL) {
  345.         arena.next = arena.last = &arena;
  346.         arena.magic = MAGIC_BUSY;
  347.         arena.size = 0;
  348.     }
  349.     /* adjust size to be an even number of mblocks
  350.             bump to next even mblock          round           add one */
  351.     size = ( (size + (sizeof(mblock) - 1) ) / sizeof(mblock) ) + 1;
  352.     /* search down free list to find a block */
  353.     for (current = arena.next; current != &arena; current = current->next)
  354.         if(current->magic == MAGIC_FREE) {
  355.             if(current->size < size)
  356.                 continue;
  357.             malloc_again:
  358.             if(current->size == size || current->size == size + 1) {
  359.                 current->magic = MAGIC_BUSY;
  360. #ifdef TEST
  361.                 current->returnadd = returnaddress();
  362. #endif
  363.                 return (void *)(current+1);
  364.             }
  365.             /* current->size must be > size, split block */
  366.             else {
  367.                 register mblock *new_guy;
  368.                 /* compute new fragment's address */
  369.                 new_guy = current + size;
  370.                 /* compute his size */
  371.                 new_guy->size = current->size - size;
  372.                 /* make him free */
  373.                 new_guy->magic = MAGIC_FREE;
  374.                 /* insert him in the arena just after current */
  375.                 q_insert(current,new_guy);
  376.                 current->magic = MAGIC_BUSY;
  377.                 current->size = size;
  378. #ifdef TEST
  379.                 current->returnadd = returnaddress();
  380. #endif
  381.                 return (void *)(current+1);
  382.             }
  383.         }
  384.     /* we have to malloc up a blob */
  385.     if(size+1 < QUANTUM)
  386.         lmsize = (long)QUANTUM * (long)sizeof(mblock);
  387.     else
  388.         lmsize = (long)size * (long)sizeof(mblock);
  389.     /*
  390.     ** call brkctl to get memory.
  391.     */
  392.     if((mblock *)-1 == 
  393.         (current = (mblock *)brkctl(1,lmsize,((char far *)&arena)))) {
  394.         
  395.         return NULL;
  396.     }
  397.     /*
  398.     ** _asegds seems to be a pointer to the first block allocated in
  399.     ** the heap.
  400.     */
  401.     if(_asegds == NULL)
  402.         _asegds = (char *)current;
  403.     /* make the new block an empty list */
  404.     current->next = current->last = current;
  405.     current->size = (unsigned)(lmsize/sizeof(mblock));
  406.     current->magic = MAGIC_BUSY;
  407.     (void)free(current + 1);
  408.     goto malloc_again;
  409.     /*lint -unreachable */
  410. }
  411.  
  412. void *calloc(size,num)
  413. unsigned size,num;
  414. {
  415.     register char *rval;
  416.     long msize = size *num;
  417.     if(msize >= 65536L)
  418.         return NULL;
  419.     if(NULL != (rval = malloc((unsigned)msize)))
  420.         memset(rval,0,msize);
  421.     return rval;
  422. }
  423.  
  424. void *realloc(ptr,size)
  425. mblock *ptr; unsigned size;
  426. {
  427.     /* save the 'real' size for later */
  428.     unsigned bytesize = size;
  429.     unsigned freespace;    /* size of the new block */
  430.     mblock *new_guy;    /* pointer to following fragment */
  431.     /* check for initialization */
  432.     if(arena.next == NULL) {
  433.         arena.next = arena.last = &arena;
  434.         arena.magic = MAGIC_BUSY;
  435.         arena.size = 0;
  436.         /*
  437.         ** if there isn't anything allocated, freeing anything is an error.
  438.         */
  439.         return NULL;
  440.     }
  441.     /* look at the memory block */
  442.     --ptr;
  443.     /* adjust size to be an even number of mblocks
  444.             bump to next even mblock          round           add one */
  445.     size = ( (size + (sizeof(mblock) - 1) ) / sizeof(mblock) ) + 1;
  446.     if(check_malloc(ptr)) {
  447. #ifdef TEST
  448.         fprintf(stderr,"realloc : bogus pointer %x from %x\n",ptr+1,
  449.             returnaddress());
  450. #endif
  451.         return NULL;
  452.     }
  453.     /* none of this reallocing free blocks bullshit.  It might work, but
  454.     ** why encourage people in that kind of behavior?
  455.     */
  456.     if(ptr->magic != MAGIC_BUSY)
  457.         return NULL;
  458.     /* pathological case - realloc of same size */
  459.     if(size == ptr->size) {
  460. #ifdef TEST
  461.                 ptr->returnadd = returnaddress();
  462. #endif
  463.         return (void *)(ptr+1);
  464.     }
  465.     /* are we shrinking ? */
  466.     if(ptr->size > size)
  467.         goto split_block; /* split the current block into two chunks */
  468.  
  469.     /* try and grow it in place */
  470.     if((ptr->next->magic == MAGIC_FREE) &&    /* is it free ? */
  471.         (ptr->next == (ptr + ptr->size)) && /* is it contiguous ? */
  472.         ((ptr->size + ptr->next->size) >= size)) /* is it big enough ? */ {
  473.         /* join with the next block */
  474.         freespace = ptr->size + ptr->next->size;
  475.         /* get rid of the next block */
  476.         (void)q_remove(ptr->next);
  477.         ptr->size = freespace;    /* put in the new size */
  478.         /* if there isn't enough room for a fragment, don't break the chunk */
  479.         if(freespace == size || freespace == size+1) {
  480. #ifdef TEST
  481.                 ptr->returnadd = returnaddress();
  482. #endif
  483.             return (void *)(ptr+1);
  484.         }
  485.     split_block:
  486.         /* otherwise, split the block, a la malloc */
  487.         new_guy = ptr + size;/* compute new fragment's address */
  488.         /* compute his size */
  489.         new_guy->size = ptr->size - size;
  490.         /* make him free */
  491.         new_guy->magic = MAGIC_FREE;
  492.         /* insert him in the arena just after ptr */
  493.         q_insert(ptr,new_guy);
  494.         ptr->size = size;
  495. #ifdef TEST
  496.         ptr->returnadd = returnaddress();
  497. #endif
  498.         return (void *)(ptr+1);
  499.     } else
  500.     /* easiest case to implement - malloc new block, copy then free
  501.     ** the old block
  502.     */ {
  503.         ++ptr;    /* undo the decrement to look at memory block stuff */
  504.         if(NULL == (new_guy = (mblock *)malloc(bytesize))) {
  505. #ifdef TEST
  506.             fprintf(stderr,"realloc: out of memory\n");
  507. #endif
  508.             return NULL;
  509.         }
  510.         memcpy((char *)new_guy,(char *)ptr,bytesize);
  511.         (void)free(ptr);
  512. #ifdef TEST
  513.         new_guy[-1].returnadd = returnaddress();
  514. #endif
  515.          return (void *)new_guy;
  516.     }
  517. }
  518.  
  519. #ifdef TEST
  520. void free_all()
  521. {
  522.     mblock *current;
  523.     restart:
  524.     for (current = arena.next; current != &arena; current = current->next)
  525.         if(current->magic == MAGIC_BUSY) {
  526.             if(0 != free(current+1)) {
  527.                 printf("error freeing %p\n",(char far *)current);
  528.                 return;
  529.             }
  530.             goto restart;
  531.         }
  532.     while (NULL != (current = dequeue(&arena)))
  533.         free((char *)current);
  534. }
  535. #endif
  536.  
  537. /*
  538. ** implement a near sbrk on top of brkctl.
  539. */
  540. char *sbrk(x)
  541. int x;
  542. {
  543.     return (char *)brkctl(1,(long)x,((char far *)&arena));
  544. }
  545.  
  546. /*
  547. ** implement a brk on top of brkctl.
  548. */
  549. int brk(p)
  550. char *p;
  551. {
  552.     char *current = sbrk(0);
  553.     return (int)brkctl(1,(long)(p-current),((char far *)&arena));
  554. }
  555.  
  556. #ifdef MALLOCTEST
  557. void print_chain()
  558. {
  559.     mblock *current;
  560.     for (current = arena.next; current != &arena; current = current->next) {
  561.         printf("[%04x] %04x (%04x) : next = %04x last = %04x size = %u %s\n",
  562.             current->returnadd,
  563.             current,
  564.             (¤t[1]),current->next,
  565.             current->last,
  566.             current->size * sizeof(mblock),
  567.             current->magic == MAGIC_FREE ? "FREE" : "BUSY");
  568.     }
  569. }
  570.  
  571. #include <signal.h>
  572. #include <setjmp.h>
  573. static jmp_buf flubber;
  574. static int sig_catcher()
  575. {
  576.     signal(SIGINT,sig_catcher);
  577.     longjmp(flubber,-1);
  578. }
  579.  
  580. void memtest()
  581. {
  582.     static char buf[256];
  583.     unsigned siz;
  584.     char *ptr;
  585.     signal(SIGINT,sig_catcher);
  586.     for(;;) {
  587.         setjmp(flubber);
  588.         fputs("> ",stdout);
  589.         if(NULL == getline(buf))
  590.             break;
  591.         switch(toupper(buf[0])) {
  592.         case 'Q':
  593.             return 0;
  594.         case 'M':
  595.             if(1 == sscanf(buf+1,"%u",&siz))
  596.                 printf("malloc(%u) = %04x\n",siz,malloc(siz));
  597.             else
  598.                 printf("bad malloc string : %s\n",buf);
  599.             continue;
  600.         case 'F':
  601.             if(1 == sscanf(buf+1,
  602. #ifndef LARGE_DATA
  603.                         "%x"
  604. #else
  605.                         "%04x"
  606. #endif
  607.                 ,&ptr))
  608.                 printf("free(%04x) = %d\n",ptr,free(ptr));
  609.             else
  610.                 printf("bad free string : %s\n",buf);
  611.             continue;
  612.         case 'P':
  613.             print_chain();
  614.             continue;
  615.         case 'A':
  616.             free_all();
  617.             continue;
  618.         case 'H':
  619.             printf("M = malloc <siz>\nF = free <ptr>\n");
  620.             printf("P = print malloc chain\nA = free everything\n");
  621.             printf("H = help\nQ = quit\nR = realloc <ptr> <size>\n");
  622.             printf("S = printf <ptr>\n");
  623.             continue;
  624.         case 'S':
  625.             if(sscanf(buf+1,"%x",&ptr) == 1)
  626.                 printf("string = %s\n",ptr);
  627.             else
  628.                 printf("bad pointer : %s\n",buf);
  629.             continue;
  630.         case 'R':
  631.             if(2 == sscanf(buf+1,"%x %u",&ptr,&siz))
  632.                 printf("realloc(%04x,%u) = %04x\n",
  633.                     ptr,siz,realloc(ptr,siz));
  634.             else
  635.                 printf("bad realloc string : %s\n",buf);
  636.             continue;
  637.         }
  638.     }
  639. }
  640. #endif
  641.  
  642.  
  643.