home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_C / SNIP9404.ZIP / MEM.C < prev    next >
C/C++ Source or Header  |  1994-04-03  |  14KB  |  682 lines

  1. /*
  2. **  This is a copyrighted work which is functionally identical to work
  3. **  originally published in Micro Cornucopia magazine (issue #52, March-April,
  4. **  1990) and is freely licensed by the author, Walter Bright, for any use.
  5. */
  6.  
  7. /*_ mem.c   Fri Jan 26 1990   Modified by: Walter Bright */
  8. /* $Header: /proj/products/merlin/port/RCS/mem.c,v 1.19 89/10/20 14:36:02 bright Exp Locker: bright $ */
  9. /* Memory management package                */
  10.  
  11. #if defined(VAX11C)
  12. #define  __FILE__  "mem.c"
  13. #endif
  14.  
  15. #include    <stdio.h>
  16. #include    <stdlib.h>
  17. #include    <io.h>
  18.  
  19. #ifndef MEM_H
  20. #include    "mem.h"
  21. #endif
  22.  
  23. #ifndef assert
  24. #include    <assert.h>
  25. #endif
  26.  
  27. #if defined(_MSC_VER)
  28. #include    <dos.h>
  29. #endif
  30.  
  31. #if !defined(VAX11C)
  32. #ifdef BSDUNIX
  33. #include <strings.h>
  34. #else
  35. #include <string.h>
  36. #endif
  37. #else
  38. extern char *strcpy(),*memcpy();
  39. extern int strlen();
  40. #endif  /* VAX11C */
  41.  
  42. int mem_inited = 0;        /* != 0 if initialized            */
  43.  
  44. static int mem_behavior = MEM_ABORTMSG;
  45. static int (*fp)() = NULL;    /* out-of-memory handler    */
  46. static int mem_count;        /* # of allocs that haven't been free'd    */
  47. static int mem_scount;        /* # of sallocs that haven't been free'd */
  48. static int near mem_exception(); /* called when out of memory */
  49.  
  50. /* Determine where to send error messages    */
  51. #ifdef MSDOS
  52. #define ferr    stdout    /* stderr can't be redirected with MS-DOS    */
  53. #else
  54. #define ferr    stderr
  55. #endif
  56.  
  57. /*******************************/
  58.  
  59. void mem_setexception(flag,handler_fp)
  60. #if __cplusplus
  61. enum MEM_E flag;
  62. #else
  63. int flag;
  64. #endif
  65. int (*handler_fp)();
  66. {
  67.     mem_behavior = flag;
  68.     fp = (mem_behavior == MEM_CALLFP) ? handler_fp : 0;
  69. #if MEM_DEBUG
  70.     assert(0 <= flag && flag <= MEM_RETRY);
  71. #endif
  72. }
  73.  
  74. /*************************
  75.  * This is called when we're out of memory.
  76.  * Returns:
  77.  *    1:    try again to allocate the memory
  78.  *    0:    give up and return NULL
  79.  */
  80.  
  81. static int near mem_exception()
  82. {   int behavior;
  83.  
  84.     behavior = mem_behavior;
  85.     while (1)
  86.     {
  87.     switch (behavior)
  88.     {
  89.         case MEM_ABORTMSG:
  90. #if defined(MSDOS) || defined(__OS2__)
  91.         /* Avoid linking in buffered I/O */
  92.         {    static char msg[] = "Fatal error: out of memory\r\n";
  93.  
  94.         write(1,msg,sizeof(msg) - 1);
  95.         }
  96. #else
  97.         fputs("Fatal error: out of memory\n",ferr);
  98. #endif
  99.         /* FALL-THROUGH */
  100.         case MEM_ABORT:
  101.         exit(EXIT_FAILURE);
  102.         /* NOTREACHED */
  103.         case MEM_CALLFP:
  104.         assert(fp);
  105.         behavior = (*fp)();
  106.         break;
  107.         case MEM_RETNULL:
  108.         return 0;
  109.         case MEM_RETRY:
  110.         return 1;
  111.         default:
  112.         assert(0);
  113.     }
  114.     }
  115. }
  116.  
  117. /****************************/
  118.  
  119. #if MEM_DEBUG
  120.  
  121. #undef mem_strdup
  122.  
  123. char *mem_strdup(s)
  124. const char *s;
  125. {
  126.     return mem_strdup_debug(s,__FILE__,__LINE__);
  127. }
  128.  
  129. char *mem_strdup_debug(s,file,line)
  130. char *file;
  131. const char *s;
  132. int line;
  133. {
  134.     char *p;
  135.  
  136.     p = s
  137.         ? (char *) mem_malloc_debug((unsigned) strlen(s) + 1,file,line)
  138.         : NULL;
  139.     return p ? strcpy(p,s) : p;
  140. }
  141. #else
  142. char *mem_strdup(s)
  143. const char *s;
  144. {
  145.     char *p;
  146.  
  147.     p = s ? (char *) mem_malloc((unsigned) strlen(s) + 1) : NULL;
  148.     return p ? strcpy(p,s) : p;
  149. }
  150.  
  151. #endif /* MEM_DEBUG */
  152.  
  153. #ifdef MEM_DEBUG
  154.  
  155. static long mem_maxalloc;    /* max # of bytes allocated        */
  156. static long mem_numalloc;    /* current # of bytes allocated        */
  157.  
  158. #define BEFOREVAL    0x12345678    /* value to detect underrun    */
  159. #define AFTERVAL    0x87654321    /* value to detect overrun    */
  160.  
  161. #if SUN || SUN386
  162. static long afterval = AFTERVAL;    /* so we can do &afterval    */
  163. #endif
  164.  
  165. /* The following should be selected to give maximum probability that    */
  166. /* pointers loaded with these values will cause an obvious crash. On    */
  167. /* Unix machines, a large value will cause a segment fault.        */
  168. /* MALLOCVAL is the value to set malloc'd data to.            */
  169.  
  170. #if MSDOS || __OS2__
  171. #define BADVAL        0xFF
  172. #define MALLOCVAL    0xEE
  173. #else
  174. #define BADVAL        0x7A
  175. #define MALLOCVAL    0xEE
  176. #endif
  177.  
  178. /* Disable mapping macros    */
  179. #undef    mem_malloc
  180. #undef    mem_calloc
  181. #undef    mem_realloc
  182. #undef    mem_free
  183.  
  184. /* Create a list of all alloc'ed pointers, retaining info about where    */
  185. /* each alloc came from. This is a real memory and speed hog, but who    */
  186. /* cares when you've got obscure pointer bugs.                */
  187.  
  188. static struct mem_debug
  189. {    struct mh
  190.     { struct mem_debug *Mnext;    /* next in list            */
  191.       struct mem_debug *Mprev;    /* previous value in list    */
  192.       char *Mfile;        /* filename of where allocated        */
  193.       int Mline;        /* line number of where allocated    */
  194.       unsigned Mnbytes;    /* size of the allocation        */
  195.       long Mbeforeval;    /* detect underrun of data        */
  196.     } m;
  197.     char data[1];        /* the data actually allocated        */
  198. } mem_alloclist =
  199. {
  200.    {    (struct mem_debug *) NULL,
  201.     (struct mem_debug *) NULL,
  202.     "noname",
  203.     11111,
  204.     0,
  205.     BEFOREVAL
  206.    },
  207.    AFTERVAL
  208. };
  209.  
  210. /* Convert from a void *to a mem_debug struct.    */
  211. #define mem_ptrtodl(p)    ((struct mem_debug *) ((char *)p - sizeof(struct mh)))
  212.  
  213. /* Convert from a mem_debug struct to a mem_ptr.    */
  214. #define mem_dltoptr(dl)    ((void *) &((dl)->data[0]))
  215.  
  216. #define next        m.Mnext
  217. #define prev        m.Mprev
  218. #define file        m.Mfile
  219. #define line        m.Mline
  220. #define nbytes        m.Mnbytes
  221. #define beforeval    m.Mbeforeval
  222.  
  223. /*****************************
  224.  * Set new value of file,line
  225.  */
  226.  
  227. void mem_setnewfileline(ptr,fil,lin)
  228. void *ptr;
  229. char *fil;
  230. int lin;
  231. {
  232.     struct mem_debug *dl;
  233.  
  234.     dl = mem_ptrtodl(ptr);
  235.     dl->file = fil;
  236.     dl->line = lin;
  237. }
  238.  
  239. /****************************
  240.  * Print out struct mem_debug.
  241.  */
  242.  
  243. static void near mem_printdl(dl)
  244. struct mem_debug *dl;
  245. {
  246. #if LPTR
  247.     fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%lx\n",
  248.         dl->file,dl->line,dl->nbytes,mem_dltoptr(dl));
  249. #else
  250.     fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%x\n",
  251.         dl->file,dl->line,dl->nbytes,mem_dltoptr(dl));
  252. #endif
  253. }
  254.  
  255. /****************************
  256.  * Print out file and line number.
  257.  */
  258.  
  259. static void near mem_fillin(fil,lin)
  260. char *fil;
  261. int lin;
  262. {
  263.     fprintf(ferr,"File '%s' line %d\n",fil,lin);
  264.     fflush(ferr);
  265. }
  266.  
  267. /****************************
  268.  * If MEM_DEBUG is not on for some modules, these routines will get
  269.  * called.
  270.  */
  271.  
  272. void *mem_calloc(u)
  273. unsigned u;
  274. {
  275.          return mem_calloc_debug(u,__FILE__,__LINE__);
  276. }
  277.  
  278. void *mem_malloc(u)
  279. unsigned u;
  280. {
  281.          return mem_malloc_debug(u,__FILE__,__LINE__);
  282. }
  283.  
  284. void *mem_realloc(p,u)
  285. void *p;
  286. unsigned u;
  287. {
  288.          return mem_realloc_debug(p,u,__FILE__,__LINE__);
  289. }
  290.  
  291. void mem_free(p)
  292. void *p;
  293. {
  294.     mem_free_debug(p,__FILE__,__LINE__);
  295. }    
  296.  
  297.  
  298. /**************************/
  299.  
  300. void mem_freefp(p)
  301. void *p;
  302. {
  303.     mem_free(p);
  304. }
  305.  
  306. /***********************
  307.  * Debug versions of mem_calloc(), mem_free() and mem_realloc().
  308.  */
  309.  
  310. void *mem_malloc_debug(n,fil,lin)
  311. unsigned n;
  312. char *fil;
  313. int lin;
  314. {   void *p;
  315.  
  316.     p = mem_calloc_debug(n,fil,lin);
  317.     if (p)
  318.     memset(p,MALLOCVAL,n);
  319.     return p;
  320. }
  321.  
  322. void *mem_calloc_debug(n,fil,lin)
  323. unsigned n;
  324. char *fil;
  325. int lin;
  326. {
  327.     struct mem_debug *dl;
  328.  
  329.     do
  330.     dl = (struct mem_debug *)
  331.         calloc(sizeof(*dl) + n + sizeof(AFTERVAL) - 1,1);
  332.     while (dl == NULL && mem_exception());
  333.     if (dl == NULL)
  334.     {
  335. #if 0
  336.     printf("Insufficient memory for alloc of %d at ",n);
  337.     mem_fillin(fil,lin);
  338.     printf("Max allocated was: %ld\n",mem_maxalloc);
  339. #endif
  340.     return NULL;
  341.     }
  342.     dl->file = fil;
  343.     dl->line = lin;
  344.     dl->nbytes = n;
  345.     dl->beforeval = BEFOREVAL;
  346. #if SUN || SUN386 /* bus error if we store a long at an odd address */
  347.     memcpy(&(dl->data[n]),&afterval,sizeof(AFTERVAL));
  348. #else
  349.     *(long *) &(dl->data[n]) = AFTERVAL;
  350. #endif
  351.  
  352.     /* Add dl to start of allocation list    */
  353.     dl->next = mem_alloclist.next;
  354.     dl->prev = &mem_alloclist;
  355.     mem_alloclist.next = dl;
  356.     if (dl->next != NULL)
  357.     dl->next->prev = dl;
  358.  
  359.     mem_count++;
  360.     mem_numalloc += n;
  361.     if (mem_numalloc > mem_maxalloc)
  362.     mem_maxalloc = mem_numalloc;
  363.     return mem_dltoptr(dl);
  364. }
  365.  
  366. void mem_free_debug(ptr,fil,lin)
  367. void *ptr;
  368. char *fil;
  369. int lin;
  370. {
  371.     struct mem_debug *dl;
  372.  
  373.     if (ptr == NULL)
  374.         return;
  375. #if 0
  376.     {    fprintf(ferr,"Freeing NULL pointer at ");
  377.         goto err;
  378.     }
  379. #endif
  380.     if (mem_count <= 0)
  381.     {    fprintf(ferr,"More frees than allocs at ");
  382.         goto err;
  383.     }
  384.     dl = mem_ptrtodl(ptr);
  385.     if (dl->beforeval != BEFOREVAL)
  386.     {
  387. #if LPTR
  388.         fprintf(ferr,"Pointer x%lx underrun\n",ptr);
  389. #else
  390.         fprintf(ferr,"Pointer x%x underrun\n",ptr);
  391. #endif
  392.         goto err2;
  393.     }
  394. #if SUN || SUN386 /* Bus error if we read a long from an odd address    */
  395.     if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0)
  396. #else
  397.     if (*(long *) &dl->data[dl->nbytes] != AFTERVAL)
  398. #endif
  399.     {
  400. #if LPTR
  401.         fprintf(ferr,"Pointer x%lx overrun\n",ptr);
  402. #else
  403.         fprintf(ferr,"Pointer x%x overrun\n",ptr);
  404. #endif
  405.         goto err2;
  406.     }
  407.     mem_numalloc -= dl->nbytes;
  408.     if (mem_numalloc < 0)
  409.     {    fprintf(ferr,"error: mem_numalloc = %ld, dl->nbytes = %d\n",
  410.             mem_numalloc,dl->nbytes);
  411.         goto err2;
  412.     }
  413.  
  414.     /* Remove dl from linked list    */
  415.     if (dl->prev)
  416.         dl->prev->next = dl->next;
  417.     if (dl->next)
  418.         dl->next->prev = dl->prev;
  419.  
  420.     /* Stomp on the freed storage to help detect references    */
  421.     /* after the storage was freed.                */
  422.     memset((void *) dl,BADVAL,sizeof(*dl) + dl->nbytes);
  423.     mem_count--;
  424.  
  425.     /* Some compilers can detect errors in the heap.    */
  426. #if defined(DLC)
  427.     {    int i;
  428.         i = free(dl);
  429.         assert(i == 0);
  430.     }
  431. #else
  432.     free((void *) dl);
  433. #endif
  434.     return;
  435.  
  436. err2:
  437.     mem_printdl(dl);
  438. err:
  439.     fprintf(ferr,"free'd from ");
  440.     mem_fillin(fil,lin);
  441.     assert(0);
  442.     /* NOTREACHED */
  443. }
  444.  
  445. /*******************
  446.  * Debug version of mem_realloc().
  447.  */
  448.  
  449. void *mem_realloc_debug(oldp,n,fil,lin)
  450. void *oldp;
  451. unsigned n;
  452. char *fil;
  453. int lin;
  454. {   void *p;
  455.     struct mem_debug *dl;
  456.  
  457.     if (n == 0)
  458.     {    mem_free_debug(oldp,fil,lin);
  459.     p = NULL;
  460.     }
  461.     else if (oldp == NULL)
  462.     p = mem_malloc_debug(n,fil,lin);
  463.     else
  464.     {
  465.     p = mem_malloc_debug(n,fil,lin);
  466.     if (p != NULL)
  467.     {
  468.         dl = mem_ptrtodl(oldp);
  469.         if (dl->nbytes < n)
  470.         n = dl->nbytes;
  471.         memcpy(p,oldp,n);
  472.         mem_free_debug(oldp,fil,lin);
  473.     }
  474.     }
  475.     return p;
  476. }
  477.  
  478. /***************************/
  479.  
  480. void mem_check()
  481. {   register struct mem_debug *dl;
  482.  
  483.     for (dl = mem_alloclist.next; dl != NULL; dl = dl->next)
  484.     mem_checkptr(mem_dltoptr(dl));
  485. }
  486.  
  487. /***************************/
  488.  
  489. void mem_checkptr(p)
  490. register void *p;
  491. {   register struct mem_debug *dl;
  492.  
  493.     for (dl = mem_alloclist.next; dl != NULL; dl = dl->next)
  494.     {
  495.     if (p >= (void *) &(dl->data[0]) &&
  496.         p < (void *)((char *)dl + sizeof(struct mem_debug)-1 + dl->nbytes))
  497.         goto L1;
  498.     }
  499.     assert(0);
  500.  
  501. L1:
  502.     dl = mem_ptrtodl(p);
  503.     if (dl->beforeval != BEFOREVAL)
  504.     {
  505. #if LPTR
  506.         fprintf(ferr,"Pointer x%lx underrun\n",p);
  507. #else
  508.         fprintf(ferr,"Pointer x%x underrun\n",p);
  509. #endif
  510.         goto err2;
  511.     }
  512. #if SUN || SUN386 /* Bus error if we read a long from an odd address    */
  513.     if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0)
  514. #else
  515.     if (*(long *) &dl->data[dl->nbytes] != AFTERVAL)
  516. #endif
  517.     {
  518. #if LPTR
  519.         fprintf(ferr,"Pointer x%lx overrun\n",p);
  520. #else
  521.         fprintf(ferr,"Pointer x%x overrun\n",p);
  522. #endif
  523.         goto err2;
  524.     }
  525.     return;
  526.  
  527. err2:
  528.     mem_printdl(dl);
  529.     assert(0);
  530. }
  531.  
  532. #else
  533.  
  534. /***************************/
  535.  
  536. void *mem_malloc(numbytes)
  537. unsigned numbytes;
  538. {    void *p;
  539.  
  540.     if (numbytes == 0)
  541.         return NULL;
  542.     while (1)
  543.     {
  544.         p = malloc(numbytes);
  545.         if (p == NULL)
  546.         {    if (mem_exception())
  547.                 continue;
  548.         }
  549.         else
  550.             mem_count++;
  551.         break;
  552.     }
  553.     /*printf("malloc(%d) = x%lx\n",numbytes,p);*/
  554.     return p;
  555. }
  556.  
  557. /***************************/
  558.  
  559. void *mem_calloc(numbytes)
  560. unsigned numbytes;
  561. {    void *p;
  562.  
  563.     if (numbytes == 0)
  564.         return NULL;
  565.     while (1)
  566.     {
  567.         p = calloc(numbytes,1);
  568.         if (p == NULL)
  569.         {    if (mem_exception())
  570.                 continue;
  571.         }
  572.         else
  573.             mem_count++;
  574.         break;
  575.     }
  576.     /*printf("calloc(%d) = x%lx\n",numbytes,p);*/
  577.     return p;
  578. }
  579.  
  580. /***************************/
  581.  
  582. void *mem_realloc(oldmem_ptr,newnumbytes)
  583. void *oldmem_ptr;
  584. unsigned newnumbytes;
  585. {   void *p;
  586.  
  587.     if (oldmem_ptr == NULL)
  588.     p = mem_malloc(newnumbytes);
  589.     else if (newnumbytes == 0)
  590.     {    mem_free(oldmem_ptr);
  591.     p = NULL;
  592.     }
  593.     else
  594.     {
  595.     do
  596.         p = realloc(oldmem_ptr,newnumbytes);
  597.     while (p == NULL && mem_exception());
  598.     }
  599.     /*printf("realloc(x%lx,%d) = x%lx\n",oldmem_ptr,newnumbytes,p);*/
  600.     return p;
  601. }
  602.  
  603. /***************************/
  604.  
  605. void mem_free(ptr)
  606. void *ptr;
  607. {
  608.     /*printf("free(x%lx)\n",ptr);*/
  609.     if (ptr != NULL)
  610.     {    assert(mem_count > 0);
  611.     mem_count--;
  612. #if DLC
  613.     {    int i;
  614.  
  615.         i = free(ptr);
  616.         assert(i == 0);
  617.     }
  618. #else
  619.     free(ptr);
  620. #endif
  621.     }
  622. }
  623.  
  624. #endif /* MEM_DEBUG */
  625.  
  626. /***************************/
  627.  
  628. void mem_init()
  629. {
  630.     if (mem_inited == 0)
  631.     {    mem_count = 0;
  632. #if MEM_DEBUG
  633.         mem_numalloc = 0;
  634.         mem_maxalloc = 0;
  635.         mem_alloclist.next = NULL;
  636. #endif
  637. #if defined(__ZTC__) || defined(__SC__)
  638.         /* Necessary if mem_sfree() calls free() before any    */
  639.         /* calls to malloc().                    */
  640.         free(malloc(1));    /* initialize storage allocator    */
  641. #endif
  642.         mem_inited++;
  643.     }
  644. }
  645.  
  646. /***************************/
  647.  
  648. void mem_term()
  649. {
  650.  
  651.     if (mem_inited)
  652.     {
  653. #if MEM_DEBUG
  654.         register struct mem_debug *dl;
  655.  
  656.         for (dl = mem_alloclist.next; dl; dl = dl->next)
  657.         {    fprintf(ferr,"Unfreed pointer: ");
  658.             mem_printdl(dl);
  659.         }
  660. #if 0
  661.         fprintf(ferr,"Max amount ever allocated == %ld bytes\n",
  662.             mem_maxalloc);
  663. #endif
  664. #else
  665.         if (mem_count)
  666.             fprintf(ferr,"%d unfreed items\n",mem_count);
  667.         if (mem_scount)
  668.             fprintf(ferr,"%d unfreed s items\n",mem_scount);
  669. #endif /* MEM_DEBUG */
  670.         assert(mem_count == 0 && mem_scount == 0);
  671.         mem_inited = 0;
  672.     }
  673. }
  674.  
  675. #undef next
  676. #undef prev
  677. #undef file
  678. #undef line
  679. #undef nbytes
  680. #undef beforeval
  681.  
  682.