/******************************************************************************
 *
 * PROJECT: Carnegie Mellon Planetary Rover Project
 *          Task Control Architecture 
 * 
 * (c) Copyright 1991 Christopher Fedor and Reid Simmons.  All rights reserved.
 * 
 * MODULE: hash
 *
 * FILE: hash.c
 *
 * ABSTRACT:
 * 
 * Generic hash table abstract data type.
 *
 * $Source: /cvsroot/carmen/carmen/src/ipc/hash.c,v $ 
 * $Revision: 1.1.1.1 $
 * $Date: 2004/10/15 14:33:15 $
 * $Author: tomkol $
 *
 * REVISION HISTORY:
 *
 * $Log: hash.c,v $
 * Revision 1.1.1.1  2004/10/15 14:33:15  tomkol
 * Initial Import
 *
 * Revision 1.4  2003/04/20 02:28:13  nickr
 * Upgraded to IPC 3.7.6.
 * Reversed meaning of central -s to be default silent,
 * -s turns silent off.
 *
 * Revision 2.2  2000/07/03 17:03:24  hersh
 * Removed all instances of "tca" in symbols and messages, plus changed
 * the names of all other symbols which conflicted with TCA.  This new
 * version of IPC should be able to interoperate TCA fully.  Client
 * programs can now link to both tca and ipc.
 *
 * Revision 2.1.1.1  1999/11/23 19:07:35  reids
 * Putting IPC Version 2.9.0 under local (CMU) CVS control.
 *
 * Revision 1.1.2.4  1996/12/18 15:12:50  reids
 * Changed logging code to remove VxWorks dependence on varargs
 *
 * Revision 1.1.2.3  1996/10/28 16:20:33  reids
 * More informative hash table statistics.
 *
 * Revision 1.1.2.2  1996/10/18 18:05:54  reids
 * Fixed x_ipc_hashTableIterate.
 *
 * Revision 1.1.2.1  1996/10/16 15:21:33  reids
 * Fixed memory and other problems found with purify.
 *
 * Revision 1.1  1996/05/09 01:01:29  reids
 * Moved all the X_IPC files over to the IPC directory.
 * Fixed problem with sending NULL data.
 * Added function IPC_setCapacity.
 * VxWorks m68k version released.
 *
 * Revision 1.1  1996/03/03 04:31:35  reids
 * First release of IPC files.  X_IPC code (8.5), modified to support NM-DS1 IPC.
 *
 * Revision 1.15  1996/06/25  20:50:43  rich
 * Fixed memory and other problems found with purify.
 *
 * Revision 1.14  1996/02/10  16:50:01  rich
 * Fixed header problems and a crash related to direct connections.
 *
 * Revision 1.13  1996/02/06  19:04:51  rich
 * Changes for VXWORKS pipes.  Note: the read and write sockets descriptors
 * can be different.
 *
 * Revision 1.12  1996/01/30  15:04:20  rich
 * Fixed var array index problem.  Index refers to the enclosing structure.
 * Added ability to force 32 bit enums and changed some #defs to enums to
 * ease debugging.  Fixed initialization problems for central.
 *
 * Revision 1.11  1996/01/12  00:53:13  rich
 * Simplified GNUmakefiles.  Fixed some dbmalloc problems.
 *
 * Revision 1.10  1995/12/17  20:21:30  rich
 * Have free routines set pointers to NULL.
 * Removed old makefiles.
 *
 * Revision 1.9  1995/10/25  22:48:29  rich
 * Fixed problems with context switching.  Now the context is a separate
 * data structure accessed from the module data structure, using the
 * currentContext field.  GET_C_GLOBAL is used instead of GET_M_GLOBAL for
 * the context dependent fields.
 *
 * Revision 1.8  1995/10/07  19:07:24  rich
 * Pre-alpha release of x_ipc-8.2.
 * Added PROJECT_DIR. Added x_ipcWillListen.
 * Only transmit broadcast messages when there is a handler to receive them.
 * All system messages now start with "x_ipc_".  Old messages are also supported.
 *
 * Revision 1.7  1995/07/06  21:16:35  rich
 * Solaris and Linux changes.
 *
 * Revision 1.6  1995/01/18  22:40:45  rich
 * X_IPC 7.9: Speed improvements.
 * Use unix sockets for communication on the same machine.
 * Eliminate copying.
 * Optimize loop for arrays, especially simple, primitive arrays.
 * Optimize the buffer size.
 *
 * Revision 1.5  1994/05/25  04:57:31  rich
 * Defined macros for registering simple messages and handlers at once.
 * Added function to ignore logging for all messages associated with a
 * global variable.
 * Moved module global variable routines to a new file so they are not
 * included in the .sa library file.  Gets better code sharing and lets you
 * debug these routines.
 * Added code to force the module variables to be re-initialized after the
 * server goes down.
 * x_ipcClose now will not crash if the server is down and frees some module
 * memory.
 * The command line flag "-u" turns off the simple user interface.
 * Added routines to free hash tables and id tables.
 *
 * Revision 1.4  1993/08/30  21:53:41  fedor
 * V7+V6+VXWORKS Everything compiles but there are initialization problems.
 *
 * Revision 1.5  1993/08/23  17:38:29  rich
 * Fixed the type definitions for function pointers. Added const
 * declarations.  Removed definitions VOID_FN and INT_FN.
 *
 * Revision 1.4  1993/07/08  05:38:32  rich
 * Added function prototypes
 *
 * Revision 1.3  1993/06/22  13:59:07  rich
 * Added makefile.depend.  Dependencies automatically generated using gcc.
 * Fixed some warnings.
 * Updated the -D<arch> flags to correspond to those generated
 * automatically by the makefile.
 * Changed system includes to the proper format "stdio.h" -> <stdio.h>.
 * This was needed so that the automatic dependency generation can
 * distinguish between "local" and system headers.  The location of the
 * system headers changes from architecture to architecture and should not
 * be included in the dependency list.
 *
 * Revision 1.2  1993/05/27  22:17:15  rich
 * Added automatic logging.
 *
 *  6-Apr-90 Christopher Fedor, School of Computer Science, CMU
 * Revised to Software Standards.
 *
 * 17-Nov-89 Reid Simmons, School of Computer Science, CMU
 * Added function for printing hash table stats.
 *
 * 10-Feb-89 Christopher Fedor, School of Computer Science, CMU
 * Created.
 *
 *****************************************************************************/

