]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/uhash.c
ICU-8.11.4.tar.gz
[apple/icu.git] / icuSources / common / uhash.c
index 54bbdc8bd5f143b76f3546446f24355336a39dd1..7907205dbc75607a3f895bffdf74026bdbe53a31 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ******************************************************************************
 /*
 ******************************************************************************
-*   Copyright (C) 1997-2001, International Business Machines
+*   Copyright (C) 1997-2006, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 ******************************************************************************
 *   Date        Name        Description
 *   Corporation and others.  All Rights Reserved.
 ******************************************************************************
 *   Date        Name        Description
  * to 2.18; the inverse ratio ranges from 0.459 to 0.552.)  If this
  * ratio is changed, the low and high water ratios should also be
  * adjusted to suit.
  * to 2.18; the inverse ratio ranges from 0.459 to 0.552.)  If this
  * ratio is changed, the low and high water ratios should also be
  * adjusted to suit.
+ *
+ * These prime numbers were also chosen so that they are the largest
+ * prime number while being less than a power of two.
  */
 static const int32_t PRIMES[] = {
  */
 static const int32_t PRIMES[] = {
-    17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411, 32771,
-    65537, 131101, 262147, 524309, 1048583, 2097169, 4194319, 8388617,
-    16777259, 33554467, 67108879, 134217757, 268435459, 536870923,
-    1073741827, 2147483647
+    13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749,
+    65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593,
+    16777213, 33554393, 67108859, 134217689, 268435399, 536870909,
+    1073741789, 2147483647 /*, 4294967291 */
 };
 
 #define PRIMES_LENGTH (sizeof(PRIMES) / sizeof(PRIMES[0]))
 };
 
 #define PRIMES_LENGTH (sizeof(PRIMES) / sizeof(PRIMES[0]))
+#define DEFAULT_PRIME_INDEX 3
 
 /* These ratios are tuned to the PRIMES array such that a resize
  * places the table back into the zone of non-resizing.  That is,
 
 /* These ratios are tuned to the PRIMES array such that a resize
  * places the table back into the zone of non-resizing.  That is,
@@ -129,775 +133,858 @@ static const float RESIZE_POLICY_RATIO_TABLE[6] = {
 #define HINT_VALUE_POINTER (2)
 
 /********************************************************************
 #define HINT_VALUE_POINTER (2)
 
 /********************************************************************
- * Debugging
+ * PRIVATE Implementation
  ********************************************************************/
 
  ********************************************************************/
 
+static UHashTok
+_uhash_setElement(UHashtable *hash, UHashElement* e,
+                  int32_t hashcode,
+                  UHashTok key, UHashTok value, int8_t hint) {
 
 
-/********************************************************************
- * PRIVATE Prototypes
- ********************************************************************/
-
-static UHashtable* _uhash_create(UHashFunction *keyHash, UKeyComparator *keyComp,
-                                 int32_t primeIndex, UErrorCode *status);
+    UHashTok oldValue = e->value;
+    if (hash->keyDeleter != NULL && e->key.pointer != NULL &&
+        e->key.pointer != key.pointer) { /* Avoid double deletion */
+        (*hash->keyDeleter)(e->key.pointer);
+    }
+    if (hash->valueDeleter != NULL) {
+        if (oldValue.pointer != NULL &&
+            oldValue.pointer != value.pointer) { /* Avoid double deletion */
+            (*hash->valueDeleter)(oldValue.pointer);
+        }
+        oldValue.pointer = NULL;
+    }
+    /* Compilers should copy the UHashTok union correctly, but even if
+     * they do, memory heap tools (e.g. BoundsChecker) can get
+     * confused when a pointer is cloaked in a union and then copied.
+     * TO ALLEVIATE THIS, we use hints (based on what API the user is
+     * calling) to copy pointers when we know the user thinks
+     * something is a pointer. */
+    if (hint & HINT_KEY_POINTER) {
+        e->key.pointer = key.pointer;
+    } else {
+        e->key = key;
+    }
+    if (hint & HINT_VALUE_POINTER) {
+        e->value.pointer = value.pointer;
+    } else {
+        e->value = value;
+    }
+    e->hashcode = hashcode;
+    return oldValue;
+}
 
 
-static void _uhash_allocate(UHashtable *hash, int32_t primeIndex,
-                            UErrorCode *status);
+/**
+ * Assumes that the given element is not empty or deleted.
+ */
+static UHashTok
+_uhash_internalRemoveElement(UHashtable *hash, UHashElement* e) {
+    UHashTok empty;
+    U_ASSERT(!IS_EMPTY_OR_DELETED(e->hashcode));
+    --hash->count;
+    empty.pointer = NULL; empty.integer = 0;
+    return _uhash_setElement(hash, e, HASH_DELETED, empty, empty, 0);
+}
 
 
-static void _uhash_rehash(UHashtable *hash);
+static void
+_uhash_internalSetResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) {
+    U_ASSERT(hash != NULL);
+    U_ASSERT(((int32_t)policy) >= 0);
+    U_ASSERT(((int32_t)policy) < 3);
+    hash->lowWaterRatio  = RESIZE_POLICY_RATIO_TABLE[policy * 2];
+    hash->highWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2 + 1];
+}
 
 
-static UHashElement* _uhash_find(const UHashtable *hash, UHashTok key,
-                                 int32_t hashcode);
+/**
+ * Allocate internal data array of a size determined by the given
+ * prime index.  If the index is out of range it is pinned into range.
+ * If the allocation fails the status is set to
+ * U_MEMORY_ALLOCATION_ERROR and all array storage is freed.  In
+ * either case the previous array pointer is overwritten.
+ *
+ * Caller must ensure primeIndex is in range 0..PRIME_LENGTH-1.
+ */
+static void
+_uhash_allocate(UHashtable *hash,
+                int32_t primeIndex,
+                UErrorCode *status) {
 
 
-static UHashTok _uhash_put(UHashtable *hash,
-                           UHashTok key,
-                           UHashTok value,
-                           int8_t hint,
-                           UErrorCode *status);
+    UHashElement *p, *limit;
+    UHashTok emptytok;
 
 
-static UHashTok _uhash_remove(UHashtable *hash,
-                           UHashTok key);
+    if (U_FAILURE(*status)) return;
 
 
-static UHashTok _uhash_internalRemoveElement(UHashtable *hash, UHashElement* e);
+    U_ASSERT(primeIndex >= 0 && primeIndex < PRIMES_LENGTH);
 
 
-static UHashTok _uhash_setElement(UHashtable* hash, UHashElement* e,
-                                  int32_t hashcode,
-                                  UHashTok key, UHashTok value,
-                                  int8_t hint);
+    hash->primeIndex = primeIndex;
+    hash->length = PRIMES[primeIndex];
 
 
-static void _uhash_internalSetResizePolicy(UHashtable *hash, enum UHashResizePolicy policy);
+    p = hash->elements = (UHashElement*)
+        uprv_malloc(sizeof(UHashElement) * hash->length);
 
 
-/********************************************************************
- * PUBLIC API
- ********************************************************************/
+    if (hash->elements == NULL) {
+        *status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
 
 
-U_CAPI UHashtable* U_EXPORT2
-uhash_open(UHashFunction *keyHash, UKeyComparator *keyComp,
-           UErrorCode *status) {
+    emptytok.pointer = NULL; /* Only one of these two is needed */
+    emptytok.integer = 0;    /* but we don't know which one. */
+    
+    limit = p + hash->length;
+    while (p < limit) {
+        p->key = emptytok;
+        p->value = emptytok;
+        p->hashcode = HASH_EMPTY;
+        ++p;
+    }
 
 
-    return _uhash_create(keyHash, keyComp, 3, status);
+    hash->count = 0;
+    hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio);
+    hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio);
 }
 
 }
 
