1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  *  Management of internal temporary memory.
19  *
20  * Author:	P. Wessel, F. Wobbe
21  * Date:	1-SEPT-2013
22  * Version:	5.x
23  *
24  */
25 
26 /* Two functions called elsewhere:
27    gmt_prep_tmp_arrays	: Called wherever temporary column vectors are needed, such
28 			  as in data reading and fix_up_path and elsewhere.
29    gmtlib_free_tmp_arrays  : Called when ready to free up stuff
30    gmt_M_malloc              Memory management
31    gmt_M_memory              Memory allocation/reallocation
32    gmt_M_free                Memory deallocation
33  */
34 
35 #include "gmt_dev.h"
36 #include "gmt_internals.h"
37 
38 #ifdef HAVE_MEMALIGN
39 #	include <malloc.h>
40 #endif
41 
42 /* Local functions */
43 
44 /* To avoid lots of alloc and realloc calls we prefer to allocate a sizeable array
45  * per coordinate axes once, then use that temporary space for reading and
46  * calculations, and then alloc permanent space elsewhere and call memcpy to
47  * place the final memory there.  We assume that for most purposes we will
48  * need GMT_INITIAL_MEM_COL_ALLOC columns [2] and allocate GMT_INITIAL_MEM_ROW_ALLOC
49  * [2097152U] rows for each column.  This is 32 Mb for double precision data.
50  * These arrays are expected to hardly ever being reallocated as that would
51  * only happen for very long segments, a rare occurrence. For most typical data
52  * we may have lots of smaller segments but rarely do any segment exceed the
53  * 1048576U length initialized above.  Thus, reallocs are generally avoided.
54  * Note: (1) All columns share a single n_alloc counter and the code belows will
55  *           check whenever arrays need to be extended.
56  *	 (2) We chose to maintain a small set of column vectors rather than a single
57  *	     item since GMT tends to use columns vectors and thus the book-keeping is
58  *	     simpler and the number of columns is typically very small (2-3).
59  */
60 
gmtmemory_init_tmp_arrays(struct GMT_CTRL * GMT,int direction,size_t n_cols)61 GMT_LOCAL void gmtmemory_init_tmp_arrays (struct GMT_CTRL *GMT, int direction, size_t n_cols) {
62 	/* Initialization of GMT coordinate temp arrays - this is called at most once per GMT session  */
63 
64 	if (!GMT->hidden.mem_set) {
65 		if (n_cols == 0 && (direction == GMT_NOTSET || (GMT->current.io.record_type[direction] & GMT_READ_DATA))) n_cols = GMT_INITIAL_MEM_COL_ALLOC;	/* Allocate at least this many */
66 	}
67 	if (n_cols) {	/* Records have numerical content */
68 		size_t col;
69 		GMT->hidden.mem_coord  = gmt_M_memory (GMT, GMT->hidden.mem_coord, n_cols, double *);	/* These are all NULL */
70 		GMT->hidden.mem_cols = n_cols;	/* How many columns we have initialized */
71 		for (col = 0; col < n_cols; col++)	/* For each column, reallocate space for n_rows */
72 			GMT->hidden.mem_coord[col] = gmt_M_memory (GMT, NULL, GMT_INITIAL_MEM_ROW_ALLOC, double);
73 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT memory: Initialize %" PRIuS " temporary column double arrays, each of length : %" PRIuS "\n", GMT->hidden.mem_cols, GMT->hidden.mem_rows);
74 		GMT->hidden.mem_rows = GMT_INITIAL_MEM_ROW_ALLOC;
75 	}
76 	if (direction != GMT_NOTSET && GMT->current.io.record_type[direction] & GMT_READ_TEXT) {	/* For text or mixed records */
77 		GMT->hidden.mem_txt = gmt_M_memory (GMT, NULL, GMT_INITIAL_MEM_ROW_ALLOC, char *);
78 		GMT->hidden.mem_rows = GMT_INITIAL_MEM_ROW_ALLOC;
79 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT memory: Initialize a temporary column char * array of length : %" PRIuS "\n", GMT->hidden.mem_rows);
80 	}
81 	GMT->hidden.mem_set = true;
82 }
83 
gmtmemory_die_if_memfail(struct GMT_CTRL * GMT,size_t nelem,size_t size,const char * where)84 GMT_LOCAL void gmtmemory_die_if_memfail (struct GMT_CTRL *GMT, size_t nelem, size_t size, const char *where) {
85 	/* Handle reporting and aborting if memory allocation fails */
86 	double mem = ((double)nelem) * ((double)size);
87 	unsigned int k = 0;
88 	static char *m_unit[4] = {"bytes", "kb", "Mb", "Gb"};
89 	while (mem >= 1024.0 && k < 3) mem /= 1024.0, k++;
90 	gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "Could not reallocate memory [%.2f %s, %" PRIuS " items of %" PRIuS " bytes]\n", mem, m_unit[k], nelem, size);
91 #ifdef DEBUG
92 	gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "gmt_M_memory [realloc] called\n");
93 #endif
94 }
95 
96 #ifdef MEMDEBUG
97 /* Memory tracking used to assist in finding memory leaks.  We internally keep track
98  * of all memory allocated by gmt_M_memory and subsequently freed with gmt_M_free.  If
99  * upon exit there are unreleased memory we issue a report of how many items were
100  * not freed and where they were first allocated.  This is only used by the developers
101  * and if -DMEMDEBUG is not set then all of this is left out.
102  * The environmental variable GMT_TRACK_MEMORY controls the memory tracking:
103  * a. Unset GMT_TRACK_MEMORY or set to '0' to deactivate the memory tracking.
104  * b. Set GMT_TRACK_MEMORY to 1 (or any but 0) to activate the memory tracking.
105  * c. Set GMT_TRACK_MEMORY to 2 to activate the memory tracking and to write a
106  *    detailed log of all transactions taking place during a session to the file
107  *    gmt_memtrack_<pid>.log
108  *
109  * Paul Wessel, Latest revision June 2012.
110  * Florian Wobbe, Latest revision August 2013.
111  * Splay tree manipulation functions are modified after Sleator and Tarjan, 1985:
112  * Self-adjusting binary search trees. JACM, 32(3), doi:10.1145/3828.3835 */
113 
gmtmemory_memtrack_mem(size_t mem,unsigned int * unit)114 static inline double gmtmemory_memtrack_mem (size_t mem, unsigned int *unit) {
115 	/* Report the memory in the chosen unit */
116 	unsigned int k = 0;
117 	double val = mem / 1024.0;	/* Kb */
118 	if (val > 1024.0) {val /= 1024.0; k++;}	/* Now in Mb */
119 	if (val > 1024.0) {val /= 1024.0; k++;}	/* Now in Gb */
120 	*unit = k;
121 	return (val);
122 }
123 
gmt_memtrack_init(struct GMT_CTRL * GMT)124 int gmt_memtrack_init (struct GMT_CTRL *GMT) {
125 	/* Called in gmt_begin() */
126 	time_t now = time (NULL);
127 	char *env = getenv ("GMT_TRACK_MEMORY"); /* 0: off; any: track; 2: log to file */
128 	struct MEMORY_TRACKER *M = calloc (1, sizeof (struct MEMORY_TRACKER));
129 	GMT->hidden.mem_keeper = M;
130 	M->active = ( env && strncmp (env, "0", 1) != 0 ); /* track if GMT_TRACK_MEMORY != 0 */
131 	M->do_log = ( env && strncmp (env, "2", 1) == 0 ); /* log if GMT_TRACK_MEMORY == 2 */
132 	if (M->active) {
133 		size_t ID;
134 		ID = atoi (env);
135 		if (ID > 2) M->find = ID;
136 	}
137 	M->search = true;
138 	if (!M->do_log) /* Logging not requested */ {
139 		if (M->active) M->fp = stderr;
140 		return GMT_OK;
141 	}
142 	else
143 	{
144 		int pid = getpid();
145 		char logfile[GMT_LEN32];
146 		snprintf (logfile, GMT_LEN32, "gmt_memtrack_%d.log", pid);
147 		if ((M->fp = fopen (logfile, "w")) == NULL) {
148 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not create log file gmt_memtrack_%d.log\n", pid);
149 			M->active = 0;
150 			return GMT_ERROR_ON_FOPEN;
151 		}
152 	}
153 	fprintf (M->fp, "# %s", ctime (&now));
154 	fprintf (M->fp, "#           addr     size_b total_k module line/function\n");
155 	return (GMT_OK);
156 }
157 
gmtmemory_treesplay(struct MEMORY_ITEM * t,void * addr)158 static inline struct MEMORY_ITEM *gmtmemory_treesplay (struct MEMORY_ITEM *t, void *addr) {
159 	/* Simple top-down splay, not requiring addr to be in the tree t.
160 	 * This function searches for an item with key addr in the tree rooted at t. If
161 	 * it's there, it is splayed to the root. If it isn't there, then the node put
162 	 * at the root is the last one before NULL that would have been reached in
163 	 * a normal binary search for addr. (It's a neighbor of addr in the tree.) */
164 	struct MEMORY_ITEM N, *l, *r, *y;
165 
166 	if (t == NULL) return t;
167 	N.l = N.r = NULL;
168 	l = r = &N;
169 
170 	for (;;) {
171 		if (addr < t->ptr) {
172 			if (t->l == NULL) break;
173 			if (addr < t->l->ptr) {
174 				y = t->l; /* rotate right */
175 				t->l = y->r;
176 				y->r = t;
177 				t = y;
178 				if (t->l == NULL) break;
179 			}
180 			r->l = t;   /* link right */
181 			r = t;
182 			t = t->l;
183 		}
184 		else if (addr > t->ptr) {
185 			if (t->r == NULL) break;
186 			if (addr > t->r->ptr) {
187 				y = t->r; /* rotate left */
188 				t->r = y->l;
189 				y->l = t;
190 				t = y;
191 				if (t->r == NULL) break;
192 			}
193 			l->r = t;   /* link left */
194 			l = t;
195 			t = t->r;
196 		}
197 		else
198 			break;
199 	}
200 	l->r = t->l;    /* assemble */
201 	r->l = t->r;
202 	t->l = N.r;
203 	t->r = N.l;
204 	return t;
205 }
206 
gmtmemory_treeinsert(struct MEMORY_ITEM * t,void * addr)207 static inline struct MEMORY_ITEM *gmtmemory_treeinsert (struct MEMORY_ITEM *t, void *addr) {
208 	/* Insert addr into the tree t, unless it's already there.
209 	 * Return a pointer to the resulting tree. */
210 	struct MEMORY_ITEM *new = calloc (1, sizeof (struct MEMORY_ITEM));
211 
212 	new->ptr = addr;
213 	if (t == NULL)
214 		return new;
215 	t = gmtmemory_treesplay (t, addr);
216 	if (addr < t->ptr) {
217 		new->l = t->l;
218 		new->r = t;
219 		t->l = NULL;
220 		return new;
221 	} else if (addr > t->ptr) {
222 		new->r = t->r;
223 		new->l = t;
224 		t->r = NULL;
225 		return new;
226 	} else {
227 		/* We get here if addr is already in the tree. Don't add it again. */
228 		gmt_M_str_free (new);
229 		return t;
230 	}
231 }
232 
gmtmemory_treefind(struct MEMORY_ITEM ** t,void * addr)233 static inline struct MEMORY_ITEM *gmtmemory_treefind (struct MEMORY_ITEM **t, void *addr) {
234 	/* Splay item with addr to the root and update tree pointer t. Return
235 	 * pointer to the resulting tree. If it isn't there, then return NULL. */
236 	struct MEMORY_ITEM *x = *t;
237 	x = gmtmemory_treesplay (x, addr);
238 	*t = x;
239 	return (x != NULL && x->ptr == addr) ? x : NULL;
240 }
241 
gmtmemory_treedelete(struct MEMORY_ITEM * t,void * addr)242 static inline struct MEMORY_ITEM *gmtmemory_treedelete (struct MEMORY_ITEM *t, void *addr) {
243 	/* Delete addr from the tree t, if it's there.
244 	 * Return a pointer to the resulting tree. */
245 	struct MEMORY_ITEM *x;
246 
247 	if (t==NULL) return NULL;
248 	t = gmtmemory_treesplay(t, addr);
249 	if (addr == t->ptr) { /* found it */
250 		if (t->l == NULL) {
251 			x = t->r;
252 		} else {
253 			x = gmtmemory_treesplay (t->l, addr);
254 			x->r = t->r;
255 		}
256 		gmt_M_str_free (t->name);
257 		gmt_M_str_free (t);
258 		return x;
259 	}
260 	return t; /* It wasn't there */
261 }
262 
gmtmemory_treedestroy(struct MEMORY_ITEM ** t)263 static inline void gmtmemory_treedestroy (struct MEMORY_ITEM **t) {
264 	/* Removes all items from the tree rooted at t. */
265 	struct MEMORY_ITEM *x = *t;
266 	if (x != NULL) {
267 		gmtmemory_treedestroy (&x->l);
268 		gmtmemory_treedestroy (&x->r);
269 		gmt_M_str_free (x->name);
270 		gmt_M_str_free (x);
271 		*t = NULL;
272 	}
273 }
274 
gmtmemory_memtrack_add(struct GMT_CTRL * GMT,const char * where,void * ptr,void * prev_ptr,size_t size)275 static inline void gmtmemory_memtrack_add (struct GMT_CTRL *GMT, const char *where, void *ptr, void *prev_ptr, size_t size) {
276 	/* Called from gmt_M_memory to update current list of memory allocated */
277 	size_t old, diff;
278 	void *use = NULL;
279 	struct MEMORY_ITEM *entry = NULL;
280 	static const char *mode[3] = {"INI", "ADD", "SET"};
281 	int kind;
282 	struct MEMORY_TRACKER *M = GMT->hidden.mem_keeper;
283 
284 	use = (prev_ptr) ? prev_ptr : ptr;
285 	entry = (M->search) ? gmtmemory_treefind (&M->root, use) : NULL;
286 	if (!entry) { /* Not found, must insert new_entry entry at end */
287 		entry = gmtmemory_treeinsert (M->root, use);
288 		entry->name = strdup (where);
289 		old = 0;
290 		M->n_ptr++;
291 		M->n_allocated++;
292 		kind = 0;
293 	}
294 	else {	/* Found existing pointer, get its previous size */
295 		old = entry->size;
296 		if (entry->ptr != ptr) {	/* Must delete and add back since the address changed */
297 			char *name = entry->name; /* remember pointer of name */
298 			entry->name = NULL; /* prevent pointer from being freed in gmtmemory_treedelete */
299 			entry = gmtmemory_treedelete (entry, entry->ptr);
300 			entry = gmtmemory_treeinsert (entry, ptr);
301 			entry->name = name; /* put name back */
302 		}
303 		M->n_reallocated++;
304 		kind = 1;
305 	}
306 	if (M->find && M->find == M->n_ID) {	/* All code to stop here if set in ddd */
307 		/* The item you are looking for is being allocated now */
308 		int found = 1;	/* Add a debug stop point here and then examine where you are */
309 		found ++;	/* Just to keep compiler happy */
310 	}
311 	entry->ID = M->n_ID++;
312 
313 	if (old > size) {	/* Reduction in memory */
314 		kind = 2;
315 		diff = old - size;	/* Change in memory */
316 		if (diff > M->current) {
317 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Memory tracker reports < 0 bytes allocated!\n");
318 			M->current = 0;	/* Cannot have negative in size_t */
319 		}
320 		else
321 			M->current -= diff;	/* Revised memory tally */
322 	}
323 	else {			/* Addition in memory */
324 		diff = size - old;	/* Change in memory */
325 		M->current += diff;	/* Revised memory tally */
326 	}
327 
328 	entry->size = size;
329 	if (M->do_log)
330 		fprintf (M->fp, "%s: 0x%zx %10" PRIuS " %7.0lf %s %s\n", mode[kind], (size_t)entry->ptr, entry->size, M->current / 1024.0, GMT->init.module_name, entry->name);
331 	if (M->current > M->maximum) M->maximum = M->current;	/* Update total allocation */
332 	if (size > M->largest) M->largest = size;		/* Update largest single item */
333 	M->root = entry; /* Update root pointer */
334 }
335 
gmtmemory_memtrack_sub(struct GMT_CTRL * GMT,const char * where,void * ptr)336 static inline bool gmtmemory_memtrack_sub (struct GMT_CTRL *GMT, const char *where, void *ptr) {
337 	/* Called from gmt_M_free to remove memory pointer */
338 	struct MEMORY_TRACKER *M = GMT->hidden.mem_keeper;
339 	struct MEMORY_ITEM *entry = gmtmemory_treefind (&M->root, ptr);
340 
341 	M->n_freed++; /* Increment first to also count multiple frees on same address */
342 	if (!entry) {
343 		/* Error, trying to free something not allocated by gmt_memory_func */
344 		gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "Wrongly tries to free item\n");
345 		if (M->do_log)
346 			fprintf (M->fp, "!!!: 0x%zx ---------- %7.0lf %s @%s\n", (size_t)ptr, M->current / 1024.0, GMT->init.module_name, where);
347 		return false; /* Notify calling function that something went wrong */
348 	}
349 	if (entry->size > M->current) {
350 		gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "Memory tracker reports < 0 bytes allocated!\n");
351 		M->current = 0;
352 	}
353 	else
354 		M->current -= entry->size;	/* "Free" the memory */
355 	if (M->do_log)
356 		fprintf (M->fp, "DEL: 0x%zx %10" PRIuS " %7.0lf %s %s\n", (size_t)entry->ptr, entry->size, M->current / 1024.0, GMT->init.module_name, entry->name);
357 	M->root = gmtmemory_treedelete (entry, entry->ptr);
358 	M->n_ptr--;
359 	return true;
360 }
361 
gmtmemory_treereport(struct GMT_CTRL * GMT,struct MEMORY_ITEM * x)362 static inline void gmtmemory_treereport (struct GMT_CTRL *GMT, struct MEMORY_ITEM *x) {
363 	unsigned int u;
364 	char *unit[3] = {"kb", "Mb", "Gb"};
365 	double size = gmtmemory_memtrack_mem (x->size, &u);
366 	struct MEMORY_TRACKER *M = GMT->hidden.mem_keeper;
367 	GMT_Report (GMT->parent, GMT_MSG_WARNING, "Memory not freed first allocated in %s (ID = %" PRIuS "): %.3f %s [%" PRIuS " bytes]\n", x->name, x->ID, size, unit[u], x->size);
368 	if (M->do_log)
369 		fprintf (M->fp, "# Memory not freed first allocated in %s (ID = %" PRIuS "): %.3f %s [%"
370 						 PRIuS " bytes]\n", x->name, x->ID, size, unit[u], x->size);
371 }
372 
gmtmemory_treeprint(struct GMT_CTRL * GMT,struct MEMORY_ITEM * t)373 static inline void gmtmemory_treeprint (struct GMT_CTRL *GMT, struct MEMORY_ITEM *t) {
374 	if (t != NULL) {
375 		gmtmemory_treeprint (GMT, t->l);
376 		gmtmemory_treereport (GMT, t);
377 		gmtmemory_treeprint (GMT, t->r);
378 	}
379 }
380 
gmt_memtrack_report(struct GMT_CTRL * GMT)381 void gmt_memtrack_report (struct GMT_CTRL *GMT) {
382 	/* Called at end of gmt_end() */
383 	unsigned int u, level;
384 	uint64_t excess = 0, n_multi_frees = 0;
385 	double size;
386 	char *unit[3] = {"kb", "Mb", "Gb"};
387 	struct MEMORY_TRACKER *M = GMT->hidden.mem_keeper;
388 
389 	if (!M->active) return;	/* Not activated */
390 	if (M->n_allocated > M->n_freed)
391 		excess = M->n_allocated - M->n_freed;
392 	else if (M->n_freed > M->n_allocated)
393 		n_multi_frees = M->n_freed - M->n_allocated;
394 	/* Only insist on report if a leak or multi free, otherwise requires -Vd: */
395 	level = (excess || n_multi_frees) ? GMT_MSG_WARNING : GMT_MSG_DEBUG;
396 	size = gmtmemory_memtrack_mem (M->maximum, &u);
397 	GMT_Report (GMT->parent, level, "Max total memory allocated was %.3f %s [%" PRIuS " bytes]\n",
398 							size, unit[u], M->maximum);
399 		if (M->do_log)
400 			fprintf (M->fp, "# Max total memory allocated was %.3f %s [%"
401 							 PRIuS " bytes]\n", size, unit[u], M->maximum);
402 	size = gmtmemory_memtrack_mem (M->largest, &u);
403 	GMT_Report (GMT->parent, level, "Single largest allocation was %.3f %s [%" PRIuS " bytes]\n", size, unit[u], M->largest);
404 		if (M->do_log)
405 			fprintf (M->fp, "# Single largest allocation was %.3f %s [%"
406 							 PRIuS " bytes]\n", size, unit[u], M->largest);
407 	if (M->current) {
408 		size = gmtmemory_memtrack_mem (M->current, &u);
409 		GMT_Report (GMT->parent, level, "MEMORY NOT FREED: %.3f %s [%" PRIuS " bytes]\n",
410 								size, unit[u], M->current);
411 		if (M->do_log)
412 			fprintf (M->fp, "# MEMORY NOT FREED: %.3f %s [%" PRIuS " bytes]\n",
413 							 size, unit[u], M->current);
414 	}
415 	GMT_Report (GMT->parent, level, "Items allocated: %" PRIu64 " reallocated: %" PRIu64 " freed: %" PRIu64 "\n", M->n_allocated, M->n_reallocated, M->n_freed);
416 	if (M->do_log)
417 		fprintf (M->fp, "# Items allocated: %" PRIu64 " reallocated: %" PRIu64 " freed: %"
418 						 PRIu64 "\n", M->n_allocated, M->n_reallocated, M->n_freed);
419 	if (M->n_freed > M->n_allocated) {
420 		uint64_t n_multi_frees = M->n_freed - M->n_allocated;
421 		GMT_Report (GMT->parent, level, "Items FREED MULTIPLE TIMES: %" PRIu64 "\n", n_multi_frees);
422 		if (M->do_log)
423 			fprintf (M->fp, "# Items FREED MULTIPLE TIMES: %" PRIu64 "\n", n_multi_frees);
424 	}
425 	if (excess) {
426 		GMT_Report (GMT->parent, level, "Items NOT PROPERLY FREED: %" PRIu64 "\n", excess);
427 		if (M->do_log)
428 			fprintf (M->fp, "# Items NOT PROPERLY FREED: %" PRIu64 "\n", excess);
429 	}
430 	gmtmemory_treeprint (GMT, M->root);
431 	gmtmemory_treedestroy (&M->root); /* Remove remaining items from tree if any */
432 
433 	if (M->do_log) {
434 		time_t now = time (NULL);
435 		fprintf (M->fp, "# %s", ctime (&now));
436 		fclose (M->fp);
437 	}
438 }
439 #endif
440 
gmtlib_free_tmp_arrays(struct GMT_CTRL * GMT)441 void gmtlib_free_tmp_arrays (struct GMT_CTRL *GMT) {
442 	/* Free temporary coordinate memory used by this session */
443 	size_t col;
444 
445 	if (GMT->hidden.mem_cols) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT memory: Free %" PRIuS " temporary column arrays, each of length : %" PRIuS "\n", GMT->hidden.mem_cols, GMT->hidden.mem_rows);
446 	for (col = 0; col < GMT->hidden.mem_cols; col++) {	/* For each column, free an array */
447 		gmt_M_free (GMT, GMT->hidden.mem_coord[col]);
448 	}
449 	if (GMT->hidden.mem_coord)
450 		gmt_M_free (GMT, GMT->hidden.mem_coord);
451 	if (GMT->hidden.mem_txt)
452 		gmt_M_free (GMT, GMT->hidden.mem_txt);
453 	GMT->hidden.mem_rows = GMT->hidden.mem_cols = 0;
454 	GMT->hidden.mem_set = false;	/* Back to where we started */
455 }
456 
gmt_prep_tmp_arrays(struct GMT_CTRL * GMT,int direction,size_t row,size_t n_cols)457 void gmt_prep_tmp_arrays (struct GMT_CTRL *GMT, int direction, size_t row, size_t n_cols) {
458 	size_t col;
459 
460 	/* Check if this is the very first time, if so we initialize the arrays */
461 	if (!GMT->hidden.mem_set)
462 		gmtmemory_init_tmp_arrays (GMT, direction, n_cols);	/* First time we get here */
463 
464 	/* Check if we are exceeding our column count so far, if so we must allocate more columns */
465 	else if (n_cols > GMT->hidden.mem_cols) {	/* Must allocate more columns, this is expected to happen rarely */
466 		GMT->hidden.mem_coord = gmt_M_memory (GMT, GMT->hidden.mem_coord, n_cols, double *);	/* New ones are NOT NULL */
467 		for (col = GMT->hidden.mem_cols; col < n_cols; col++)	/* Explicitly allocate the new additions */
468 			GMT->hidden.mem_coord[col] = gmt_M_memory (GMT, NULL, GMT->hidden.mem_rows, double);
469 		GMT->hidden.mem_cols = n_cols;		/* Updated column count */
470 	}
471 
472 	/* Check if we are exceeding our allocated count for this column.  If so allocate more rows */
473 
474 	if (row < GMT->hidden.mem_rows) return;	/* Nothing to do */
475 
476 	/* Here we must allocate more rows, this is expected to happen rarely given the large initial allocation */
477 
478 	while (row >= GMT->hidden.mem_rows) GMT->hidden.mem_rows = (size_t)lrint (1.5 * GMT->hidden.mem_rows);	/* Increase by 50% */
479 	for (col = 0; col < GMT->hidden.mem_cols; col++)	/* Add more memory via realloc */
480 		GMT->hidden.mem_coord[col] = gmt_M_memory (GMT, GMT->hidden.mem_coord[col], GMT->hidden.mem_rows, double);
481 	if (direction != GMT_NOTSET && GMT->current.io.record_type[direction] & GMT_READ_TEXT)
482 		GMT->hidden.mem_txt = gmt_M_memory (GMT, GMT->hidden.mem_txt, GMT->hidden.mem_rows, char *);
483 
484 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT memory: Increase %" PRIuS " temporary column arrays to new length : %" PRIuS "\n", GMT->hidden.mem_cols, GMT->hidden.mem_rows);
485 	/* Note: Any additions to these arrays are not guaranteed to be set to zero */
486 }
487 
gmt_memory_func(struct GMT_CTRL * GMT,void * prev_addr,size_t nelem,size_t size,bool align,const char * where)488 void *gmt_memory_func (struct GMT_CTRL *GMT, void *prev_addr, size_t nelem, size_t size, bool align, const char *where) {
489 	/* Multi-functional memory allocation subroutine.
490 	   If prev_addr is NULL, allocate new memory of nelem elements of size bytes.
491 		Ignore when nelem == 0.
492 	   If prev_addr exists, reallocate the memory to a larger or smaller chunk of nelem elements of size bytes.
493 		When nelem = 0, free the memory.
494 	   If align is true we seek to get aligned memory.
495 	*/
496 
497 	void *tmp = NULL;
498 
499 	if (nelem == SIZE_MAX) {	/* Probably 32-bit overflow */
500 		gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "Requesting SIZE_MAX number of items (%" PRIuS ") - exceeding 32-bit counting?\n", nelem);
501 #ifdef DEBUG
502 		gmtlib_report_func (GMT, GMT_MSG_WARNING, where, "gmt_M_memory called\n");
503 #endif
504 		GMT->parent->error = GMT_MEMORY_ERROR; return NULL;
505 	}
506 
507 #if defined(WIN32) && !defined(USE_MEM_ALIGNED)
508 	align = false;	/* Turn off alignment for Windows if not specifically selected via USE_MEM_ALIGNED */
509 #endif
510 
511 	if (prev_addr) {
512 		if (nelem == 0) { /* Take care of n == 0 */
513 			gmt_M_free (GMT, prev_addr);
514 			return (NULL);
515 		}
516 		if (align) {
517 #ifdef HAVE_FFTW3F
518 			tmp = NULL; /* currently defunct */
519 #elif defined(WIN32) || defined(USE_MEM_ALIGNED)
520 			tmp = _aligned_realloc ( prev_addr, nelem * size, 16U);
521 #elif defined(HAVE_POSIX_MEMALIGN)
522 			tmp = NULL; /* currently defunct */
523 #elif defined(HAVE_MEMALIGN)
524 			tmp = NULL; /* currently defunct */
525 #else
526 #			error "missing memalign"
527 #endif
528 		}
529 		else
530 			tmp = realloc ( prev_addr, nelem * size);
531 		if (tmp == NULL) {
532 			gmtmemory_die_if_memfail (GMT, nelem, size, where);
533 			return (NULL);
534 		}
535 	}
536 	else {
537 		if (nelem == 0) return (NULL); /* Take care of n == 0 */
538 		if (align) {
539 #ifdef HAVE_FFTW3F
540 			tmp = fftwf_malloc (nelem * size);
541 #elif defined(WIN32) || defined(USE_MEM_ALIGNED)
542 			tmp = _aligned_malloc (nelem * size, 16U);
543 #elif defined(HAVE_POSIX_MEMALIGN)
544 			(void)posix_memalign (&tmp, 16U, nelem * size);
545 #elif defined(HAVE_MEMALIGN)
546 			tmp = memalign (16U, nelem * size);
547 #else
548 #			error "missing memalign"
549 #endif
550 			if (tmp != NULL)
551 				tmp = memset (tmp, 0, nelem * size);
552 		}
553 		else
554 			tmp = calloc (nelem, size);
555 		if (tmp == NULL) {
556 			gmtmemory_die_if_memfail (GMT, nelem, size, where);
557 			return (NULL);
558 		}
559 	}
560 
561 #ifdef MEMDEBUG
562 	if (GMT->hidden.mem_keeper->active)
563 		gmtmemory_memtrack_add (GMT, where, tmp, prev_addr, nelem * size);
564 #endif
565 	return (tmp);
566 }
567 
gmt_free_func(struct GMT_CTRL * GMT,void * addr,bool align,const char * where)568 void gmt_free_func (struct GMT_CTRL *GMT, void *addr, bool align, const char *where) {
569 	if (addr == NULL) {
570 #ifdef DEBUG
571 		/* Report attempt at freeing unallocated memory only in level GMT_MSG_DEBUG (-V4) */
572 		gmtlib_report_func (GMT, GMT_MSG_DEBUG, where,
573 				"tried to free unallocated memory\n");
574 #endif
575 		return; /* Do not free a NULL pointer, although allowed */
576 	}
577 
578 #ifdef MEMDEBUG
579 	if (GMT->hidden.mem_keeper && GMT->hidden.mem_keeper->active) {
580 		bool is_safe_to_free = gmtmemory_memtrack_sub (GMT, where, addr);
581 		if (is_safe_to_free == false)
582 			return; /* Address addr was not allocated by gmt_memory_func before */
583 	}
584 #endif
585 
586 #if defined(WIN32) && !defined(USE_MEM_ALIGNED)
587 	align = false;	/* Turn off alignment for Windows if not specifically selected via USE_MEM_ALIGNED */
588 #endif
589 
590 	if (align) {	/* Must free aligned memory */
591 #ifdef HAVE_FFTW3F
592 		fftwf_free (addr);
593 #elif defined(WIN32) && defined(USE_MEM_ALIGNED)
594 		_aligned_free (addr);
595 #else
596 		free (addr);
597 #endif
598 	}
599 	else
600 		free (addr);
601 	addr = NULL;
602 }
603 
gmt_malloc_func(struct GMT_CTRL * GMT,void * ptr,size_t n,size_t * n_alloc,size_t element_size,const char * where)604 void *gmt_malloc_func (struct GMT_CTRL *GMT, void *ptr, size_t n, size_t *n_alloc, size_t element_size, const char *where) {
605 	/* gmt_M_malloc is used to initialize, grow, and finalize an array allocation in cases
606 	 * were more memory is needed as new data are read.  There are three different situations:
607 	 * A) Initial allocation of memory:
608 	 *	  Signaled by passing *n_alloc == 0 or n_alloc = NULL or ptr = NULL.
609 	 *    This will initialize the pointer to NULL first.
610 	 *	  Allocation size is controlled by GMT->session.min_meminc, unless n > 0 which then is used.
611 	 *	  If n_alloc == NULL then we also do not need to rreturn back the n_alloc value set herein.
612 	 * B) Incremental increase in memory:
613 	 *	  Signaled by passing n >= n_alloc.
614 	 *    The incremental memory is set to 50% of the
615 	 *	  previous size, but no more than GMT->session.max_meminc. Note, *ptr[n] is the location
616 	 *	  of where the next assignment will take place, hence n >= n_alloc is used.
617 	 * C) Finalize memory:
618 	 *	  Signaled by passing n == 0 and n_alloc > 0.
619 	 *    Unused memory beyond n_alloc is freed up.
620 	 * You can use gmt_set_meminc to temporarily change GMT_min_mininc and gmt_reset_meminc will
621 	 * reset this value to the compilation default.
622 	 * For 32-bit systems there are safety-values to avoid 32-bit overflow.
623 	 * Note that n_alloc refers to the number of items to allocate, not the total memory taken
624 	 * up by the allocated items (which is n_alloc * element_size).
625 	 * module is the name of the module requesting the memory (main program or library function).
626 	 * Note: This memory, used for all kinds of things, is not requested to be aligned (align = false),
627 	 */
628 	size_t in_n_alloc = (n_alloc) ? *n_alloc : 0U;	/* If NULL it means init, i.e. 0, and we don't pass n_alloc back out */
629 	if (in_n_alloc == 0 || !ptr) {	/* A) First time allocation, use default minimum size, unless n > 0 is given */
630 		in_n_alloc = (n == 0) ? GMT->session.min_meminc : n;
631 		ptr = NULL;	/* Initialize a new pointer to NULL before calling gmt_M_memory with it */
632 	}
633 	else if (n == 0 && in_n_alloc > 0)	/* C) Final allocation, set to actual final size */
634 		n = in_n_alloc;		/* Keep the given n_alloc */
635 	else if (n < in_n_alloc)	/* Nothing to do, already has enough memory.  This is a safety valve. */
636 		return (ptr);
637 	else {		/* B) n >= n_alloc: Compute an increment, but make sure not to exceed int limit under 32-bit systems */
638 		size_t add;	/* The increment of memory (in items) */
639 		add = MAX (GMT->session.min_meminc, MIN (*n_alloc/2, GMT->session.max_meminc));	/* Suggested increment from 50% rule, but no less than GMT->session.min_meminc */
640 		if (add < SIZE_MAX - in_n_alloc) /* test if addition of add and in_n_alloc is safe */
641 			/* add + in_n_alloc will not overflow */
642 			in_n_alloc = add + in_n_alloc;
643 		if (n >= in_n_alloc) in_n_alloc = n + 1;	/* If still not big enough, set n_alloc to n + 1 */
644 	}
645 
646 	/* Here n_alloc is set one way or another.  Do the actual [re]allocation for non-aligned memory */
647 
648 	ptr = gmt_memory_func (GMT, ptr, in_n_alloc, element_size, false, where);
649 	if (n_alloc) *n_alloc = in_n_alloc;	/* Pass allocated count back out unless given NULL */
650 
651 	return (ptr);
652 }
653 
gmt_this_alloc_level(struct GMT_CTRL * GMT,unsigned int alloc_level)654 bool gmt_this_alloc_level (struct GMT_CTRL *GMT, unsigned int alloc_level) {
655 	/* Returns true if the tested alloc_level matches the current function level */
656 	return (alloc_level == GMT->hidden.func_level);
657 }
658 
659 #ifdef FISH_STRDUP_LEAKS
gmt_strdup(struct GMT_CTRL * GMT,const char * s)660 char *gmt_strdup(struct GMT_CTRL *GMT, const char *s) {
661 	char *p = gmt_M_memory(GMT, NULL, strlen(s) + 1, unsigned char);
662 	if (p) { strcpy(p, s); }
663 	return p;
664 }
665 #endif
666 
gmt_set_meminc(struct GMT_CTRL * GMT,size_t increment)667 void gmt_set_meminc (struct GMT_CTRL *GMT, size_t increment) {
668 	/* Temporarily set the GMT_min_memic to this value; restore with gmt_reset_meminc */
669 	GMT->session.min_meminc = increment;
670 }
671 
gmt_reset_meminc(struct GMT_CTRL * GMT)672 void gmt_reset_meminc (struct GMT_CTRL *GMT) {
673 	/* Temporarily set the GMT_min_memic to this value; restore with gmt_reset_meminc */
674 	GMT->session.min_meminc = GMT_MIN_MEMINC;
675 }
676