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