-U_CAPI UHashtable* U_EXPORT2
-uhash_openSize(UHashFunction *keyHash, UKeyComparator *keyComp,
-               int32_t size,
-               UErrorCode *status) {
+static UHashtable*
+_uhash_init(UHashtable *result,
+              UHashFunction *keyHash, 
+              UKeyComparator *keyComp,
+              UValueComparator *valueComp,
+              int32_t primeIndex,
+              UErrorCode *status)
+{
+    if (U_FAILURE(*status)) return NULL;
+    U_ASSERT(keyHash != NULL);
+    U_ASSERT(keyComp != NULL);
 
 
-    /* Find the smallest index i for which PRIMES[i] >= size. */
-    int32_t i = 0;
-    while (i<(PRIMES_LENGTH-1) && PRIMES[i]<size) {
-        ++i;
-    }
+    result->keyHasher       = keyHash;
+    result->keyComparator   = keyComp;
+    result->valueComparator = valueComp;
+    result->keyDeleter      = NULL;
+    result->valueDeleter    = NULL;
+    result->allocated       = FALSE;
+    _uhash_internalSetResizePolicy(result, U_GROW);
 
 
-    return _uhash_create(keyHash, keyComp, i, status);
-}
+    _uhash_allocate(result, primeIndex, status);
 
 
-U_CAPI void U_EXPORT2
-uhash_close(UHashtable *hash) {
-    U_ASSERT(hash != NULL);
-    if (hash->elements != NULL) {
-        if (hash->keyDeleter != NULL || hash->valueDeleter != NULL) {
-            int32_t pos=-1;
-            UHashElement *e;
-            while ((e = (UHashElement*) uhash_nextElement(hash, &pos)) != NULL) {
-                HASH_DELETE_KEY_VALUE(hash, e->key.pointer, e->value.pointer);
-            }
-        }
-        uprv_free(hash->elements);
-        hash->elements = NULL;
+    if (U_FAILURE(*status)) {
+        return NULL;
     }
     }
-    uprv_free(hash);
-}
 
 
-U_CAPI UHashFunction *U_EXPORT2
-uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn) {
-    UHashFunction *result = hash->keyHasher;
-    hash->keyHasher = fn;
     return result;
 }
 
     return result;
 }
 
-U_CAPI UKeyComparator *U_EXPORT2
-uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn) {
-    UKeyComparator *result = hash->keyComparator;
-    hash->keyComparator = fn;
-    return result;
-}
+static UHashtable*
+_uhash_create(UHashFunction *keyHash, 
+              UKeyComparator *keyComp,
+              UValueComparator *valueComp,
+              int32_t primeIndex,
+              UErrorCode *status) {
+    UHashtable *result;
 
 
-U_CAPI UObjectDeleter *U_EXPORT2
-uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn) {
-    UObjectDeleter *result = hash->keyDeleter;
-    hash->keyDeleter = fn;
-    return result;
-}
+    if (U_FAILURE(*status)) return NULL;
 
 
-U_CAPI UObjectDeleter *U_EXPORT2
-uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn) {
-    UObjectDeleter *result = hash->valueDeleter;
-    hash->valueDeleter = fn;
-    return result;
-}
+    result = (UHashtable*) uprv_malloc(sizeof(UHashtable));
+    if (result == NULL) {
+        *status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
 
 
-U_CAPI void U_EXPORT2
-uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) {
-    _uhash_internalSetResizePolicy(hash, policy);
-    hash->lowWaterMark  = (int32_t)(hash->length * hash->lowWaterRatio);
-    hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio);    
-    _uhash_rehash(hash);
-}
+    _uhash_init(result, keyHash, keyComp, valueComp, primeIndex, status);
+    result->allocated       = TRUE;
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_count(const UHashtable *hash) {
-    return hash->count;
-}
+    if (U_FAILURE(*status)) {
+        uprv_free(result);
+        return NULL;
+    }
 
 
-U_CAPI void* U_EXPORT2
-uhash_get(const UHashtable *hash,
-          const void* key) {
-    UHashTok keyholder;
-    keyholder.pointer = (void*) key;
-    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer;
+    return result;
 }
 
 }
 
-U_CAPI void* U_EXPORT2
-uhash_iget(const UHashtable *hash,
-           int32_t key) {
-    UHashTok keyholder;
-    keyholder.integer = key;
-    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer;
-}
+/**
+ * Look for a key in the table, or if no such key exists, the first
+ * empty slot matching the given hashcode.  Keys are compared using
+ * the keyComparator function.
+ *
+ * First find the start position, which is the hashcode modulo
+ * the length.  Test it to see if it is:
+ *
+ * a. identical:  First check the hash values for a quick check,
+ *    then compare keys for equality using keyComparator.
+ * b. deleted
+ * c. empty
+ *
+ * Stop if it is identical or empty, otherwise continue by adding a
+ * "jump" value (moduloing by the length again to keep it within
+ * range) and retesting.  For efficiency, there need enough empty
+ * values so that the searchs stop within a reasonable amount of time.
+ * This can be changed by changing the high/low water marks.
+ *
+ * In theory, this function can return NULL, if it is full (no empty
+ * or deleted slots) and if no matching key is found.  In practice, we
+ * prevent this elsewhere (in uhash_put) by making sure the last slot
+ * in the table is never filled.
+ *
+ * The size of the table should be prime for this algorithm to work;
+ * otherwise we are not guaranteed that the jump value (the secondary
+ * hash) is relatively prime to the table length.
+ */
+static UHashElement*
+_uhash_find(const UHashtable *hash, UHashTok key,
+            int32_t hashcode) {
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_geti(const UHashtable *hash,
-           const void* key) {
-    UHashTok keyholder;
-    keyholder.pointer = (void*) key;
-    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer;
-}
+    int32_t firstDeleted = -1;  /* assume invalid index */
+    int32_t theIndex, startIndex;
+    int32_t jump = 0; /* lazy evaluate */
+    int32_t tableHash;
+    UHashElement *elements = hash->elements;
 
 
-U_CAPI void* U_EXPORT2
-uhash_put(UHashtable *hash,
-          void* key,
-          void* value,
-          UErrorCode *status) {
-    UHashTok keyholder, valueholder;
-    keyholder.pointer = key;
-    valueholder.pointer = value;
-    return _uhash_put(hash, keyholder, valueholder,
-                      HINT_KEY_POINTER | HINT_VALUE_POINTER,
-                      status).pointer;
-}
+    hashcode &= 0x7FFFFFFF; /* must be positive */
+    startIndex = theIndex = (hashcode ^ 0x4000000) % hash->length;
 
 
-U_CAPI void* U_EXPORT2
-uhash_iput(UHashtable *hash,
-           int32_t key,
-           void* value,
-           UErrorCode *status) {
-    UHashTok keyholder, valueholder;
-    keyholder.integer = key;
-    valueholder.pointer = value;
-    return _uhash_put(hash, keyholder, valueholder,
-                      HINT_VALUE_POINTER,
-                      status).pointer;
-}
+    do {
+        tableHash = elements[theIndex].hashcode;
+        if (tableHash == hashcode) {          /* quick check */
+            if ((*hash->keyComparator)(key, elements[theIndex].key)) {
+                return &(elements[theIndex]);
+            }
+        } else if (!IS_EMPTY_OR_DELETED(tableHash)) {
+            /* We have hit a slot which contains a key-value pair,
+             * but for which the hash code does not match.  Keep
+             * looking.
+             */
+        } else if (tableHash == HASH_EMPTY) { /* empty, end o' the line */
+            break;
+        } else if (firstDeleted < 0) { /* remember first deleted */
+            firstDeleted = theIndex;
+        }
+        if (jump == 0) { /* lazy compute jump */
+            /* The jump value must be relatively prime to the table
+             * length.  As long as the length is prime, then any value
+             * 1..length-1 will be relatively prime to it.
+             */
+            jump = (hashcode % (hash->length - 1)) + 1;
+        }
+        theIndex = (theIndex + jump) % hash->length;
+    } while (theIndex != startIndex);
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_puti(UHashtable *hash,
-           void* key,
-           int32_t value,
-           UErrorCode *status) {
-    UHashTok keyholder, valueholder;
-    keyholder.pointer = key;
-    valueholder.integer = value;
-    return _uhash_put(hash, keyholder, valueholder,
-                      HINT_KEY_POINTER,
-                      status).integer;
+    if (firstDeleted >= 0) {
+        theIndex = firstDeleted; /* reset if had deleted slot */
+    } else if (tableHash != HASH_EMPTY) {
+        /* We get to this point if the hashtable is full (no empty or
+         * deleted slots), and we've failed to find a match.  THIS
+         * WILL NEVER HAPPEN as long as uhash_put() makes sure that
+         * count is always < length.
+         */
+        U_ASSERT(FALSE);
+        return NULL; /* Never happens if uhash_put() behaves */
+    }
+    return &(elements[theIndex]);
 }
 
 }
 