#include "globalM.h"

/******************************************************************************
 * Forward Declarations
 *****************************************************************************/

#if !defined(DBMALLOC)
HASH_TABLE_PTR x_ipc_hashTableDBCreate(int32 size, HASH_FN hashFunc, 
				 EQ_HASH_FN eqFunc,const char* file, int line);
#else
#undef x_ipc_hashTableCreate
HASH_TABLE_PTR x_ipc_hashTableCreate(int32 size, HASH_FN hashFunc,
			       EQ_HASH_FN eqFunc);
#endif


/******************************************************************************
 *
 * FUNCTION: HASH_TABLE_PTR x_ipc_hashTableCreate(size, hashFunc, eqFunc)
 *
 * DESCRIPTION:
 * Create a hash table.
 *
 * HashFunc(Key)  
 * - returns a hash value of the key.
 * - this value will be modded with table size when (key, item) pair
 *   is added to the table.
 *
 * EqualityFunc(Key1, Key2) 
 * - returns 1 if the key values Key1 and Key2 are equal, 0 otherwise.
 *
 * INPUTS: 
 * int32 size;
 * HASH_FN hashFunc;
 * EQ_HASH_FN eqFunc;
 *
 * OUTPUTS: HASH_TABLE_PTR
 *
 *****************************************************************************/

