/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1991 by John E. Lloyd.				    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	hashSetup
 *
 * DESCRIPTION:	Routines to set up the RCI global memory hash table and
 *		add or delete items from it. The global memory hash table
 *		it used as a general resource for referencing items by
 *		name across different CPUs.
 * 
 * CAVEATS:	The hash table can only be created and modified by the
 *		planning task.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1989
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include "../h/cdefs.h"
#include "../h/rciTask.h"
#include "../h/assignMem.h"
#include "../h/errorCodes.h"

#include "../h/rciConfig.h"
#include "../rci/hashTable.h"
#include "../rci/firstAlloc.h"

extern char *hashTableTask0Addr;
extern void *allocSharedMemUpper();

/* ----- Error Messages. Defined externally to save space ----- */

static char *nullMsg		= "%s --\n";
static char *notSetupMsg	= "%s -- table not set up\n";
static char *hashTableFullMsg   = "%s -- hash table is full\n";

static HASH_LINK **hashTable;
static FIRST_ALLOC_DESC hashPool;

static int initDone = 0;
#define INIT_CHECK()	if (!initDone) doInit();
static doInit()
{
	initDone = 1;
	hashTable = NULL;
	hashTableTask0Addr = NULL;
	requestFlagInit (&initDone);
}

hashTableCreate()
{
	/* Create the hash table. Allocate the memory for the table
	   itself (and initialize the buckets), and also allocate
	   (and initialize) a pool of memory for the hash links and names */

	char *fxnName = "hashTableCreate()";
	char *mem;
	int size;
	int k;

	INIT_CHECK();

	if (hashTableTask0Addr)
	 { return (1);
	 }
	if (!planningLevelCheck(fxnName))
	 { return (-1);
	 }
	size =   ALIGN_SIZE(sizeof(HASH_LINK*)*HASHTAB_SIZE)
	       + ALIGN_SIZE(HASHPOOL_SIZE);
	if ((mem = (char*)allocSharedMemUpper (ARBITER_CPU, size)) == NULL)
	 { errorMsg (EMemory, nullMsg, fxnName);
	   return (-1);
	 }

	ASSIGN_MEM(hashTable,HASH_LINK**,sizeof(HASH_LINK*)*HASHTAB_SIZE,mem);
	for (k=0; k<HASHTAB_SIZE; k++)
	 { hashTable[k] = NULL;
	 }

	firstAllocInit (&hashPool, NULLF, 0, (void*)mem, HASHPOOL_SIZE);

	hashTableTask0Addr = (char*)hashTable;
	return (1);
}

char *hashTableInsert (name, id, addr, cpu)
char *name;
int id;
void *addr;
int cpu;
{
	/* Insert an item into the hash table. Items are identified
	   by both an integer id and a string name. The integer id
	   is useful for allowing different packages simultaneous
	   use of the table without risk of name collision.

	   The information stored in the table includes
	   an address pointer and a cpu number.
	 */

	char *fxnName = "hashTableInsert()";

	HASH_LINK *hlink;
	char *tableNameCopy;
	char *mem;
	int index;

	INIT_CHECK();

	if (hashTable == NULL)
	 { if (hashTableCreate() < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      return (NULL);
	    }
	 }
	if ((mem = (char*)firstAlloc (&hashPool, 
			       sizeof(HASH_LINK)+strlen(name)+1)) == NULL)
	 { errorMsg (EMemory, hashTableFullMsg, fxnName);
	   return (NULL);
	 }
	hlink = (HASH_LINK*)mem;
	tableNameCopy = (char*)(mem + sizeof(HASH_LINK));
	hlink->addr = addr;
	hlink->cpu = cpu;
	hlink->id = id;
	strcpy (tableNameCopy, name);

	index = hashTableFxn(name, id);
	hlink->next = hashTable[index];
	hashTable[index] = hlink;
	return (tableNameCopy);
}

hashTableDelete (name, id, addr)
char *name;
int id;
void *addr;
{
	/* Delete an item from the hash table */

	char *fxnName = "hashTableDelete()";

	HASH_LINK *hlink;
	HASH_LINK *last;
	int index;

	INIT_CHECK();

	if (hashTable == NULL)
	 { return (0);
	 }

	index = hashTableFxn(name, id);
	if ((hlink = hashTable[index]) == NULL)
	 { return (0);
	 }
	if (hlink->id == id && 
	    strcmp((char*)hlink+sizeof(HASH_LINK), name) == 0)
	 { hashTable[index] = hlink->next;
	 }
	else
	 { last = hlink;
	   for (hlink = hlink->next; hlink != NULL; hlink = hlink->next)
	    { if (hlink->id == id && 
		  strcmp((char*)hlink+sizeof(HASH_LINK), name) == 0)
	       { /* found it */
		 last->next = hlink->next;
		 goto foundit;
	       }
	      last = hlink;
	    }
	   return (0);	/* didn't find it */
	 }
foundit:
	firstFree (&hashPool, (void*)hlink);
	return (1);
}

hashTableHasRoom (name)
char *name;
{
	/* Check to see if the has table has room for an entry
	   with identifier 'name' */

	char *fxnName = "hashTableHasRoom()";
	char *mem;

	INIT_CHECK();

	if (hashTable == NULL)
	 { if (hashTableCreate() < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      return (-1);
	    }
	 }
	if (mem = (char*)firstAlloc (&hashPool, 
				     sizeof(HASH_LINK)+strlen(name)+1))
	 { firstFree (&hashPool, (void*)mem);
	   return (1);
	 }
	else
	 { return (0);
	 }
}