-U_CAPI void* U_EXPORT2
-uhash_remove(UHashtable *hash,
-             const void* key) {
-    UHashTok keyholder;
-    keyholder.pointer = (void*) key;
-    return _uhash_remove(hash, keyholder).pointer;
-}
+/**
+ * Attempt to grow or shrink the data arrays in order to make the
+ * count fit between the high and low water marks.  hash_put() and
+ * hash_remove() call this method when the count exceeds the high or
+ * low water marks.  This method may do nothing, if memory allocation
+ * fails, or if the count is already in range, or if the length is
+ * already at the low or high limit.  In any case, upon return the
+ * arrays will be valid.
+ */
+static void
+_uhash_rehash(UHashtable *hash) {
 
 
-U_CAPI void* U_EXPORT2
-uhash_iremove(UHashtable *hash,
-              int32_t key) {
-    UHashTok keyholder;
-    keyholder.integer = key;
-    return _uhash_remove(hash, keyholder).pointer;
-}
+    UHashElement *old = hash->elements;
+    int32_t oldLength = hash->length;
+    int32_t newPrimeIndex = hash->primeIndex;
+    int32_t i;
+    UErrorCode status = U_ZERO_ERROR;
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_removei(UHashtable *hash,
-              const void* key) {
-    UHashTok keyholder;
-    keyholder.pointer = (void*) key;
-    return _uhash_remove(hash, keyholder).integer;
-}
+    if (hash->count > hash->highWaterMark) {
+        if (++newPrimeIndex >= PRIMES_LENGTH) {
+            return;
+        }
+    } else if (hash->count < hash->lowWaterMark) {
+        if (--newPrimeIndex < 0) {
+            return;
+        }
+    } else {
+        return;
+    }
 
 
-U_CAPI void U_EXPORT2
-uhash_removeAll(UHashtable *hash) {
-    int32_t pos = -1;
-    const UHashElement *e;
-    U_ASSERT(hash != NULL);
-    if (hash->count != 0) {
-        while ((e = uhash_nextElement(hash, &pos)) != NULL) {
-            uhash_removeElement(hash, e);
+    _uhash_allocate(hash, newPrimeIndex, &status);
+
+    if (U_FAILURE(status)) {
+        hash->elements = old;
+        hash->length = oldLength;       
+        return;
+    }
+
+    for (i = oldLength - 1; i >= 0; --i) {
+        if (!IS_EMPTY_OR_DELETED(old[i].hashcode)) {
+            UHashElement *e = _uhash_find(hash, old[i].key, old[i].hashcode);
+            U_ASSERT(e != NULL);
+            U_ASSERT(e->hashcode == HASH_EMPTY);
+            e->key = old[i].key;
+            e->value = old[i].value;
+            e->hashcode = old[i].hashcode;
+            ++hash->count;
         }
     }
         }
     }
-    U_ASSERT(hash->count == 0);
-}
 
 
-U_CAPI const UHashElement* U_EXPORT2
-uhash_find(const UHashtable *hash, const void* key) {
-    UHashTok keyholder;
-    const UHashElement *e;
-    keyholder.pointer = (void*) key;
-    e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
-    return IS_EMPTY_OR_DELETED(e->hashcode) ? NULL : e;
+    uprv_free(old);
 }
 
 }
 
-U_CAPI const UHashElement* U_EXPORT2
-uhash_nextElement(const UHashtable *hash, int32_t *pos) {
-    /* Walk through the array until we find an element that is not
-     * EMPTY and not DELETED.
+static UHashTok
+_uhash_remove(UHashtable *hash,
+              UHashTok key) {
+    /* First find the position of the key in the table.  If the object
+     * has not been removed already, remove it.  If the user wanted
+     * keys deleted, then delete it also.  We have to put a special
+     * hashcode in that position that means that something has been
+     * deleted, since when we do a find, we have to continue PAST any
+     * deleted values.
      */
      */
-    int32_t i;
-    U_ASSERT(hash != NULL);
-    for (i = *pos + 1; i < hash->length; ++i) {
-        if (!IS_EMPTY_OR_DELETED(hash->elements[i].hashcode)) {
-            *pos = i;
-            return &(hash->elements[i]);
+    UHashTok result;
+    UHashElement* e = _uhash_find(hash, key, hash->keyHasher(key));
+    U_ASSERT(e != NULL);
+    result.pointer = NULL; result.integer = 0;
+    if (!IS_EMPTY_OR_DELETED(e->hashcode)) {
+        result = _uhash_internalRemoveElement(hash, e);
+        if (hash->count < hash->lowWaterMark) {
+            _uhash_rehash(hash);
         }
     }
         }
     }
-
-    /* No more elements */
-    return NULL;
+    return result;
 }
 
 }
 
