/* ========================================================================== */
/* === klu_memory =========================================================== */
/* ========================================================================== */

/* KLU memory management routines:
 *
 * klu_malloc			malloc wrapper
 * klu_free			free wrapper
 * klu_realloc			realloc wrapper
 */

#include "klu_internal.h"

/* ========================================================================== */
/* === klu_add_size_t ======================================================= */
/* ========================================================================== */

/* Safely compute a+b, and check for integer overflow */

size_t klu_add_size_t (size_t a, size_t b, int *ok)
{
    (*ok) = (*ok) && ((a + b) >= MAX (a,b)) ;
    return ((*ok) ? (a + b) : 0) ;
}

/* ========================================================================== */
/* === klu_mult_size_t ====================================================== */
/* ========================================================================== */

/* Safely compute a*k, where k should be small, & check for integer overflow */

size_t klu_mult_size_t (size_t a, size_t k, int *ok)
{
    size_t i, s = 0 ;
    for (i = 0 ; i < k ; i++)
    {
	s = klu_add_size_t (s, a, ok) ;
    }
    return (s) ;
}

/* ========================================================================== */
/* === klu_malloc =========================================================== */
/* ========================================================================== */

/* Wrapper around malloc routine (mxMalloc for a mexFunction).  Allocates
 * space of size MAX(1,n)*size, where size is normally a sizeof (...).
 *
 * This routine and klu_realloc do not set Common->status to KLU_OK on success,
 * so that a sequence of klu_malloc's or klu_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 = klu_malloc (n, sizeof (int), Common)
 *
 * Uses a pointer to the malloc routine (or its equivalent) defined in Common.
 */

void *klu_malloc	/* returns pointer to the newly malloc'd block */
(
    /* ---- input ---- */
    size_t n,		/* number of items */
    size_t size,	/* size of each item */
    /* --------------- */
    klu_common *Common
)
{
    void *p ;
    size_t s ;
    int ok = TRUE ;

    if (size == 0)
    {
	/* size must be > 0 */
	Common->status = KLU_INVALID ;
	p = NULL ;
    }
    else if (n >= INT_MAX)
    {
	/* object is too big to allocate; p[i] where i is an int will not
	 * be enough. */
	Common->status = KLU_TOO_LARGE ;
	p = NULL ;
    }
    else
    {
	/* call malloc, or its equivalent */
	/*
	p = (Common->malloc_memory) (MAX (1,n) * size) ;
	*/
	s = klu_mult_size_t (MAX (1,n), size, &ok) ;
	p = ok ? ((Common->malloc_memory) (s)) : NULL ;
	if (p == NULL)
	{
	    /* failure: out of memory */
	    Common->status = KLU_OUT_OF_MEMORY ;
	}
    }
    return (p) ;
}


/* ========================================================================== */
/* === klu_free ============================================================= */
/* ========================================================================== */

/* Wrapper around free routine (mxFree for a mexFunction).  Returns NULL,
 * which can be assigned to the pointer being freed, as in:
 *
 *	p = klu_free (p, Common) ;
 */

void *klu_free		/* always returns NULL */
(
    /* ---- in/out --- */
    void *p,		/* block of memory to free */
    /* --------------- */
    klu_common *Common
)
{
    if (p != NULL)
    {
	/* only free the object if the pointer is not NULL */
	/* call free, or its equivalent */
	(Common->free_memory) (p) ;
    }
    /* return NULL, and the caller should assign this to p.  This avoids
     * freeing the same pointer twice. */
    return (NULL) ;
}


/* ========================================================================== */
/* === klu_realloc ========================================================== */
/* ========================================================================== */

/* Wrapper around realloc routine (mxRealloc for a mexFunction).  Given a
 * pointer p to a block allocated by klu_malloc, 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 = klu_realloc (nnew, sizeof (int), p, Common) ;
 *
 * If p is NULL, this is the same as p = klu_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 KLU_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 *klu_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 */
    /* --------------- */
    klu_common *Common
)
{
    void *pnew ;
    size_t s ;
    int ok = TRUE ;

    if (size == 0)
    {
	/* size must be > 0 */
	Common->status = KLU_INVALID ;
	p = NULL ;
    }
    else if (p == NULL)
    {
	/* A fresh object is being allocated. */
	p = klu_malloc (nnew, size, Common) ;
    }
    else if (nnew >= INT_MAX)
    {
	/* failure: nnew is too big.  Do not change p */
	Common->status = KLU_TOO_LARGE ;
    }
    else
    {
	/* The object exists, and is changing to some other nonzero size. */
	/* call realloc, or its equivalent */
	/*
	pnew = (Common->realloc_memory) (p, MAX (1,nnew) * size) ;
	*/
	s = klu_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 */
	    Common->status = KLU_OUT_OF_MEMORY ;
	}
	else
	{
	    /* success: return the new p and change the size of the block */
	    p = pnew ;
	}
    }
    return (p) ;
}
