/*--------------------------------------------------------------------------*/
/* ALBERTA_UTIL:  tools for messages, memory allocation, parameters, etc.   */
/*                                                                          */
/* file:     alloc.c                                                        */
/*                                                                          */
/* description:  utilities for memory allocation                            */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.alberta-fem.de/                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  (c) by A. Schmidt and K.G. Siebert (1996-2004)                          */
/*                                                                          */
/*     This program is free software; you can redistribute it and/or modify */
/*     it under the terms of the GNU General Public License as published by */
/*     the Free Software Foundation; either version 2 of the License, or    */
/*     any later version.                                                   */
/*                                                                          */
/*     This program 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 General Public License for more details.                         */
/*--------------------------------------------------------------------------*/

/*
 * Memory allocation under debug control 
 * 
 * This is a cloned code from alberta_util
 * and only compiled if DMALLOC is defined.
 */

#ifdef DMALLOC

#include <sys/types.h>
#include <stdlib.h>
#include "alberta_util.h"

#include <dmalloc.h>

/*--------------------------------------------------------------------------*/
/*  routines for allocation/deallocation of memory. every (de)allocation is */
/*  done via alberta_alloc(), alberta_realloc(), alberta_free() resp.       */
/*--------------------------------------------------------------------------*/

static const char *size_as_string(double size)
{
  static char  sas[128];

  if (size < 1024)
    sprintf(sas, "%d %s", (int) size, "B");
  else if (size < 1048576)
    sprintf(sas, "%.2f %s", size/1024.0, "KB");
  else
    sprintf(sas, "%.2f %s", size/1048576.0, "MB");
  return(sas);
}

#define ALLOC_ERR(fct,file,line)\
if (fct && file) ERROR_EXIT("called by %s in %s, line %d\n",fct,file,line);\
else if (fct)    ERROR_EXIT("called by %s, (unknown filename)\n", fct);\
else if (file)   ERROR_EXIT("called in %s, line %d\n", file, line);\
else             ERROR_EXIT("location unknow\n")

static double  size_used = 0;

void *alberta_alloc(size_t size, const char *fct, const char *file, int line)
{
  FUNCNAME("alberta_alloc(debug)");
  void  *mem;

  if (size <= 0)
  {
    ERROR("size == 0\n");
    ALLOC_ERR(fct, file, line);
  }

  if (!(mem = dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0)))
  {
    ERROR("can not allocate %s\n", size_as_string(size));
    ALLOC_ERR(fct, file, line);
  }

  size_used += size;
  return(mem);
}


void *alberta_realloc(void *ptr, size_t old_size, size_t new_size,
		      const char *fct, const char *file, int line)
{
  FUNCNAME("alberta_realloc(debug)");
  void  *mem;

  if (new_size <= 0)
  {
    ERROR("size == 0\n");
    ALLOC_ERR(fct, file, line);
  }
  if (!(mem = dmalloc_realloc(file, line, ptr, new_size, 
			      DMALLOC_FUNC_REALLOC, 0)))
  {
    ERROR("can not allocate %s\n", size_as_string(new_size));
    ALLOC_ERR(fct, file, line);
  }

  size_used += new_size - old_size;

  return(mem);
}

void *alberta_calloc(size_t size, size_t elsize, const char *fct, 
		     const char *file, int line)
{
  FUNCNAME("alberta_calloc(debug)");
  void  *mem;

  if (size <= 0 || elsize <= 0)
  {
    ERROR("one of size = %d or data size = %d is zero\n", size, elsize);
    ALLOC_ERR(fct, file, line);
  }

  if (!(mem = dmalloc_malloc(file, line, size*elsize, 
			     DMALLOC_FUNC_CALLOC, 0, 0)))
  {
    ERROR("can not allocate %s\n", size_as_string(size*elsize));
    ALLOC_ERR(fct, file, line);
  }

  size_used += size*elsize;
  return(mem);
}

void alberta_free(void *ptr, size_t size)
{
  if (ptr)
  {
    dmalloc_free( __FILE__, __LINE__, ptr, DMALLOC_FUNC_FREE);
    size_used -= size;
  }
  return;
}

void print_mem_use(void)
{
  FUNCNAME("print_mem_use");

  MSG("%s memory allocated\n", size_as_string(size_used));

  return;
}

WORKSPACE *get_workspace(size_t size, const char *fct, const char *f, int l)
{
  WORKSPACE *ws = (WORKSPACE*)alberta_alloc(sizeof(WORKSPACE), fct, f, l);

  ws->work = alberta_alloc(size, fct, f, l);
  ws->size = size;

  return(ws);
}

WORKSPACE  *realloc_workspace(WORKSPACE *ws, size_t new_size, 
			      const char *fct, const char *file, int line)
{
  WORKSPACE  *workspace = ws;

  if (!workspace)
  {
    workspace = (WORKSPACE*)alberta_alloc(sizeof(WORKSPACE), fct, file, line);
    workspace->work = nil;
  }

  if (!workspace->work)
  {
    workspace->work = alberta_alloc(new_size, fct, file, line);
    workspace->size = new_size;
  }
  else if (workspace->size < new_size)
  {
    workspace->work = alberta_realloc(workspace->work, workspace->size,
				     new_size, fct, file, line);
    workspace->size = new_size;
  }
  return(workspace);
}

void clear_workspace(WORKSPACE *ws)
{
  if (!ws) return;
  alberta_free(ws->work, ws->size);
  ws->work = nil;
  ws->size = 0;
  return;
}

void free_workspace(WORKSPACE *ws)
{
  if (!ws) return;
  alberta_free(ws->work, ws->size);
  MEM_FREE(ws, 1, WORKSPACE);

  return;
}

/*--------------------------------------------------------------------------*/
/*  matrix (de-)allocation routines: 					    */
/*  free_matrix deallocates such a matrix				    */
/*--------------------------------------------------------------------------*/


void **alberta_matrix(size_t nr, size_t nc, size_t size,
		      const char *fct, const char *file, int line)
{
  size_t    i;
  size_t    row_length = nc*size;
  char      **mat, *mrows;

  mat = (char **) alberta_alloc(nr*sizeof(char *), fct, file, line);
  mrows = (char *) alberta_alloc(nr*nc*size, fct, file, line);

  for(i = 0; i < nr; i++)
    mat[i] = mrows + i*row_length;

  return((void **)mat);
}

void  free_alberta_matrix(void **ptr, size_t nr, size_t nc, size_t size)
{
  alberta_free(ptr[0],nr*nc*size);
  alberta_free(ptr,nr*sizeof(char *));

  return;
}

#endif