-U_CAPI void* U_EXPORT2
-uhash_removeElement(UHashtable *hash, const UHashElement* e) {
+static UHashTok
+_uhash_put(UHashtable *hash,
+           UHashTok key,
+           UHashTok value,
+           int8_t hint,
+           UErrorCode *status) {
+
+    /* Put finds the position in the table for the new value.  If the
+     * key is already in the table, it is deleted, if there is a
+     * non-NULL keyDeleter.  Then the key, the hash and the value are
+     * all put at the position in their respective arrays.
+     */
+    int32_t hashcode;
+    UHashElement* e;
+    UHashTok emptytok;
+
+    if (U_FAILURE(*status)) {
+        goto err;
+    }
     U_ASSERT(hash != NULL);
     U_ASSERT(hash != NULL);
+    /* Cannot always check pointer here or iSeries sees NULL every time. */
+    if ((hint & HINT_VALUE_POINTER) && value.pointer == NULL) {
+        /* Disallow storage of NULL values, since NULL is returned by
+         * get() to indicate an absent key.  Storing NULL == removing.
+         */
+        return _uhash_remove(hash, key);
+    }
+    if (hash->count > hash->highWaterMark) {
+        _uhash_rehash(hash);
+    }
+
+    hashcode = (*hash->keyHasher)(key);
+    e = _uhash_find(hash, key, hashcode);
     U_ASSERT(e != NULL);
     U_ASSERT(e != NULL);
-    if (!IS_EMPTY_OR_DELETED(e->hashcode)) {
-        return _uhash_internalRemoveElement(hash, (UHashElement*) e).pointer;
+
+    if (IS_EMPTY_OR_DELETED(e->hashcode)) {
+        /* Important: We must never actually fill the table up.  If we
+         * do so, then _uhash_find() will return NULL, and we'll have
+         * to check for NULL after every call to _uhash_find().  To
+         * avoid this we make sure there is always at least one empty
+         * or deleted slot in the table.  This only is a problem if we
+         * are out of memory and rehash isn't working.
+         */
+        ++hash->count;
+        if (hash->count == hash->length) {
+            /* Don't allow count to reach length */
+            --hash->count;
+            *status = U_MEMORY_ALLOCATION_ERROR;
+            goto err;
+        }
     }
     }
-    return NULL;
-}
 
 
-/********************************************************************
- * UHashTok convenience
- ********************************************************************/
+    /* We must in all cases handle storage properly.  If there was an
+     * old key, then it must be deleted (if the deleter != NULL).
+     * Make hashcodes stored in table positive.
+     */
+    return _uhash_setElement(hash, e, hashcode & 0x7FFFFFFF, key, value, hint);
 
 
-/**
- * Return a UHashTok for an integer.
- */
-U_CAPI UHashTok U_EXPORT2
-uhash_toki(int32_t i) {
-    UHashTok tok;
-    tok.integer = i;
-    return tok;
+ err:
+    /* If the deleters are non-NULL, this method adopts its key and/or
+     * value arguments, and we must be sure to delete the key and/or
+     * value in all cases, even upon failure.
+     */
+    HASH_DELETE_KEY_VALUE(hash, key.pointer, value.pointer);
+    emptytok.pointer = NULL; emptytok.integer = 0;
+    return emptytok;
 }
 
 }
 
-/**
- * Return a UHashTok for a pointer.
- */
-U_CAPI UHashTok U_EXPORT2
-uhash_tokp(void* p) {
-    UHashTok tok;
-    tok.pointer = p;
-    return tok;
-}
 
 /********************************************************************
 
 /********************************************************************
- * PUBLIC Key Hash Functions
+ * PUBLIC API
  ********************************************************************/
 
  ********************************************************************/
 
-/*
-  Compute the hash by iterating sparsely over about 32 (up to 63)
-  characters spaced evenly through the string.  For each character,
-  multiply the previous hash value by a prime number and add the new
-  character in, like a linear congruential random number generator,
-  producing a pseudorandom deterministic value well distributed over
-  the output range. [LIU]
-*/
-
-#define STRING_HASH(TYPE, STR, STRLEN, DEREF) \
-    int32_t hash = 0;                         \
-    const TYPE *p = (const TYPE*) STR;        \
-    if (p != NULL) {                          \
-        int32_t len = (int32_t)(STRLEN);                 \
-        int32_t inc = ((len - 32) / 32) + 1;  \
-        const TYPE *limit = p + len;          \
-        while (p<limit) {                     \
-            hash = (hash * 37) + DEREF;       \
-            p += inc;                         \
-        }                                     \
-    }                                         \
-    return hash
+U_CAPI UHashtable* U_EXPORT2
+uhash_open(UHashFunction *keyHash, 
+           UKeyComparator *keyComp,
+           UValueComparator *valueComp,
+           UErrorCode *status) {
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_hashUChars(const UHashTok key) {
-    STRING_HASH(UChar, key.pointer, u_strlen(p), *p);
+    return _uhash_create(keyHash, keyComp, valueComp, DEFAULT_PRIME_INDEX, status);
 }
 
 }
 
-/* Used by UnicodeString to compute its hashcode - Not public API. */
-U_CAPI int32_t U_EXPORT2
-uhash_hashUCharsN(const UChar *str, int32_t length) {
-    STRING_HASH(UChar, str, length, *p);
-}
+U_CAPI UHashtable* U_EXPORT2
+uhash_openSize(UHashFunction *keyHash, 
+               UKeyComparator *keyComp,
+               UValueComparator *valueComp,
+               int32_t size,
+               UErrorCode *status) {
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_hashChars(const UHashTok key) {
-    STRING_HASH(uint8_t, key.pointer, uprv_strlen((char*)p), *p);
-}
+    /* Find the smallest index i for which PRIMES[i] >= size. */
+    int32_t i = 0;
+    while (i<(PRIMES_LENGTH-1) && PRIMES[i]<size) {
+        ++i;
+    }
 
 
-U_CAPI int32_t U_EXPORT2
-uhash_hashIChars(const UHashTok key) {
-    STRING_HASH(uint8_t, key.pointer, uprv_strlen((char*)p), uprv_tolower(*p));
+    return _uhash_create(keyHash, keyComp, valueComp, i, status);
 }
 
 }
 
-/********************************************************************
- * PUBLIC Comparator Functions
- ********************************************************************/
+U_CAPI UHashtable* U_EXPORT2
+uhash_init(UHashtable *fillinResult,
+           UHashFunction *keyHash, 
+           UKeyComparator *keyComp,
+           UValueComparator *valueComp,
+           UErrorCode *status) {
 
 
-U_CAPI UBool U_EXPORT2
-uhash_compareUChars(const UHashTok key1, const UHashTok key2) {
-    const UChar *p1 = (const UChar*) key1.pointer;
-    const UChar *p2 = (const UChar*) key2.pointer;
-    if (p1 == p2) {
-        return TRUE;
-    }
-    if (p1 == NULL || p2 == NULL) {
-        return FALSE;
-    }
-    while (*p1 != 0 && *p1 == *p2) {
-        ++p1;
-        ++p2;
-    }
-    return (UBool)(*p1 == *p2);
+    return _uhash_init(fillinResult, keyHash, keyComp, valueComp, DEFAULT_PRIME_INDEX, status);
 }
 
 }
 
