mirror of
https://github.com/python/cpython.git
synced 2024-11-23 18:04:37 +08:00
bpo-44206: Add a version number to dictionary keys (GH-26333)
* Store log2(size) instead of size in dict-keys. * Use enum instead of function pointer to record kind of keys. * Add version number to dict keys.
This commit is contained in:
parent
8994e9c2cd
commit
f8a95df84b
@ -82,3 +82,7 @@ typedef struct {
|
||||
|
||||
PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *);
|
||||
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
|
||||
|
||||
/* Gets a version number unique to the current state of the keys of dict, if possible.
|
||||
* Returns the version number, or zero if it was not possible to get a version number. */
|
||||
uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictObject *dict);
|
||||
|
@ -752,7 +752,7 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
|
||||
check = self.check_sizeof
|
||||
|
||||
basicsize = size('nQ2P' + '3PnPn2P')
|
||||
keysize = calcsize('2nP2n')
|
||||
keysize = calcsize('n2BI2n')
|
||||
|
||||
entrysize = calcsize('n2P')
|
||||
p = calcsize('P')
|
||||
|
@ -22,6 +22,7 @@ import warnings
|
||||
# strings to intern in test_intern()
|
||||
INTERN_NUMRUNS = 0
|
||||
|
||||
DICT_KEY_STRUCT_FORMAT = 'n2BI2n'
|
||||
|
||||
class DisplayHookTest(unittest.TestCase):
|
||||
|
||||
@ -1229,9 +1230,9 @@ class SizeofTest(unittest.TestCase):
|
||||
# empty dict
|
||||
check({}, size('nQ2P'))
|
||||
# dict
|
||||
check({"a": 1}, size('nQ2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P'))
|
||||
check({"a": 1}, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + (8*2//3)*calcsize('n2P'))
|
||||
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
|
||||
check(longdict, size('nQ2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P'))
|
||||
check(longdict, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + (16*2//3)*calcsize('n2P'))
|
||||
# dictionary-keyview
|
||||
check({}.keys(), size('P'))
|
||||
# dictionary-valueview
|
||||
@ -1385,13 +1386,13 @@ class SizeofTest(unittest.TestCase):
|
||||
'5P')
|
||||
class newstyleclass(object): pass
|
||||
# Separate block for PyDictKeysObject with 8 keys and 5 entries
|
||||
check(newstyleclass, s + calcsize("2nP2n0P") + 8 + 5*calcsize("n2P"))
|
||||
check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + 5*calcsize("n2P"))
|
||||
# dict with shared keys
|
||||
check(newstyleclass().__dict__, size('nQ2P') + 5*self.P)
|
||||
o = newstyleclass()
|
||||
o.a = o.b = o.c = o.d = o.e = o.f = o.g = o.h = 1
|
||||
# Separate block for PyDictKeysObject with 16 keys and 10 entries
|
||||
check(newstyleclass, s + calcsize("2nP2n0P") + 16 + 10*calcsize("n2P"))
|
||||
check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + 10*calcsize("n2P"))
|
||||
# dict with shared keys
|
||||
check(newstyleclass().__dict__, size('nQ2P') + 10*self.P)
|
||||
# unicode
|
||||
|
@ -8,37 +8,34 @@ typedef struct {
|
||||
PyObject *me_value; /* This field is only meaningful for combined tables */
|
||||
} PyDictKeyEntry;
|
||||
|
||||
/* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index].
|
||||
/* _Py_dict_lookup() returns index of entry which can be used like DK_ENTRIES(dk)[index].
|
||||
* -1 when no entry found, -3 when compare raises error.
|
||||
*/
|
||||
typedef Py_ssize_t (*dict_lookup_func)
|
||||
(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
||||
Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
||||
|
||||
|
||||
#define DKIX_EMPTY (-1)
|
||||
#define DKIX_DUMMY (-2) /* Used internally */
|
||||
#define DKIX_ERROR (-3)
|
||||
|
||||
typedef enum {
|
||||
DICT_KEYS_GENERAL = 0,
|
||||
DICT_KEYS_UNICODE = 1,
|
||||
DICT_KEYS_SPLIT = 2
|
||||
} DictKeysKind;
|
||||
|
||||
/* See dictobject.c for actual layout of DictKeysObject */
|
||||
struct _dictkeysobject {
|
||||
Py_ssize_t dk_refcnt;
|
||||
|
||||
/* Size of the hash table (dk_indices). It must be a power of 2. */
|
||||
Py_ssize_t dk_size;
|
||||
uint8_t dk_log2_size;
|
||||
|
||||
/* Function to lookup in the hash table (dk_indices):
|
||||
/* Kind of keys */
|
||||
uint8_t dk_kind;
|
||||
|
||||
- lookdict(): general-purpose, and may return DKIX_ERROR if (and
|
||||
only if) a comparison raises an exception.
|
||||
|
||||
- lookdict_unicode(): specialized to Unicode string keys, comparison of
|
||||
which can never raise an exception; that function can never return
|
||||
DKIX_ERROR.
|
||||
|
||||
- lookdict_unicode_nodummy(): similar to lookdict_unicode() but further
|
||||
specialized for Unicode string keys that cannot be the <dummy> value.
|
||||
|
||||
- lookdict_split(): Version of lookdict() for split tables. */
|
||||
dict_lookup_func dk_lookup;
|
||||
/* Version number -- Reset to 0 by any modification to keys */
|
||||
uint32_t dk_version;
|
||||
|
||||
/* Number of usable entries in dk_entries. */
|
||||
Py_ssize_t dk_usable;
|
||||
|
@ -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_IXSIZE(dk) \
|
||||
(DK_SIZE(dk) <= 0xff ? \
|
||||
1 : DK_SIZE(dk) <= 0xffff ? \
|
||||
2 : DK_SIZE(dk) <= 0xffffffff ? \
|
||||
#define DK_SIZE(dk) (((int64_t)1)<<DK_LOG_SIZE(dk))
|
||||
#define DK_IXSIZE(dk) \
|
||||
(DK_LOG_SIZE(dk) <= 7 ? \
|
||||
1 : DK_LOG_SIZE(dk) <= 15 ? \
|
||||
2 : DK_LOG_SIZE(dk) <= 31 ? \
|
||||
4 : sizeof(int64_t))
|
||||
#else
|
||||
#define DK_IXSIZE(dk) \
|
||||
(DK_SIZE(dk) <= 0xff ? \
|
||||
1 : DK_SIZE(dk) <= 0xffff ? \
|
||||
#define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk))
|
||||
#define DK_IXSIZE(dk) \
|
||||
(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 (;;) {
|
||||
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 (;;) {
|
||||
Py_ssize_t ix = dictkeys_get_index(dk, i);
|
||||
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,133 +855,14 @@ 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);
|
||||
found:
|
||||
if (dk->dk_kind == DICT_KEYS_SPLIT) {
|
||||
*value_addr = mp->ma_values[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);
|
||||
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);
|
||||
else {
|
||||
*value_addr = ep0[ix].me_value;
|
||||
}
|
||||
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) {
|
||||
*value_addr = mp->ma_values[ix];
|
||||
}
|
||||
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();
|
||||
return ix;
|
||||
}
|
||||
|
||||
int
|
||||
@ -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;
|
||||
}
|
||||
|
@ -40,9 +40,9 @@ we've considered:
|
||||
|
||||
The approach with the least performance impact (time and space) is #2,
|
||||
mirroring the key order of dict's dk_entries with an array of node pointers.
|
||||
While lookdict() and friends (dk_lookup) don't give us the index into the
|
||||
array, we make use of pointer arithmetic to get that index. An alternative
|
||||
would be to refactor lookdict() to provide the index, explicitly exposing
|
||||
While _Py_dict_lookup() does not give us the index into the array,
|
||||
we make use of pointer arithmetic to get that index. An alternative would
|
||||
be to refactor _Py_dict_lookup() to provide the index, explicitly exposing
|
||||
the implementation detail. We could even just use a custom lookup function
|
||||
for OrderedDict that facilitates our need. However, both approaches are
|
||||
significantly more complicated than just using pointer arithmetic.
|
||||
@ -535,7 +535,7 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
|
||||
Py_ssize_t ix;
|
||||
|
||||
ix = (keys->dk_lookup)((PyDictObject *)od, key, hash, &value);
|
||||
ix = _Py_dict_lookup((PyDictObject *)od, key, hash, &value);
|
||||
if (ix == DKIX_EMPTY) {
|
||||
return keys->dk_nentries; /* index of new entry */
|
||||
}
|
||||
@ -553,7 +553,7 @@ _odict_resize(PyODictObject *od)
|
||||
_ODictNode **fast_nodes, *node;
|
||||
|
||||
/* Initialize a new "fast nodes" table. */
|
||||
size = ((PyDictObject *)od)->ma_keys->dk_size;
|
||||
size = 1 << (((PyDictObject *)od)->ma_keys->dk_log2_size);
|
||||
fast_nodes = PyMem_NEW(_ODictNode *, size);
|
||||
if (fast_nodes == NULL) {
|
||||
PyErr_NoMemory();
|
||||
@ -592,7 +592,7 @@ _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
|
||||
/* Ensure od_fast_nodes and dk_entries are in sync. */
|
||||
if (od->od_resize_sentinel != keys ||
|
||||
od->od_fast_nodes_size != keys->dk_size) {
|
||||
od->od_fast_nodes_size != (1 << (keys->dk_log2_size))) {
|
||||
int resize_res = _odict_resize(od);
|
||||
if (resize_res < 0)
|
||||
return -1;
|
||||
|
@ -730,7 +730,7 @@ class PyDictObjectPtr(PyObjectPtr):
|
||||
|
||||
def _get_entries(self, keys):
|
||||
dk_nentries = int(keys['dk_nentries'])
|
||||
dk_size = int(keys['dk_size'])
|
||||
dk_size = 1<<int(keys['dk_log2_size'])
|
||||
try:
|
||||
# <= Python 3.5
|
||||
return keys['dk_entries'], dk_size
|
||||
|
Loading…
Reference in New Issue
Block a user