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