-U_CAPI UBool U_EXPORT2
-uhash_compareChars(const UHashTok key1, const UHashTok key2) {
-    const char *p1 = (const char*) key1.pointer;
-    const char *p2 = (const char*) key2.pointer;
-    if (p1 == p2) {
-        return TRUE;
+U_CAPI void U_EXPORT2
+uhash_close(UHashtable *hash) {
+    U_ASSERT(hash != NULL);
+    if (hash->elements != NULL) {
+        if (hash->keyDeleter != NULL || hash->valueDeleter != NULL) {
+            int32_t pos=-1;
+            UHashElement *e;
+            while ((e = (UHashElement*) uhash_nextElement(hash, &pos)) != NULL) {
+                HASH_DELETE_KEY_VALUE(hash, e->key.pointer, e->value.pointer);
+            }
+        }
+        uprv_free(hash->elements);
+        hash->elements = NULL;
     }
     }
-    if (p1 == NULL || p2 == NULL) {
-        return FALSE;
+    if (hash->allocated) {
+        uprv_free(hash);
     }
     }
-    while (*p1 != 0 && *p1 == *p2) {
-        ++p1;
-        ++p2;
-    }
-    return (UBool)(*p1 == *p2);
 }
 
 }
 
-U_CAPI UBool U_EXPORT2
-uhash_compareIChars(const UHashTok key1, const UHashTok key2) {
-    const char *p1 = (const char*) key1.pointer;
-    const char *p2 = (const char*) key2.pointer;
-    if (p1 == p2) {
-        return TRUE;
-    }
-    if (p1 == NULL || p2 == NULL) {
-        return FALSE;
-    }
-    while (*p1 != 0 && uprv_tolower(*p1) == uprv_tolower(*p2)) {
-        ++p1;
-        ++p2;
-    }
-    return (UBool)(*p1 == *p2);
+U_CAPI UHashFunction *U_EXPORT2
+uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn) {
+    UHashFunction *result = hash->keyHasher;
+    hash->keyHasher = fn;
+    return result;
 }
 
 }
 
-/********************************************************************
- * PUBLIC int32_t Support Functions
- ********************************************************************/
-
-U_CAPI int32_t U_EXPORT2
-uhash_hashLong(const UHashTok key) {
-    return key.integer;
+U_CAPI UKeyComparator *U_EXPORT2
+uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn) {
+    UKeyComparator *result = hash->keyComparator;
+    hash->keyComparator = fn;
+    return result;
+}
+U_CAPI UValueComparator *U_EXPORT2 
+uhash_setValueComparator(UHashtable *hash, UValueComparator *fn){
+    UValueComparator *result = hash->valueComparator;
+    hash->valueComparator = fn;
+    return result;
 }
 
 }
 
-U_CAPI UBool U_EXPORT2
-uhash_compareLong(const UHashTok key1, const UHashTok key2) {
-    return (UBool)(key1.integer == key2.integer);
+U_CAPI UObjectDeleter *U_EXPORT2
+uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn) {
+    UObjectDeleter *result = hash->keyDeleter;
+    hash->keyDeleter = fn;
+    return result;
 }
 
 }
 
-/********************************************************************
- * PUBLIC Deleter Functions
- ********************************************************************/
+U_CAPI UObjectDeleter *U_EXPORT2
+uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn) {
+    UObjectDeleter *result = hash->valueDeleter;
+    hash->valueDeleter = fn;
+    return result;
+}
 
 U_CAPI void U_EXPORT2
 
 U_CAPI void U_EXPORT2
-uhash_freeBlock(void *obj) {
-    uprv_free(obj);
+uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) {
+    _uhash_internalSetResizePolicy(hash, policy);
+    hash->lowWaterMark  = (int32_t)(hash->length * hash->lowWaterRatio);
+    hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio);    
+    _uhash_rehash(hash);
 }
 
 }
 
-/********************************************************************
- * PRIVATE Implementation
- ********************************************************************/
-
-static UHashtable*
-_uhash_create(UHashFunction *keyHash, UKeyComparator *keyComp,
-              int32_t primeIndex,
-              UErrorCode *status) {
-    UHashtable *result;
+U_CAPI int32_t U_EXPORT2
+uhash_count(const UHashtable *hash) {
+    return hash->count;
+}
 
 
-    if (U_FAILURE(*status)) return NULL;
-    U_ASSERT(keyHash != NULL);
-    U_ASSERT(keyComp != NULL);
+U_CAPI void* U_EXPORT2
+uhash_get(const UHashtable *hash,
+          const void* key) {
+    UHashTok keyholder;
+    keyholder.pointer = (void*) key;
+    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer;
+}
 
 
-    result = (UHashtable*) uprv_malloc(sizeof(UHashtable));
-    if (result == NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
+U_CAPI void* U_EXPORT2
+uhash_iget(const UHashtable *hash,
+           int32_t key) {
+    UHashTok keyholder;
+    keyholder.integer = key;
+    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer;
+}
 
 
-    result->keyHasher      = keyHash;
-    result->keyComparator  = keyComp;
-    result->keyDeleter     = NULL;
-    result->valueDeleter   = NULL;
-    _uhash_internalSetResizePolicy(result, U_GROW);
+U_CAPI int32_t U_EXPORT2
+uhash_geti(const UHashtable *hash,
+           const void* key) {
+    UHashTok keyholder;
+    keyholder.pointer = (void*) key;
+    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer;
+}
 
 
-    _uhash_allocate(result, primeIndex, status);
+U_CAPI int32_t U_EXPORT2
+uhash_igeti(const UHashtable *hash,
+           int32_t key) {
+    UHashTok keyholder;
+    keyholder.integer = key;
+    return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer;
+}
 
 
-    if (U_FAILURE(*status)) {
-        uprv_free(result);
-        return NULL;
-    }
+U_CAPI void* U_EXPORT2
+uhash_put(UHashtable *hash,
+          void* key,
+          void* value,
+          UErrorCode *status) {
+    UHashTok keyholder, valueholder;
+    keyholder.pointer = key;
+    valueholder.pointer = value;
+    return _uhash_put(hash, keyholder, valueholder,
+                      HINT_KEY_POINTER | HINT_VALUE_POINTER,
+                      status).pointer;
+}
 
 
-    return result;
+U_CAPI void* U_EXPORT2
+uhash_iput(UHashtable *hash,
+           int32_t key,
+           void* value,
+           UErrorCode *status) {
+    UHashTok keyholder, valueholder;
+    keyholder.integer = key;
+    valueholder.pointer = value;
+    return _uhash_put(hash, keyholder, valueholder,
+                      HINT_VALUE_POINTER,
+                      status).pointer;
 }
 
 }
 
-/**
- * Allocate internal data array of a size determined by the given
- * prime index.  If the index is out of range it is pinned into range.
- * If the allocation fails the status is set to
- * U_MEMORY_ALLOCATION_ERROR and all array storage is freed.  In
- * either case the previous array pointer is overwritten.
- *
- * Caller must ensure primeIndex is in range 0..PRIME_LENGTH-1.
- */
-static void
-_uhash_allocate(UHashtable *hash,
-                int32_t primeIndex,
-                UErrorCode *status) {
+U_CAPI int32_t U_EXPORT2
+uhash_puti(UHashtable *hash,
+           void* key,
+           int32_t value,
+           UErrorCode *status) {
+    UHashTok keyholder, valueholder;
+    keyholder.pointer = key;
+    valueholder.integer = value;
+    return _uhash_put(hash, keyholder, valueholder,
+                      HINT_KEY_POINTER,
+                      status).integer;
+}
 
 
-    UHashElement *p, *limit;
-    UHashTok emptytok;
 
 
-    if (U_FAILURE(*status)) return;
+U_CAPI int32_t U_EXPORT2
+uhash_iputi(UHashtable *hash,
+           int32_t key,
+           int32_t value,
+           UErrorCode *status) {
+    UHashTok keyholder, valueholder;
+    keyholder.integer = key;
+    valueholder.integer = value;
+    return _uhash_put(hash, keyholder, valueholder,
+                      0, /* neither is a ptr */
+                      status).integer;
+}
 
 
-    U_ASSERT(primeIndex >= 0 && primeIndex < PRIMES_LENGTH);
+U_CAPI void* U_EXPORT2
+uhash_remove(UHashtable *hash,
+             const void* key) {
+    UHashTok keyholder;
+    keyholder.pointer = (void*) key;
+    return _uhash_remove(hash, keyholder).pointer;
+}
 
 
-    hash->primeIndex = primeIndex;
-    hash->length = PRIMES[primeIndex];
+U_CAPI void* U_EXPORT2
+uhash_iremove(UHashtable *hash,
+              int32_t key) {
+    UHashTok keyholder;
+    keyholder.integer = key;
+    return _uhash_remove(hash, keyholder).pointer;
+}
 
 
-    p = hash->elements = (UHashElement*)
-        uprv_malloc(sizeof(UHashElement) * hash->length);
+U_CAPI int32_t U_EXPORT2
+uhash_removei(UHashtable *hash,
+              const void* key) {
+    UHashTok keyholder;
+    keyholder.pointer = (void*) key;
+    return _uhash_remove(hash, keyholder).integer;
+}
 
 
-    if (hash->elements == NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
+U_CAPI int32_t U_EXPORT2
+uhash_iremovei(UHashtable *hash,
+               int32_t key) {
+    UHashTok keyholder;
+    keyholder.integer = key;
+    return _uhash_remove(hash, keyholder).integer;
+}
 
 
-    emptytok.pointer = NULL; /* Only one of these two is needed */
-    emptytok.integer = 0;    /* but we don't know which one. */
-    
-    limit = p + hash->length;
-    while (p < limit) {
-        p->key = emptytok;
-        p->value = emptytok;
-        p->hashcode = HASH_EMPTY;
-        ++p;
+U_CAPI void U_EXPORT2
+uhash_removeAll(UHashtable *hash) {
+    int32_t pos = -1;
+    const UHashElement *e;
+    U_ASSERT(hash != NULL);
+    if (hash->count != 0) {
+        while ((e = uhash_nextElement(hash, &pos)) != NULL) {
+            uhash_removeElement(hash, e);
+        }
     }
     }
-
-    hash->count = 0;
-    hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio);
-    hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio);
+    U_ASSERT(hash->count == 0);
 }
 
 }
 
