ICU-20698 UHashtable allow integer value zero

This commit is contained in:
Markus Scherer 2020-12-28 20:22:48 -08:00
parent 9963b4d62a
commit 1863bea230
6 changed files with 266 additions and 27 deletions

View file

@ -345,9 +345,8 @@ UBool compareLSRs(const UHashTok t1, const UHashTok t2) {
int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength,
UErrorCode &errorCode) {
if (U_FAILURE(errorCode)) { return suppLength; }
int32_t index = uhash_geti(supportedLsrToIndex, &lsr);
if (index == 0) {
uhash_puti(supportedLsrToIndex, const_cast<LSR *>(&lsr), i + 1, &errorCode);
if (!uhash_containsKey(supportedLsrToIndex, &lsr)) {
uhash_putiAllowZero(supportedLsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);
if (U_SUCCESS(errorCode)) {
supportedLSRs[suppLength] = &lsr;
supportedIndexes[suppLength++] = i;
@ -685,12 +684,11 @@ int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remai
int32_t bestSupportedLsrIndex = -1;
for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) {
// Quick check for exact maximized LSR.
// Returns suppIndex+1 where 0 means not found.
if (supportedLsrToIndex != nullptr) {
desiredLSR.setHashCode();
int32_t index = uhash_geti(supportedLsrToIndex, &desiredLSR);
if (index != 0) {
int32_t suppIndex = index - 1;
UBool found = false;
int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found);
if (found) {
if (remainingIter != nullptr) {
remainingIter->rememberCurrent(desiredIndex, errorCode);
}

View file

@ -133,8 +133,10 @@ static const float RESIZE_POLICY_RATIO_TABLE[6] = {
* or a pointer. If a hint bit is zero, then the associated
* token is assumed to be an integer.
*/
#define HINT_BOTH_INTEGERS (0)
#define HINT_KEY_POINTER (1)
#define HINT_VALUE_POINTER (2)
#define HINT_ALLOW_ZERO (4)
/********************************************************************
* PRIVATE Implementation
@ -479,8 +481,9 @@ _uhash_put(UHashtable *hash,
goto err;
}
U_ASSERT(hash != NULL);
/* Cannot always check pointer here or iSeries sees NULL every time. */
if ((hint & HINT_VALUE_POINTER) && value.pointer == NULL) {
if ((hint & HINT_VALUE_POINTER) ?
value.pointer == NULL :
value.integer == 0 && (hint & HINT_ALLOW_ZERO) == 0) {
/* Disallow storage of NULL values, since NULL is returned by
* get() to indicate an absent key. Storing NULL == removing.
*/
@ -687,6 +690,28 @@ uhash_igeti(const UHashtable *hash,
return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer;
}
U_CAPI int32_t U_EXPORT2
uhash_getiAndFound(const UHashtable *hash,
const void *key,
UBool *found) {
UHashTok keyholder;
keyholder.pointer = (void *)key;
const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
*found = !IS_EMPTY_OR_DELETED(e->hashcode);
return e->value.integer;
}
U_CAPI int32_t U_EXPORT2
uhash_igetiAndFound(const UHashtable *hash,
int32_t key,
UBool *found) {
UHashTok keyholder;
keyholder.integer = key;
const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
*found = !IS_EMPTY_OR_DELETED(e->hashcode);
return e->value.integer;
}
U_CAPI void* U_EXPORT2
uhash_put(UHashtable *hash,
void* key,
@ -736,7 +761,34 @@ uhash_iputi(UHashtable *hash,
keyholder.integer = key;
valueholder.integer = value;
return _uhash_put(hash, keyholder, valueholder,
0, /* neither is a ptr */
HINT_BOTH_INTEGERS,
status).integer;
}
U_CAPI int32_t U_EXPORT2
uhash_putiAllowZero(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 | HINT_ALLOW_ZERO,
status).integer;
}
U_CAPI int32_t U_EXPORT2
uhash_iputiAllowZero(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,
HINT_BOTH_INTEGERS | HINT_ALLOW_ZERO,
status).integer;
}
@ -785,6 +837,29 @@ uhash_removeAll(UHashtable *hash) {
U_ASSERT(hash->count == 0);
}
U_CAPI UBool U_EXPORT2
uhash_containsKey(const UHashtable *hash, const void *key) {
UHashTok keyholder;
keyholder.pointer = (void *)key;
const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
return !IS_EMPTY_OR_DELETED(e->hashcode);
}
/**
* Returns true if the UHashtable contains an item with this integer key.
*
* @param hash The target UHashtable.
* @param key An integer key stored in a hashtable
* @return true if the key is found.
*/
U_CAPI UBool U_EXPORT2
uhash_icontainsKey(const UHashtable *hash, int32_t key) {
UHashTok keyholder;
keyholder.integer = key;
const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder));
return !IS_EMPTY_OR_DELETED(e->hashcode);
}
U_CAPI const UHashElement* U_EXPORT2
uhash_find(const UHashtable *hash, const void* key) {
UHashTok keyholder;

View file

@ -54,6 +54,13 @@
* uhash_remove() on that key. This keeps uhash_get(), uhash_count(),
* and uhash_nextElement() consistent with one another.
*
* Keys and values can be integers.
* Functions that work with an integer key have an "i" prefix.
* Functions that work with an integer value have an "i" suffix.
* As with putting a NULL value pointer, putting a zero value integer removes the item.
* Except, there are pairs of functions that allow setting zero values
* and fetching (value, found) pairs.
*
* To see everything in a hashtable, use uhash_nextElement() to
* iterate through its contents. Each call to this function returns a
* UHashElement pointer. A hash element contains a key, value, and
@ -405,6 +412,44 @@ uhash_iputi(UHashtable *hash,
int32_t value,
UErrorCode *status);
/**
* Put a (key=pointer, value=integer) item in a UHashtable. If the
* keyDeleter is non-NULL, then the hashtable owns 'key' after this
* call. valueDeleter must be NULL.
* Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero.
*
* @param hash The target UHashtable.
* @param key The key to store.
* @param value The integer value to store.
* @param status A pointer to an UErrorCode to receive any errors.
* @return The previous value, or 0 if none.
* @see uhash_getiAndFound
*/
U_CAPI int32_t U_EXPORT2
uhash_putiAllowZero(UHashtable *hash,
void *key,
int32_t value,
UErrorCode *status);
/**
* Put a (key=integer, value=integer) item in a UHashtable. If the
* keyDeleter is non-NULL, then the hashtable owns 'key' after this
* call. valueDeleter must be NULL.
* Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero.
*
* @param hash The target UHashtable.
* @param key The key to store.
* @param value The integer value to store.
* @param status A pointer to an UErrorCode to receive any errors.
* @return The previous value, or 0 if none.
* @see uhash_igetiAndFound
*/
U_CAPI int32_t U_EXPORT2
uhash_iputiAllowZero(UHashtable *hash,
int32_t key,
int32_t value,
UErrorCode *status);
/**
* Retrieve a pointer value from a UHashtable using a pointer key,
* as previously stored by uhash_put().
@ -448,6 +493,34 @@ U_CAPI int32_t U_EXPORT2
uhash_igeti(const UHashtable *hash,
int32_t key);
/**
* Retrieves an integer value from a UHashtable using a pointer key,
* as previously stored by uhash_putiAllowZero() or uhash_puti().
*
* @param hash The target UHashtable.
* @param key A pointer key stored in a hashtable
* @param found A pointer to a boolean which will be set for whether the key was found.
* @return The requested item, or 0 if not found.
*/
U_CAPI int32_t U_EXPORT2
uhash_getiAndFound(const UHashtable *hash,
const void *key,
UBool *found);
/**
* Retrieves an integer value from a UHashtable using an integer key,
* as previously stored by uhash_iputiAllowZero() or uhash_iputi().
*
* @param hash The target UHashtable.
* @param key An integer key stored in a hashtable
* @param found A pointer to a boolean which will be set for whether the key was found.
* @return The requested item, or 0 if not found.
*/
U_CAPI int32_t U_EXPORT2
uhash_igetiAndFound(const UHashtable *hash,
int32_t key,
UBool *found);
/**
* Remove an item from a UHashtable stored by uhash_put().
* @param hash The target UHashtable.
@ -495,6 +568,26 @@ uhash_iremovei(UHashtable *hash,
U_CAPI void U_EXPORT2
uhash_removeAll(UHashtable *hash);
/**
* Returns true if the UHashtable contains an item with this pointer key.
*
* @param hash The target UHashtable.
* @param key A pointer key stored in a hashtable
* @return true if the key is found.
*/
U_CAPI UBool U_EXPORT2
uhash_containsKey(const UHashtable *hash, const void *key);
/**
* Returns true if the UHashtable contains an item with this integer key.
*
* @param hash The target UHashtable.
* @param key An integer key stored in a hashtable
* @return true if the key is found.
*/
U_CAPI UBool U_EXPORT2
uhash_icontainsKey(const UHashtable *hash, int32_t key);
/**
* Locate an element of a UHashtable. The caller must not modify the
* returned object. The primary use of this function is to obtain the

View file

@ -704,7 +704,7 @@ private:
LSR *lsrs;
int32_t supportedLocalesLength;
// These are in preference order: 1. Default locale 2. paradigm locales 3. others.
UHashtable *supportedLsrToIndex; // Map<LSR, Integer> stores index+1 because 0 is "not found"
UHashtable *supportedLsrToIndex; // Map<LSR, Integer>
// Array versions of the supportedLsrToIndex keys and values.
// The distance lookup loops over the supportedLSRs and returns the index of the best match.
const LSR **supportedLSRs;

View file

@ -11,6 +11,7 @@
*******************************************************************************
*/
#include <stdbool.h>
#include "cintltst.h"
#include "uhash.h"
#include "unicode/ctest.h"
@ -22,6 +23,7 @@
*********************************************************************/
static void TestBasic(void);
static void TestAllowZero(void);
static void TestOtherAPI(void);
static void hashIChars(void);
@ -87,6 +89,7 @@ _compareLong(int32_t a, int32_t b) {
void addHashtableTest(TestNode** root) {
addTest(root, &TestBasic, "tsutil/chashtst/TestBasic");
addTest(root, &TestAllowZero, "tsutil/chashtst/TestAllowZero");
addTest(root, &TestOtherAPI, "tsutil/chashtst/TestOtherAPI");
addTest(root, &hashIChars, "tsutil/chashtst/hashIChars");
@ -133,6 +136,9 @@ static void TestBasic(void) {
_get(hash, omega, 48);
_get(hash, two, 200);
// puti(key, value==0) removes the key's element.
_put(hash, two, 0, 200);
if(_compareChars((void*)one, (void*)three) == TRUE ||
_compareChars((void*)one, (void*)one2) != TRUE ||
_compareChars((void*)one, (void*)one) != TRUE ||
@ -145,9 +151,56 @@ static void TestBasic(void) {
_compareIChars((void*)one, NULL) == TRUE ) {
log_err("FAIL: compareIChars failed\n");
}
uhash_close(hash);
uhash_close(hash);
}
static void TestAllowZero() {
UErrorCode status = U_ZERO_ERROR;
UHashtable *hash = uhash_open(hashChars, isEqualChars, NULL, &status);
if (U_FAILURE(status)) {
log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n",
u_errorName(status), hash);
return;
}
if (hash == NULL) {
log_err("FAIL: uhash_open returned NULL\n");
return;
}
log_verbose("Ok: uhash_open returned 0x%08X\n", hash);
int32_t oldValue = uhash_putiAllowZero(hash, (char *)"one", 1, &status);
UBool found = false;
if (U_FAILURE(status) || oldValue != 0 || !uhash_containsKey(hash, "one") ||
uhash_geti(hash, "one") != 1 ||
uhash_getiAndFound(hash, "one", &found) != 1 || !found) {
log_err("FAIL: uhash_putiAllowZero(one, 1)");
}
oldValue = uhash_putiAllowZero(hash, (char *)"zero", 0, &status);
found = false;
if (U_FAILURE(status) || oldValue != 0 || !uhash_containsKey(hash, "zero") ||
uhash_geti(hash, "zero") != 0 ||
uhash_getiAndFound(hash, "zero", &found) != 0 || !found) {
log_err("FAIL: uhash_putiAllowZero(zero, 0)");
}
// Overwrite "one" to 0.
oldValue = uhash_putiAllowZero(hash, (char *)"one", 0, &status);
found = false;
if (U_FAILURE(status) || oldValue != 1 || !uhash_containsKey(hash, "one") ||
uhash_geti(hash, "one") != 0 ||
uhash_getiAndFound(hash, "one", &found) != 0 || !found) {
log_err("FAIL: uhash_putiAllowZero(one, 0)");
}
// Remove "zero" using puti(zero, 0).
oldValue = uhash_puti(hash, (char *)"zero", 0, &status);
found = true;
if (U_FAILURE(status) || oldValue != 0 || uhash_containsKey(hash, "zero") ||
uhash_geti(hash, "zero") != 0 ||
uhash_getiAndFound(hash, "zero", &found) != 0 || found) {
log_err("FAIL: uhash_puti(zero, 0)");
}
uhash_close(hash);
}
static void TestOtherAPI(void){
@ -343,30 +396,46 @@ static void _put(UHashtable* hash,
int32_t oldValue =
uhash_puti(hash, (void*) key, value, &status);
if (U_FAILURE(status)) {
log_err("FAIL: uhash_put(%s) failed with %s and returned %ld\n",
log_err("FAIL: uhash_puti(%s) failed with %s and returned %ld\n",
key, u_errorName(status), oldValue);
} else if (oldValue != expectedOldValue) {
log_err("FAIL: uhash_put(%s) returned old value %ld; expected %ld\n",
log_err("FAIL: uhash_puti(%s) returned old value %ld; expected %ld\n",
key, oldValue, expectedOldValue);
} else {
log_verbose("Ok: uhash_put(%s, %d) returned old value %ld\n",
log_verbose("Ok: uhash_puti(%s, %d) returned old value %ld\n",
key, value, oldValue);
}
int32_t newValue = uhash_geti(hash, key);
if (newValue != value) {
log_err("FAIL: uhash_puti(%s) failed to set the intended value %ld: "
"uhash_geti() returns %ld\n",
key, value, newValue);
}
UBool contained = uhash_containsKey(hash, key);
if (value == 0) {
if (contained) {
log_err("FAIL: uhash_puti(%s, zero) failed to remove the key item: "
"uhash_containsKey() returns true\n",
key);
}
} else {
if (!contained) {
log_err("FAIL: uhash_puti(%s, not zero) appears to have removed the key item: "
"uhash_containsKey() returns false\n",
key);
}
}
}
static void _get(UHashtable* hash,
const char* key,
int32_t expectedValue) {
UErrorCode status = U_ZERO_ERROR;
int32_t value = uhash_geti(hash, key);
if (U_FAILURE(status)) {
log_err("FAIL: uhash_get(%s) failed with %s and returned %ld\n",
key, u_errorName(status), value);
} else if (value != expectedValue) {
log_err("FAIL: uhash_get(%s) returned %ld; expected %ld\n",
if (value != expectedValue) {
log_err("FAIL: uhash_geti(%s) returned %ld; expected %ld\n",
key, value, expectedValue);
} else {
log_verbose("Ok: uhash_get(%s) returned value %ld\n",
log_verbose("Ok: uhash_geti(%s) returned value %ld\n",
key, value);
}
}
@ -376,11 +445,15 @@ static void _remove(UHashtable* hash,
int32_t expectedValue) {
int32_t value = uhash_removei(hash, key);
if (value != expectedValue) {
log_err("FAIL: uhash_remove(%s) returned %ld; expected %ld\n",
log_err("FAIL: uhash_removei(%s) returned %ld; expected %ld\n",
key, value, expectedValue);
} else {
log_verbose("Ok: uhash_remove(%s) returned old value %ld\n",
log_verbose("Ok: uhash_removei(%s) returned old value %ld\n",
key, value);
}
if (uhash_containsKey(hash, key)) {
log_err("FAIL: uhash_removei(%s) failed to remove the key item: "
"uhash_containsKey() returns false\n",
key);
}
}

View file

@ -658,7 +658,7 @@ UXMLParser::intern(const UnicodeString &s, UErrorCode &errorCode) {
return (const UnicodeString *)he->key.pointer;
} else {
// add this new name and return its hashed key pointer
fNames.puti(s, 0, errorCode);
fNames.puti(s, 1, errorCode);
he=fNames.find(s);
return (const UnicodeString *)he->key.pointer;
}