From: Francis De Brabandere <francisdb@gmail.com> Boolean and Currency keys were rejected outright; hash them by their numeric value like the other numeric types. Compare numeric keys at double precision instead of single, so that values differing only beyond float precision (e.g. 16777216 and 16777217) remain distinct keys. Treat Empty as the zero/default value of the other key's type, matching any numeric zero (including False and date 0) or the empty string. --- dlls/scrrun/dictionary.c | 54 ++++++++++++++++++++++++++++++---- dlls/scrrun/tests/dictionary.c | 28 +++++++++--------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/dlls/scrrun/dictionary.c b/dlls/scrrun/dictionary.c index af771c9245a..07cd05d32b0 100644 --- a/dlls/scrrun/dictionary.c +++ b/dlls/scrrun/dictionary.c @@ -119,6 +119,8 @@ static inline BOOL is_numeric_key(const VARIANT *key) case VT_DATE: case VT_R4: case VT_R8: + case VT_BOOL: + case VT_CY: return TRUE; default: return FALSE; @@ -152,19 +154,48 @@ static inline BOOL numeric_key_eq(const VARIANT *key1, const VARIANT *key2) VARIANT v1, v2; VariantInit(&v1); - if (FAILED(VariantChangeType(&v1, key1, 0, VT_R4))) + if (FAILED(VariantChangeType(&v1, key1, 0, VT_R8))) return FALSE; VariantInit(&v2); - if (FAILED(VariantChangeType(&v2, key2, 0, VT_R4))) + if (FAILED(VariantChangeType(&v2, key2, 0, VT_R8))) return FALSE; - return V_R4(&v1) == V_R4(&v2); + return V_R8(&v1) == V_R8(&v2); +} + +/* Empty matches the zero/default value of the other key's type: any numeric + * zero (including False and date 0), the empty string, or another Empty. */ +static BOOL empty_matches_key(const VARIANT *key) +{ + VARIANT v; + + switch (V_VT(key)) + { + case VT_EMPTY: + return TRUE; + case VT_BSTR: + { + const WCHAR *str = get_key_strptr(key); + return !str || !*str; + } + default: + if (!is_numeric_key(key)) + return FALSE; + VariantInit(&v); + if (FAILED(VariantChangeType(&v, key, 0, VT_R8))) + return FALSE; + return V_R8(&v) == 0.0; + } } static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_pair *pair, const VARIANT *key, DWORD hash) { - if (is_string_key(key) != is_string_key(&pair->key)) + if (V_VT(key) == VT_EMPTY || V_VT(&pair->key) == VT_EMPTY) + { + return empty_matches_key(V_VT(key) == VT_EMPTY ? &pair->key : key); + } + else if (is_string_key(key) != is_string_key(&pair->key)) { return FALSE; } @@ -188,9 +219,9 @@ static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_ { return hash == pair->hash && numeric_key_eq(key, &pair->key); } - else if (V_VT(&pair->key) == VT_EMPTY || V_VT(&pair->key) == VT_NULL) + else if (V_VT(&pair->key) == VT_NULL) { - return V_VT(&pair->key) == V_VT(key); + return V_VT(key) == VT_NULL; } else { @@ -919,6 +950,17 @@ static HRESULT WINAPI dictionary_get_HashVal(IDictionary *iface, VARIANT *key, V case VT_R8|VT_BYREF: case VT_R8: return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R8REF(key) : V_R8(key), &V_I4(hash)); + case VT_BOOL|VT_BYREF: + case VT_BOOL: + V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_BOOLREF(key) : V_BOOL(key)); + break; + case VT_CY|VT_BYREF: + case VT_CY: + { + CY cy = V_VT(key) & VT_BYREF ? *V_CYREF(key) : V_CY(key); + V_I4(hash) = get_num_hash((FLOAT)((double)cy.int64 / 10000.0)); + break; + } case VT_EMPTY: case VT_NULL: V_I4(hash) = 0; diff --git a/dlls/scrrun/tests/dictionary.c b/dlls/scrrun/tests/dictionary.c index b12606ec8a4..90fb0e283c2 100644 --- a/dlls/scrrun/tests/dictionary.c +++ b/dlls/scrrun/tests/dictionary.c @@ -749,34 +749,34 @@ if (0) { /* crashes on native */ V_BOOL(&key) = VARIANT_FALSE; V_I4(&hash) = 5678; hr = IDictionary_get_HashVal(dict, &key, &hash); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); - todo_wine ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); V_VT(&key) = VT_BOOL; V_BOOL(&key) = VARIANT_TRUE; V_I4(&hash) = 5678; hr = IDictionary_get_HashVal(dict, &key, &hash); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); - todo_wine ok(V_I4(&hash) == get_num_hash((FLOAT)VARIANT_TRUE), "Unexpected hash %#lx.\n", V_I4(&hash)); + ok(V_I4(&hash) == get_num_hash((FLOAT)VARIANT_TRUE), "Unexpected hash %#lx.\n", V_I4(&hash)); /* Currency keys hash by their numeric value, matching equal numbers. */ V_VT(&key) = VT_CY; V_CY(&key).int64 = 0; V_I4(&hash) = 5678; hr = IDictionary_get_HashVal(dict, &key, &hash); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); - todo_wine ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); V_VT(&key) = VT_CY; V_CY(&key).int64 = 5 * 10000; V_I4(&hash) = 5678; hr = IDictionary_get_HashVal(dict, &key, &hash); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); - todo_wine ok(V_I4(&hash) == get_num_hash(5.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + ok(V_I4(&hash) == get_num_hash(5.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); V_VT(&key) = VT_EMPTY; V_I4(&key) = 1234; @@ -1127,7 +1127,7 @@ static void test_key_matching(void) V_VT(&b) = VT_CY; V_CY(&b).int64 = 5 * 10000; - todo_wine ok(keys_match(&a, &b), "I2 5 should match CY 5\n"); + ok(keys_match(&a, &b), "I2 5 should match CY 5\n"); V_VT(&b) = VT_DATE; V_DATE(&b) = 5.0; @@ -1137,7 +1137,7 @@ static void test_key_matching(void) V_BOOL(&a) = VARIANT_TRUE; V_VT(&b) = VT_I2; V_I2(&b) = -1; - todo_wine ok(keys_match(&a, &b), "True should match I2 -1\n"); + ok(keys_match(&a, &b), "True should match I2 -1\n"); /* Comparison uses double precision: values that differ in R8 but collapse * to the same R4 are still distinct keys. */ @@ -1145,11 +1145,11 @@ static void test_key_matching(void) V_R8(&a) = 16777216.0; V_VT(&b) = VT_R8; V_R8(&b) = 16777217.0; - todo_wine ok(!keys_match(&a, &b), "16777216.0 and 16777217.0 should be distinct keys\n"); + ok(!keys_match(&a, &b), "16777216.0 and 16777217.0 should be distinct keys\n"); V_R8(&a) = 5.0; V_R8(&b) = 5.0000001; - todo_wine ok(!keys_match(&a, &b), "5.0 and 5.0000001 should be distinct keys\n"); + ok(!keys_match(&a, &b), "5.0 and 5.0000001 should be distinct keys\n"); /* Empty matches the zero/default value of any numeric type, and False. */ for (i = 0; i < ARRAY_SIZE(zero_keys); i++) @@ -1164,14 +1164,14 @@ static void test_key_matching(void) V_R4(&b) = 0.0f; else V_I4(&b) = 0; - todo_wine ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]); + ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]); } /* Empty matches the empty string, but not a non-empty one. */ V_VT(&a) = VT_EMPTY; V_VT(&b) = VT_BSTR; V_BSTR(&b) = SysAllocString(L""); - todo_wine ok(keys_match(&a, &b), "Empty should match empty string\n"); + ok(keys_match(&a, &b), "Empty should match empty string\n"); VariantClear(&b); V_VT(&b) = VT_BSTR; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10960