#if defined(DBMALLOC)
HASH_TABLE_PTR x_ipc_hashTableCreate(int32 size, HASH_FN hashFunc, EQ_HASH_FN eqFunc)
{
  return x_ipc_hashTableDBCreate(size, hashFunc, eqFunc, "Unknown", 9999);
}

HASH_TABLE_PTR x_ipc_hashTableDBCreate(int32 size, HASH_FN hashFunc, 
				 EQ_HASH_FN eqFunc,const char* file, int line)
     
#else
HASH_TABLE_PTR x_ipc_hashTableDBCreate(int32 size, HASH_FN hashFunc, 
					EQ_HASH_FN eqFunc,
					const char* file, int line)
{
#ifdef UNUSED_PRAGMA
#pragma unused(file, line)
#endif
  return x_ipc_hashTableCreate( size, hashFunc, eqFunc);
}     

HASH_TABLE_PTR x_ipc_hashTableCreate(int32 size, HASH_FN hashFunc, EQ_HASH_FN eqFunc)
#endif
{
  int32 i;
  HASH_ELEM_PTR *table;
  HASH_TABLE_PTR hashTable;
  
#if defined(DBMALLOC)
  hashTable = NEW_DB(file,line,HASH_TABLE_TYPE);
  table = (HASH_ELEM_PTR *)x_ipcDBMalloc(file,line,
				       sizeof(HASH_ELEM_PTR)*(unsigned)size);
#else
  hashTable = NEW(HASH_TABLE_TYPE);
  table = (HASH_ELEM_PTR *)x_ipcMalloc(sizeof(HASH_ELEM_PTR)*(unsigned)size);
#endif
  
  for(i=0;i < size;i++)
    table[i] = NULL;
  
  hashTable->size = size;
  hashTable->hashFunc = hashFunc;
  hashTable->eqFunc = eqFunc;
  hashTable->table = table;
  
  return hashTable;
}


/******************************************************************************
 *
 * FUNCTION: void x_ipc_hashTableFree(HASH_TABLE_PTR table);
 *
 * DESCRIPTION:
 * Release memory used by hash table.  Does not release the memory for the 
 * items in the hash table.
 *
 *****************************************************************************/
static void x_ipc_hashElementFree(HASH_ELEM_PTR element)
{
  
  if (element != NULL) {
    x_ipc_hashElementFree(element->next);
    if (element->key) {
      x_ipcFree((char *) element->key);
      element->key = NULL;
    }
    x_ipcFree((char *) element);
  }
}

int32 x_ipc_hashItemsFree(const void *key, const void *item, void *ignore)
{
#ifdef UNUSED_PRAGMA
#pragma unused(key, ignore)
#endif
  if (item != NULL)
    x_ipcFree((char *)item);
  return 1;
}

void x_ipc_hashTableFree(HASH_TABLE_PTR *table,HASH_ITER_FN iterFunc, void *param)
{
  int32 i;
  
  if (*table == NULL)
    return;
  
  if (iterFunc != NULL) 
    x_ipc_hashTableIterate(iterFunc, *table, param);
  
  for (i=0;i < (*table)->size;i++) {
    x_ipc_hashElementFree((*table)->table[i]);
  }
  x_ipcFree((char *)(*table)->table);
  x_ipcFree((char *)*table);
  *table = NULL;
}


/******************************************************************************
 *
 * FUNCTION: HASH_ELEM_PTR x_ipc_findElement(eq, list, key)
 *
 * DESCRIPTION: 
 * Iterate through the element list returning the element of the list whose
 * key value is "eq" to the given key. NULL is returned if no match is found.
 *
 * INPUTS:
 * EQ_HASH_FN eq;
 * HASH_ELEM_PTR list;
 * char *key;
 *
 * OUTPUTS: HASH_ELEM_PTR
 *
 *****************************************************************************/

static HASH_ELEM_PTR x_ipc_findElement(EQ_HASH_FN eq, 
				 HASH_ELEM_PTR list, 
				 const void *key)
{
  HASH_ELEM_PTR tmp;
  
  tmp = list;
  while (tmp) {
    if ((*eq)(tmp->key, key))
      return tmp;
    tmp = tmp->next;
  }
  
  return NULL;
}


