nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// map.c
//
// similar to a hashtable, but keys as well as values can be can be anything.
//
//*****************************************************************************

#include <stdlib.h>
#include "list.h"
#include "map.h"


// how big of a size does our map start out at?
#define DEFAULT_MAP_SIZE        5


int gen_hash_cmp(const void *key1, const void *key2) {
  int val = (key2 - key1);
  if(val < 0)      return -1;
  else if(val > 0) return  1;
  else             return  0;
  return val;
}

int gen_hash_func(const void *key) {
  int val = (int)key;
  if(val < 0) return -val;
  else        return  val;
}

struct map_iterator {
  int curr_bucket;
  MAP *map;
  LIST_ITERATOR *bucket_i;
};

typedef struct map_entry {
  const void *key;
  void *val;
} MAP_ENTRY;

struct map_data {
  int size;
  int num_buckets;
  struct list **buckets;
  int (* hash_func)(const void *key);
  int (*  compares)(const void *key1, const void *key2);
};


//
// an internal form of hashGet that returns the entire entry (key and val)
//
MAP_ENTRY *mapGetEntry(MAP *map, const void *key) {
  int bucket = map->hash_func(key) % map->num_buckets;

  if(map->buckets[bucket] == NULL)
    return NULL;
  else {
    struct list_iterator *list_i = newListIterator(map->buckets[bucket]);
    MAP_ENTRY *elem   = NULL;

    for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
      if(!map->compares(key, elem->key))
	break;
    deleteListIterator(list_i);

    return elem;
  }
}


MAP_ENTRY *newMapEntry(const void *key, void *val) {
  MAP_ENTRY *entry = malloc(sizeof(MAP_ENTRY));
  entry->key = key;
  entry->val = val;
  return entry;
}

void deleteMapEntry(MAP_ENTRY *entry) {
  free(entry);
}


//
// Collect all of the MAP_ENTRYs in a map into a single list
LIST *mapCollectEntries(MAP *map) {
  LIST *list = newList();
  int i;
  for(i = 0; i < map->num_buckets; i++) {
    if(map->buckets[i] == NULL) continue;
    LIST_ITERATOR *list_i = newListIterator(map->buckets[i]);
    MAP_ENTRY       *elem = NULL;
    for(;(elem=listIteratorCurrent(list_i)) != NULL;listIteratorNext(list_i))
      listPut(list, elem);
    deleteListIterator(list_i);
  }
  return list;
}


//
// expand a map to the new size
void mapExpand(MAP *map, int size) {
  // collect all of the key:value pairs
  LIST     *entries = mapCollectEntries(map);
  MAP_ENTRY  *entry = NULL;
  int i;

  // delete all of the current buckets
  for(i = 0; i < map->num_buckets; i++) {
    if(map->buckets[i] == NULL) continue;
    deleteList(map->buckets[i]);
  }
  free(map->buckets);

  // now, make new buckets and set them to NULL
  map->buckets = calloc(size, sizeof(LIST *));
  map->num_buckets = size;

  // now, we put all of our entries back into the new buckets
  while((entry = listPop(entries)) != NULL) {
    int bucket = map->hash_func(entry->key) % map->num_buckets;
    if(map->buckets[bucket] == NULL) map->buckets[bucket] = newList();
    listPut(map->buckets[bucket], entry);
  }
  deleteList(entries);
}



//*****************************************************************************
//
// implementation of map.h
// documentation found in map.h
//
//*****************************************************************************
MAP *newMap(void *hash_func, void *compares) {
  int i;
  MAP *map = malloc(sizeof(MAP));
  map->size        = 0;
  map->num_buckets = DEFAULT_MAP_SIZE;
  map->hash_func   = (hash_func ? hash_func : gen_hash_func);
  map->compares    = (compares  ? compares  : gen_hash_cmp);
  map->buckets = malloc(sizeof(LIST *) * map->num_buckets);
  for(i = 0; i < map->num_buckets; i++)
    map->buckets[i] = NULL;
  return map;
}

void  deleteMap(MAP *map) {
  int i;
  for(i = 0; i < map->num_buckets; i++) {
    if(map->buckets[i])
      deleteListWith(map->buckets[i], deleteMapEntry);
  }
  free(map->buckets);
  free(map);
}

