|
|
|
@ -18,8 +18,8 @@ layout:
|
|
|
|
|
|
|
|
|
|
+---------------+
|
|
|
|
|
| dk_refcnt |
|
|
|
|
|
| dk_size |
|
|
|
|
|
| dk_lookup |
|
|
|
|
|
| dk_log2_size |
|
|
|
|
|
| dk_kind |
|
|
|
|
|
| dk_usable |
|
|
|
|
|
| dk_nentries |
|
|
|
|
|
+---------------+
|
|
|
|
@ -108,6 +108,7 @@ converting the dict to the combined table.
|
|
|
|
|
* Making this 8, rather than 4 reduces the number of resizes for most
|
|
|
|
|
* dictionaries, without any significant extra memory use.
|
|
|
|
|
*/
|
|
|
|
|
#define PyDict_LOG_MINSIZE 3
|
|
|
|
|
#define PyDict_MINSIZE 8
|
|
|
|
|
|
|
|
|
|
#include "Python.h"
|
|
|
|
@ -226,18 +227,7 @@ equally good collision statistics, needed less code & used less memory.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* forward declarations */
|
|
|
|
|
static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr);
|
|
|
|
|
static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr);
|
|
|
|
|
static Py_ssize_t
|
|
|
|
|
lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr);
|
|
|
|
|
static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr);
|
|
|
|
|
|
|
|
|
|
static int dictresize(PyDictObject *mp, Py_ssize_t newsize);
|
|
|
|
|
static int dictresize(PyDictObject *mp, uint8_t log_newsize);
|
|
|
|
|
|
|
|
|
|
static PyObject* dict_iter(PyDictObject *dict);
|
|
|
|
|
|
|
|
|
@ -295,24 +285,25 @@ _PyDict_DebugMallocStats(FILE *out)
|
|
|
|
|
state->numfree, sizeof(PyDictObject));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DK_SIZE(dk) ((dk)->dk_size)
|
|
|
|
|
#define DK_LOG_SIZE(dk) ((dk)->dk_log2_size)
|
|
|
|
|
#if SIZEOF_VOID_P > 4
|
|
|
|
|
#define DK_SIZE(dk) (((int64_t)1)<<DK_LOG_SIZE(dk))
|
|
|
|
|
#define DK_IXSIZE(dk) \
|
|
|
|
|
(DK_SIZE(dk) <= 0xff ? \
|
|
|
|
|
1 : DK_SIZE(dk) <= 0xffff ? \
|
|
|
|
|
2 : DK_SIZE(dk) <= 0xffffffff ? \
|
|
|
|
|
(DK_LOG_SIZE(dk) <= 7 ? \
|
|
|
|
|
1 : DK_LOG_SIZE(dk) <= 15 ? \
|
|
|
|
|
2 : DK_LOG_SIZE(dk) <= 31 ? \
|
|
|
|
|
4 : sizeof(int64_t))
|
|
|
|
|
#else
|
|
|
|
|
#define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk))
|
|
|
|
|
#define DK_IXSIZE(dk) \
|
|
|
|
|
(DK_SIZE(dk) <= 0xff ? \
|
|
|
|
|
1 : DK_SIZE(dk) <= 0xffff ? \
|
|
|
|
|
(DK_LOG_SIZE(dk) <= 7 ? \
|
|
|
|
|
1 : DK_LOG_SIZE(dk) <= 15 ? \
|
|
|
|
|
2 : sizeof(int32_t))
|
|
|
|
|
#endif
|
|
|
|
|
#define DK_ENTRIES(dk) \
|
|
|
|
|
((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)]))
|
|
|
|
|
|
|
|
|
|
#define DK_MASK(dk) (((dk)->dk_size)-1)
|
|
|
|
|
#define DK_MASK(dk) (DK_SIZE(dk)-1)
|
|
|
|
|
#define IS_POWER_OF_2(x) (((x) & (x-1)) == 0)
|
|
|
|
|
|
|
|
|
|
static void free_keys_object(PyDictKeysObject *keys);
|
|
|
|
@ -374,6 +365,7 @@ dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix)
|
|
|
|
|
Py_ssize_t s = DK_SIZE(keys);
|
|
|
|
|
|
|
|
|
|
assert(ix >= DKIX_DUMMY);
|
|
|
|
|
assert(keys->dk_version == 0);
|
|
|
|
|
|
|
|
|
|
if (s <= 0xff) {
|
|
|
|
|
int8_t *indices = (int8_t*)(keys->dk_indices);
|
|
|
|
@ -413,25 +405,25 @@ dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix)
|
|
|
|
|
#define USABLE_FRACTION(n) (((n) << 1)/3)
|
|
|
|
|
|
|
|
|
|
/* Find the smallest dk_size >= minsize. */
|
|
|
|
|
static inline Py_ssize_t
|
|
|
|
|
calculate_keysize(Py_ssize_t minsize)
|
|
|
|
|
static inline uint8_t
|
|
|
|
|
calculate_log2_keysize(Py_ssize_t minsize)
|
|
|
|
|
{
|
|
|
|
|
#if SIZEOF_LONG == SIZEOF_SIZE_T
|
|
|
|
|
minsize = (minsize | PyDict_MINSIZE) - 1;
|
|
|
|
|
return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1));
|
|
|
|
|
return _Py_bit_length(minsize | (PyDict_MINSIZE-1));
|
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
|
// On 64bit Windows, sizeof(long) == 4.
|
|
|
|
|
minsize = (minsize | PyDict_MINSIZE) - 1;
|
|
|
|
|
unsigned long msb;
|
|
|
|
|
_BitScanReverse64(&msb, (uint64_t)minsize);
|
|
|
|
|
return 1LL << (msb + 1);
|
|
|
|
|
return msb + 1;
|
|
|
|
|
#else
|
|
|
|
|
Py_ssize_t size;
|
|
|
|
|
for (size = PyDict_MINSIZE;
|
|
|
|
|
size < minsize && size > 0;
|
|
|
|
|
size <<= 1)
|
|
|
|
|
uint8_t log2_size;
|
|
|
|
|
for (log2_size = PyDict_LOG_MINSIZE;
|
|
|
|
|
(((Py_ssize_t)1) << log2_size) < minsize;
|
|
|
|
|
log2_size++)
|
|
|
|
|
;
|
|
|
|
|
return size;
|
|
|
|
|
return log2_size;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -440,10 +432,10 @@ calculate_keysize(Py_ssize_t minsize)
|
|
|
|
|
* This can be used to reserve enough size to insert n entries without
|
|
|
|
|
* resizing.
|
|
|
|
|
*/
|
|
|
|
|
static inline Py_ssize_t
|
|
|
|
|
estimate_keysize(Py_ssize_t n)
|
|
|
|
|
static inline uint8_t
|
|
|
|
|
estimate_log2_keysize(Py_ssize_t n)
|
|
|
|
|
{
|
|
|
|
|
return calculate_keysize((n*3 + 1) / 2);
|
|
|
|
|
return calculate_log2_keysize((n*3 + 1) / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -459,18 +451,14 @@ estimate_keysize(Py_ssize_t n)
|
|
|
|
|
*/
|
|
|
|
|
#define GROWTH_RATE(d) ((d)->ma_used*3)
|
|
|
|
|
|
|
|
|
|
#define ENSURE_ALLOWS_DELETIONS(d) \
|
|
|
|
|
if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \
|
|
|
|
|
(d)->ma_keys->dk_lookup = lookdict_unicode; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This immutable, empty PyDictKeysObject is used for PyDict_Clear()
|
|
|
|
|
* (which cannot fail and thus can do no allocation).
|
|
|
|
|
*/
|
|
|
|
|
static PyDictKeysObject empty_keys_struct = {
|
|
|
|
|
1, /* dk_refcnt */
|
|
|
|
|
1, /* dk_size */
|
|
|
|
|
lookdict_split, /* dk_lookup */
|
|
|
|
|
0, /* dk_log2_size */
|
|
|
|
|
DICT_KEYS_SPLIT, /* dk_kind */
|
|
|
|
|
1, /* dk_version */
|
|
|
|
|
0, /* dk_usable (immutable) */
|
|
|
|
|
0, /* dk_nentries */
|
|
|
|
|
{DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY,
|
|
|
|
@ -503,10 +491,9 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
|
|
|
|
|
|
|
|
|
|
PyDictKeysObject *keys = mp->ma_keys;
|
|
|
|
|
int splitted = _PyDict_HasSplitTable(mp);
|
|
|
|
|
Py_ssize_t usable = USABLE_FRACTION(keys->dk_size);
|
|
|
|
|
Py_ssize_t usable = USABLE_FRACTION(DK_SIZE(keys));
|
|
|
|
|
|
|
|
|
|
CHECK(0 <= mp->ma_used && mp->ma_used <= usable);
|
|
|
|
|
CHECK(IS_POWER_OF_2(keys->dk_size));
|
|
|
|
|
CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable);
|
|
|
|
|
CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable);
|
|
|
|
|
CHECK(keys->dk_usable + keys->dk_nentries <= usable);
|
|
|
|
@ -520,7 +507,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
|
|
|
|
|
PyDictKeyEntry *entries = DK_ENTRIES(keys);
|
|
|
|
|
Py_ssize_t i;
|
|
|
|
|
|
|
|
|
|
for (i=0; i < keys->dk_size; i++) {
|
|
|
|
|
for (i=0; i < DK_SIZE(keys); i++) {
|
|
|
|
|
Py_ssize_t ix = dictkeys_get_index(keys, i);
|
|
|
|
|
CHECK(DKIX_DUMMY <= ix && ix <= usable);
|
|
|
|
|
}
|
|
|
|
@ -563,23 +550,22 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static PyDictKeysObject*
|
|
|
|
|
new_keys_object(Py_ssize_t size)
|
|
|
|
|
new_keys_object(uint8_t log2_size)
|
|
|
|
|
{
|
|
|
|
|
PyDictKeysObject *dk;
|
|
|
|
|
Py_ssize_t es, usable;
|
|
|
|
|
|
|
|
|
|
assert(size >= PyDict_MINSIZE);
|
|
|
|
|
assert(IS_POWER_OF_2(size));
|
|
|
|
|
assert(log2_size >= PyDict_LOG_MINSIZE);
|
|
|
|
|
|
|
|
|
|
usable = USABLE_FRACTION(size);
|
|
|
|
|
if (size <= 0xff) {
|
|
|
|
|
usable = USABLE_FRACTION(1<<log2_size);
|
|
|
|
|
if (log2_size <= 7) {
|
|
|
|
|
es = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (size <= 0xffff) {
|
|
|
|
|
else if (log2_size <= 15) {
|
|
|
|
|
es = 2;
|
|
|
|
|
}
|
|
|
|
|
#if SIZEOF_VOID_P > 4
|
|
|
|
|
else if (size <= 0xffffffff) {
|
|
|
|
|
else if (log2_size <= 31) {
|
|
|
|
|
es = 4;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
@ -592,13 +578,13 @@ new_keys_object(Py_ssize_t size)
|
|
|
|
|
// new_keys_object() must not be called after _PyDict_Fini()
|
|
|
|
|
assert(state->keys_numfree != -1);
|
|
|
|
|
#endif
|
|
|
|
|
if (size == PyDict_MINSIZE && state->keys_numfree > 0) {
|
|
|
|
|
if (log2_size == PyDict_LOG_MINSIZE && state->keys_numfree > 0) {
|
|
|
|
|
dk = state->keys_free_list[--state->keys_numfree];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dk = PyObject_Malloc(sizeof(PyDictKeysObject)
|
|
|
|
|
+ es * size
|
|
|
|
|
+ (es<<log2_size)
|
|
|
|
|
+ sizeof(PyDictKeyEntry) * usable);
|
|
|
|
|
if (dk == NULL) {
|
|
|
|
|
PyErr_NoMemory();
|
|
|
|
@ -609,11 +595,12 @@ new_keys_object(Py_ssize_t size)
|
|
|
|
|
_Py_RefTotal++;
|
|
|
|
|
#endif
|
|
|
|
|
dk->dk_refcnt = 1;
|
|
|
|
|
dk->dk_size = size;
|
|
|
|
|
dk->dk_log2_size = log2_size;
|
|
|
|
|
dk->dk_usable = usable;
|
|
|
|
|
dk->dk_lookup = lookdict_unicode_nodummy;
|
|
|
|
|
dk->dk_kind = DICT_KEYS_UNICODE;
|
|
|
|
|
dk->dk_nentries = 0;
|
|
|
|
|
memset(&dk->dk_indices[0], 0xff, es * size);
|
|
|
|
|
dk->dk_version = 0;
|
|
|
|
|
memset(&dk->dk_indices[0], 0xff, es * (1<<log2_size));
|
|
|
|
|
memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable);
|
|
|
|
|
return dk;
|
|
|
|
|
}
|
|
|
|
@ -632,7 +619,7 @@ free_keys_object(PyDictKeysObject *keys)
|
|
|
|
|
// free_keys_object() must not be called after _PyDict_Fini()
|
|
|
|
|
assert(state->keys_numfree != -1);
|
|
|
|
|
#endif
|
|
|
|
|
if (keys->dk_size == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) {
|
|
|
|
|
if (DK_SIZE(keys) == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) {
|
|
|
|
|
state->keys_free_list[state->keys_numfree++] = keys;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -778,36 +765,62 @@ probe indices are computed as explained earlier.
|
|
|
|
|
|
|
|
|
|
All arithmetic on hash should ignore overflow.
|
|
|
|
|
|
|
|
|
|
The details in this version are due to Tim Peters, building on many past
|
|
|
|
|
contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and
|
|
|
|
|
Christian Tismer.
|
|
|
|
|
|
|
|
|
|
lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a
|
|
|
|
|
_Py_dict_lookup() is general-purpose, and may return DKIX_ERROR if (and only if) a
|
|
|
|
|
comparison raises an exception.
|
|
|
|
|
lookdict_unicode() below is specialized to string keys, comparison of which can
|
|
|
|
|
never raise an exception; that function can never return DKIX_ERROR when key
|
|
|
|
|
is string. Otherwise, it falls back to lookdict().
|
|
|
|
|
lookdict_unicode_nodummy is further specialized for string keys that cannot be
|
|
|
|
|
the <dummy> value.
|
|
|
|
|
For both, when the key isn't found a DKIX_EMPTY is returned.
|
|
|
|
|
When the key isn't found a DKIX_EMPTY is returned.
|
|
|
|
|
*/
|
|
|
|
|
static Py_ssize_t _Py_HOT_FUNCTION
|
|
|
|
|
lookdict(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr)
|
|
|
|
|
Py_ssize_t _Py_HOT_FUNCTION
|
|
|
|
|
_Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
|
|
|
|
|
{
|
|
|
|
|
size_t i, mask, perturb;
|
|
|
|
|
PyDictKeysObject *dk;
|
|
|
|
|
PyDictKeyEntry *ep0;
|
|
|
|
|
|
|
|
|
|
top:
|
|
|
|
|
start:
|
|
|
|
|
dk = mp->ma_keys;
|
|
|
|
|
ep0 = DK_ENTRIES(dk);
|
|
|
|
|
mask = DK_MASK(dk);
|
|
|
|
|
perturb = hash;
|
|
|
|
|
i = (size_t)hash & mask;
|
|
|
|
|
|
|
|
|
|
DictKeysKind kind = dk->dk_kind;
|
|
|
|
|
PyDictKeyEntry *ep0 = DK_ENTRIES(dk);
|
|
|
|
|
size_t mask = DK_MASK(dk);
|
|
|
|
|
size_t perturb = hash;
|
|
|
|
|
size_t i = (size_t)hash & mask;
|
|
|
|
|
Py_ssize_t ix;
|
|
|
|
|
if (PyUnicode_CheckExact(key) && kind != DICT_KEYS_GENERAL) {
|
|
|
|
|
/* Strings only */
|
|
|
|
|
for (;;) {
|
|
|
|
|
Py_ssize_t ix = dictkeys_get_index(dk, i);
|
|
|
|
|
ix = dictkeys_get_index(mp->ma_keys, i);
|
|
|
|
|
if (ix >= 0) {
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
assert(PyUnicode_CheckExact(ep->me_key));
|
|
|
|
|
if (ep->me_key == key ||
|
|
|
|
|
(ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return DKIX_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
|
i = mask & (i*5 + perturb + 1);
|
|
|
|
|
ix = dictkeys_get_index(mp->ma_keys, i);
|
|
|
|
|
if (ix >= 0) {
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
assert(PyUnicode_CheckExact(ep->me_key));
|
|
|
|
|
if (ep->me_key == key ||
|
|
|
|
|
(ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return DKIX_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
|
i = mask & (i*5 + perturb + 1);
|
|
|
|
|
}
|
|
|
|
|
Py_UNREACHABLE();
|
|
|
|
|
}
|
|
|
|
|
for (;;) {
|
|
|
|
|
ix = dictkeys_get_index(dk, i);
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return ix;
|
|
|
|
@ -816,8 +829,7 @@ top:
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
if (ep->me_key == key) {
|
|
|
|
|
*value_addr = ep->me_value;
|
|
|
|
|
return ix;
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
if (ep->me_hash == hash) {
|
|
|
|
|
PyObject *startkey = ep->me_key;
|
|
|
|
@ -830,13 +842,12 @@ top:
|
|
|
|
|
}
|
|
|
|
|
if (dk == mp->ma_keys && ep->me_key == startkey) {
|
|
|
|
|
if (cmp > 0) {
|
|
|
|
|
*value_addr = ep->me_value;
|
|
|
|
|
return ix;
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* The dict was mutated, restart */
|
|
|
|
|
goto top;
|
|
|
|
|
goto start;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -844,135 +855,16 @@ top:
|
|
|
|
|
i = (i*5 + perturb + 1) & mask;
|
|
|
|
|
}
|
|
|
|
|
Py_UNREACHABLE();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Specialized version for string-only keys */
|
|
|
|
|
static Py_ssize_t _Py_HOT_FUNCTION
|
|
|
|
|
lookdict_unicode(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr)
|
|
|
|
|
{
|
|
|
|
|
assert(mp->ma_values == NULL);
|
|
|
|
|
/* Make sure this function doesn't have to handle non-unicode keys,
|
|
|
|
|
including subclasses of str; e.g., one reason to subclass
|
|
|
|
|
unicodes is to override __eq__, and for speed we don't cater to
|
|
|
|
|
that here. */
|
|
|
|
|
if (!PyUnicode_CheckExact(key)) {
|
|
|
|
|
return lookdict(mp, key, hash, value_addr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys);
|
|
|
|
|
size_t mask = DK_MASK(mp->ma_keys);
|
|
|
|
|
size_t perturb = (size_t)hash;
|
|
|
|
|
size_t i = (size_t)hash & mask;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i);
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return DKIX_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
if (ix >= 0) {
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
assert(PyUnicode_CheckExact(ep->me_key));
|
|
|
|
|
if (ep->me_key == key ||
|
|
|
|
|
(ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
|
|
|
|
|
*value_addr = ep->me_value;
|
|
|
|
|
return ix;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
|
i = mask & (i*5 + perturb + 1);
|
|
|
|
|
}
|
|
|
|
|
Py_UNREACHABLE();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Faster version of lookdict_unicode when it is known that no <dummy> keys
|
|
|
|
|
* will be present. */
|
|
|
|
|
static Py_ssize_t _Py_HOT_FUNCTION
|
|
|
|
|
lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr)
|
|
|
|
|
{
|
|
|
|
|
assert(mp->ma_values == NULL);
|
|
|
|
|
/* Make sure this function doesn't have to handle non-unicode keys,
|
|
|
|
|
including subclasses of str; e.g., one reason to subclass
|
|
|
|
|
unicodes is to override __eq__, and for speed we don't cater to
|
|
|
|
|
that here. */
|
|
|
|
|
if (!PyUnicode_CheckExact(key)) {
|
|
|
|
|
return lookdict(mp, key, hash, value_addr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys);
|
|
|
|
|
size_t mask = DK_MASK(mp->ma_keys);
|
|
|
|
|
size_t perturb = (size_t)hash;
|
|
|
|
|
size_t i = (size_t)hash & mask;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i);
|
|
|
|
|
assert (ix != DKIX_DUMMY);
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return DKIX_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
assert(PyUnicode_CheckExact(ep->me_key));
|
|
|
|
|
if (ep->me_key == key ||
|
|
|
|
|
(ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
|
|
|
|
|
*value_addr = ep->me_value;
|
|
|
|
|
return ix;
|
|
|
|
|
}
|
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
|
i = mask & (i*5 + perturb + 1);
|
|
|
|
|
}
|
|
|
|
|
Py_UNREACHABLE();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Version of lookdict for split tables.
|
|
|
|
|
* All split tables and only split tables use this lookup function.
|
|
|
|
|
* Split tables only contain unicode keys and no dummy keys,
|
|
|
|
|
* so algorithm is the same as lookdict_unicode_nodummy.
|
|
|
|
|
*/
|
|
|
|
|
static Py_ssize_t _Py_HOT_FUNCTION
|
|
|
|
|
lookdict_split(PyDictObject *mp, PyObject *key,
|
|
|
|
|
Py_hash_t hash, PyObject **value_addr)
|
|
|
|
|
{
|
|
|
|
|
/* mp must split table */
|
|
|
|
|
assert(mp->ma_values != NULL);
|
|
|
|
|
if (!PyUnicode_CheckExact(key)) {
|
|
|
|
|
Py_ssize_t ix = lookdict(mp, key, hash, value_addr);
|
|
|
|
|
if (ix >= 0) {
|
|
|
|
|
found:
|
|
|
|
|
if (dk->dk_kind == DICT_KEYS_SPLIT) {
|
|
|
|
|
*value_addr = mp->ma_values[ix];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
*value_addr = ep0[ix].me_value;
|
|
|
|
|
}
|
|
|
|
|
return ix;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys);
|
|
|
|
|
size_t mask = DK_MASK(mp->ma_keys);
|
|
|
|
|
size_t perturb = (size_t)hash;
|
|
|
|
|
size_t i = (size_t)hash & mask;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i);
|
|
|
|
|
assert (ix != DKIX_DUMMY);
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
*value_addr = NULL;
|
|
|
|
|
return DKIX_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
PyDictKeyEntry *ep = &ep0[ix];
|
|
|
|
|
assert(ep->me_key != NULL);
|
|
|
|
|
assert(PyUnicode_CheckExact(ep->me_key));
|
|
|
|
|
if (ep->me_key == key ||
|
|
|
|
|
(ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
|
|
|
|
|
*value_addr = mp->ma_values[ix];
|
|
|
|
|
return ix;
|
|
|
|
|
}
|
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
|
i = mask & (i*5 + perturb + 1);
|
|
|
|
|
}
|
|
|
|
|
Py_UNREACHABLE();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
_PyDict_HasOnlyStringKeys(PyObject *dict)
|
|
|
|
|
{
|
|
|
|
@ -980,7 +872,7 @@ _PyDict_HasOnlyStringKeys(PyObject *dict)
|
|
|
|
|
PyObject *key, *value;
|
|
|
|
|
assert(PyDict_Check(dict));
|
|
|
|
|
/* Shortcut */
|
|
|
|
|
if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict)
|
|
|
|
|
if (((PyDictObject *)dict)->ma_keys->dk_kind != DICT_KEYS_GENERAL)
|
|
|
|
|
return 1;
|
|
|
|
|
while (PyDict_Next(dict, &pos, &key, &value))
|
|
|
|
|
if (!PyUnicode_Check(key))
|
|
|
|
@ -1057,7 +949,7 @@ find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash)
|
|
|
|
|
static int
|
|
|
|
|
insertion_resize(PyDictObject *mp)
|
|
|
|
|
{
|
|
|
|
|
return dictresize(mp, calculate_keysize(GROWTH_RATE(mp)));
|
|
|
|
|
return dictresize(mp, calculate_log2_keysize(GROWTH_RATE(mp)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -1078,7 +970,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
|
|
|
|
|
goto Fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value);
|
|
|
|
|
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
goto Fail;
|
|
|
|
|
|
|
|
|
@ -1097,14 +989,15 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
|
|
|
|
|
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
/* Insert into new slot. */
|
|
|
|
|
mp->ma_keys->dk_version = 0;
|
|
|
|
|
assert(old_value == NULL);
|
|
|
|
|
if (mp->ma_keys->dk_usable <= 0) {
|
|
|
|
|
/* Need to resize. */
|
|
|
|
|
if (insertion_resize(mp) < 0)
|
|
|
|
|
goto Fail;
|
|
|
|
|
}
|
|
|
|
|
if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_lookup != lookdict) {
|
|
|
|
|
mp->ma_keys->dk_lookup = lookdict;
|
|
|
|
|
if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_kind != DICT_KEYS_GENERAL) {
|
|
|
|
|
mp->ma_keys->dk_kind = DICT_KEYS_GENERAL;
|
|
|
|
|
}
|
|
|
|
|
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
|
|
|
|
|
ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
|
|
|
|
@ -1160,12 +1053,12 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash,
|
|
|
|
|
{
|
|
|
|
|
assert(mp->ma_keys == Py_EMPTY_KEYS);
|
|
|
|
|
|
|
|
|
|
PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE);
|
|
|
|
|
PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE);
|
|
|
|
|
if (newkeys == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!PyUnicode_CheckExact(key)) {
|
|
|
|
|
newkeys->dk_lookup = lookdict;
|
|
|
|
|
newkeys->dk_kind = DICT_KEYS_GENERAL;
|
|
|
|
|
}
|
|
|
|
|
dictkeys_decref(Py_EMPTY_KEYS);
|
|
|
|
|
mp->ma_keys = newkeys;
|
|
|
|
@ -1217,19 +1110,18 @@ After resizing a table is always combined,
|
|
|
|
|
but can be resplit by make_keys_shared().
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
dictresize(PyDictObject *mp, Py_ssize_t newsize)
|
|
|
|
|
dictresize(PyDictObject *mp, uint8_t log2_newsize)
|
|
|
|
|
{
|
|
|
|
|
Py_ssize_t numentries;
|
|
|
|
|
PyDictKeysObject *oldkeys;
|
|
|
|
|
PyObject **oldvalues;
|
|
|
|
|
PyDictKeyEntry *oldentries, *newentries;
|
|
|
|
|
|
|
|
|
|
if (newsize <= 0) {
|
|
|
|
|
if (log2_newsize >= SIZEOF_SIZE_T*8) {
|
|
|
|
|
PyErr_NoMemory();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
assert(IS_POWER_OF_2(newsize));
|
|
|
|
|
assert(newsize >= PyDict_MINSIZE);
|
|
|
|
|
assert(log2_newsize >= PyDict_LOG_MINSIZE);
|
|
|
|
|
|
|
|
|
|
oldkeys = mp->ma_keys;
|
|
|
|
|
|
|
|
|
@ -1239,15 +1131,15 @@ dictresize(PyDictObject *mp, Py_ssize_t newsize)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Allocate a new table. */
|
|
|
|
|
mp->ma_keys = new_keys_object(newsize);
|
|
|
|
|
mp->ma_keys = new_keys_object(log2_newsize);
|
|
|
|
|
if (mp->ma_keys == NULL) {
|
|
|
|
|
mp->ma_keys = oldkeys;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
// New table must be large enough.
|
|
|
|
|
assert(mp->ma_keys->dk_usable >= mp->ma_used);
|
|
|
|
|
if (oldkeys->dk_lookup == lookdict)
|
|
|
|
|
mp->ma_keys->dk_lookup = lookdict;
|
|
|
|
|
if (oldkeys->dk_kind == DICT_KEYS_GENERAL)
|
|
|
|
|
mp->ma_keys->dk_kind = DICT_KEYS_GENERAL;
|
|
|
|
|
|
|
|
|
|
numentries = mp->ma_used;
|
|
|
|
|
oldentries = DK_ENTRIES(oldkeys);
|
|
|
|
@ -1287,7 +1179,7 @@ dictresize(PyDictObject *mp, Py_ssize_t newsize)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(oldkeys->dk_lookup != lookdict_split);
|
|
|
|
|
assert(oldkeys->dk_kind != DICT_KEYS_SPLIT);
|
|
|
|
|
assert(oldkeys->dk_refcnt == 1);
|
|
|
|
|
#ifdef Py_REF_DEBUG
|
|
|
|
|
_Py_RefTotal--;
|
|
|
|
@ -1297,7 +1189,7 @@ dictresize(PyDictObject *mp, Py_ssize_t newsize)
|
|
|
|
|
// dictresize() must not be called after _PyDict_Fini()
|
|
|
|
|
assert(state->keys_numfree != -1);
|
|
|
|
|
#endif
|
|
|
|
|
if (oldkeys->dk_size == PyDict_MINSIZE &&
|
|
|
|
|
if (DK_SIZE(oldkeys) == PyDict_MINSIZE &&
|
|
|
|
|
state->keys_numfree < PyDict_MAXFREELIST)
|
|
|
|
|
{
|
|
|
|
|
state->keys_free_list[state->keys_numfree++] = oldkeys;
|
|
|
|
@ -1328,15 +1220,15 @@ make_keys_shared(PyObject *op)
|
|
|
|
|
PyDictKeyEntry *ep0;
|
|
|
|
|
PyObject **values;
|
|
|
|
|
assert(mp->ma_keys->dk_refcnt == 1);
|
|
|
|
|
if (mp->ma_keys->dk_lookup == lookdict) {
|
|
|
|
|
if (mp->ma_keys->dk_kind == DICT_KEYS_GENERAL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (mp->ma_keys->dk_lookup == lookdict_unicode) {
|
|
|
|
|
else if (mp->ma_used > mp->ma_keys->dk_nentries) {
|
|
|
|
|
/* Remove dummy keys */
|
|
|
|
|
if (dictresize(mp, DK_SIZE(mp->ma_keys)))
|
|
|
|
|
if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys)))
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy);
|
|
|
|
|
assert(mp->ma_used == mp->ma_keys->dk_nentries);
|
|
|
|
|
/* Copy values into a new array */
|
|
|
|
|
ep0 = DK_ENTRIES(mp->ma_keys);
|
|
|
|
|
size = USABLE_FRACTION(DK_SIZE(mp->ma_keys));
|
|
|
|
@ -1350,7 +1242,7 @@ make_keys_shared(PyObject *op)
|
|
|
|
|
values[i] = ep0[i].me_value;
|
|
|
|
|
ep0[i].me_value = NULL;
|
|
|
|
|
}
|
|
|
|
|
mp->ma_keys->dk_lookup = lookdict_split;
|
|
|
|
|
mp->ma_keys->dk_kind = DICT_KEYS_SPLIT;
|
|
|
|
|
mp->ma_values = values;
|
|
|
|
|
}
|
|
|
|
|
dictkeys_incref(mp->ma_keys);
|
|
|
|
@ -1360,8 +1252,9 @@ make_keys_shared(PyObject *op)
|
|
|
|
|
PyObject *
|
|
|
|
|
_PyDict_NewPresized(Py_ssize_t minused)
|
|
|
|
|
{
|
|
|
|
|
const Py_ssize_t max_presize = 128 * 1024;
|
|
|
|
|
Py_ssize_t newsize;
|
|
|
|
|
const uint8_t log2_max_presize = 17;
|
|
|
|
|
const Py_ssize_t max_presize = ((Py_ssize_t)1) << log2_max_presize;
|
|
|
|
|
uint8_t log2_newsize;
|
|
|
|
|
PyDictKeysObject *new_keys;
|
|
|
|
|
|
|
|
|
|
if (minused <= USABLE_FRACTION(PyDict_MINSIZE)) {
|
|
|
|
@ -1372,13 +1265,13 @@ _PyDict_NewPresized(Py_ssize_t minused)
|
|
|
|
|
* large dict or MemoryError.
|
|
|
|
|
*/
|
|
|
|
|
if (minused > USABLE_FRACTION(max_presize)) {
|
|
|
|
|
newsize = max_presize;
|
|
|
|
|
log2_newsize = log2_max_presize;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
newsize = estimate_keysize(minused);
|
|
|
|
|
log2_newsize = estimate_log2_keysize(minused);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_keys = new_keys_object(newsize);
|
|
|
|
|
new_keys = new_keys_object(log2_newsize);
|
|
|
|
|
if (new_keys == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
return new_dict(new_keys, NULL);
|
|
|
|
@ -1426,14 +1319,13 @@ PyDict_GetItem(PyObject *op, PyObject *key)
|
|
|
|
|
Py_ssize_t ix;
|
|
|
|
|
|
|
|
|
|
_PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
|
|
|
|
|
/* Ignore any exception raised by the lookup */
|
|
|
|
|
_PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
|
|
|
|
|
|
|
|
|
|
if (ix < 0) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(ix >= 0 || value == NULL);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1450,7 +1342,7 @@ _PyDict_GetItemHint(PyDictObject *mp, PyObject *key,
|
|
|
|
|
|
|
|
|
|
PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys) + (size_t)hint;
|
|
|
|
|
if (ep->me_key == key) {
|
|
|
|
|
if (mp->ma_keys->dk_lookup == lookdict_split) {
|
|
|
|
|
if (mp->ma_keys->dk_kind == DICT_KEYS_SPLIT) {
|
|
|
|
|
assert(mp->ma_values != NULL);
|
|
|
|
|
res = mp->ma_values[(size_t)hint];
|
|
|
|
|
}
|
|
|
|
@ -1472,7 +1364,7 @@ _PyDict_GetItemHint(PyDictObject *mp, PyObject *key,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (mp->ma_keys->dk_lookup)(mp, key, hash, value);
|
|
|
|
|
return _Py_dict_lookup(mp, key, hash, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Same as PyDict_GetItemWithError() but with hash supplied by caller.
|
|
|
|
@ -1491,10 +1383,8 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
if (ix < 0) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
assert(ix >= 0 || value == NULL);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1523,9 +1413,8 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
if (ix < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
assert(ix >= 0 || value == NULL);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1577,16 +1466,15 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* namespace 1: globals */
|
|
|
|
|
ix = globals->ma_keys->dk_lookup(globals, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(globals, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ix != DKIX_EMPTY && value != NULL)
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
|
|
/* namespace 2: builtins */
|
|
|
|
|
ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value);
|
|
|
|
|
if (ix < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
ix = _Py_dict_lookup(builtins, key, hash, &value);
|
|
|
|
|
assert(ix >= 0 || value == NULL);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1659,7 +1547,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
|
|
|
|
mp->ma_version_tag = DICT_NEXT_VERSION();
|
|
|
|
|
ep = &DK_ENTRIES(mp->ma_keys)[ix];
|
|
|
|
|
dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
|
|
|
|
|
ENSURE_ALLOWS_DELETIONS(mp);
|
|
|
|
|
mp->ma_keys->dk_version = 0;
|
|
|
|
|
old_key = ep->me_key;
|
|
|
|
|
ep->me_key = NULL;
|
|
|
|
|
ep->me_value = NULL;
|
|
|
|
@ -1699,7 +1587,7 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
|
|
|
|
assert(key);
|
|
|
|
|
assert(hash != -1);
|
|
|
|
|
mp = (PyDictObject *)op;
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return -1;
|
|
|
|
|
if (ix == DKIX_EMPTY || old_value == NULL) {
|
|
|
|
@ -1709,10 +1597,10 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
|
|
|
|
|
|
|
|
|
// Split table doesn't allow deletion. Combine it.
|
|
|
|
|
if (_PyDict_HasSplitTable(mp)) {
|
|
|
|
|
if (dictresize(mp, DK_SIZE(mp->ma_keys))) {
|
|
|
|
|
if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
assert(ix >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1742,7 +1630,7 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key,
|
|
|
|
|
if (hash == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
mp = (PyDictObject *)op;
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return -1;
|
|
|
|
|
if (ix == DKIX_EMPTY || old_value == NULL) {
|
|
|
|
@ -1752,10 +1640,10 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key,
|
|
|
|
|
|
|
|
|
|
// Split table doesn't allow deletion. Combine it.
|
|
|
|
|
if (_PyDict_HasSplitTable(mp)) {
|
|
|
|
|
if (dictresize(mp, DK_SIZE(mp->ma_keys))) {
|
|
|
|
|
if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
assert(ix >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1902,7 +1790,7 @@ _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *d
|
|
|
|
|
_PyErr_SetKeyError(key);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ix == DKIX_EMPTY || old_value == NULL) {
|
|
|
|
@ -1916,10 +1804,10 @@ _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *d
|
|
|
|
|
|
|
|
|
|
// Split table doesn't allow deletion. Combine it.
|
|
|
|
|
if (_PyDict_HasSplitTable(mp)) {
|
|
|
|
|
if (dictresize(mp, DK_SIZE(mp->ma_keys))) {
|
|
|
|
|
if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
|
|
|
assert(ix >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1930,7 +1818,7 @@ _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *d
|
|
|
|
|
mp->ma_version_tag = DICT_NEXT_VERSION();
|
|
|
|
|
dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
|
|
|
|
|
ep = &DK_ENTRIES(mp->ma_keys)[ix];
|
|
|
|
|
ENSURE_ALLOWS_DELETIONS(mp);
|
|
|
|
|
mp->ma_keys->dk_version = 0;
|
|
|
|
|
old_key = ep->me_key;
|
|
|
|
|
ep->me_key = NULL;
|
|
|
|
|
ep->me_value = NULL;
|
|
|
|
@ -1983,7 +1871,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
|
|
|
|
|
PyObject *key;
|
|
|
|
|
Py_hash_t hash;
|
|
|
|
|
|
|
|
|
|
if (dictresize(mp, estimate_keysize(PyDict_GET_SIZE(iterable)))) {
|
|
|
|
|
if (dictresize(mp, estimate_log2_keysize(PyDict_GET_SIZE(iterable)))) {
|
|
|
|
|
Py_DECREF(d);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -2002,7 +1890,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
|
|
|
|
|
PyObject *key;
|
|
|
|
|
Py_hash_t hash;
|
|
|
|
|
|
|
|
|
|
if (dictresize(mp, estimate_keysize(PySet_GET_SIZE(iterable)))) {
|
|
|
|
|
if (dictresize(mp, estimate_log2_keysize(PySet_GET_SIZE(iterable)))) {
|
|
|
|
|
Py_DECREF(d);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -2192,7 +2080,7 @@ dict_subscript(PyDictObject *mp, PyObject *key)
|
|
|
|
|
if (hash == -1)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ix == DKIX_EMPTY || value == NULL) {
|
|
|
|
@ -2578,8 +2466,8 @@ dict_merge(PyObject *a, PyObject *b, int override)
|
|
|
|
|
// If other is clean, combined, and just allocated, just clone it.
|
|
|
|
|
if (other->ma_values == NULL &&
|
|
|
|
|
other->ma_used == okeys->dk_nentries &&
|
|
|
|
|
(okeys->dk_size == PyDict_MINSIZE ||
|
|
|
|
|
USABLE_FRACTION(okeys->dk_size/2) < other->ma_used)) {
|
|
|
|
|
(DK_SIZE(okeys) == PyDict_MINSIZE ||
|
|
|
|
|
USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)) {
|
|
|
|
|
PyDictKeysObject *keys = clone_combined_dict_keys(other);
|
|
|
|
|
if (keys == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
@ -2610,8 +2498,8 @@ dict_merge(PyObject *a, PyObject *b, int override)
|
|
|
|
|
* incrementally resizing as we insert new items. Expect
|
|
|
|
|
* that there will be no (or few) overlapping keys.
|
|
|
|
|
*/
|
|
|
|
|
if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) {
|
|
|
|
|
if (dictresize(mp, estimate_keysize(mp->ma_used + other->ma_used))) {
|
|
|
|
|
if (USABLE_FRACTION(DK_SIZE(mp->ma_keys)) < other->ma_used) {
|
|
|
|
|
if (dictresize(mp, estimate_log2_keysize(mp->ma_used + other->ma_used))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -2908,7 +2796,7 @@ dict_equal(PyDictObject *a, PyDictObject *b)
|
|
|
|
|
/* ditto for key */
|
|
|
|
|
Py_INCREF(key);
|
|
|
|
|
/* reuse the known hash value */
|
|
|
|
|
b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval);
|
|
|
|
|
_Py_dict_lookup(b, key, ep->me_hash, &bval);
|
|
|
|
|
if (bval == NULL) {
|
|
|
|
|
Py_DECREF(key);
|
|
|
|
|
Py_DECREF(aval);
|
|
|
|
@ -2975,7 +2863,7 @@ dict___contains__(PyDictObject *self, PyObject *key)
|
|
|
|
|
if (hash == -1)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ix == DKIX_EMPTY || value == NULL)
|
|
|
|
@ -3007,7 +2895,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
|
|
|
|
|
if (hash == -1)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ix = (self->ma_keys->dk_lookup) (self, key, hash, &val);
|
|
|
|
|
ix = _Py_dict_lookup(self, key, hash, &val);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ix == DKIX_EMPTY || val == NULL) {
|
|
|
|
@ -3047,7 +2935,7 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
@ -3061,6 +2949,7 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ix == DKIX_EMPTY) {
|
|
|
|
|
mp->ma_keys->dk_version = 0;
|
|
|
|
|
PyDictKeyEntry *ep, *ep0;
|
|
|
|
|
value = defaultobj;
|
|
|
|
|
if (mp->ma_keys->dk_usable <= 0) {
|
|
|
|
@ -3068,8 +2957,8 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_lookup != lookdict) {
|
|
|
|
|
mp->ma_keys->dk_lookup = lookdict;
|
|
|
|
|
if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_kind != DICT_KEYS_GENERAL) {
|
|
|
|
|
mp->ma_keys->dk_kind = DICT_KEYS_GENERAL;
|
|
|
|
|
}
|
|
|
|
|
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
|
|
|
|
|
ep0 = DK_ENTRIES(mp->ma_keys);
|
|
|
|
@ -3194,13 +3083,13 @@ dict_popitem_impl(PyDictObject *self)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
/* Convert split table to combined table */
|
|
|
|
|
if (self->ma_keys->dk_lookup == lookdict_split) {
|
|
|
|
|
if (dictresize(self, DK_SIZE(self->ma_keys))) {
|
|
|
|
|
if (self->ma_keys->dk_kind == DICT_KEYS_SPLIT) {
|
|
|
|
|
if (dictresize(self, DK_LOG_SIZE(self->ma_keys))) {
|
|
|
|
|
Py_DECREF(res);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ENSURE_ALLOWS_DELETIONS(self);
|
|
|
|
|
self->ma_keys->dk_version = 0;
|
|
|
|
|
|
|
|
|
|
/* Pop last item */
|
|
|
|
|
ep0 = DK_ENTRIES(self->ma_keys);
|
|
|
|
@ -3236,7 +3125,7 @@ dict_traverse(PyObject *op, visitproc visit, void *arg)
|
|
|
|
|
PyDictKeyEntry *entries = DK_ENTRIES(keys);
|
|
|
|
|
Py_ssize_t i, n = keys->dk_nentries;
|
|
|
|
|
|
|
|
|
|
if (keys->dk_lookup == lookdict) {
|
|
|
|
|
if (keys->dk_kind == DICT_KEYS_GENERAL) {
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
if (entries[i].me_value != NULL) {
|
|
|
|
|
Py_VISIT(entries[i].me_value);
|
|
|
|
@ -3401,7 +3290,7 @@ PyDict_Contains(PyObject *op, PyObject *key)
|
|
|
|
|
if (hash == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return -1;
|
|
|
|
|
return (ix != DKIX_EMPTY && value != NULL);
|
|
|
|
@ -3415,7 +3304,7 @@ _PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
|
|
|
|
PyObject *value;
|
|
|
|
|
Py_ssize_t ix;
|
|
|
|
|
|
|
|
|
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
|
|
|
|
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
|
|
|
if (ix == DKIX_ERROR)
|
|
|
|
|
return -1;
|
|
|
|
|
return (ix != DKIX_EMPTY && value != NULL);
|
|
|
|
@ -4974,12 +4863,12 @@ dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
|
|
|
|
|
PyDictKeysObject *
|
|
|
|
|
_PyDict_NewKeysForClass(void)
|
|
|
|
|
{
|
|
|
|
|
PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE);
|
|
|
|
|
PyDictKeysObject *keys = new_keys_object(PyDict_LOG_MINSIZE);
|
|
|
|
|
if (keys == NULL) {
|
|
|
|
|
PyErr_Clear();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
keys->dk_lookup = lookdict_split;
|
|
|
|
|
keys->dk_kind = DICT_KEYS_SPLIT;
|
|
|
|
|
}
|
|
|
|
|
return keys;
|
|
|
|
|
}
|
|
|
|
@ -5091,3 +4980,18 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys)
|
|
|
|
|
{
|
|
|
|
|
dictkeys_decref(keys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t next_dict_keys_version = 2;
|
|
|
|
|
|
|
|
|
|
uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictObject *dict)
|
|
|
|
|
{
|
|
|
|
|
if (dict->ma_keys->dk_version != 0) {
|
|
|
|
|
return dict->ma_keys->dk_version;
|
|
|
|
|
}
|
|
|
|
|
if (next_dict_keys_version == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
uint32_t v = next_dict_keys_version++;
|
|
|
|
|
dict->ma_keys->dk_version = v;
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|