/* ========================================================================== */ /* === Core/cholmod_memory ================================================== */ /* ========================================================================== */ /* ----------------------------------------------------------------------------- * CHOLMOD/Core Module. Copyright (C) 2005-2006, * Univ. of Florida. Author: Timothy A. Davis * The CHOLMOD/Core Module is licensed under Version 2.1 of the GNU * Lesser General Public License. See lesser.txt for a text of the license. * CHOLMOD is also available under other licenses; contact authors for details. * http://www.cise.ufl.edu/research/sparse * -------------------------------------------------------------------------- */ /* Core memory management routines: * * Primary routines: * ----------------- * cholmod_malloc malloc wrapper * cholmod_free free wrapper * * Secondary routines: * ------------------- * cholmod_calloc calloc wrapper * cholmod_realloc realloc wrapper * cholmod_realloc_multiple realloc wrapper for multiple objects * * The user may make use of these, just like malloc and free. You can even * malloc an object and safely free it with cholmod_free, and visa versa * (except that the memory usage statistics will be corrupted). These routines * do differ from malloc and free. If cholmod_free is given a NULL pointer, * for example, it does nothing (unlike the ANSI free). cholmod_realloc does * not return NULL if given a non-NULL pointer and a nonzero size, even if it * fails (it sets an error code in Common->status instead). * * CHOLMOD keeps track of the amount of memory it has allocated, and so the * cholmod_free routine includes as a parameter the size of the object being * freed. This is only used for memory usage statistics, which are very useful * in finding memory leaks in your program. If you, the user of CHOLMOD, pass * the wrong size, the only consequence is that the memory usage statistics * will be invalid. This will causes assertions to fail if CHOLMOD is * compiled with debugging enabled, but otherwise it will cause no errors. * * The cholmod_free_* routines for each CHOLMOD object keep track of the size * of the blocks they free, so they do not require you to pass their sizes * as a parameter. * * If a block of size zero is requested, these routines allocate a block of * size one instead. */ #include "cholmod_internal.h" #include "cholmod_core.h" /* ========================================================================== */ /* === cholmod_add_size_t =================================================== */ /* ========================================================================== */ /* Safely compute a+b, and check for integer overflow. If overflow occurs, * return 0 and set OK to FALSE. Also return 0 if OK is FALSE on input. */ size_t CHOLMOD(add_size_t) (size_t a, size_t b, int *ok) { size_t s = a + b ; (*ok) = (*ok) && (s >= a) ; return ((*ok) ? s : 0) ; } /* ========================================================================== */ /* === cholmod_mult_size_t ================================================== */ /* ========================================================================== */ /* Safely compute a*k, where k should be small, and check for integer overflow. * If overflow occurs, return 0 and set OK to FALSE. Also return 0 if OK is * FALSE on input. */ size_t CHOLMOD(mult_size_t) (size_t a, size_t k, int *ok) { size_t p = 0, s ; while (*ok) { if (k % 2) { p = p + a ; (*ok) = (*ok) && (p >= a) ; } k = k / 2 ; if (!k) return (p) ; s = a + a ; (*ok) = (*ok) && (s >= a) ; a = s ; } return (0) ; } /* ========================================================================== */ /* === cholmod_malloc ======================================================= */ /* ========================================================================== */ /* Wrapper around malloc routine. Allocates space of size MAX(1,n)*size, where * size is normally a sizeof (...). * * This routine, cholmod_calloc, and cholmod_realloc do not set Common->status * to CHOLMOD_OK on success, so that a sequence of cholmod_malloc's, _calloc's, * or _realloc's can be used. If any of them fails, the Common->status will * hold the most recent error status. * * Usage, for a pointer to int: * * p = cholmod_malloc (n, sizeof (int), Common) * * Uses a pointer to the malloc routine (or its equivalent) defined in Common. */ void *CHOLMOD(malloc) /* returns pointer to the newly malloc'd block */ ( /* ---- input ---- */ size_t n, /* number of items */ size_t size, /* size of each item */ /* --------------- */ cholmod_common *Common ) { void *p ; size_t s ; int ok = TRUE ; RETURN_IF_NULL_COMMON (NULL) ; if (size == 0) { ERROR (CHOLMOD_INVALID, "sizeof(item) must be > 0") ; p = NULL ; } else if (n >= (Size_max / size) || n >= Int_max) { /* object is too big to allocate without causing integer overflow */ ERROR (CHOLMOD_TOO_LARGE, "problem too large") ; p = NULL ; } else { /* call malloc, or its equivalent */ s = CHOLMOD(mult_size_t) (MAX (1,n), size, &ok) ; p = ok ? ((Common->malloc_memory) (s)) : NULL ; if (p == NULL) { /* failure: out of memory */ ERROR (CHOLMOD_OUT_OF_MEMORY, "out of memory") ; } else { /* success: increment the count of objects allocated */ Common->malloc_count++ ; Common->memory_inuse += (n * size) ; Common->memory_usage = MAX (Common->memory_usage, Common->memory_inuse) ; PRINTM (("cholmod_malloc %p %d cnt: %d inuse %d\n", p, n*size, Common->malloc_count, Common->memory_inuse)) ; } } return (p) ; } /* ========================================================================== */ /* === cholmod_free ========================================================= */ /* ========================================================================== */ /* Wrapper around free routine. Returns NULL, which can be assigned to the * pointer being freed, as in: * * p = cholmod_free (n, sizeof (int), p, Common) ; * * In CHOLMOD, the syntax: * * cholmod_free (n, sizeof (int), p, Common) ; * * is used if p is a local pointer and the routine is returning shortly. * Uses a pointer to the free routine (or its equivalent) defined in Common. * Nothing is freed if the pointer is NULL. */ void *CHOLMOD(free) /* always returns NULL */ ( /* ---- input ---- */ size_t n, /* number of items */ size_t size, /* size of each item */ /* ---- in/out --- */ void *p, /* block of memory to free */ /* --------------- */ cholmod_common *Common ) { RETURN_IF_NULL_COMMON (NULL) ; if (p != NULL) { /* only free the object if the pointer is not NULL */ /* call free, or its equivalent */ (Common->free_memory) (p) ; Common->malloc_count-- ; Common->memory_inuse -= (n * size) ; PRINTM (("cholmod_free %p %d cnt: %d inuse %d\n", p, n*size, Common->malloc_count, Common->memory_inuse)) ; /* This assertion will fail if the user calls cholmod_malloc and * cholmod_free with mismatched memory sizes. It shouldn't fail * otherwise. */ ASSERT (IMPLIES (Common->malloc_count == 0, Common->memory_inuse == 0)); } /* return NULL, and the caller should assign this to p. This avoids * freeing the same pointer twice. */ return (NULL) ; } /* ========================================================================== */ /* === cholmod_calloc ======================================================= */ /* ========================================================================== */ /* Wrapper around calloc routine. * * Uses a pointer to the calloc routine (or its equivalent) defined in Common. * This routine is identical to malloc, except that it zeros the newly allocated * block to zero. */ void *CHOLMOD(calloc) /* returns pointer to the newly calloc'd block */ ( /* ---- input ---- */ size_t n, /* number of items */ size_t size, /* size of each item */ /* --------------- */ cholmod_common *Common ) { void *p ; RETURN_IF_NULL_COMMON (NULL) ; if (size == 0) { ERROR (CHOLMOD_INVALID, "sizeof(item) must be > 0") ; p = NULL ; } else if (n >= (Size_max / size) || n >= Int_max) { /* object is too big to allocate without causing integer overflow */ ERROR (CHOLMOD_TOO_LARGE, "problem too large") ; p = NULL ; } else { /* call calloc, or its equivalent */ p = (Common->calloc_memory) (MAX (1,n), size) ; if (p == NULL) { /* failure: out of memory */ ERROR (CHOLMOD_OUT_OF_MEMORY, "out of memory") ; } else { /* success: increment the count of objects allocated */ Common->malloc_count++ ; Common->memory_inuse += (n * size) ; Common->memory_usage = MAX (Common->memory_usage, Common->memory_inuse) ; PRINTM (("cholmod_malloc %p %d cnt: %d inuse %d\n", p, n*size, Common->malloc_count, Common->memory_inuse)) ; } } return (p) ; } /* ========================================================================== */ /* === cholmod_realloc ====================================================== */ /* ========================================================================== */ /* Wrapper around realloc routine. Given a pointer p to a block of size * (*n)*size memory, it changes the size of the block pointed to by p to be * MAX(1,nnew)*size in size. It may return a pointer different than p. This * should be used as (for a pointer to int): * * p = cholmod_realloc (nnew, sizeof (int), p, *n, Common) ; * * If p is NULL, this is the same as p = cholmod_malloc (...). * A size of nnew=0 is treated as nnew=1. * * If the realloc fails, p is returned unchanged and Common->status is set * to CHOLMOD_OUT_OF_MEMORY. If successful, Common->status is not modified, * and p is returned (possibly changed) and pointing to a large block of memory. * * Uses a pointer to the realloc routine (or its equivalent) defined in Common. */ void *CHOLMOD(realloc) /* returns pointer to reallocated block */ ( /* ---- input ---- */ size_t nnew, /* requested # of items in reallocated block */ size_t size, /* size of each item */ /* ---- in/out --- */ void *p, /* block of memory to realloc */ size_t *n, /* current size on input, nnew on output if successful*/ /* --------------- */ cholmod_common *Common ) { size_t nold = (*n) ; void *pnew ; size_t s ; int ok = TRUE ; RETURN_IF_NULL_COMMON (NULL) ; if (size == 0) { ERROR (CHOLMOD_INVALID, "sizeof(item) must be > 0") ; p = NULL ; } else if (p == NULL) { /* A fresh object is being allocated. */ PRINT1 (("realloc fresh: %d %d\n", nnew, size)) ; p = CHOLMOD(malloc) (nnew, size, Common) ; *n = (p == NULL) ? 0 : nnew ; } else if (nold == nnew) { /* Nothing to do. Do not change p or n. */ PRINT1 (("realloc nothing: %d %d\n", nnew, size)) ; } else if (nnew >= (Size_max / size) || nnew >= Int_max) { /* failure: nnew is too big. Do not change p or n. */ ERROR (CHOLMOD_TOO_LARGE, "problem too large") ; } else { /* The object exists, and is changing to some other nonzero size. */ /* call realloc, or its equivalent */ PRINT1 (("realloc : %d to %d, %d\n", nold, nnew, size)) ; pnew = NULL ; s = CHOLMOD(mult_size_t) (MAX (1,nnew), size, &ok) ; pnew = ok ? ((Common->realloc_memory) (p, s)) : NULL ; if (pnew == NULL) { /* Do not change p, since it still points to allocated memory */ if (nnew <= nold) { /* The attempt to reduce the size of the block from n to * nnew has failed. The current block is not modified, so * pretend to succeed, but do not change p. Do change * CHOLMOD's notion of the size of the block, however. */ *n = nnew ; PRINTM (("nnew <= nold failed, pretend to succeed\n")) ; PRINTM (("cholmod_free %p %d cnt: %d inuse %d\n" "cholmod_malloc %p %d cnt: %d inuse %d\n", p, nold*size, Common->malloc_count-1, Common->memory_inuse - nold*size, p, nnew*size, Common->malloc_count, Common->memory_inuse + (nnew-nold)*size)) ; Common->memory_inuse += ((nnew-nold) * size) ; } else { /* Increasing the size of the block has failed. * Do not change n. */ ERROR (CHOLMOD_OUT_OF_MEMORY, "out of memory") ; } } else { /* success: return revised p and change the size of the block */ PRINTM (("cholmod_free %p %d cnt: %d inuse %d\n" "cholmod_malloc %p %d cnt: %d inuse %d\n", p, nold*size, Common->malloc_count-1, Common->memory_inuse - nold*size, pnew, nnew*size, Common->malloc_count, Common->memory_inuse + (nnew-nold)*size)) ; p = pnew ; *n = nnew ; Common->memory_inuse += ((nnew-nold) * size) ; } Common->memory_usage = MAX (Common->memory_usage, Common->memory_inuse); } return (p) ; } /* ========================================================================== */ /* === cholmod_realloc_multiple ============================================= */ /* ========================================================================== */ /* reallocate multiple blocks of memory, all of the same size (up to two integer * and two real blocks). Either reallocations all succeed, or all are returned * in the original size (they are freed if the original size is zero). The nnew * blocks are of size 1 or more. */ int CHOLMOD(realloc_multiple) ( /* ---- input ---- */ size_t nnew, /* requested # of items in reallocated blocks */ int nint, /* number of int/UF_long blocks */ int xtype, /* CHOLMOD_PATTERN, _REAL, _COMPLEX, or _ZOMPLEX */ /* ---- in/out --- */ void **I, /* int or UF_long block */ void **J, /* int or UF_long block */ void **X, /* complex or double block */ void **Z, /* zomplex case only: double block */ size_t *nold_p, /* current size of the I,J,X,Z blocks on input, * nnew on output if successful */ /* --------------- */ cholmod_common *Common ) { double *xx, *zz ; size_t i, j, x, z, nold ; RETURN_IF_NULL_COMMON (FALSE) ; if (xtype < CHOLMOD_PATTERN || xtype > CHOLMOD_ZOMPLEX) { ERROR (CHOLMOD_INVALID, "invalid xtype") ; return (FALSE) ; } nold = *nold_p ; if (nint < 1 && xtype == CHOLMOD_PATTERN) { /* nothing to do */ return (TRUE) ; } i = nold ; j = nold ; x = nold ; z = nold ; if (nint > 0) { *I = CHOLMOD(realloc) (nnew, sizeof (Int), *I, &i, Common) ; } if (nint > 1) { *J = CHOLMOD(realloc) (nnew, sizeof (Int), *J, &j, Common) ; } switch (xtype) { case CHOLMOD_REAL: *X = CHOLMOD(realloc) (nnew, sizeof (double), *X, &x, Common) ; break ; case CHOLMOD_COMPLEX: *X = CHOLMOD(realloc) (nnew, 2*sizeof (double), *X, &x, Common) ; break ; case CHOLMOD_ZOMPLEX: *X = CHOLMOD(realloc) (nnew, sizeof (double), *X, &x, Common) ; *Z = CHOLMOD(realloc) (nnew, sizeof (double), *Z, &z, Common) ; break ; } if (Common->status < CHOLMOD_OK) { /* one or more realloc's failed. Resize all back down to nold. */ if (nold == 0) { if (nint > 0) { *I = CHOLMOD(free) (i, sizeof (Int), *I, Common) ; } if (nint > 1) { *J = CHOLMOD(free) (j, sizeof (Int), *J, Common) ; } switch (xtype) { case CHOLMOD_REAL: *X = CHOLMOD(free) (x, sizeof (double), *X, Common) ; break ; case CHOLMOD_COMPLEX: *X = CHOLMOD(free) (x, 2*sizeof (double), *X, Common) ; break ; case CHOLMOD_ZOMPLEX: *X = CHOLMOD(free) (x, sizeof (double), *X, Common) ; *Z = CHOLMOD(free) (x, sizeof (double), *Z, Common) ; break ; } } else { if (nint > 0) { *I = CHOLMOD(realloc) (nold, sizeof (Int), *I, &i, Common) ; } if (nint > 1) { *J = CHOLMOD(realloc) (nold, sizeof (Int), *J, &j, Common) ; } switch (xtype) { case CHOLMOD_REAL: *X = CHOLMOD(realloc) (nold, sizeof (double), *X, &x, Common) ; break ; case CHOLMOD_COMPLEX: *X = CHOLMOD(realloc) (nold, 2*sizeof (double), *X, &x, Common) ; break ; case CHOLMOD_ZOMPLEX: *X = CHOLMOD(realloc) (nold, sizeof (double), *X, &x, Common) ; *Z = CHOLMOD(realloc) (nold, sizeof (double), *Z, &z, Common) ; break ; } } return (FALSE) ; } if (nold == 0) { /* New space was allocated. Clear the first entry so that valgrind * doesn't complain about its access in change_complexity * (Core/cholmod_complex.c). */ xx = *X ; zz = *Z ; switch (xtype) { case CHOLMOD_REAL: xx [0] = 0 ; break ; case CHOLMOD_COMPLEX: xx [0] = 0 ; xx [1] = 0 ; break ; case CHOLMOD_ZOMPLEX: xx [0] = 0 ; zz [0] = 0 ; break ; } } /* all realloc's succeeded, change size to reflect realloc'ed size. */ *nold_p = nnew ; return (TRUE) ; }