int  mapPut    (MAP *map, const void *key, void *val) {
  MAP_ENTRY *elem = mapGetEntry(map, key);

  // update the val if it's already here
  if(elem) {
    elem->val = val;
    return 1;
  }
  else {
    // first, see if we'll need to expand the map
    if((map->size * 80)/100 > map->num_buckets)
      mapExpand(map, (map->num_buckets * 150)/100);

    int bucket = map->hash_func(key) % map->num_buckets;

    // if the bucket doesn't exist yet, create it
    if(map->buckets[bucket] == NULL)
      map->buckets[bucket] = newList();

    MAP_ENTRY *entry = newMapEntry(key, val);
    listPut(map->buckets[bucket], entry);
    map->size++;
    return 1;
  }
}

void *mapGet    (MAP *map, const void *key) {
  MAP_ENTRY *elem = mapGetEntry(map, key);
  if(elem)
    return elem->val;
  else
    return NULL;
}

void *mapRemove (MAP *map, const void *key) {
  int bucket = map->hash_func(key) % map->num_buckets;

  if(map->buckets[bucket] == NULL)
    return NULL;
  else {
    struct list_iterator *list_i = newListIterator(map->buckets[bucket]);
    MAP_ENTRY *elem   = NULL;

    for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
      if(!map->compares(key, elem->key))
	break;
    deleteListIterator(list_i);

    if(elem) {
      void *val = elem->val;
      listRemove(map->buckets[bucket], elem);
      deleteMapEntry(elem);
      map->size--;
      return val;
    }
    else
      return NULL;
  }
}

int   mapIn     (MAP *map, const void *key) {
  return (mapGetEntry(map, key) != NULL);
}

int   mapSize   (MAP *map) {
  return map->size;
}


//*****************************************************************************
//
// implementation of the hashmap iterator
// documentation in hashmap.h
//
//*****************************************************************************
MAP_ITERATOR *newMapIterator(MAP *map) {
  MAP_ITERATOR *I = malloc(sizeof(MAP_ITERATOR));
  I->map = map;
  I->bucket_i = NULL;
  mapIteratorReset(I);

  return I;
}

void        deleteMapIterator     (MAP_ITERATOR *I) {
  if(I->bucket_i) deleteListIterator(I->bucket_i);
  free(I);
}

void        mapIteratorReset      (MAP_ITERATOR *I) {
  int i;

  if(I->bucket_i) deleteListIterator(I->bucket_i);
  I->bucket_i = NULL;
  I->curr_bucket = 0;

  // bucket_i will be NULL if there are no elements
  for(i = 0; i < I->map->num_buckets; i++) {
    if(I->map->buckets[i] != NULL &&
       listSize(I->map->buckets[i]) > 0) {
      I->curr_bucket = i;
      I->bucket_i = newListIterator(I->map->buckets[i]);
      break;
    }
  }
}


void        mapIteratorNext       (MAP_ITERATOR *I) {
  // no elements in the hashmap
  if(I->bucket_i == NULL)
    return;
  // we're at the end of our list
  else if(listIteratorNext(I->bucket_i) == NULL) {
    deleteListIterator(I->bucket_i);
    I->bucket_i = NULL;
    I->curr_bucket++;
    
    for(; I->curr_bucket < I->map->num_buckets; I->curr_bucket++) {
      if(I->map->buckets[I->curr_bucket] != NULL &&
	 listSize(I->map->buckets[I->curr_bucket]) > 0) {
	I->bucket_i = newListIterator(I->map->buckets[I->curr_bucket]);
	break;
      }
    }
  }
}

const void *mapIteratorCurrentKey(MAP_ITERATOR *I) {
  if(!I->bucket_i)
    return NULL;
  else {
    MAP_ENTRY *entry = ((MAP_ENTRY *) listIteratorCurrent(I->bucket_i));
    if(entry)
      return entry->key;
    else
      return NULL;
  }
}

void       *mapIteratorCurrentVal (MAP_ITERATOR *I) {
  if(!I->bucket_i) 
    return NULL;
  else {
    MAP_ENTRY *entry = ((MAP_ENTRY *) listIteratorCurrent(I->bucket_i));
    if(entry)
      return entry->val;
    else
      return NULL;
  }
}