/*
  This File is copied from
  
  http://www.oreilly.com/catalog/masteralgoc/index.html
  Mastering Algorithms with C
  By Kyle Loudon
  ISBN: 1-56592-453-3
  Publishd by O'Reilly
  
 */

/*****************************************************************************
 *                                                                            *
 *  ------------------------------- chtbl.c --------------------------------  *
 *                                                                            *
 *****************************************************************************/

#include <stdlib.h>
#include <string.h>

#include <include/list.h>
#include <include/chtbl.h>

/*****************************************************************************
 *                                                                            *
 *  ------------------------------ chtbl_init ------------------------------  *
 *                                                                            *
 *****************************************************************************/

int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
	       (*match)(const void *key1, const void *key2), void (*destroy)(void*data)) {

  int                i;

  /*****************************************************************************
   *                                                                            *
   *  Allocate space for the hash table.                                        *
   *                                                                            *
   *****************************************************************************/

  if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL)
    return -1;

  /*****************************************************************************
   *                                                                            *
   *  Initialize the buckets.                                                   *
   *                                                                            *
   *****************************************************************************/

  htbl->buckets = buckets;

  for (i = 0; i < htbl->buckets; i++)
    list_init(&htbl->table[i], destroy);

  /*****************************************************************************
   *                                                                            *
   *  Encapsulate the functions.                                                *
   *                                                                            *
   *****************************************************************************/

  htbl->h = h;
  htbl->match = match;
  htbl->destroy = destroy;

  /*****************************************************************************
   *                                                                            *
   *  Initialize the number of elements in the table.                           *
   *                                                                            *
   *****************************************************************************/

  htbl->size = 0;

  return 0;
} /* end chtbl_init () */

/*****************************************************************************
 *                                                                            *
 *  ---------------------------- chtbl_destroy -----------------------------  *
 *                                                                            *
 *****************************************************************************/

void chtbl_destroy(CHTbl *htbl) {

  int                i;

  /*****************************************************************************
   *                                                                            *
   *  Destroy each bucket.                                                      *
   *                                                                            *
   *****************************************************************************/

  for (i = 0; i < htbl->buckets; i++) {
    list_destroy(&htbl->table[i]);
  } /* end for () */

  /*****************************************************************************
   *                                                                            *
   *  Free the storage allocated for the hash table.                            *
   *                                                                            *
   *****************************************************************************/

  free(htbl->table);

  /*****************************************************************************
   *                                                                            *
   *  No operations are allowed now, but clear the structure as a precaution.   *
   *                                                                            *
   *****************************************************************************/

  memset(htbl, 0, sizeof(CHTbl));
  
  return;
} /* end chtbl_destroy() */

/*****************************************************************************
 *                                                                            *
 *  ----------------------------- chtbl_insert -----------------------------  *
 *                                                                            *
 *****************************************************************************/

int chtbl_insert(CHTbl *htbl, const void *data) {

  void               *temp;
  int                bucket,retval;
 
  /*****************************************************************************
   *                                                                            *
   *  Do nothing if the data is already in the table.                           *
   *                                                                            *
   *****************************************************************************/

  temp = (void *)data;

  if (chtbl_lookup(htbl, &temp) == 0)
    return 1;

  /*****************************************************************************
   *                                                                            *
   *  Hash the key.                                                             *
   *                                                                            *
   *****************************************************************************/

  bucket = htbl->h(data) % htbl->buckets;

  /*****************************************************************************
   *                                                                            *
   *  Insert the data into the bucket.                                          *
   *                                                                            *
   *****************************************************************************/

  if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
    htbl->size++;

  return retval;
} /* end chtbl_insert() */

/*****************************************************************************
 *                                                                            *
 *  ----------------------------- chtbl_remove -----------------------------  *
 *                                                                            *
 *****************************************************************************/

int chtbl_remove(CHTbl *htbl, void **data) {

  ListElmt           *element, *prev;
  int                bucket;
 
  /*****************************************************************************
   *                                                                            *
   *  Hash the key.                                                             *
   *                                                                            *
   *****************************************************************************/

  bucket = htbl->h(*data) % htbl->buckets;

  /*****************************************************************************
   *                                                                            *
   *  Search for the data in the bucket.                                        *
   *                                                                            *
   *****************************************************************************/

  prev = NULL;

  for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
    if (htbl->match(*data, list_data(element))) {

      /***********************************************************************
       *                                                                      *
       *  Remove the data from the bucket.                                    *
       *                                                                      *
       ***********************************************************************/

      if (list_rem_next(&htbl->table[bucket], prev, data) == 0) {
	htbl->size--;
	return 0;
      } /* end if() */
      else {
	return -1;
      }/* end else */
    }/* end if (htbl->match(*data, list_data(element))) */
    
    prev = element;
  }/* end for() */

  /*****************************************************************************
   *                                                                            *
   *  Return that the data was not found.                                       *
   *                                                                            *
   *****************************************************************************/

  return -1;
} /* end int chtbl_remove(CHTbl *htbl, void **data) */

/*****************************************************************************
 *                                                                            *
 *  ----------------------------- chtbl_lookup -----------------------------  *
 *                                                                            *
 *****************************************************************************/

int chtbl_lookup(const CHTbl *htbl, void **data) {

  ListElmt           *element;
  int                bucket;
 
  /*****************************************************************************
   *                                                                            *
   *  Hash the key.                                                             *
   *                                                                            *
   *****************************************************************************/

  bucket = htbl->h(*data) % htbl->buckets;

  /*****************************************************************************
   *                                                                            *
   *  Search for the data in the bucket.                                        *
   *                                                                            *
   *****************************************************************************/

  for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
    if (htbl->match(*data, list_data(element))) {

      /***********************************************************************
       *                                                                      *
       *  Pass back the data from the table.                                  *
       *                                                                      *
       ***********************************************************************/

      *data = list_data(element);
      return 0;
    }/* end if() */
  }/* end for() */

  /*****************************************************************************
   *                                                                            *
   *  Return that the data was not found.                                       *
   *                                                                            *
   *****************************************************************************/

  return -1;
} /* end chtbl_lookup() */


syntax highlighted by Code2HTML, v. 0.9.1