-/**
- * Attempt to grow or shrink the data arrays in order to make the
- * count fit between the high and low water marks.  hash_put() and
- * hash_remove() call this method when the count exceeds the high or
- * low water marks.  This method may do nothing, if memory allocation
- * fails, or if the count is already in range, or if the length is
- * already at the low or high limit.  In any case, upon return the
- * arrays will be valid.
- */
-static void
-_uhash_rehash(UHashtable *hash) {
+U_CAPI const UHashElement* U_EXPORT2
+uhash_find(const UHashtable *hash, const void* key) {
+    UHashTok keyholder;
+    const UHashElement *e;
+    keyholder.pointer = (void*) key;
+    e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
+    return IS_EMPTY_OR_DELETED(e->hashcode) ? NULL : e;
+}
 
 
-    UHashElement *old = hash->elements;
-    int32_t oldLength = hash->length;
-    int32_t newPrimeIndex = hash->primeIndex;
+U_CAPI const UHashElement* U_EXPORT2
+uhash_nextElement(const UHashtable *hash, int32_t *pos) {
+    /* Walk through the array until we find an element that is not
+     * EMPTY and not DELETED.
+     */
     int32_t i;
     int32_t i;
-    UErrorCode status = U_ZERO_ERROR;
-
-    if (hash->count > hash->highWaterMark) {
-        if (++newPrimeIndex >= PRIMES_LENGTH) {
-            return;
-        }
-    } else if (hash->count < hash->lowWaterMark) {
-        if (--newPrimeIndex < 0) {
-            return;
+    U_ASSERT(hash != NULL);
+    for (i = *pos + 1; i < hash->length; ++i) {
+        if (!IS_EMPTY_OR_DELETED(hash->elements[i].hashcode)) {
+            *pos = i;
+            return &(hash->elements[i]);
         }
         }
-    } else {
-        return;
     }
 
     }
 
-    _uhash_allocate(hash, newPrimeIndex, &status);
+    /* No more elements */
+    return NULL;
+}
 
 
-    if (U_FAILURE(status)) {
-        hash->elements = old;
-        hash->length = oldLength;       
-        return;
+U_CAPI void* U_EXPORT2
+uhash_removeElement(UHashtable *hash, const UHashElement* e) {
+    U_ASSERT(hash != NULL);
+    U_ASSERT(e != NULL);
+    if (!IS_EMPTY_OR_DELETED(e->hashcode)) {
+        return _uhash_internalRemoveElement(hash, (UHashElement*) e).pointer;
     }
     }
+    return NULL;
+}
 
 
-    for (i = oldLength - 1; i >= 0; --i) {
-        if (!IS_EMPTY_OR_DELETED(old[i].hashcode)) {
-            UHashElement *e = _uhash_find(hash, old[i].key, old[i].hashcode);
-            U_ASSERT(e != NULL);
-            U_ASSERT(e->hashcode == HASH_EMPTY);
-            e->key = old[i].key;
-            e->value = old[i].value;
-            e->hashcode = old[i].hashcode;
-            ++hash->count;
-        }
-    }
+/********************************************************************
+ * UHashTok convenience
+ ********************************************************************/
 
 
-    uprv_free(old);
-}
+/**
+ * Return a UHashTok for an integer.
+ */
+/*U_CAPI UHashTok U_EXPORT2
+uhash_toki(int32_t i) {
+    UHashTok tok;
+    tok.integer = i;
+    return tok;
+}*/
 
 /**
 
 /**
- * Look for a key in the table, or if no such key exists, the first
- * empty slot matching the given hashcode.  Keys are compared using
- * the keyComparator function.
- *
- * First find the start position, which is the hashcode modulo
- * the length.  Test it to see if it is:
- *
- * a. identical:  First check the hash values for a quick check,
- *    then compare keys for equality using keyComparator.
- * b. deleted
- * c. empty
- *
- * Stop if it is identical or empty, otherwise continue by adding a
- * "jump" value (moduloing by the length again to keep it within
- * range) and retesting.  For efficiency, there need enough empty
- * values so that the searchs stop within a reasonable amount of time.
- * This can be changed by changing the high/low water marks.
- *
- * In theory, this function can return NULL, if it is full (no empty
- * or deleted slots) and if no matching key is found.  In practice, we
- * prevent this elsewhere (in uhash_put) by making sure the last slot
- * in the table is never filled.
- *
- * The size of the table should be prime for this algorithm to work;
- * otherwise we are not guaranteed that the jump value (the secondary
- * hash) is relatively prime to the table length.
+ * Return a UHashTok for a pointer.
  */
  */
