circlemud_squared_0.5.153/cnf/
circlemud_squared_0.5.153/etc/
circlemud_squared_0.5.153/etc/etc/
circlemud_squared_0.5.153/etc/house/
circlemud_squared_0.5.153/etc/misc/
circlemud_squared_0.5.153/etc/plralias/A-E/
circlemud_squared_0.5.153/etc/plralias/F-J/
circlemud_squared_0.5.153/etc/plralias/K-O/
circlemud_squared_0.5.153/etc/plralias/P-T/
circlemud_squared_0.5.153/etc/plralias/U-Z/
circlemud_squared_0.5.153/etc/plralias/ZZZ/
circlemud_squared_0.5.153/etc/plrobjs/
circlemud_squared_0.5.153/etc/plrobjs/A-E/
circlemud_squared_0.5.153/etc/plrobjs/F-J/
circlemud_squared_0.5.153/etc/plrobjs/K-O/
circlemud_squared_0.5.153/etc/plrobjs/P-T/
circlemud_squared_0.5.153/etc/plrobjs/U-Z/
circlemud_squared_0.5.153/etc/plrobjs/ZZZ/
circlemud_squared_0.5.153/etc/text/
circlemud_squared_0.5.153/etc/text/help/
circlemud_squared_0.5.153/src/util/
circlemud_squared_0.5.153/src/util/worldconv/
/**
 * @file hashMap.c
 * @ingroup collection
 *
 * Hashmap module
 *
 * @author Geoff Davis <geoff@circlemudsquared.org>
 *
 * @par Copyright:
 *   Copyright (C) 2006 Geoff Davis <geoff@circlemudsquared.org><br>
 *                      Greg Buxton <greg@circlemudsquared.org>
 *
 * @par
 *   Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br>
 *   CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
 *
 * @par
 *   All rights reserved.  See license.doc for complete information.
 *
 * @package cs
 * @version 1.0
 */

#define __HASHMAP_C__

#include "base.h"
#include "hashMap.h"
#include "log.h"
#include "memory.h"

/*
 * hashmap functions
 */

/**
 * Removes all elements from a hashmap.
 * @param hashMap the hashmap to be cleared
 * @return none
 */
void hashMap_clear(hashMap_t *hashMap)
{
  if (hashMap == NULL) {
    log("hashMap_clear(): invalid 'hashMap' hashMap_t.");
  } else if (hashMap->size) {
    /* Declare an iterator variable. */
    register int i = 0;

    /* Iterate over the hashmap's entry table. */
    for (i = 0; i < hashMap->entryTableSize; i++) {
      /* Delete any entries found in any hash list. */
      while (hashMap->entryTable[i]) {
        hashMapPrivate_deleteEntry(hashMap, hashMap->entryTable[i]);
      }
    }
  }
}

/**
 * Returns whether a hashmap contains a particular key.
 * @param hashMap the hashmap whose contents are to be tested
 * @param key the key for which to test
 * @return true if the hashmap contains a matching key;
 *         false otherwise
 */
