Skip to content

Commit 745b3b9

Browse files
frozendict_hash : hash entry mixed hashes rather than just keys and values
1 parent 94bca40 commit 745b3b9

1 file changed

Lines changed: 13 additions & 7 deletions

File tree

Objects/dictobject.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8222,10 +8222,18 @@ frozendict_repr(PyObject *self)
82228222
return res;
82238223
}
82248224

8225-
static Py_uhash_t
8226-
_shuffle_bits(Py_uhash_t h)
8225+
// based on boost's old hash_combine
8226+
static inline Py_uhash_t
8227+
_combine_hashes(Py_uhash_t h1, Py_uhash_t h2)
82278228
{
8228-
return ((h ^ 89869747UL) ^ (h << 16)) * 3644798167UL;
8229+
// 2^sizeof(Py_hash_t) / phi
8230+
#if SIZEOF_PY_HASH_T == 8
8231+
const Py_uhash_t GOLDEN_C = 0x9e3779b97f4a7c15ULL;
8232+
#else
8233+
const Py_uhash_t GOLDEN_C = 0x9e3779b9UL;
8234+
#endif
8235+
h1 ^= h2 + GOLDEN_C + (h1 << 6) + (h1 >> 2);
8236+
return h1;
82298237
}
82308238

82318239
// Code copied from frozenset_hash()
@@ -8239,7 +8247,7 @@ frozendict_hash(PyObject *op)
82398247
}
82408248

82418249
PyDictObject *mp = _PyAnyDict_CAST(op);
8242-
Py_uhash_t hash = 0;
8250+
Py_uhash_t hash = 0xfd1c74; // start at a different value from frozenset to avoid collision with empty frozenset
82438251

82448252
PyObject *key, *value; // borrowed refs
82458253
Py_ssize_t pos = 0;
@@ -8248,13 +8256,11 @@ frozendict_hash(PyObject *op)
82488256
if (key_hash == -1) {
82498257
return -1;
82508258
}
8251-
hash ^= _shuffle_bits(key_hash);
8252-
82538259
Py_hash_t value_hash = PyObject_Hash(value);
82548260
if (value_hash == -1) {
82558261
return -1;
82568262
}
8257-
hash ^= _shuffle_bits(value_hash);
8263+
hash ^= _combine_hashes(key_hash, value_hash);
82588264
}
82598265

82608266
/* Factor in the number of active entries */

0 commit comments

Comments
 (0)