/******************************************************************************
 *
 * FUNCTION: char *x_ipc_hashTableFind(key, table)
 *
 * DESCRIPTION: The item is returned or NULL if not found.
 *
 * INPUTS: 
 * char *key;
 * HASH_TABLE_PTR table;
 *
 * OUTPUTS: char *
 *
 *****************************************************************************/

const void *x_ipc_hashTableFind(const void *key, HASH_TABLE_PTR table)
{
  HASH_ELEM_PTR tmp;
  int32 hash, location;
  
  if (table == NULL) return NULL;
  hash = (*table->hashFunc)(key);
  location = hash % table->size;
  
  tmp = table->table[location];
  if (tmp) {
    tmp = x_ipc_findElement(table->eqFunc, tmp, key);
    if (tmp)
      return(tmp->data);
    else
      return NULL;
  }
  else
    return NULL;
}


/******************************************************************************
 *
 * FUNCTION: char *x_ipc_hashTableInvFind(item, table)
 *
 * DESCRIPTION: The key for the given item ptr.
 *
 * INPUTS: 
 * char *key;
 * HASH_TABLE_PTR table;
 *
 * OUTPUTS: char *
 *
 *****************************************************************************/

const void *x_ipc_hashTableInvFind(const void *item, HASH_TABLE_PTR table)
{
  int32 i;
  HASH_ELEM_PTR tmp;
  
  for (i=0;i < table->size;i++) {
    tmp = table->table[i];
    while (tmp){
      if ((const void *)tmp->data == item)
	return tmp->key;
      tmp = tmp->next;
    }
  }
  return NULL;
}


/******************************************************************************
 *
 * FUNCTION: char *x_ipc_hashTableInsert(key, keySize, item, table)
 *
 * DESCRIPTION:
 * The key value is copied for future lookups.
 * The old Item will be returned if the new item replaces 
 * one of the same key value. 
 * - A warning is also issued - the warning may go away soon.
 *
 * INPUTS: 
 * char *key;
 * int32 keySize;
 * char *item;
 * HASH_TABLE_PTR table;
 *
 * OUTPUTS: char * (any item that was already stored under this key or NULL)
 *
 * NOTES:
 * It is common to use a string key be sure that keySize is then
 * equal to strlen(key)+1 so that the NULL terminator is also stored.
 *
 *****************************************************************************/

const void *x_ipc_hashTableInsert(const void *key, int32 keySize, 
			    const void *item, HASH_TABLE_PTR table)
{
  const char *oldData;
  int32 hash, location;
  HASH_ELEM_PTR tmp, element;
  char *keyPtr=NULL;
  
  hash = (*table->hashFunc)(key);
  location = hash % table->size;
  
  tmp = table->table[location];
  if (tmp) {
    tmp = x_ipc_findElement(table->eqFunc, tmp, key);
    if (tmp) {
      /* replace item with new information */
      oldData = tmp->data;
      tmp->data = (const char *)item;
      return oldData;
    }
  }
  
  element = NEW(HASH_ELEM_TYPE);
  keyPtr = (char *)x_ipcMalloc((unsigned)keySize);
  BCOPY(key, keyPtr, keySize);
  element->key = (const char *) keyPtr;
  element->data = (const char *)item;
  element->next = table->table[location];
  table->table[location] = element;
  
  return NULL;
}


/******************************************************************************
 *
 * FUNCTION: char *x_ipc_hashTableRemove(key, table)
 *
 * DESCRIPTION: The item stored with this key value is returned.
 *
 * INPUTS: 
 * char *key;
 * HASH_TABLE_PTR table;
 *
 * OUTPUTS: char *
 *
 *****************************************************************************/

