/*
 *  SMARTMEM.C
 *
 *  (c)Copyright 1991-93 by Tobias Ferber,  All Rights Reserved.
 */

#include <stdlib.h>

#include <exec/types.h>
#include <exec/memory.h>

/* exec.library prototypes */
extern void *AllocMem(ULONG,ULONG);
extern void FreeMem(void *,ULONG);

#include "newlook.h"

/*
 *  --- PRIVATE ---
 */

/****i* newlook.lib/--background-- ******************************************
*
*   SMARTMEM
*	smartmem.o offers some very useful functions concering
*	memory management on Amiga systems.
*	... bla, bla, bla ...
*
*   IDEAS
*	SmartAllocRaster()
*	SmartAllocMem(<bytesize>,<memory-type>)
*
*	typedef struct memnode {
*
*	  struct memnode *succ,*pred;
*	  ULONG handle;
*
*	  union {
*	    APTR memory;
*	    PLANEPTR plane;
*	  } ptr;
*
*	  union {
*	    ULONG bytesize;
*	    struct {
*	      USHORT width,
*	             height;
*	    } dimen;
*	  } size;
*
*	  enum { RASTER,MEMORY } type;  /* memtype attributes */
*	} memnode_t;
*
******************************************************************************
*
*/

typedef struct memnode {

  struct memnode *succ,
                 *pred;
  ULONG handle;
  void *memory;
  ULONG bytesize;

} memnode_t;

#define NIL(type) (type)0L
static memnode_t *memlist= NIL(memnode_t *);
static ULONG current_handle= 0L;

/****i* newlook.lib/tail ******************************************
*
*   NAME
*	tail -- Return the last memnode pointer in a list
*
*   SYNOPSIS
*	t = tail( n )
*
*	memnode_t *tail( memnode_t * );
*
*   FUNCTION
*	Traverse the list of memnodes linked to n->succ until
*	n->succ is == NIL(menode_t *).
*
*   INPUTS
*	n - pointer to any memnode or NIL(memnode_t *)
*
*   RESULT
*	t - the last memnode or NIL(memnode_t *) if n was NIL(memnode_t *)
*
*   SEE ALSO
*	head()
*
******************************************************************************
*
*/

static memnode_t *tail(memnode_t *n)
{
  return (n && n->succ) ? tail(n->succ) : n;
}


/****i* newlook.lib/head ******************************************
*
*   NAME
*	head -- Return the first memnode pointer in a list
*
*   SYNOPSIS
*	t = head( n )
*
*	memnode_t *head( memnode_t * );
*
*   FUNCTION
*	Traverse the list of memnodes linked to n->pred until
*	n->pred is == NIL(menode_t *).
*
*   INPUTS
*	n - pointer to any memnode or NIL(memnode_t *)
*
*   RESULT
*	t - the last memnode or NIL(memnode_t *) if n was NIL(memnode_t *)
*
*   SEE ALSO
*	tail()
*
******************************************************************************
*
*/

static memnode_t *head(memnode_t *n)
{
  return (n && n->pred) ? head(n->pred) : n;
}


/****i* newlook.lib/addnode ******************************************
*
*   NAME
*	addnode -- Add a (list of) memnode(s) to the head of memlist
*
*   SYNOPSIS
*	addnode( n )
*
*	void addnode( memnode_t * );
*
*   FUNCTION
*	Add memlist to the tail of n and make n the head of the new memlist
*
*   INPUTS
*	n - pointer to any memnode or NIL(memnode_t *)
*
*   RESULT
*	None.
*
*   NOTES
*	calling addnode( NIL(memnode_t *) ) is a no-op.
*
*   SEE ALSO
*	new()
*
******************************************************************************
*
*/

static void addnode(memnode_t *n)
{
  register memnode_t *t;

  if(t= tail(n))
  {
    if( t->succ= head(memlist) )
      t->succ->pred= t;

    memlist= head(n);
  }
}


/****i* newlook.lib/new ******************************************
*
*   NAME
*	new -- Allocate and initialize a new memnode
*
*   SYNOPSIS
*	n= new()
*
*	memnode_t *new( void );
*
*   FUNCTION
*	Allocate a new memnode using malloc() and initialize it.
*	Especially the current handle will be copied to n->handle.
*
*   INPUTS
*	None.
*
*   RESULT
*	n - pointer to the new memnode or NIL(memnode_t *) on error.
*
*   NOTES
*	calling new() will not automatically chain the the new memnode
*
*   SEE ALSO
*	addnode()
*
******************************************************************************
*
*/

static memnode_t *new(void)
{
  register memnode_t *n;

  if(n= (memnode_t *)malloc(sizeof(memnode_t)))
  {
    n->succ=
    n->pred= NIL(memnode_t *);

    n->handle= current_handle;

    n->memory= NIL(void *);
    n->bytesize= 0UL;
  }
  return n;
}


