gtsam/spqr_mini/cholmod_memory.c

564 lines
17 KiB
C

/* ========================================================================== */
/* === 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) ;
}