const void *x_ipc_hashTableRemove(const void *key, HASH_TABLE_PTR table)
{
  EQ_HASH_FN eq;
  const char *oldData;
  int32 hash, location;
  HASH_ELEM_PTR previous, current;
  
  hash = (*table->hashFunc)(key);
  location = hash % table->size;
  
  eq = table->eqFunc;
  
  previous = table->table[location];
  if (!previous)
    return NULL;
  
  if ((*eq)(previous->key, key)) {
    table->table[location] = previous->next;
    oldData = previous->data;
    x_ipcFree((char *)previous->key);
    x_ipcFree((char *)previous);
    return oldData;
  }
  current = previous->next;
  while (current) {
    if ((*eq)(current->key, key)) {
      oldData = current->data;
      previous->next = current->next;
      x_ipcFree((char *)current->key);
      x_ipcFree((char *)current);
      return oldData;
    }
    previous = current;
    current = current->next;
  }
  
  return NULL;
}


/******************************************************************************
 *
 * FUNCTION: void x_ipc_hashTableIterate(iterFunc, table, param)
 *
 * DESCRIPTION:
 * iterFunc(key, data)
 * char *key, *data;
 *
 * iterFunc
 *  - takes two arguments, a pointer to Key information and a pointer to Data.
 *
 * x_ipc_hashTableIterate will call the function on all of its elements stoping
 * when the list is finished or when iterFunc returns 0 (ie FALSE)
 *
 * INPUTS: 
 * INT_FN iterFunc;
 * HASH_TABLE_PTR table;
 *
 * OUTPUTS: void.
 *
 *****************************************************************************/

void x_ipc_hashTableIterate(HASH_ITER_FN iterFunc, HASH_TABLE_PTR table, void *param)
{
  int32 i;
  HASH_ELEM_PTR tmp,next;
  
  for (i=0;i < table->size;i++) {
    tmp = table->table[i];
    while (tmp){
      next = tmp->next;
      if (!(*iterFunc)(tmp->key, tmp->data, param))
	return;
      tmp = next;
    }
  }
}


/******************************************************************************
 *
 * FUNCTION: void x_ipc_hashTableStats(hashTable)
 *
 * DESCRIPTION: Calculates and displays hash table stats.
 *
 * INPUTS: HASH_TABLE_PTR hashTable;
 *
 * OUTPUTS: void. Results displayed.
 *
 *****************************************************************************/

void x_ipc_hashTableStats(HASH_TABLE_PTR hashTable)
{
  HASH_ELEM_PTR elem;
  int32 i, num=0, max=0, full=0, length;
  
  if (hashTable) {
    for (i=0;i < hashTable->size; i++) {
      elem = hashTable->table[i];
      if (elem) {
	full++;
	length = 0;
	while (elem) {
	  num++;
	  length++;
	  elem = elem->next;
	}
	if (length > max) 
	  max = length;
      }
    }
    X_IPC_MOD_WARNING2("x_ipc_hashTableStats: Has %d elements in %d slots\n",
		  num, full);
    X_IPC_MOD_WARNING2("   Out of %d slots (%.0f percent full)\n", 
		  hashTable->size, 100*((double)full/hashTable->size));
    X_IPC_MOD_WARNING2("   Average list is %.1f; maximum is %d\n", 
		  (double)num/full, max);
  } else {
    X_IPC_MOD_WARNING("x_ipc_hashTableStats: No Hash Table\n");
  }
}


/******************************************************************************
 *
 * FUNCTION: void x_ipc_hashTableCount(hashTable)
 *
 * DESCRIPTION: Calculates number of items in the hash table.
 *
 * INPUTS: HASH_TABLE_PTR hashTable;
 *
 * OUTPUTS: int, the count.
 *
 *****************************************************************************/

int x_ipc_hashTableCount(HASH_TABLE_PTR hashTable)
{
  HASH_ELEM_PTR elem;
  int32 i, num=0;
  
  for (i=0;i < hashTable->size; i++) {
    elem = hashTable->table[i];
    if (elem) {
      while (elem) {
	num++;
	elem = elem->next;
      }
    }
  }
  return num;
}
