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