bool hashMap_containsKey(hashMap_t *hashMap, const void *key) {
  if (hashMap == NULL) {
    log("hashMap_containsKey(): invalid 'hashMap' hashMap_t.");
  } else if (hashMap->size) {
    /* Find the entry for the user-provided key. */
    hashMapEntry_t *entry = hashMapPrivate_getEntry(hashMap, key);

    if (entry) {
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
 * Constructs a new hashmap.
 * @param entryTableSize the initial size of the hashmap entry table
 * @param hashFunction the hash function to be used to hash keys
 * @param keyFreeFunction the free function to be called for map keys
 * @param valueFreeFunction the free function to be called for map values
 * @return a new hashmap, or NULL
 */
hashMap_t *hashMap_create(size_t entryTableSize, hashFunction_t hashFunction,
  freeFunction_t keyFreeFunction, freeFunction_t valueFreeFunction) {
  if (hashFunction == NULL) {
    log("hashMap_create(): invalid 'hashFunction' hashFunction_t.");
  } else {
    /* Allocate memory for a hashMap_t. */
    hashMap_t *hashMap = CIRCLE_ALLOCN(hashMap_t, 1);

    if (hashMap == NULL) {
      log("hashMap_create(): CIRCLE_ALLOCN() failed.");
    } else {
      /* If 'entryTableSize' is zero, pick a prime number. */
      if (entryTableSize == 0) {
        entryTableSize = 1009;
      }

      /* Allocate memory for the hashmap's entry table. */
      hashMap->entryTable = CIRCLE_ALLOCN(hashMapEntry_t*, entryTableSize);

      if (hashMap->entryTable == NULL) {
        log("hashMap_create(): CIRCLE_ALLOCN() failed.");
        free(hashMap);
      } else {
        /* Initialize the hashmap. */
        hashMap->entryTableSize = entryTableSize;
        hashMap->keyFreeFunction = keyFreeFunction;
        hashMap->keyHashFunction = hashFunction;
        hashMap->valueFreeFunction = valueFreeFunction;

        return (hashMap);
      }
    }
  }
  return (NULL);
}

/**
 * Deletes a mapping from a hashmap.
 * @param hashMap the hashmap from which to remove a mapping
 * @param key the key of the mapping to be removed
 * @return true if the size of the hashmap was changed by this function;
 *         false otherwise
 */
bool hashMap_delete(hashMap_t *hashMap, const void *key) {
  if (hashMap == NULL) {
    log("hashMap_delete(): invalid 'hashMap' hashMap_t.");
  } else if (hashMap->size) {
    /* Get the hashmap entry for the user-provided key. */
    hashMapEntry_t *entry = hashMapPrivate_getEntry(hashMap, key);

    if (entry) {
      hashMapPrivate_deleteEntry(hashMap, entry);
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
 * Frees a hashmap.
 * @param hashMap the hashmap to be freed
 * @return none
 */
void hashMap_free(hashMap_t *hashMap) {
  if (hashMap == NULL) {
    log("hashMap_free(): invalid 'hashMap' hashMap_t.");
  } else {
    hashMap_clear(hashMap);
    free(hashMap);
  }
}

/**
 * Returns the size of a hashmap.
 * @param hashMap the hashmap to be tested
 * @return the size of the hashmap, or zero (0)
 */
size_t hashMap_getSize(hashMap_t *hashMap) {
  if (hashMap == NULL) {
    log("hashMap_getSize(): invalid 'hashMap' hashMap_t.");
  } else {
    return (hashMap->size);
  }
  return (0);
}

/**
 * Returns the value for a given key.
 * @param hashMap the hashmap to be searched
 * @param key the key for which to retreive a value
 * @param defaultValue the value to be retuned if the key cannot be not found
 * @return the value associated with the key, the default value, or NULL
 */
void *hashMap_getValue(hashMap_t *hashMap, const void *key, void *defaultValue) {
  if (hashMap == NULL) {
    log("hashMap_getValue(): invalid 'hashMap' hashMap_t.");
  } else {
    /* Get the hashmap entry for the user-provided key. */
    hashMapEntry_t *entry = hashMapPrivate_getEntry(hashMap, key);

    if (entry) {
      return (entry->value);
    }
  }
  return (defaultValue);
}

/**
 * Returns whether a hashmap is empty.
 * @param hashMap the hashmap to be tested
 * @return true if the hashmap is empty, having a size of zero (0);
 *         false otherwise
 */
bool hashMap_isEmpty(hashMap_t *hashMap) {
  if (hashMap == NULL) {
    log("hashMap_isEmpty(): invalid 'hashMap' hashMap_t.");
  } else {
    if (hashMap->size == 0) {
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
 * Inserts a key-to-value mapping into a hashmap.
 * @param hashMap the hashmap into which a mapping is to be inserted
 * @param key the key to be inserted
 * @param value the value to be inserted
 * @return true if the size of the hashmap was modified by this function;
 *         false otherwise
 */
bool hashMap_insert(hashMap_t *hashMap, void *key, void *value) {
  if (hashMap == NULL) {
    log("hashMap_insert(): invalid 'hashMap' hashMap_t.");
  } else {
    /* Save off the original size of the hashmap. */
    const size_t size = hashMap->size;

    /* Find the entry for the user-provided key. */
    hashMapEntry_t *entry = hashMapPrivate_getEntry(hashMap, key);

    /*
     * If an entry was located, we will do an "in-place" insertion
     * by overwriting the its key and value. Otherwise, a new entry
     * must be inserted.
     */

    if (entry) {
      /* If possible, free the entry's key. */
      if (hashMap->keyFreeFunction) {
        hashMap->keyFreeFunction(entry->key);
      }
      /* If possible, free the entry's value. */
      if (hashMap->valueFreeFunction) {
        hashMap->valueFreeFunction(entry->value);
      }
      /* Overwrite the entry's key and value. */
      entry->key = key;
      entry->value = value;
    } else {
      /* Get a hash key for the user-provided key. */
      const hashKey_t hashKey = hashMap->keyHashFunction(key);

      /* Calculate the entry table index where the entry is stored. */
      const size_t entryTableIndex = hashKey % hashMap->entryTableSize;

      /* Allocate memory for a hashMapEntry_t. */
      hashMapEntry_t *entry = CIRCLE_ALLOCN(hashMapEntry_t, 1);

      if (entry == NULL) {
        log("hashMap_insert(): CIRCLE_ALLOCN() failed.");
      } else {
        /* Initialize the new hash entry. */
        entry->hashKey = hashKey;
        entry->key = key;
        entry->nextEntry = NULL;
        entry->value = value;

        /* Add the new hash entry to the hash list. */
        entry->nextEntry = hashMap->entryTable[entryTableIndex];
        hashMap->entryTable[entryTableIndex] = entry;

        /* Increment the size of the hashmap. */
        hashMap->size++;
      }
    }
    if (size != hashMap->size) {
      return (TRUE);
    }
  }
  return (FALSE);
}

/*
 * hashmap iterator functions
 */

/**
 * Example of hashMapIterator implementation
 * @example hashMapIterator_example.c
 */

/**
 * Constructs a new hashmap iterator.
 * @param hashMap the hashmap to be iterated
 * @return a new hashmap iterator, or NULL
 */
hashMapIterator_t *hashMapIterator_create(hashMap_t *hashMap) {
  if (hashMap == NULL) {
    log("hashMapIterator_create(): invalid 'hashMap' hashMap_t.");
  } else {
    /* Allocate memory for a hashMapIterator_t. */
    hashMapIterator_t *iter = CIRCLE_ALLOCN(hashMapIterator_t, 1);

    if (iter == NULL) {
      log("hashMapIterator_create(): CIRCLE_ALLOCN() failed.");
    } else {
      /* Initialize the new hashmap iterator. */
      iter->hashMap = hashMap;
      iter->lastReturned = NULL;
      iter->nextEntryIndex = 0;
      iter->nextEntry = NULL;


      if (hashMap->size) {
        /* Find the first entry and set lastReturned to it */
        while (hashMap->entryTable[iter->nextEntryIndex] == NULL && iter->nextEntryIndex < hashMap->entryTableSize) {
          ++iter->nextEntryIndex;
        }
        if (iter->nextEntryIndex < hashMap->entryTableSize) {
          iter->lastReturned = hashMap->entryTable[iter->nextEntryIndex];
        }
        /* Advance */
        ++iter->nextEntryIndex;
        /* Next is the nextEntry */
        while (hashMap->entryTable[iter->nextEntryIndex] == NULL && iter->nextEntryIndex < hashMap->entryTableSize) {
          ++iter->nextEntryIndex;
        }
        if (iter->nextEntryIndex < hashMap->entryTableSize) {
          iter->nextEntry = hashMap->entryTable[iter->nextEntryIndex];
        }

        return (iter);
      } else {
        iter->nextEntryIndex = hashMap->entryTableSize;
      }
    }
  }
  return (NULL);
}

/**
 * Frees a hashmap iterator.
 * @param iter the hashmap iterator to be freed
 * @return none
 */
void hashMapIterator_free(hashMapIterator_t *iter) {
  if (iter) {
    free(iter);
    iter = NULL;
  }
}

/**
 * Returns whether a hashmap iterator has more mappings.
 * @param iter the hashmap iterator to be tested
 * @return true if there are more mappings to be iterated;
 *         false otherwise
 */
bool hashMapIterator_hasNext(hashMapIterator_t *iter) {
  if (iter == NULL) {
    log("hashMapIterator_hasNext(): invalid 'iter' hashMapIterator_t.");
  } else {
    if (iter->nextEntry) {
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
 * Returns the key of the next mapping in the underlying hashmap and
 * advances the hashmap iterator.
 * @param iter the hashmap iterator to be advanced
 * @return the next key in the hashmap, or NULL
 */
void *hashMapIterator_nextKey(hashMapIterator_t *iter) {
  return (NULL);
}

/**
 * Returns the value of the next mapping in the underlying hashmap and
 * advances the hashmap iterator.
 * @param iter the hashmap iterator to be advanced
 * @return the next value in the hashmap, or NULL
 */
void *hashMapIterator_nextValue(hashMapIterator_t *iter) {
  return (NULL);
}

/**
 * Removes the last element returned by a hashmap iterator.
 * @param iter the hashmap iterator from which to remove an element
 * @return none
 */
void hashMapIterator_remove(hashMapIterator_t *iter) {
  if (iter == NULL) {
    log("hashMapIterator_remove(): invalid 'iter' hashMapIterator_t.");
  } else if (iter->lastReturned) {
    hashMapPrivate_deleteEntry(iter->hashMap, iter->lastReturned);
    iter->lastReturned = NULL;
  }
}

/*
 * private hashmap functions
 */

/**
 * Removes a hashmap entry from a hashmap and updates the hashmap size
 * using a pair of free functions.
 * @param hashMap the hashmap whose entry is to be unlinked
 * @param entry the entry to be removed
 * @param freeKeyFunction the free function to be called for each key
 * @param freeValueFunction the free function to be called for each value
 * @return none
 */                    
void hashMapPrivate_deleteEntry(hashMap_t *hashMap, hashMapEntry_t *entry) {
  if (hashMap == NULL) {
    log("hashMapPrivate_deleteEntry(): invalid 'hashMap' hashMap_t.");
  } else {
    /* Save off the current size of the hashmap. */
    const size_t size = hashMap->size;

    /* Calculate the entry table index where the entry is stored. */
    const size_t entryTableIndex = entry->hashKey % hashMap->entryTableSize;

    /* Declare an iterator variable. */
    register hashMapEntry_t *tempEntry = NULL;

    if (hashMap->entryTable[entryTableIndex] == entry) {
      /* Delete the entry from the hash list. */
      hashMap->entryTable[entryTableIndex] = entry->nextEntry;
      hashMap->size--;
    } else {
      /* Search the hash list for the specified hashmap entry. */
      for (tempEntry = hashMap->entryTable[entryTableIndex];
           tempEntry && tempEntry->nextEntry != entry;
           tempEntry = tempEntry->nextEntry);

      /* Delete the entry from the hash list. */
      if (tempEntry && tempEntry->nextEntry == entry) {
        tempEntry->nextEntry = entry->nextEntry;
        hashMap->size--;
      }
    }

    if (size == hashMap->size) {
      log("hashMapPrivate_deleteEntry(): hashmap entry is not in hashmap.");
    } else {
      /* If possible, free the entry's key. */
      if (hashMap->keyFreeFunction) {
        hashMap->keyFreeFunction(entry->key);
      }

      /* If possible, free the entry's value. */
      if (hashMap->valueFreeFunction) {
        hashMap->valueFreeFunction(entry->value);
      }

      /* Now free the hashmap entry. */
      free(entry);
    }
  }
}

/**
 * Returns the hashmap entry corresponding to a particular key.
 * @param hashMap the hashmap whose entry is to be retrieved
 * @return the matching hashmap entry, or NULL
 */
hashMapEntry_t *hashMapPrivate_getEntry(hashMap_t *hashMap, const void *key) {
  if (hashMap == NULL) {
    log("hashMapPrivate_getEntry(): invalid 'hashMap' hashMap_t.");
  } else if (hashMap->size) {
    /* Get a hash key for the user-provided key. */
    const hashKey_t hashKey = hashMap->keyHashFunction(key);

    /* Calculate the entry table index where the entry is stored. */
    const size_t entryTableIndex = hashKey % hashMap->entryTableSize;

    /* Declare an iterator variable. */
    register hashMapEntry_t *entry = NULL;

    /* Search the hash list for the hash key. */
    for (entry = hashMap->entryTable[entryTableIndex]; entry;
         entry = entry->nextEntry) {
      if (entry->hashKey == hashKey) {
        return (entry);
      }
    }
  }
  return (NULL);
}

/*
 * Let's see if we can fix up the iterators
 */

/**
 * Advance the hashMapIterator
 *
 * lastReturned is set with nextEntry
 * nextEntryIndex and nextEntry are advanced or NULL'd/max'd
 *
 * @param hashMapIterator_t to advance
 *
 */
void hashMapIterator_next(hashMapIterator_t *iter) {

  if (iter == NULL) {
    log("hashMapIterator_next(): Invalid hashMapIterator_t 'iter'.");
  } else {
    if (iter->nextEntry) {
      iter->lastReturned = iter->nextEntry;

      if (iter->hashMap->size) {
        if (iter->hashMap->entryTable[iter->nextEntryIndex] != NULL) {
          ++iter->nextEntryIndex;
        }

        while (iter->hashMap->entryTable[iter->nextEntryIndex] == NULL && iter->nextEntryIndex < iter->hashMap->entryTableSize) {
          ++iter->nextEntryIndex;
        }

        if (iter->nextEntryIndex < iter->hashMap->entryTableSize) {
          iter->nextEntry = iter->hashMap->entryTable[iter->nextEntryIndex];
        } else {
          iter->nextEntry = NULL;
        }

      } else {
        iter->nextEntryIndex = iter->hashMap->entryTableSize;
      }    
    } else {
      iter->nextEntryIndex = iter->hashMap->entryTableSize;
      iter->lastReturned = NULL;
      iter->nextEntry = NULL;
    }
  }
}

/**
 * Get the value pointed to by the current iterator entry
 */
void *hashMapIterator_getValue(hashMapIterator_t *iter) {

  if (iter) {
    if (iter->lastReturned) {
      return iter->lastReturned->value;
    }
  }
  return NULL;
}