/****i* newlook.lib/dispose ******************************************
*
*   NAME
*	dispose -- Unchain a memnode from memlist and free it
*
*   SYNOPSIS
*	dispose( n );
*
*	void dispose( memnode_t * );
*
*   FUNCTION
*	Unchain n and keep memlist sane.
*
*   INPUTS
*	n - pointer to the memnode which is about to be disposed
*
*   RESULT
*	None.
*
*   NOTES
*	n->memory is _NOT_ freed by this function!
*	If n is the last node in memlist then memlist is set to NIL.
*
*   SEE ALSO
*	new(), addnode()
*
******************************************************************************
*
*/

static void dispose(memnode_t *n)
{
  if(n->pred == n->succ)
  { /* hacky: they _must_ be both NIL to be equal... */
    memlist= NIL(memnode_t *);
  }
  else
  {
    if(n == memlist)
    {
      if(n->pred)
        memlist= head(n->pred);  /* should not happen */
      else
        memlist= n->succ;
    }

    if(n->pred)
      n->pred->succ= n->succ;

    if(n->succ)
      n->succ->pred= n->pred;

    n->pred=
    n->succ= NIL(memnode_t *);
  }
  free(n);
}


/****i* newlook.lib/findnode ******************************************
*
*   NAME
*	findnode -- Find a memnode with memory at a certain location
*
*   SYNOPSIS
*	n = findnode( mem );
*
*	memnode_t *findnode( void * );
*
*   FUNCTION
*	Scan memlist for a memnode which has memory allocated at given
*	location "mem" with the current memory management handle.
*
*   INPUTS
*	mem   - pointer to the memory returned by SmartAllocMem()
*		or the macro SmartAllocate()
*
*   RESULT
*	n     - the memnode with n->memory equal to mem and n->handle
*		equal to the current_handle.
*
*   SEE ALSO
*	addnode()
*
******************************************************************************
*
*/

static memnode_t *findnode(void *mem)
{
  register memnode_t *n= head(memlist);

  /*for(n= head(memlist); n && n->memory != mem; n= n->succ) ;*/

  while(n && n->memory != mem)
    n= n->succ;

  return (n && n->handle == current_handle) ? n : NIL(memnode_t *);
}


/*
 *  --- PUBLIC ---
 */

/****** newlook.lib/SetNewLookHandle ******************************************
*
*   NAME
*	SetNewLookHandle -- Set the current memory management handle
*
*   SYNOPSIS
*	lastHandle = SetNewLookHandle( newHandle )
*
*	ULONG SetNewLookHandle( ULONG );
*
*   FUNCTION
*	Set the current memory management handle to newHandle and return
*	the last handle.
*
*   INPUTS
*	newHandle  - a unique number > 0 to identify your memory.
*
*   RESULT
*	lastHandle - the last handle.
*
*   NOTES
*	There are some reserved handles such as PRIVATE_HANDLE or
*	MAGIC_HANDLE which you must not use here!
*
*   SEE ALSO
*	MakePrivateHandlePublic()
*
******************************************************************************
*
*/

ULONG SetNewLookHandle(ULONG new_handle)
{
  register ULONG last_handle= current_handle;
  current_handle= new_handle;
  return last_handle;
}


/****** newlook.lib/SmartAllocMem ******************************************
*
*   NAME
*	SmartAllocMem -- Allocate and remember memory for a handle
*
*   SYNOPSIS
*	mem = SmartAllocMem( numbytes, attributes )
*
*	void *SmartAllocMem( ULONG, ULONG );
*
*   FUNCTION
*	Allocate system memory via AllocMem() and chain it to the
*	newlook memlist using the current memory management handle.
*
*   INPUTS
*	numbytes      - The size of the desired block in bytes. (The
*			operating system will automatically round this
*			number to a multiple of the system memory chunk
*			size.)
*	attributes    - requirements as explained in exec.library autodoc
*			file and/or exec/memory.h
*
*   RESULT
*	mem - a pointer to the newly allocated memory block.
*		If there are no free memory regions large enough to satisfy
*		the request, zero will be returned.  The pointer must be
*		checked for zero before the memory block may be used!
*		The memory block returned is long word aligned.
*
*   SEE ALSO
*	SetNewLookHandle(), SmartAllocate(), SmartFree(), SmartFreeAll()
*	exec.library/AllocMem()
*
******************************************************************************
*
*/

void *SmartAllocMem(ULONG numbytes, ULONG attributes)
{
  register memnode_t *n;
  register void *mem= NIL(void *);

  if(n= new())
  {
    if(mem= (void *)AllocMem(numbytes, attributes))
    {
      n->memory= mem;
      n->bytesize= numbytes;
      addnode(n);
    }
    else dispose(n);
  }
  return mem;
}


#ifdef OBSOLETE

/****** newlook.lib/SmartRemember ******************************************
*
*   NAME
*	SmartRemember -- Remember manually allocated memory (obsolete)
*
*   SYNOPSIS
*	success = SmartRemember( mem, numbytes )
*
*	int SmartRemember( void *, ULONG );
*
*   FUNCTION
*	Chain given memory block "mem" to the newlook memlist using
*	the current memory management handle.
*
*   INPUTS
*	mem           - pointer to a memory block which has been
*			allocated via AllocMem()
*	numbytes      - the size of the allocated memory block in
*			bytes.
*
*   RESULT
*	success - 1 on success, 0 on error
*
*   NOTES
*	This function is declared obsolete and will be removed!
*
*   SEE ALSO
*	SmartAllocMem(), exec.library/AllocMem()
*
******************************************************************************
*
*/

