/*
** mg_arr.c for  in 
** 
** Made by vianney rancurel
** Login   <vianney@epita.fr>
** 
** Started on  Wed Aug 25 12:17:11 1999 vianney rancurel
** Last update Thu Oct 28 20:16:36 1999 
*/
#include "mg.h"

/* allocates at least n elts.
   Should not be called directly.
   Returns 0 or various errors from alloc_procs */
t_status		arr_alloc(arr,n)
t_arr			*arr;
int			n;		/* Numbers of elts wanted	*/
{
  t_status		status;
  size_t		real_size;
  VOID_PTR		ptr;

  if (n < arr->base)
    n = arr->base;
  if ((status = arr->alloc_algorithm_proc(arr->allocated * arr->eltsize,
					  n * arr->eltsize,
					  &real_size)) != 0)
    return (status);
  if (arr->elts == NULL)
    {
      if ((ptr = arr->alloc_proc(real_size,
				 arr->comment,
				 "arr_alloc:elts",
				 &status)) == NULL)
	return (status);
    }
  else
    {
      if ((ptr = arr->realloc_proc(arr->elts,
				   real_size,
				   arr->comment,
				   "arr_alloc:elts",
				   &status)) == NULL)
	return (status);
    }
  arr->elts = ptr;
  arr->allocated = real_size / arr->eltsize;
  return (0);
}

/* instantiates a new array.
   An array (t_arr) is a container,
   by opposition to a vector (t_vec), it stores objects
   entirely instead of storing a reference to them.
   Returns a new array structure, if NULL status is filled */
t_arr			*arr_new(eltsize,	
				 base,		
				 now,		
				 alloc_algorithm_proc,
				 alloc_proc,
				 realloc_proc,
				 free_proc,
				 comment,
				 status)	
size_t			eltsize;		/* Base size of object	*/
int			base;			/* Base allocation	*/
t_boolean		now;			/* Allocates elts now	*/
t_alloc_algorithm_proc	alloc_algorithm_proc;	/* Alloc. strategy	*/
t_alloc_proc		alloc_proc;		/* E.g. alloc_malloc(3)	*/
t_realloc_proc		realloc_proc;		/* E.g. realloc_realloc(3)*/
t_free_proc		free_proc;		/* E.g. free_free(3)	*/
char			*comment;		/* Major comment	*/
t_status		*status;		/* Various		*/
{
  t_arr			*arr;

  if ((arr = alloc_proc(sizeof (t_arr),
			comment,
			"arr_new:arr",
			status)) == NULL)
    return (NULL);
  arr->eltsize = eltsize;
  arr->base = base;
  arr->elts = NULL;
  arr->count = 0;
  arr->allocated = 0;
  arr->alloc_algorithm_proc = alloc_algorithm_proc;
  arr->alloc_proc = alloc_proc; 
  arr->realloc_proc = realloc_proc;
  arr->free_proc = free_proc;
  arr->comment = comment;
  if (now)
    if (((*status) = arr_alloc(arr,base)) != 0)
      {
	arr->free_proc(arr,
		       comment,
		       "arr_new:arr");
	return (NULL);
      }
  return (arr);
}

/* frees elts but not the array.*/
VOID_FUNC		arr_destroy(arr)
t_arr			*arr;
{
  if (arr->elts)
    arr->free_proc(arr->elts,
		   arr->comment,
		   "*:elts");
  arr->elts = NULL;
  arr->count = 0;
  arr->allocated = 0;
}

/* frees elts and the array. */
VOID_FUNC		arr_delete(arr)
t_arr			*arr;
{
  arr_destroy(arr);
  arr->free_proc(arr,
		 arr->comment,
		 "*:arr");
}

/* allocates at least one more elt.
   The function can of course allocate more than 1 elt according to
   the alloc_algorithm_proc.
   Returns a pointer to the new elt */
VOID_PTR		arr_next(arr,status)
t_arr			*arr;
t_status		*status;
{
  VOID_PTR		ptr;

  if (arr->allocated < (arr->count + 1))
    if (((*status) = arr_alloc(arr,arr->count + 1)) != 0)
      return (NULL);
  ptr = ((char *)(arr->elts)) + arr->count * arr->eltsize;
  arr->count++;
  return (ptr);
}

/* "removes" one elt.
   The elt is not deallocated but elts are moved back and the counter
   is decremented.*/
VOID_FUNC		arr_rm(arr,idx)
t_arr			*arr;
int			idx;
{
  FBCOPY(arr->elts + idx + 1,
	 arr->elts + idx,
	 (arr->count - idx - 1) * arr->eltsize);
  arr->count--;
}

#ifdef DEBUG
/* shows an array structure.
   This is a debug function.*/
VOID_FUNC		arr_show(arr)
t_arr			*arr;
{
  fprintf(stderr,"eltsize=%d\n",arr->eltsize);
  fprintf(stderr,"base=%d\n",arr->base);
  fprintf(stderr,"count=%d\n",arr->count);
  fprintf(stderr,"allocated=%d\n",arr->allocated);
  fprintf(stderr,"elts=%p\n",arr->elts);
}
#endif
