/* ========================================================================== */
/* === KLU_memory =========================================================== */
/* ========================================================================== */
// @HEADER
// ***********************************************************************
//
//                   KLU2: A Direct Linear Solver package
//                    Copyright 2011 Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, with Sandia Corporation, the 
// U.S. Government retains certain rights in this software.
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
// USA
// Questions? Contact Mike A. Heroux (maherou@sandia.gov)
//
// KLU2 is derived work from KLU, licensed under LGPL, and copyrighted by
// University of Florida. The Authors of KLU are Timothy A. Davis and
// Eka Palamadai. See Doc/KLU_README.txt for the licensing and copyright
// information for KLU.
//
// ***********************************************************************
// @HEADER

/* KLU memory management routines:
 *
 * KLU_malloc                   malloc wrapper
 * KLU_free                     free wrapper
 * KLU_realloc                  realloc wrapper
 */

#ifndef KLU2_MEMORY_H
#define KLU2_MEMORY_H

#include "klu2_internal.h"

/* ========================================================================== */
/* === KLU_add_size_t ======================================================= */
/* ========================================================================== */

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

template <typename Int>
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) : ((size_t) -1)) ;
}

/* ========================================================================== */
/* === KLU_mult_size_t ====================================================== */
/* ========================================================================== */

/* Safely compute a*k, where k should be small, and check for size_t overflow */

template <typename Int>
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 ((*ok) ? s : ((size_t) -1)) ;
}

/* ========================================================================== */
/* === 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.
 */

template <typename Entry, typename Int>
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<Entry, Int> *Common
)
{
    void *p ;
    size_t s ;
    Int ok = TRUE ;

    if (Common == NULL)
    {
        p = NULL ;
    }
    else 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 */
        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 ;
        }
        else
        {
            Common->memusage += s ;
            Common->mempeak = MAX (Common->mempeak, Common->memusage) ;
        }
    }
    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, n, sizeof (int), Common) ;
 */

template <typename Entry, typename Int>
void *KLU_free          /* always returns NULL */
(
    /* ---- in/out --- */
    void *p,            /* block of memory to free */
    /* ---- input --- */
    size_t n,           /* size of block to free, in # of items */
    size_t size,        /* size of each item */
    /* --------------- */
    KLU_common<Entry, Int> *Common
)
{
    size_t s ;
    Int ok = TRUE ;
    if (p != NULL && Common != NULL)
    {
        /* only free the object if the pointer is not NULL */
        /* call free, or its equivalent */
        (Common->free_memory) (p) ;
        s = KLU_mult_size_t (MAX (1,n), size, &ok) ;
        Common->memusage -= s ;
    }
    /* 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, nold, 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.
 */

template <typename Entry, typename Int>
void *KLU_realloc       /* returns pointer to reallocated block */
(
    /* ---- input ---- */
    size_t nnew,        /* requested # of items in reallocated block */
    size_t nold,        /* old # of items */
    size_t size,        /* size of each item */
    /* ---- in/out --- */
    void *p,            /* block of memory to realloc */
    /* --------------- */
    KLU_common<Entry, Int> *Common
)
{
    void *pnew ;
    size_t snew, sold ;
    Int ok = TRUE ;

    if (Common == NULL)
    {
        p = NULL ;
    }
    else 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 */
        snew = KLU_mult_size_t (MAX (1,nnew), size, &ok) ;
        sold = KLU_mult_size_t (MAX (1,nold), size, &ok) ;
        pnew = ok ? ((Common->realloc_memory) (p, snew)) : 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 */
            Common->memusage += (snew - sold) ;
            Common->mempeak = MAX (Common->mempeak, Common->memusage) ;
            p = pnew ;
        }
    }
    return (p) ;
}

#endif /* KLU_MEMORY_H */
