1 /****************************************************************************
2  *               pov_mem.cpp
3  *
4  * This module contains the code for our own memory allocation/deallocation,
5  * providing memory tracing, statistics, and garbage collection options.
6  *
7  * from Persistence of Vision(tm) Ray Tracer version 3.6.
8  * Copyright 1991-2003 Persistence of Vision Team
9  * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
10  *---------------------------------------------------------------------------
11  * NOTICE: This source code file is provided so that users may experiment
12  * with enhancements to POV-Ray and to port the software to platforms other
13  * than those supported by the POV-Ray developers. There are strict rules
14  * regarding how you are permitted to use this file. These rules are contained
15  * in the distribution and derivative versions licenses which should have been
16  * provided with this file.
17  *
18  * These licences may be found online, linked from the end-user license
19  * agreement that is located at http://www.povray.org/povlegal.html
20  *---------------------------------------------------------------------------
21  * This program is based on the popular DKB raytracer version 2.12.
22  * DKBTrace was originally written by David K. Buck.
23  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
24  *---------------------------------------------------------------------------
25  * $File: //depot/povray/3.6-release/source/pov_mem.cpp $
26  * $Revision: #3 $
27  * $Change: 3032 $
28  * $DateTime: 2004/08/02 18:43:41 $
29  * $Author: chrisc $
30  * $Log$
31  *****************************************************************************/
32 
33 #include "frame.h"
34 #include "userio.h"           /* Error() */
35 
36 #include "pov_mem.h"
37 #include "parse.h"              /* MAError() */
38 #include "povray.h"             /* stats[] global var */
39 #include "optout.h"
40 #include "pov_util.h"
41 #include "povmsend.h"
42 
43 BEGIN_POV_NAMESPACE
44 
45 /************************************************************************
46 * AUTHOR
47 *
48 *   Steve Anger:70714,3113
49 *
50 * DESCRIPTION
51 *
52 This module replaces the memory allocation calls malloc, calloc, realloc
53 and free with the macros POV_MALLOC, POV_CALLOC, POV_REALLOC, and POV_FREE.
54 These macros work the same as the standard C functions except that the
55 POV_xALLOC functions also take a message as the last parameter and
56 automatically call MAError(msg) if the allocation fails. That means that
57 instead of writing
58 
59   if ((New = malloc(sizeof(*New))) == NULL)
60     {
61     MAError ("new object");
62     }
63 
64 you'd just use
65 
66   New = POV_MALLOC (sizeof(*New), "new object");
67 
68 This also expands the function of the macros to include error checking and
69 memory tracking.
70 
71 The following macros need to be defined in config.h, depending of what
72 features the compile needs:
73 
74 #define MEM_TAG     - Enables memory tag debugging
75 --------------------------------------------------
76 Memory tag debugging adds a 32-bit identifier to the beginning of each
77 allocated memory block and erases it after the block has been free'd. This
78 lets POV_FREE verify that the block it's freeing is valid and issue an
79 error message if it isn't. Makes it easy to find those nasty double free's
80 which usually corrupt the heap.
81 
82 #define MEM_RECLAIM - Enables garbage collection
83 ------------------------------------------------
84 Garbage collection maintains a list of all currently allocated memory so
85 that it can be free'd when the program exits. Normally POV-Ray will free all
86 of its memory on its own, however abnormal exits such as parser errors or
87 user aborts bypass the destructors. There are four functions which control
88 the garbage collection:
89 
90 mem_init()
91   Initializes global variables used by the garbage collection routines.
92   This function should be called once before any memory allocation functions
93   are called.
94 
95 mem_mark()
96   Starts a new memory pool. The next call to mem_release() will only release
97   memory allocated after this call.
98 
99 mem_release ()
100   Releases all unfree'd memory allocated since the last call to mem_mark().
101   The LogFile parameter determines if it dumps the list of unfree'd memory to
102   a file.
103 
104 mem_release_all ()
105   Releases all unfree'd memory allocated since the program started running.
106 
107 POV-Ray only uses the mem_release_all() function however mem_mark() and
108 mem_release() might be useful for implenting a leak-free animation loop.
109 
110 #define MEM_TRACE   - Enables garbage collection and memory tracing
111 -------------------------------------------------------------------
112 Memory tracing stores the file name and line number for ever POV_xALLOC
113 call and dumps a list of unfree'd blocks when POV-Ray terminates.
114 
115 #define MEM_STATS 1 - enables tracking of memory statistics
116 -------------------------------------------------------------------
117 Memory statistics enables routines that will track overall memory usage.
118 After all memory allocation/deallocation has taken place, and before you
119 re-initialize everything with another mem_init() call, you can call some
120 accessor routines to determine how memory was used.  Setting MEM_STATS
121 to 1 only tracks peak memory usage.  Setting it to 2 additionally tracks
122 number of calls to malloc/free and some other statistics.
123 *
124 * CHANGES
125 *
126 *   Aug 1995 : Steve Anger - Creation.
127 *   Apr 1996 : Eduard Schwan - Added MEM_STATS code
128 *   Jul 1996 : Andreas Dilger - Force mem_header to align on double boundary
129 **************************************************************************/
130 
131 
132 /****************************************************************************/
133 /* Allow user definable replacements for memory functions                   */
134 /****************************************************************************/
135 #ifndef MALLOC
136 	#define MALLOC malloc
137 #endif
138 
139 #ifndef CALLOC
140 	#define CALLOC calloc
141 #endif
142 
143 #ifndef REALLOC
144 	#define REALLOC realloc
145 #endif
146 
147 #ifndef FREE
148 	#define FREE free
149 #endif
150 
151 
152 /****************************************************************************/
153 /* internal use                                                             */
154 /****************************************************************************/
155 
156 // if PREFILL or GUARD on, STATS must also be on
157 #if (defined(MEM_PREFILL) || defined (MEM_GUARD)) && !defined (MEM_STATS)
158 	#define MEM_STATS
159 #endif
160 
161 // if TRACE is on, the RECLAIM must also be on
162 #if defined(MEM_TRACE) && !defined (MEM_RECLAIM)
163 	#define MEM_RECLAIM
164 #endif
165 
166 // This is the filename created for memory leakage information
167 #if defined(MEM_TRACE)
168 	#define MEM_LOG_FNAME   "Memory.Log"
169 #endif
170 
171 // determine if we need to add a header to our memory records
172 #if defined(MEM_TAG) || defined(MEM_RECLAIM) || defined(MEM_TRACE) || defined(MEM_STATS)
173 	#define MEM_HEADER
174 #endif
175 
176 #ifdef MEM_HEADER
177 
178 	#define MEMNODE struct mem_node
179 
180 	#ifndef MEM_HEADER_ALIGNMENT
181 		#define MEM_HEADER_ALIGNMENT sizeof(double)
182 	#endif
183 
184 	struct mem_node
185 	{
186 		#ifdef MEM_TAG
187 			int tag;
188 		#endif
189 
190 		#ifdef MEM_STATS
191 			size_t size;
192 		#endif
193 
194 		#ifdef MEM_RECLAIM
195 			MEMNODE *prev;
196 			MEMNODE *next;
197 			int poolno;
198 
199 			#ifdef MEM_TRACE
200 				const char *file;
201 				int line;
202 			#endif
203 
204 		#endif
205 
206 	};
207 
208 #endif /* MEM_HEADER */
209 
210 
211 #if defined(MEM_RECLAIM)
212 	static int poolno = 0; // GLOBAL VARIABLE
213 	static MEMNODE *memlist = NULL; // GLOBAL VARIABLE
214 #endif
215 
216 #if !defined(MEM_PREFILL_STRING)
217 	#define MEM_PREFILL_STRING "POVR"
218 #endif
219 
220 #if !defined(MEM_CLEAR_STRING)
221 	#define MEM_CLEAR_STRING "CLEA"
222 #endif
223 
224 #if !defined(MEM_GUARD_STRING)
225 	#define MEM_GUARD_STRING "GURD"
226 #endif
227 
228 #if defined(MEM_GUARD)
229 	static char *mem_guard_string = MEM_GUARD_STRING;
230 	static size_t mem_guard_string_len = 0;
231 	#if !defined(MEM_GUARD_SIZE)
232 		#define MEM_GUARD_SIZE MEM_HEADER_ALIGNMENT
233 	#endif
234 #else
235 	#define MEM_GUARD_SIZE 0
236 #endif
237 
238 #if defined(MEM_PREFILL)
239 	static char *mem_prefill_string = MEM_PREFILL_STRING; // GLOBAL VARIABLE
240 	static size_t mem_prefill_string_len = 0; // GLOBAL VARIABLE
241 	static char *mem_clear_string = MEM_CLEAR_STRING; // GLOBAL VARIABLE
242 	static size_t mem_clear_string_len = 0; // GLOBAL VARIABLE
243 #endif
244 
245 static int leak_msg = false; // GLOBAL VARIABLE
246 
247 
248 #ifdef MEM_HEADER
249 	const int NODESIZE = (((sizeof(MEMNODE) + (MEM_HEADER_ALIGNMENT - 1)) / MEM_HEADER_ALIGNMENT) * MEM_HEADER_ALIGNMENT);
250 #else
251 	const int NODESIZE = 0;
252 #endif
253 
254 
255 #if defined(MEM_RECLAIM)
256 	static void add_node(MEMNODE * node);
257 	static void remove_node(MEMNODE * node);
258 #endif
259 
260 
261 #if defined(MEM_TAG)
262 	// the tag value that marks our used memory
263 	#define MEMTAG_VALUE   0x4D546167L
264 
265 	static int mem_check_tag(MEMNODE * node);
266 #endif
267 
268 
269 #if defined(MEM_RECLAIM)
270 static long num_nodes;          /* keep track of valence of node list */ // GLOBAL VARIABLE
271 #endif /* MEM_RECLAIM */
272 
273 
274 #if defined(MEM_STATS)
275 
276 typedef struct MemStats_Struct MEMSTATS;
277 
278 struct MemStats_Struct
279 {
280   size_t     smallest_alloc;    /* smallest # of bytes in one malloc() */
281   size_t     largest_alloc;     /* largest # of bytes in one malloc() */
282   size_t     current_mem_usage; /* current total # of bytes allocated */
283   size_t     largest_mem_usage; /* peak total # of bytes allocated */
284 #if (MEM_STATS>=2)
285   /* could add a running average size too, someday */
286   long int   total_allocs;      /* total # of alloc calls */
287   long int   total_frees;       /* total # of free calls */
288   const char *smallest_file;     /* file name of largest alloc */
289   int        smallest_line;     /* file line of largest alloc */
290   const char *largest_file;      /* file name of largest alloc */
291   int        largest_line;      /* file line of largest alloc */
292 #endif
293 };
294 
295 /* keep track of memory allocation statistics */
296 static MEMSTATS mem_stats; // GLOBAL VARIABLE
297 
298 /* local prototypes */
299 static void mem_stats_init (void);
300 static void mem_stats_alloc (size_t nbytes, const char *file, int line);
301 static void mem_stats_free (size_t nbytes);
302 
303 #endif
304 
305 
306 /****************************************************************************/
mem_init()307 void mem_init()
308 {
309 #if defined(MEM_RECLAIM)
310   num_nodes = 0;
311   poolno = 0;
312   memlist = NULL;
313 #endif
314 #if defined(MEM_GUARD)
315   mem_guard_string_len = strlen(mem_guard_string);
316 #endif
317 #if defined(MEM_PREFILL)
318   mem_prefill_string_len = strlen(mem_prefill_string);
319   mem_clear_string_len = strlen(mem_clear_string);
320 #endif
321 #if defined(MEM_STATS)
322   mem_stats_init();
323 #endif
324   leak_msg = false;
325 }
326 
327 
328 #if defined(MEM_TAG)
329 /****************************************************************************/
330 /* return true if pointer is non-null and has a valid tag */
mem_check_tag(MEMNODE * node)331 static int mem_check_tag(MEMNODE *node)
332 {
333   int isOK = false;
334 
335   if (node != NULL)
336     if (node->tag == MEMTAG_VALUE)
337       isOK = true;
338   return isOK;
339 }
340 
341 #endif /* MEM_TAG */
342 
343 
344 /****************************************************************************/
pov_malloc(size_t size,const char * file,int line,const char * msg)345 void *pov_malloc(size_t size, const char *file, int line, const char *msg)
346 {
347   void *block;
348   size_t totalsize;
349 #if defined(MEM_HEADER)
350   MEMNODE *node;
351 #endif
352 
353 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
354   char *memptr;
355   size_t i;
356 #endif
357 
358 #if defined(MEM_HEADER)
359   if (size == 0)
360   {
361     Error("Attempt to malloc zero size block (File: %s Line: %d).", file, line);
362   }
363 #endif
364 
365   totalsize = size + NODESIZE + (MEM_GUARD_SIZE * 2); /* number of bytes allocated in OS */
366 
367   block = (void *)MALLOC(totalsize);
368 
369   if (block == NULL)
370     MAError(msg, size);
371 
372 #if defined(MEM_HEADER)
373   node = (MEMNODE *) block;
374 #endif
375 
376 #if defined(MEM_TAG)
377   node->tag = MEMTAG_VALUE;
378 #endif
379 
380 #if defined(MEM_TRACE) || defined(MEM_STATS)
381   node->size = totalsize;
382 #endif
383 #if defined(MEM_TRACE)
384   node->file = file;
385   node->line = line;
386 #endif
387 
388 #if defined(MEM_PREFILL)
389   memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
390   for(i = 0; i < size; i++)
391      memptr[i] = mem_prefill_string[i % mem_prefill_string_len];
392 #endif
393 
394 #if defined(MEM_GUARD)
395   memptr = (char *)block + NODESIZE;
396   for(i = 0; i < MEM_GUARD_SIZE; i++)
397      memptr[i] = mem_guard_string[i % mem_guard_string_len];
398   memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
399   for(i = 0; i < MEM_GUARD_SIZE; i++)
400      memptr[i] = mem_guard_string[i % mem_guard_string_len];
401 #endif
402 
403 #if defined(MEM_RECLAIM)
404   add_node(node);
405 #endif
406 
407 #if defined(MEM_STATS)
408   mem_stats_alloc(totalsize, file, line);
409 #endif
410 
411   return (void *)((char *)block + NODESIZE + MEM_GUARD_SIZE);
412 }
413 
414 
415 /****************************************************************************/
pov_calloc(size_t nitems,size_t size,const char * file,int line,const char * msg)416 void *pov_calloc(size_t nitems, size_t size, const char *file, int line, const char *msg)
417 {
418   void *block;
419   size_t actsize;
420 
421   actsize = nitems * size;
422 
423 #if defined(MEM_HEADER)
424   if (actsize == 0)
425   {
426     Error("Attempt to calloc zero size block (File: %s Line: %d).", file, line);
427   }
428 #endif
429 
430   block = (void *)pov_malloc(actsize, file, line, msg);
431 
432   if (block != NULL)
433     memset(block, 0, actsize);
434 
435   return block;
436 }
437 
438 
439 /****************************************************************************/
pov_realloc(void * ptr,size_t size,const char * file,int line,const char * msg)440 void *pov_realloc(void *ptr, size_t size, const char *file, int line, const char *msg)
441 {
442   void *block;
443 #if defined(MEM_STATS)
444   size_t oldsize;
445 #endif
446 
447 #if defined(MEM_HEADER)
448   MEMNODE *node;
449 #endif
450 
451 #if defined(MEM_RECLAIM)
452   MEMNODE *prev;
453   MEMNODE *next;
454 #endif
455 
456 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
457   char *memptr;
458   size_t i;
459 #endif
460 
461   if (size == 0)
462   {
463     if (ptr)
464       pov_free(ptr, file, line);
465     return NULL;
466   }
467   else if (ptr == NULL)
468     return pov_malloc(size, file, line, msg);
469 
470   block = (void *)((char *)ptr - NODESIZE - MEM_GUARD_SIZE);
471 
472 #if defined(MEM_GUARD)
473   memptr = (char *)block + NODESIZE;
474   for(i = 0; i < MEM_GUARD_SIZE; i++)
475   {
476      if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
477      {
478         Warning(0, "Damaged start guard detected in resized block (File: %s Line: %d).", file, line);
479         break;
480      }
481   }
482   memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
483   for(i = 0; i < MEM_GUARD_SIZE; i++)
484   {
485      if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
486      {
487         Warning(0, "Damaged end guard detected in resized block (File: %s Line: %d).", file, line);
488         break;
489      }
490   }
491 #endif
492 
493 #if defined(MEM_HEADER)
494   node = (MEMNODE *) block;
495 #endif
496 
497 #if defined(MEM_TAG)
498   if (node->tag != MEMTAG_VALUE)
499     Error("Attempt to realloc invalid block (File: %s Line: %d).", file, line);
500 
501   node->tag = ~node->tag;
502 #endif
503 
504 #if defined(MEM_RECLAIM)
505   prev = node->prev;
506   next = node->next;
507 #endif
508 
509 #if defined(MEM_STATS)
510   oldsize = ((MEMNODE *)block)->size;
511 
512   #if defined(MEM_PREFILL)
513     memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
514     for(i = size; i < oldsize - NODESIZE - (MEM_GUARD_SIZE * 2); i++)
515        memptr[i] = mem_clear_string[i % mem_clear_string_len];
516   #endif
517 #endif
518 
519   block = (void *)REALLOC(block, NODESIZE + (MEM_GUARD_SIZE * 2) + size);
520 
521   if (block == NULL)
522     MAError(msg, size);
523 
524 #if defined(MEM_STATS)
525   /* REALLOC does an implied FREE... */
526   mem_stats_free(oldsize);
527   /* ...and an implied MALLOC... */
528   mem_stats_alloc(NODESIZE + (MEM_GUARD_SIZE * 2) + size, file, line);
529 
530   #if defined(MEM_PREFILL)
531     memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
532     for(i = oldsize - NODESIZE - (MEM_GUARD_SIZE * 2); i < size; i++)
533        memptr[i] = mem_prefill_string[i % mem_prefill_string_len];
534   #endif
535 
536 #endif
537 
538 #if defined(MEM_HEADER)
539   node = (MEMNODE *) block;
540 #endif
541 
542 #if defined(MEM_TAG)
543   node->tag = MEMTAG_VALUE;
544 #endif
545 
546 #if defined(MEM_TRACE) || defined(MEM_STATS)
547   node->size = size + NODESIZE + (MEM_GUARD_SIZE * 2);
548 #endif
549 #if defined(MEM_TRACE)
550   node->file = file;
551   node->line = line;
552 #endif
553 
554 #if defined(MEM_GUARD)
555   memptr = (char *)block + NODESIZE;
556   for(i = 0; i < MEM_GUARD_SIZE; i++)
557      memptr[i] = mem_guard_string[i % mem_guard_string_len];
558   memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
559   for(i = 0; i < MEM_GUARD_SIZE; i++)
560      memptr[i] = mem_guard_string[i % mem_guard_string_len];
561 #endif
562 
563 #if defined(MEM_RECLAIM)
564   if (prev == NULL)
565     memlist = node;
566   else
567     prev->next = node;
568   if (node->next != NULL)
569     node->next->prev = node;
570   if (next != NULL)
571     next->prev = node;
572 #endif
573 
574   return (void *)((char *)block + NODESIZE + MEM_GUARD_SIZE);
575 }
576 
577 
578 /****************************************************************************/
pov_free(void * ptr,const char * file,int line)579 void pov_free(void *ptr, const char *file, int line)
580 {
581   void *block;
582 
583 #if defined(MEM_HEADER)
584   MEMNODE *node;
585 #endif
586 
587 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
588   char *memptr;
589   size_t size;
590   size_t i;
591 #endif
592 
593   if (ptr == NULL)
594     Error("Attempt to free NULL pointer (File: %s Line: %d).", file, line);
595 
596   block = (void *)((char *)ptr - NODESIZE - MEM_GUARD_SIZE);
597 
598 #if defined(MEM_HEADER)
599   node = (MEMNODE *) block;
600 #endif
601 
602 #if defined(MEM_TAG)
603   #if defined(MEM_PREFILL)
604     size = ((MEMNODE *)block)->size;
605     memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
606     for(i = 0; i < size - NODESIZE - (MEM_GUARD_SIZE * 2); i++)
607        memptr[i] = mem_clear_string[i % mem_clear_string_len];
608   #endif
609 
610   if (node->tag == ~MEMTAG_VALUE)
611   {
612     Warning(0, "Attempt to free already free'd block (File: %s Line: %d).", file, line);
613     return;
614   }
615   else if (node->tag != MEMTAG_VALUE)
616   {
617     Warning(0, "Attempt to free invalid block (File: %s Line: %d).", file, line);
618     return;
619   }
620 
621 #endif
622 
623 #if defined(MEM_GUARD)
624   memptr = (char *)block + NODESIZE;
625   for(i = 0; i < MEM_GUARD_SIZE; i++)
626   {
627      if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
628      {
629         Warning(0, "Damaged start guard detected in free'd block (File: %s Line: %d).", file, line);
630         break;
631      }
632   }
633   memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
634   for(i = 0; i < MEM_GUARD_SIZE; i++)
635   {
636      if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
637      {
638         Warning(0, "Damaged end guard detected in free'd block (File: %s Line: %d).", file, line);
639         break;
640      }
641   }
642 #endif
643 
644 #if defined(MEM_RECLAIM)
645   remove_node(node);
646 #endif
647 
648 #if defined(MEM_TAG)
649   /* do this After remove_node, so remove_node can check validity of nodes */
650   node->tag = ~node->tag;
651 #endif
652 
653 #if defined(MEM_STATS)
654   mem_stats_free(((MEMNODE*)block)->size);
655 #endif
656 
657   FREE(block);
658 }
659 
660 
661 /****************************************************************************/
662 /* Starts a new memory pool. The next mem_release() call will
663    only release memory allocated after this call. */
mem_mark()664 void mem_mark()
665 {
666 #if defined(MEM_RECLAIM)
667   poolno++;
668 #endif
669 }
670 
671 
672 /****************************************************************************/
673 /* Releases all unfree'd memory from current memory pool */
mem_release()674 void mem_release()
675 {
676 #if defined(MEM_RECLAIM)
677   OStream *f = NULL;
678   MEMNODE *p, *tmp;
679   size_t totsize;
680 
681   p = memlist;
682   totsize = 0;
683 
684 #if defined(MEM_TRACE)
685   if (p != NULL && (p->poolno == poolno))
686     f = New_OStream(MEM_LOG_FNAME, POV_File_Data_LOG, true);
687 #endif /* MEM_TRACE */
688 
689   while (p != NULL && (p->poolno == poolno))
690   {
691 #if defined(MEM_TRACE)
692 
693 #if defined(MEM_TAG)
694     if (!mem_check_tag(p))
695       Debug_Info("mem_release(): Memory pointer corrupt!\n");
696 #endif /* MEM_TAG */
697 
698     totsize += (p->size - NODESIZE - (MEM_GUARD_SIZE * 2));
699     if (!leak_msg)
700     {
701       Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME);
702       leak_msg = true;
703     }
704 
705     if (f != NULL)
706       f->printf("File:%13s  Line:%4d  Size:%lu\n", p->file, p->line, (unsigned long)(p->size - NODESIZE - (MEM_GUARD_SIZE * 2)));
707 #endif /* MEM_TRACE */
708 
709 #if defined(MEM_STATS)
710     mem_stats_free(p->size);
711 #endif
712 
713     tmp = p;
714     p = p->next;
715     remove_node(tmp);
716     FREE(tmp);
717   }
718 
719   if (f != NULL)
720     delete f;
721 
722   if (totsize > 0)
723     Debug_Info("%lu bytes reclaimed (pool #%d)\n", totsize, poolno);
724 
725   if (poolno > 0)
726     poolno--;
727 
728 #if defined(MEM_STATS)
729   /* reinitialize the stats structure for next time through */
730   mem_stats_init();
731 #endif
732 
733 #endif /* MEM_RECLAIM */
734 }
735 
736 
737 /****************************************************************************/
738 /* Released all unfree'd memory from all pools */
mem_release_all()739 void mem_release_all()
740 {
741 #if defined(MEM_RECLAIM)
742   OStream *f = NULL;
743   MEMNODE *p, *tmp;
744   size_t totsize;
745 
746   Send_Progress("Reclaiming memory", PROGRESS_RECLAIMING_MEMORY);
747 
748   p = memlist;
749   totsize = 0;
750 
751 #if defined(MEM_TRACE)
752   if (p != NULL)
753     f = New_OStream(MEM_LOG_FNAME, POV_File_Data_LOG, true);
754 #endif
755 
756   while (p != NULL)
757   {
758 #if defined(MEM_TRACE)
759 
760     #if defined(MEM_TAG)
761     if (!mem_check_tag(p))
762       Debug_Info("mem_release_all(): Memory pointer corrupt!\n");
763     #endif /* MEM_TAG */
764 
765     totsize += (p->size - NODESIZE - (MEM_GUARD_SIZE * 2));
766     if (!leak_msg)
767     {
768       Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME);
769       leak_msg = true;
770     }
771 
772     if (f != NULL)
773       f->printf("File:%13s  Line:%4d  Size:%lu\n", p->file, p->line, (unsigned long)(p->size - NODESIZE - (MEM_GUARD_SIZE * 2)));
774 #endif
775 
776 #if defined(MEM_STATS)
777     /* This is after we have printed stats, and this may slow us down a little,      */
778     /* so we may want to simply re-initialize the mem-stats at the end of this loop. */
779     mem_stats_free(p->size);
780 #endif
781 
782     tmp = p;
783     p = p->next;
784     remove_node(tmp);
785     FREE(tmp);
786   }
787 
788   if (f != NULL)
789     delete f;
790 
791   if (totsize > 0)
792     Debug_Info("\n%lu bytes reclaimed\n", totsize);
793 
794   poolno = 0;
795   memlist = NULL;
796 #endif
797 
798 #if defined(MEM_STATS)
799   /* reinitialize the stats structure for next time through */
800   mem_stats_init();
801 #endif
802 
803 }
804 
805 
806 /****************************************************************************/
807 #if defined(MEM_RECLAIM)
808 /* Adds a new node to the 'allocated' list */
add_node(MEMNODE * node)809 static void add_node(MEMNODE *node)
810 {
811 
812 #if defined(MEM_TAG)
813   if (!mem_check_tag(node))
814     Debug_Info("add_node(): Memory pointer corrupt!\n");
815 #endif /* MEM_TAG */
816 
817   if (memlist == NULL)
818   {
819     memlist = node;
820     node->poolno = poolno;
821     node->prev = NULL;
822     node->next = NULL;
823     num_nodes = 0;
824   }
825   else
826   {
827     memlist->prev = node;
828     node->poolno = poolno;
829     node->prev = NULL;
830     node->next = memlist;
831     memlist = node;
832   }
833   num_nodes++;
834 }
835 
836 
837 /****************************************************************************/
838 /* Detatches a node from the 'allocated' list but doesn't free it */
remove_node(MEMNODE * node)839 static void remove_node(MEMNODE *node)
840 {
841 
842 #if defined(MEM_TAG)
843   if (!mem_check_tag(node))
844     Debug_Info("remove_node(): Memory pointer corrupt!\n");
845 #endif /* MEM_TAG */
846 
847   num_nodes--;
848   if (node->prev != NULL)
849     node->prev->next = node->next;
850 
851   if (node->next != NULL)
852     node->next->prev = node->prev;
853 
854   if (memlist == node)
855   {
856 #if defined(MEM_TAG)
857     /* check node->next if it is non-null, to insure it is safe to assign. */
858     /* if it is null, it is safe since it is the last in the list. */
859     if (node->next)
860       if (!mem_check_tag(node->next))
861         Debug_Info("remove_node(): memlist pointer corrupt!\n");
862 #endif /* MEM_TAG */
863 
864     memlist = node->next;
865   }
866 
867 }
868 
869 #endif /* MEM_RECLAIM */
870 
871 
872 /****************************************************************************/
873 /* A strdup routine that uses POV_MALLOC                                    */
874 /****************************************************************************/
pov_strdup(const char * s)875 char *pov_strdup(const char *s)
876 {
877   char *New;
878 
879   New=(char *)POV_MALLOC(strlen(s)+1,s);
880   strcpy(New,s);
881   return (New);
882 }
883 
884 /****************************************************************************/
885 /* A memmove routine for those systems that don't have one                  */
886 /****************************************************************************/
887 
pov_memmove(void * dest,void * src,size_t length)888 void *pov_memmove (void *dest, void  *src, size_t length)
889 {
890   char *csrc =(char *)src;
891   char *cdest=(char *)dest;
892 
893   if (csrc < cdest && csrc + length >= cdest)
894   {
895     size_t size = cdest - csrc;
896 
897     while (length > 0)
898     {
899       POV_MEMCPY(cdest + length - size, csrc + length - size, size);
900 
901       length -= size;
902 
903       if (length < size)
904         size = length;
905     }
906   }
907   /* I'm not sure if this is needed, but my docs on memcpy say the regions
908    * can't overlap, so theoretically we need to special case this.  If you
909    * don't think it's necessary, you can just comment this part out.
910    */
911   else if (cdest < csrc && cdest + length >= csrc)
912   {
913     char *new_dest = cdest;
914     size_t size = csrc - cdest;
915 
916     while (length > 0)
917     {
918       POV_MEMCPY(new_dest, csrc, length);
919 
920       new_dest += size;
921       csrc += size;
922       length -= size;
923 
924       if (length < size)
925         size = length;
926     }
927   }
928   else
929   {
930     POV_MEMCPY(cdest, csrc, length);
931   }
932 
933   return cdest;
934 }
935 
936 
937 /****************************************************************************/
938 /* Memory Statistics gathering routines                                     */
939 /****************************************************************************/
940 
941 #if defined(MEM_STATS)
942 
943 /****************************************************************************/
mem_stats_init()944 static void mem_stats_init()
945 {
946   mem_stats.smallest_alloc    = 65535;  /* Must be an unsigned number */
947   mem_stats.largest_alloc     = 0;
948   mem_stats.current_mem_usage = 0;
949   mem_stats.largest_mem_usage = 0;
950 #if (MEM_STATS>=2)
951   mem_stats.total_allocs      = 0;
952   mem_stats.total_frees       = 0;
953   mem_stats.largest_file      = "none";
954   mem_stats.largest_line      = -1;
955   mem_stats.smallest_file     = "none";
956   mem_stats.smallest_line     = -1;
957 #endif
958 }
959 
960 /****************************************************************************/
961 /* update appropriate fields when an allocation takes place                 */
mem_stats_alloc(size_t nbytes,const char * file,int line)962 static void mem_stats_alloc(size_t nbytes, const char *file, int line)
963 {
964   /* update the fields */
965   if (((int) mem_stats.smallest_alloc<0) || (nbytes<mem_stats.smallest_alloc))
966   {
967     mem_stats.smallest_alloc = nbytes;
968 #if (MEM_STATS>=2)
969     mem_stats.smallest_file = file;
970     mem_stats.smallest_line = line;
971 #endif
972   }
973 
974   if (nbytes>mem_stats.largest_alloc)
975   {
976     mem_stats.largest_alloc = nbytes;
977 #if (MEM_STATS>=2)
978     mem_stats.largest_file = file;
979     mem_stats.largest_line = line;
980 #endif
981   }
982 
983 #if (MEM_STATS>=2)
984   mem_stats.total_allocs++;
985 #endif
986 
987   mem_stats.current_mem_usage += nbytes;
988 
989   if (mem_stats.current_mem_usage>mem_stats.largest_mem_usage)
990   {
991     mem_stats.largest_mem_usage = mem_stats.current_mem_usage;
992   }
993 
994 }
995 
996 /****************************************************************************/
997 /* update appropriate fields when a free takes place                        */
mem_stats_free(size_t nbytes)998 static void mem_stats_free(size_t nbytes)
999 {
1000   /* update the fields */
1001   mem_stats.current_mem_usage -= nbytes;
1002 #if (MEM_STATS>=2)
1003   mem_stats.total_frees++;
1004 #endif
1005 }
1006 
1007 /****************************************************************************/
1008 /* Level 1                                                                  */
1009 
1010 /****************************************************************************/
mem_stats_smallest_alloc()1011 size_t mem_stats_smallest_alloc()
1012 {
1013   return mem_stats.smallest_alloc;
1014 }
1015 /****************************************************************************/
mem_stats_largest_alloc()1016 size_t mem_stats_largest_alloc()
1017 {
1018   return mem_stats.largest_alloc;
1019 }
1020 /****************************************************************************/
mem_stats_current_mem_usage()1021 size_t mem_stats_current_mem_usage()
1022 {
1023   return mem_stats.current_mem_usage;
1024 }
1025 /****************************************************************************/
mem_stats_largest_mem_usage()1026 size_t mem_stats_largest_mem_usage()
1027 {
1028   return mem_stats.largest_mem_usage;
1029 }
1030 
1031 /****************************************************************************/
1032 /* Level 2                                                                  */
1033 
1034 #if (MEM_STATS>=2)
1035 
1036 /****************************************************************************/
mem_stats_smallest_file()1037 const char *mem_stats_smallest_file()
1038 {
1039   return mem_stats.smallest_file;
1040 }
1041 /****************************************************************************/
mem_stats_smallest_line()1042 int mem_stats_smallest_line()
1043 {
1044   return mem_stats.smallest_line;
1045 }
1046 /****************************************************************************/
mem_stats_largest_file()1047 const char *mem_stats_largest_file()
1048 {
1049   return mem_stats.largest_file;
1050 }
1051 /****************************************************************************/
mem_stats_largest_line()1052 int mem_stats_largest_line()
1053 {
1054   return mem_stats.largest_line;
1055 }
1056 /****************************************************************************/
mem_stats_total_allocs()1057 long int mem_stats_total_allocs()
1058 {
1059   return mem_stats.total_allocs;
1060 }
1061 /****************************************************************************/
mem_stats_total_frees()1062 long int mem_stats_total_frees()
1063 {
1064   return mem_stats.total_frees;
1065 }
1066 
1067 #endif
1068 
1069 #endif /* MEM_STATS */
1070 
1071 END_POV_NAMESPACE
1072