-static UHashElement*
-_uhash_find(const UHashtable *hash, UHashTok key,
-            int32_t hashcode) {
+/*U_CAPI UHashTok U_EXPORT2
+uhash_tokp(void* p) {
+    UHashTok tok;
+    tok.pointer = p;
+    return tok;
+}*/
 
 
-    int32_t firstDeleted = -1;  /* assume invalid index */
-    int32_t theIndex, startIndex;
-    int32_t jump = 0; /* lazy evaluate */
-    int32_t tableHash;
+/********************************************************************
+ * PUBLIC Key Hash Functions
+ ********************************************************************/
+
+/*
+  Compute the hash by iterating sparsely over about 32 (up to 63)
+  characters spaced evenly through the string.  For each character,
+  multiply the previous hash value by a prime number and add the new
+  character in, like a linear congruential random number generator,
+  producing a pseudorandom deterministic value well distributed over
+  the output range. [LIU]
+*/
+
+#define STRING_HASH(TYPE, STR, STRLEN, DEREF) \
+    int32_t hash = 0;                         \
+    const TYPE *p = (const TYPE*) STR;        \
+    if (p != NULL) {                          \
+        int32_t len = (int32_t)(STRLEN);      \
+        int32_t inc = ((len - 32) / 32) + 1;  \
+        const TYPE *limit = p + len;          \
+        while (p<limit) {                     \
+            hash = (hash * 37) + DEREF;       \
+            p += inc;                         \
+        }                                     \
+    }                                         \
+    return hash
 
 
-    hashcode &= 0x7FFFFFFF; /* must be positive */
-    startIndex = theIndex = (hashcode ^ 0x4000000) % hash->length;
+U_CAPI int32_t U_EXPORT2
+uhash_hashUChars(const UHashTok key) {
+    STRING_HASH(UChar, key.pointer, u_strlen(p), *p);
+}
 
 
-    do {
-        tableHash = hash->elements[theIndex].hashcode;
-        if (tableHash == hashcode) {          /* quick check */
-            if ((*hash->keyComparator)(key, hash->elements[theIndex].key)) {
-                return &(hash->elements[theIndex]);
-            }
-        } else if (!IS_EMPTY_OR_DELETED(tableHash)) {
-            /* We have hit a slot which contains a key-value pair,
-             * but for which the hash code does not match.  Keep
-             * looking.
-             */
-        } else if (tableHash == HASH_EMPTY) { /* empty, end o' the line */
-            break;
-        } else if (firstDeleted < 0) { /* remember first deleted */
-            firstDeleted = theIndex;
-        }
-        if (jump == 0) { /* lazy compute jump */
-            /* The jump value must be relatively prime to the table
-             * length.  As long as the length is prime, then any value
-             * 1..length-1 will be relatively prime to it.
-             */
-            jump = (hashcode % (hash->length - 1)) + 1;
-        }
-        theIndex = (theIndex + jump) % hash->length;
-    } while (theIndex != startIndex);
+/* Used by UnicodeString to compute its hashcode - Not public API. */
+U_CAPI int32_t U_EXPORT2
+uhash_hashUCharsN(const UChar *str, int32_t length) {
+    STRING_HASH(UChar, str, length, *p);
+}
 
 
-    if (firstDeleted >= 0) {
-        theIndex = firstDeleted; /* reset if had deleted slot */
-    } else if (tableHash != HASH_EMPTY) {
-        /* We get to this point if the hashtable is full (no empty or
-         * deleted slots), and we've failed to find a match.  THIS
-         * WILL NEVER HAPPEN as long as uhash_put() makes sure that
-         * count is always < length.
-         */
-        U_ASSERT(FALSE);
-        return NULL; /* Never happens if uhash_put() behaves */
-    }
-    return &(hash->elements[theIndex]);
+U_CAPI int32_t U_EXPORT2
+uhash_hashChars(const UHashTok key) {
+    STRING_HASH(uint8_t, key.pointer, uprv_strlen((char*)p), *p);
 }
 
 }
 
-static UHashTok
-_uhash_put(UHashtable *hash,
-           UHashTok key,
-           UHashTok value,
-           int8_t hint,
-           UErrorCode *status) {
+U_CAPI int32_t U_EXPORT2
+uhash_hashIChars(const UHashTok key) {
+    STRING_HASH(uint8_t, key.pointer, uprv_strlen((char*)p), uprv_tolower(*p));
+}
 
 
-    /* Put finds the position in the table for the new value.  If the
-     * key is already in the table, it is deleted, if there is a
-     * non-NULL keyDeleter.  Then the key, the hash and the value are
-     * all put at the position in their respective arrays.
-     */
-    int32_t hashcode;
-    UHashElement* e;
-    UHashTok emptytok;
+U_CAPI UBool U_EXPORT2 
+uhash_equals(const UHashtable* hash1, const UHashtable* hash2){
+    
+    int32_t count1, count2, pos, i;
 
 
-    if (U_FAILURE(*status)) {
-        goto err;
+    if(hash1==hash2){
+        return TRUE;
     }
     }
-    U_ASSERT(hash != NULL);
-    /* Cannot always check pointer here or iSeries sees NULL every time. */
-    if ((hint & HINT_VALUE_POINTER) && value.pointer == NULL) {
-        /* Disallow storage of NULL values, since NULL is returned by
-         * get() to indicate an absent key.  Storing NULL == removing.
-         */
-        return _uhash_remove(hash, key);
+
+    if(hash1==NULL || hash2==NULL){
+        return FALSE;
     }
     }
-    if (hash->count > hash->highWaterMark) {
-        _uhash_rehash(hash);
+    /* make sure that we are comparing 2 hashes of the same type */
+    if( hash1->keyComparator != hash2->keyComparator ||
+        hash2->valueComparator != hash2->valueComparator){
+        return FALSE;
     }
 
     }
 
-    hashcode = (*hash->keyHasher)(key);
-    e = _uhash_find(hash, key, hashcode);
-    U_ASSERT(e != NULL);
-
-    if (IS_EMPTY_OR_DELETED(e->hashcode)) {
-        /* Important: We must never actually fill the table up.  If we
-         * do so, then _uhash_find() will return NULL, and we'll have
-         * to check for NULL after every call to _uhash_find().  To
-         * avoid this we make sure there is always at least one empty
-         * or deleted slot in the table.  This only is a problem if we
-         * are out of memory and rehash isn't working.
+    count1 = uhash_count(hash1);
+    count2 = uhash_count(hash2);
+    if(count1!=count2){
+        return FALSE;
+    }
+    
+    pos=-1;
+    for(i=0; i<count1; i++){
+        const UHashElement* elem1 = uhash_nextElement(hash1, &pos);
+        const UHashTok key1 = elem1->key;
+        const UHashTok val1 = elem1->value;
+        /* here the keys are not compared, instead the key form hash1 is used to fetch
+         * value from hash2. If the hashes are equal then then both hashes should 
+         * contain equal values for the same key!
          */
          */
-        ++hash->count;
-        if (hash->count == hash->length) {
-            /* Don't allow count to reach length */
-            --hash->count;
-            *status = U_MEMORY_ALLOCATION_ERROR;
-            goto err;
+        const UHashElement* elem2 = _uhash_find(hash2, key1, hash2->keyHasher(key1));
+        const UHashTok val2 = elem2->value;
+        if(hash1->valueComparator(val1, val2)==FALSE){
+            return FALSE;
         }
     }
         }
     }
+    return TRUE;
+}
 
 
-    /* We must in all cases handle storage properly.  If there was an
-     * old key, then it must be deleted (if the deleter != NULL).
-     * Make hashcodes stored in table positive.
-     */
-    return _uhash_setElement(hash, e, hashcode & 0x7FFFFFFF, key, value, hint);
+/********************************************************************
+ * PUBLIC Comparator Functions
+ ********************************************************************/
 
 
- err:
-    /* If the deleters are non-NULL, this method adopts its key and/or
-     * value arguments, and we must be sure to delete the key and/or
-     * value in all cases, even upon failure.
-     */
-    HASH_DELETE_KEY_VALUE(hash, key.pointer, value.pointer);
-    emptytok.pointer = NULL; emptytok.integer = 0;
-    return emptytok;
+U_CAPI UBool U_EXPORT2
+uhash_compareUChars(const UHashTok key1, const UHashTok key2) {
+    const UChar *p1 = (const UChar*) key1.pointer;
+    const UChar *p2 = (const UChar*) key2.pointer;
+    if (p1 == p2) {
+        return TRUE;
+    }
+    if (p1 == NULL || p2 == NULL) {
+        return FALSE;
+    }
+    while (*p1 != 0 && *p1 == *p2) {
+        ++p1;
+        ++p2;
+    }
+    return (UBool)(*p1 == *p2);
 }
 
 }
 