int SmartRemember(void *mem, ULONG numbytes)
{
  register memnode_t *n= NIL(memnode_t *);

  if(mem && numbytes)
  {
    if(n= new())
    {
      n->memory= mem;
      n->bytesize= numbytes;
      addnode(n);
    }
  }
  return n ? 1:0;
}
#endif /* OBSOLETE */


/****** newlook.lib/SmartFree ******************************************
*
*   NAME
*	SmartFree -- Free memory allocated via SmartAllocMem()
*
*   SYNOPSIS
*	nil = SmartFree( mem )
*
*	void *SmartFree( void * );
*
*   FUNCTION
*	Free the memory block allocated via SmartAllocMem() and
*	the current memory management handle.
*
*   INPUTS
*	mem - pointer to the memory block returned by SmartAllocMem()
*
*   RESULT
*	nil - a constant pointer to NIL(void *)
*
*   NOTES
*	If given pointer "mem" has never been returned by SmartAllocMem()
*	or if it has already been freed up then calling this function is
*	a no-op.
*
*   SEE ALSO
*	SmartAllocMem(), SmartFreeAll()
*
******************************************************************************
*
*/

void *SmartFree(void *mem)
{
  register memnode_t *n;

  if( n= findnode(mem) )
  {
    if(n->memory && n->bytesize)
      FreeMem(mem,n->bytesize);

    dispose(n);
  }
  return NIL(void *);
}


/****** newlook.lib/SmartFreeAll ******************************************
*
*   NAME
*	SmartFreeAll -- Free all memory allocated for a certain handle
*
*   SYNOPSIS
*	nil = SmartFreeAll( handle )
*
*	void *SmartFreeAll( ULONG );
*
*   FUNCTION
*	Free all the memory which has been allocated via SmartAllocMem()
*	for the given newlook handle.
*
*   INPUTS
*	handle - the handle to be freed or MAGIC_HANDLE for all handles
*
*   RESULT
*	nil - a constant pointer to NIL(void *)
*
*   NOTES
*	Calling SmartFreeAll( MAGIC_HANDLE ) will free all memory ever
*	chained to the memlist.
*
*   SEE ALSO
*	SmartFree(), SmartAllocMem()
*
******************************************************************************
*
*/

void *SmartFreeAll(ULONG handle)
{
  register memnode_t *n= head(memlist);

  while(n)
  {
    register memnode_t *t= n;
    n= n->succ;

    if(t->handle == handle || handle == MAGIC_HANDLE)
    {
      if(t->memory && t->bytesize)
        FreeMem(t->memory, t->bytesize);

      dispose(t);
    }
  }
  return NIL(void *);
}


#ifdef DEBUG

/****** newlook.lib/DebugSmartMemList ******************************************
*
*   NAME
*	DebugSmartMemList -- Print all memnodes in memlist
*
*   SYNOPSIS
*	DebugSmartMemList()
*
*	void DebugSmartMemList( void );
*
*   FUNCTION
*	Traverse the memlist and print information about all nodes chained
*	to it.
*
*   NOTES
*	This function is only available when comiled w/ -DDEBUG !
*
******************************************************************************
*
*/

void DebugSmartMemList(void)
{
  register memnode_t *n= head(memlist);

  if(n)
  {
    printf("  Handle  |  Memory  |  Bytesize\n"
           "----------+----------+------------\n");

    while(n)
    {
      printf("%9ld | 0x%06lX | %ld\n",n->handle,n->memory,n->bytesize);
      n= n->succ;
    }
  }
  else printf("%s: Memory list is empty.\n",__FILE__);
}
#endif /* DEBUG */


/*
 *  --- NewLook PRIVATE ---
 */

/****** newlook.lib/MakePrivateHandlePublic ******************************************
*
*   NAME
*	MakePrivateHandlePublic -- change memory handle from private to public
*
*   SYNOPSIS
*	MakePrivateHandlePublic( publicHandle )
*
*	void MakePrivateHandlePublic( ULONG );
*
*   FUNCTION
*	Traverse the memlist and change all memnodes with a memory block
*	allocated with the PRIVATE_HANDLE to the given publicHandle.
*
*   INPUTS
*	publicHandle  - The new handle for the private memnodes.
*
*   RESULT
*	None.
*
*   EXAMPLE
*	Federal regulations prohibit a demonstration of this function.
*
*   NOTES
*	This function is newlook private and should not be called by the
*	user!
*
*   SEE ALSO
*	SetNewLookHandle(), SmartAllocMem()
*
******************************************************************************
*
*/

void MakePrivateHandlePublic(ULONG public_handle)
{
  if(public_handle != PRIVATE_HANDLE)
  {
    register memnode_t *n= head(memlist);

    while(n)
    {
      if(n->handle == PRIVATE_HANDLE)
        n->handle= public_handle;

      n= n->succ;
    }
  }
  current_handle= public_handle;
}