-static UHashTok
-_uhash_remove(UHashtable *hash,
-              UHashTok key) {
-    /* First find the position of the key in the table.  If the object
-     * has not been removed already, remove it.  If the user wanted
-     * keys deleted, then delete it also.  We have to put a special
-     * hashcode in that position that means that something has been
-     * deleted, since when we do a find, we have to continue PAST any
-     * deleted values.
-     */
-    UHashTok result;
-    UHashElement* e = _uhash_find(hash, key, hash->keyHasher(key));
-    U_ASSERT(e != NULL);
-    result.pointer = NULL; result.integer = 0;
-    if (!IS_EMPTY_OR_DELETED(e->hashcode)) {
-        result = _uhash_internalRemoveElement(hash, e);
-        if (hash->count < hash->lowWaterMark) {
-            _uhash_rehash(hash);
-        }
+U_CAPI UBool U_EXPORT2
+uhash_compareChars(const UHashTok key1, const UHashTok key2) {
+    const char *p1 = (const char*) key1.pointer;
+    const char *p2 = (const char*) key2.pointer;
+    if (p1 == p2) {
+        return TRUE;
     }
     }
-    return result;
+    if (p1 == NULL || p2 == NULL) {
+        return FALSE;
+    }
+    while (*p1 != 0 && *p1 == *p2) {
+        ++p1;
+        ++p2;
+    }
+    return (UBool)(*p1 == *p2);
 }
 
 }
 
-static UHashTok
-_uhash_setElement(UHashtable *hash, UHashElement* e,
-                  int32_t hashcode,
-                  UHashTok key, UHashTok value, int8_t hint) {
-
-    UHashTok oldValue = e->value;
-    if (hash->keyDeleter != NULL && e->key.pointer != NULL &&
-        e->key.pointer != key.pointer) { /* Avoid double deletion */
-        (*hash->keyDeleter)(e->key.pointer);
-    }
-    if (hash->valueDeleter != NULL) {
-        if (oldValue.pointer != NULL &&
-            oldValue.pointer != value.pointer) { /* Avoid double deletion */
-            (*hash->valueDeleter)(oldValue.pointer);
-        }
-        oldValue.pointer = NULL;
+U_CAPI UBool U_EXPORT2
+uhash_compareIChars(const UHashTok key1, const UHashTok key2) {
+    const char *p1 = (const char*) key1.pointer;
+    const char *p2 = (const char*) key2.pointer;
+    if (p1 == p2) {
+        return TRUE;
     }
     }
-    /* Compilers should copy the UHashTok union correctly, but even if
-     * they do, memory heap tools (e.g. BoundsChecker) can get
-     * confused when a pointer is cloaked in a union and then copied.
-     * TO ALLEVIATE THIS, we use hints (based on what API the user is
-     * calling) to copy pointers when we know the user thinks
-     * something is a pointer. */
-    if (hint & HINT_KEY_POINTER) {
-        e->key.pointer = key.pointer;
-    } else {
-        e->key = key;
+    if (p1 == NULL || p2 == NULL) {
+        return FALSE;
     }
     }
-    if (hint & HINT_VALUE_POINTER) {
-        e->value.pointer = value.pointer;
-    } else {
-        e->value = value;
+    while (*p1 != 0 && uprv_tolower(*p1) == uprv_tolower(*p2)) {
+        ++p1;
+        ++p2;
     }
     }
-    e->hashcode = hashcode;
-    return oldValue;
+    return (UBool)(*p1 == *p2);
 }
 
 }
 
-/**
- * Assumes that the given element is not empty or deleted.
- */
-static UHashTok
-_uhash_internalRemoveElement(UHashtable *hash, UHashElement* e) {
-    UHashTok empty;
-    U_ASSERT(!IS_EMPTY_OR_DELETED(e->hashcode));
-    --hash->count;
-    empty.pointer = NULL; empty.integer = 0;
-    return _uhash_setElement(hash, e, HASH_DELETED, empty, empty, 0);
+/********************************************************************
+ * PUBLIC int32_t Support Functions
+ ********************************************************************/
+
+U_CAPI int32_t U_EXPORT2
+uhash_hashLong(const UHashTok key) {
+    return key.integer;
 }
 
 }
 
-static void
-_uhash_internalSetResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) {
-    U_ASSERT(hash != NULL);
-    U_ASSERT(((int32_t)policy) >= 0);
-    U_ASSERT(((int32_t)policy) < 3);
-    hash->lowWaterRatio  = RESIZE_POLICY_RATIO_TABLE[policy * 2];
-    hash->highWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2 + 1];
+U_CAPI UBool U_EXPORT2
+uhash_compareLong(const UHashTok key1, const UHashTok key2) {
+    return (UBool)(key1.integer == key2.integer);
+}
+
+/********************************************************************
+ * PUBLIC Deleter Functions
+ ********************************************************************/
+
+U_CAPI void U_EXPORT2
+uhash_freeBlock(void *obj) {
+    uprv_free(obj);
 }
 }
+