summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-10-06 12:19:53 (GMT)
committerGitHub <noreply@github.com>2021-10-06 12:19:53 (GMT)
commita7252f88d3fa33036bdd6036b8c97bc785ed6f17 (patch)
tree525655111a4423af0de21004ab1c3f6d7e765fe6 /Objects
parentf6eafe18c004c55082de40d20cad084ef9dd3db7 (diff)
downloadcpython-a7252f88d3fa33036bdd6036b8c97bc785ed6f17.zip
cpython-a7252f88d3fa33036bdd6036b8c97bc785ed6f17.tar.gz
cpython-a7252f88d3fa33036bdd6036b8c97bc785ed6f17.tar.bz2
bpo-40116: Add insertion order bit-vector to dict values to allow dicts to share keys more freely. (GH-28520)
Diffstat (limited to 'Objects')
-rw-r--r--Objects/dictobject.c281
1 files changed, 151 insertions, 130 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index ae0098b..824cba94 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -60,7 +60,6 @@ Or:
ma_values != NULL, dk_refcnt >= 1
Values are stored in the ma_values array.
Only string (unicode) keys are allowed.
- All dicts sharing same key must have same insertion order.
There are four kinds of slots in the table (slot is index, and
DK_ENTRIES(keys)[index] if index >= 0):
@@ -96,9 +95,10 @@ dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in
dk_indices, we can't increment dk_usable even though dk_nentries is
decremented.
-In split table, inserting into pending entry is allowed only for dk_entries[ix]
-where ix == mp->ma_used. Inserting into other index and deleting item cause
-converting the dict to the combined table.
+To preserve the order in a split table, a bit vector is used to record the
+insertion order. When a key is inserted the bit vector is shifted up by 4 bits
+and the index of the key is stored in the low 4 bits.
+As a consequence of this, split keys have a maximum size of 16.
*/
/* PyDict_MINSIZE is the starting size for any new dict.
@@ -445,7 +445,9 @@ static PyDictKeysObject empty_keys_struct = {
DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */
};
-static PyObject *empty_values[1] = { NULL };
+
+static PyDictValues empty_values_struct = { 0, { NULL }};
+#define empty_values (&empty_values_struct)
#define Py_EMPTY_KEYS &empty_keys_struct
@@ -458,6 +460,13 @@ static PyObject *empty_values[1] = { NULL };
# define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0))
#endif
+static inline int
+get_index_from_order(PyDictObject *mp, Py_ssize_t i)
+{
+ assert(mp->ma_used <= 16);
+ int shift = (int)(mp->ma_used-1-i)*4;
+ return (int)(mp->ma_values->mv_order >> shift) & 15;
+}
int
_PyDict_CheckConsistency(PyObject *op, int check_content)
@@ -482,17 +491,19 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
/* combined table */
CHECK(keys->dk_refcnt == 1);
}
+ else {
+ CHECK(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
+ }
if (check_content) {
PyDictKeyEntry *entries = DK_ENTRIES(keys);
- Py_ssize_t i;
- for (i=0; i < DK_SIZE(keys); i++) {
+ for (Py_ssize_t i=0; i < DK_SIZE(keys); i++) {
Py_ssize_t ix = dictkeys_get_index(keys, i);
CHECK(DKIX_DUMMY <= ix && ix <= usable);
}
- for (i=0; i < usable; i++) {
+ for (Py_ssize_t i=0; i < usable; i++) {
PyDictKeyEntry *entry = &entries[i];
PyObject *key = entry->me_key;
@@ -517,9 +528,14 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
}
if (splitted) {
+ CHECK(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
/* splitted table */
- for (i=0; i < mp->ma_used; i++) {
- CHECK(mp->ma_values[i] != NULL);
+ int duplicate_check = 0;
+ for (Py_ssize_t i=0; i < mp->ma_used; i++) {
+ int index = get_index_from_order(mp, i);
+ CHECK((duplicate_check & (1<<index)) == 0);
+ duplicate_check |= (1<<index);
+ CHECK(mp->ma_values->values[index] != NULL);
}
}
}
@@ -576,9 +592,9 @@ new_keys_object(uint8_t log2_size)
#endif
dk->dk_refcnt = 1;
dk->dk_log2_size = log2_size;
- dk->dk_usable = usable;
dk->dk_kind = DICT_KEYS_UNICODE;
dk->dk_nentries = 0;
+ dk->dk_usable = usable;
dk->dk_version = 0;
memset(&dk->dk_indices[0], 0xff, es<<log2_size);
memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable);
@@ -606,12 +622,18 @@ free_keys_object(PyDictKeysObject *keys)
PyObject_Free(keys);
}
-#define new_values(size) PyMem_NEW(PyObject *, size)
+static inline PyDictValues*
+new_values(Py_ssize_t size)
+{
+ Py_ssize_t n = sizeof(PyDictValues) + sizeof(PyObject *) * (size-1);
+ return (PyDictValues*)PyMem_Malloc(n);
+}
+
#define free_values(values) PyMem_Free(values)
/* Consumes a reference to the keys object */
static PyObject *
-new_dict(PyDictKeysObject *keys, PyObject **values)
+new_dict(PyDictKeysObject *keys, PyDictValues *values)
{
PyDictObject *mp;
assert(keys != NULL);
@@ -648,7 +670,7 @@ new_dict(PyDictKeysObject *keys, PyObject **values)
static PyObject *
new_dict_with_shared_keys(PyDictKeysObject *keys)
{
- PyObject **values;
+ PyDictValues *values;
Py_ssize_t i, size;
size = USABLE_FRACTION(DK_SIZE(keys));
@@ -657,8 +679,9 @@ new_dict_with_shared_keys(PyDictKeysObject *keys)
dictkeys_decref(keys);
return PyErr_NoMemory();
}
+ values->mv_order = 0;
for (i = 0; i < size; i++) {
- values[i] = NULL;
+ values->values[i] = NULL;
}
return new_dict(keys, values);
}
@@ -829,7 +852,7 @@ start:
*value_addr = NULL;
}
else if (kind == DICT_KEYS_SPLIT) {
- *value_addr = mp->ma_values[ix];
+ *value_addr = mp->ma_values->values[ix];
}
else {
*value_addr = DK_ENTRIES(dk)[ix].me_value;
@@ -879,7 +902,7 @@ start:
Py_UNREACHABLE();
found:
if (dk->dk_kind == DICT_KEYS_SPLIT) {
- *value_addr = mp->ma_values[ix];
+ *value_addr = mp->ma_values->values[ix];
}
else {
*value_addr = ep0[ix].me_value;
@@ -928,7 +951,7 @@ _PyDict_MaybeUntrack(PyObject *op)
numentries = mp->ma_keys->dk_nentries;
if (_PyDict_HasSplitTable(mp)) {
for (i = 0; i < numentries; i++) {
- if ((value = mp->ma_values[i]) == NULL)
+ if ((value = mp->ma_values->values[i]) == NULL)
continue;
if (_PyObject_GC_MAY_BE_TRACKED(value)) {
assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key));
@@ -998,17 +1021,6 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
MAINTAIN_TRACKING(mp, key, value);
- /* When insertion order is different from shared key, we can't share
- * the key anymore. Convert this instance to combine table.
- */
- if (_PyDict_HasSplitTable(mp) &&
- ((ix >= 0 && old_value == NULL && mp->ma_used != ix) ||
- (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) {
- if (insertion_resize(mp) < 0)
- goto Fail;
- ix = DKIX_EMPTY;
- }
-
if (ix == DKIX_EMPTY) {
/* Insert into new slot. */
mp->ma_keys->dk_version = 0;
@@ -1027,8 +1039,12 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
ep->me_key = key;
ep->me_hash = hash;
if (mp->ma_values) {
- assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL);
- mp->ma_values[mp->ma_keys->dk_nentries] = value;
+ Py_ssize_t index = mp->ma_keys->dk_nentries;
+ assert(index < SHARED_KEYS_MAX_SIZE);
+ assert((mp->ma_values->mv_order >> 60) == 0);
+ mp->ma_values->mv_order = (mp->ma_values->mv_order)<<4 | index;
+ assert (mp->ma_values->values[index] == NULL);
+ mp->ma_values->values[index] = value;
}
else {
ep->me_value = value;
@@ -1044,10 +1060,9 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
if (old_value != value) {
if (_PyDict_HasSplitTable(mp)) {
- mp->ma_values[ix] = value;
+ mp->ma_values->values[ix] = value;
if (old_value == NULL) {
- /* pending state */
- assert(ix == mp->ma_used);
+ mp->ma_values->mv_order = (mp->ma_values->mv_order << 4) | ix;
mp->ma_used++;
}
}
@@ -1136,7 +1151,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize)
{
Py_ssize_t numentries;
PyDictKeysObject *oldkeys;
- PyObject **oldvalues;
+ PyDictValues *oldvalues;
PyDictKeyEntry *oldentries, *newentries;
if (log2_newsize >= SIZEOF_SIZE_T*8) {
@@ -1173,13 +1188,14 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize)
* Note that values of split table is always dense.
*/
for (Py_ssize_t i = 0; i < numentries; i++) {
- assert(oldvalues[i] != NULL);
- PyDictKeyEntry *ep = &oldentries[i];
+ int index = oldvalues->mv_order >> ((numentries-1-i)*4) & 15;
+ assert(oldvalues->values[index] != NULL);
+ PyDictKeyEntry *ep = &oldentries[index];
PyObject *key = ep->me_key;
Py_INCREF(key);
newentries[i].me_key = key;
newentries[i].me_hash = ep->me_hash;
- newentries[i].me_value = oldvalues[i];
+ newentries[i].me_value = oldvalues->values[index];
}
dictkeys_decref(oldkeys);
@@ -1238,9 +1254,12 @@ make_keys_shared(PyObject *op)
if (!PyDict_CheckExact(op))
return NULL;
+ if (mp->ma_used > SHARED_KEYS_MAX_SIZE) {
+ return NULL;
+ }
if (!_PyDict_HasSplitTable(mp)) {
PyDictKeyEntry *ep0;
- PyObject **values;
+ PyDictValues *values;
assert(mp->ma_keys->dk_refcnt == 1);
if (mp->ma_keys->dk_kind == DICT_KEYS_GENERAL) {
return NULL;
@@ -1260,14 +1279,29 @@ make_keys_shared(PyObject *op)
"Not enough memory to allocate new values array");
return NULL;
}
- for (i = 0; i < size; i++) {
- values[i] = ep0[i].me_value;
+ uint64_t order = 0;
+ for (i = 0; i < mp->ma_used; i++) {
+ order <<= 4;
+ order |= i;
+ assert(ep0[i].me_value != NULL);
+ values->values[i] = ep0[i].me_value;
+ ep0[i].me_value = NULL;
+ }
+ values->mv_order = order;
+ for (; i < size; i++) {
+ assert(ep0[i].me_value == NULL);
+ values->values[i] = NULL;
ep0[i].me_value = NULL;
}
+ if (mp->ma_keys->dk_nentries + mp->ma_keys->dk_usable > SHARED_KEYS_MAX_SIZE) {
+ assert(mp->ma_keys->dk_nentries <= SHARED_KEYS_MAX_SIZE);
+ mp->ma_keys->dk_usable = SHARED_KEYS_MAX_SIZE - mp->ma_keys->dk_nentries;
+ }
mp->ma_keys->dk_kind = DICT_KEYS_SPLIT;
mp->ma_values = values;
}
dictkeys_incref(mp->ma_keys);
+ ASSERT_CONSISTENT(mp);
return mp->ma_keys;
}
@@ -1366,7 +1400,7 @@ _PyDict_GetItemHint(PyDictObject *mp, PyObject *key,
if (ep->me_key == key) {
if (mp->ma_keys->dk_kind == DICT_KEYS_SPLIT) {
assert(mp->ma_values != NULL);
- res = mp->ma_values[(size_t)hint];
+ res = mp->ma_values->values[(size_t)hint];
}
else {
res = ep->me_value;
@@ -1569,11 +1603,30 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
mp->ma_keys->dk_version = 0;
mp->ma_version_tag = DICT_NEXT_VERSION();
ep = &DK_ENTRIES(mp->ma_keys)[ix];
- dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
- old_key = ep->me_key;
- ep->me_key = NULL;
- ep->me_value = NULL;
- Py_DECREF(old_key);
+ if (mp->ma_values) {
+ assert(old_value == mp->ma_values->values[ix]);
+ mp->ma_values->values[ix] = NULL;
+ assert(ix < SHARED_KEYS_MAX_SIZE);
+ /* Update order */
+ for (int i = 0;; i+= 4) {
+ assert (i < 64);
+ if (((mp->ma_values->mv_order >> i) & 15) == (uint64_t)ix) {
+ /* Remove 4 bits at ith position */
+ uint64_t order = mp->ma_values->mv_order;
+ uint64_t high = ((order>>i)>>4)<<i;
+ uint64_t low = order & ((((uint64_t)1)<<i)-1);
+ mp->ma_values->mv_order = high | low;
+ break;
+ }
+ }
+ }
+ else {
+ dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
+ old_key = ep->me_key;
+ ep->me_key = NULL;
+ ep->me_value = NULL;
+ Py_DECREF(old_key);
+ }
Py_DECREF(old_value);
ASSERT_CONSISTENT(mp);
@@ -1617,15 +1670,6 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
return -1;
}
- // Split table doesn't allow deletion. Combine it.
- if (_PyDict_HasSplitTable(mp)) {
- if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
- return -1;
- }
- ix = _Py_dict_lookup(mp, key, hash, &old_value);
- assert(ix >= 0);
- }
-
return delitem_common(mp, hash, ix, old_value);
}
@@ -1660,15 +1704,6 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key,
return -1;
}
- // Split table doesn't allow deletion. Combine it.
- if (_PyDict_HasSplitTable(mp)) {
- if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
- return -1;
- }
- ix = _Py_dict_lookup(mp, key, hash, &old_value);
- assert(ix >= 0);
- }
-
res = predicate(old_value);
if (res == -1)
return -1;
@@ -1688,7 +1723,7 @@ PyDict_Clear(PyObject *op)
{
PyDictObject *mp;
PyDictKeysObject *oldkeys;
- PyObject **oldvalues;
+ PyDictValues *oldvalues;
Py_ssize_t i, n;
if (!PyDict_Check(op))
@@ -1708,7 +1743,7 @@ PyDict_Clear(PyObject *op)
if (oldvalues != NULL) {
n = oldkeys->dk_nentries;
for (i = 0; i < n; i++)
- Py_CLEAR(oldvalues[i]);
+ Py_CLEAR(oldvalues->values[i]);
free_values(oldvalues);
dictkeys_decref(oldkeys);
}
@@ -1738,11 +1773,12 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
mp = (PyDictObject *)op;
i = *ppos;
if (mp->ma_values) {
+ assert(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
if (i < 0 || i >= mp->ma_used)
return 0;
- /* values of split table is always dense */
- entry_ptr = &DK_ENTRIES(mp->ma_keys)[i];
- value = mp->ma_values[i];
+ int index = get_index_from_order(mp, i);
+ entry_ptr = &DK_ENTRIES(mp->ma_keys)[index];
+ value = mp->ma_values->values[index];
assert(value != NULL);
}
else {
@@ -1796,9 +1832,8 @@ PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
PyObject *
_PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt)
{
- Py_ssize_t ix, hashpos;
- PyObject *old_value, *old_key;
- PyDictKeyEntry *ep;
+ Py_ssize_t ix;
+ PyObject *old_value;
PyDictObject *mp;
assert(PyDict_Check(dict));
@@ -1823,29 +1858,9 @@ _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *d
_PyErr_SetKeyError(key);
return NULL;
}
-
- // Split table doesn't allow deletion. Combine it.
- if (_PyDict_HasSplitTable(mp)) {
- if (dictresize(mp, DK_LOG_SIZE(mp->ma_keys))) {
- return NULL;
- }
- ix = _Py_dict_lookup(mp, key, hash, &old_value);
- assert(ix >= 0);
- }
-
- hashpos = lookdict_index(mp->ma_keys, hash, ix);
- assert(hashpos >= 0);
assert(old_value != NULL);
- mp->ma_used--;
- mp->ma_version_tag = DICT_NEXT_VERSION();
- mp->ma_keys->dk_version = 0;
- dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
- ep = &DK_ENTRIES(mp->ma_keys)[ix];
- mp->ma_keys->dk_version = 0;
- old_key = ep->me_key;
- ep->me_key = NULL;
- ep->me_value = NULL;
- Py_DECREF(old_key);
+ Py_INCREF(old_value);
+ delitem_common(mp, hash, ix, old_value);
ASSERT_CONSISTENT(mp);
return old_value;
@@ -1966,7 +1981,7 @@ Fail:
static void
dict_dealloc(PyDictObject *mp)
{
- PyObject **values = mp->ma_values;
+ PyDictValues *values = mp->ma_values;
PyDictKeysObject *keys = mp->ma_keys;
Py_ssize_t i, n;
@@ -1976,7 +1991,7 @@ dict_dealloc(PyDictObject *mp)
if (values != NULL) {
if (values != empty_values) {
for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) {
- Py_XDECREF(values[i]);
+ Py_XDECREF(values->values[i]);
}
free_values(values);
}
@@ -2165,7 +2180,7 @@ dict_keys(PyDictObject *mp)
}
ep = DK_ENTRIES(mp->ma_keys);
if (mp->ma_values) {
- value_ptr = mp->ma_values;
+ value_ptr = mp->ma_values->values;
offset = sizeof(PyObject *);
}
else {
@@ -2208,7 +2223,7 @@ dict_values(PyDictObject *mp)
}
ep = DK_ENTRIES(mp->ma_keys);
if (mp->ma_values) {
- value_ptr = mp->ma_values;
+ value_ptr = mp->ma_values->values;
offset = sizeof(PyObject *);
}
else {
@@ -2265,7 +2280,7 @@ dict_items(PyDictObject *mp)
/* Nothing we do below makes any function calls. */
ep = DK_ENTRIES(mp->ma_keys);
if (mp->ma_values) {
- value_ptr = mp->ma_values;
+ value_ptr = mp->ma_values->values;
offset = sizeof(PyObject *);
}
else {
@@ -2534,7 +2549,7 @@ dict_merge(PyObject *a, PyObject *b, int override)
key = entry->me_key;
hash = entry->me_hash;
if (other->ma_values)
- value = other->ma_values[i];
+ value = other->ma_values->values[i];
else
value = entry->me_value;
@@ -2677,7 +2692,7 @@ PyDict_Copy(PyObject *o)
if (_PyDict_HasSplitTable(mp)) {
PyDictObject *split_copy;
Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys));
- PyObject **newvalues;
+ PyDictValues *newvalues;
newvalues = new_values(size);
if (newvalues == NULL)
return PyErr_NoMemory();
@@ -2686,15 +2701,16 @@ PyDict_Copy(PyObject *o)
free_values(newvalues);
return NULL;
}
+ newvalues->mv_order = mp->ma_values->mv_order;
split_copy->ma_values = newvalues;
split_copy->ma_keys = mp->ma_keys;
split_copy->ma_used = mp->ma_used;
split_copy->ma_version_tag = DICT_NEXT_VERSION();
dictkeys_incref(mp->ma_keys);
for (i = 0, n = size; i < n; i++) {
- PyObject *value = mp->ma_values[i];
+ PyObject *value = mp->ma_values->values[i];
Py_XINCREF(value);
- split_copy->ma_values[i] = value;
+ split_copy->ma_values->values[i] = value;
}
if (_PyObject_GC_IS_TRACKED(mp))
_PyObject_GC_TRACK(split_copy);
@@ -2806,7 +2822,7 @@ dict_equal(PyDictObject *a, PyDictObject *b)
PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i];
PyObject *aval;
if (a->ma_values)
- aval = a->ma_values[i];
+ aval = a->ma_values->values[i];
else
aval = ep->me_value;
if (aval != NULL) {
@@ -2993,8 +3009,11 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
ep->me_key = key;
ep->me_hash = hash;
if (_PyDict_HasSplitTable(mp)) {
- assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL);
- mp->ma_values[mp->ma_keys->dk_nentries] = value;
+ int index = (int)mp->ma_keys->dk_nentries;
+ assert(index < SHARED_KEYS_MAX_SIZE);
+ assert(mp->ma_values->values[index] == NULL);
+ mp->ma_values->values[index] = value;
+ mp->ma_values->mv_order = (mp->ma_values->mv_order << 4) | index;
}
else {
ep->me_value = value;
@@ -3011,7 +3030,8 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
assert(ix == mp->ma_used);
Py_INCREF(value);
MAINTAIN_TRACKING(mp, key, value);
- mp->ma_values[ix] = value;
+ mp->ma_values->values[ix] = value;
+ mp->ma_values->mv_order = (mp->ma_values->mv_order << 4) | ix;
mp->ma_used++;
mp->ma_version_tag = DICT_NEXT_VERSION();
}
@@ -3159,7 +3179,7 @@ dict_traverse(PyObject *op, visitproc visit, void *arg)
else {
if (mp->ma_values != NULL) {
for (i = 0; i < n; i++) {
- Py_VISIT(mp->ma_values[i]);
+ Py_VISIT(mp->ma_values->values[i]);
}
}
else {
@@ -3677,8 +3697,9 @@ dictiter_iternextkey(dictiterobject *di)
if (d->ma_values) {
if (i >= d->ma_used)
goto fail;
- key = DK_ENTRIES(k)[i].me_key;
- assert(d->ma_values[i] != NULL);
+ int index = get_index_from_order(d, i);
+ key = DK_ENTRIES(k)[index].me_key;
+ assert(d->ma_values->values[index] != NULL);
}
else {
Py_ssize_t n = k->dk_nentries;
@@ -3764,7 +3785,8 @@ dictiter_iternextvalue(dictiterobject *di)
if (d->ma_values) {
if (i >= d->ma_used)
goto fail;
- value = d->ma_values[i];
+ int index = get_index_from_order(d, i);
+ value = d->ma_values->values[index];
assert(value != NULL);
}
else {
@@ -3851,8 +3873,9 @@ dictiter_iternextitem(dictiterobject *di)
if (d->ma_values) {
if (i >= d->ma_used)
goto fail;
- key = DK_ENTRIES(d->ma_keys)[i].me_key;
- value = d->ma_values[i];
+ int index = get_index_from_order(d, i);
+ key = DK_ENTRIES(d->ma_keys)[index].me_key;
+ value = d->ma_values->values[index];
assert(value != NULL);
}
else {
@@ -3968,8 +3991,9 @@ dictreviter_iternext(dictiterobject *di)
goto fail;
}
if (d->ma_values) {
- key = DK_ENTRIES(k)[i].me_key;
- value = d->ma_values[i];
+ int index = get_index_from_order(d, i);
+ key = DK_ENTRIES(k)[index].me_key;
+ value = d->ma_values->values[index];
assert (value != NULL);
}
else {
@@ -4976,19 +5000,14 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr,
}
if (value == NULL) {
res = PyDict_DelItem(dict, key);
- // Since key sharing dict doesn't allow deletion, PyDict_DelItem()
- // always converts dict to combined form.
- if ((cached = CACHED_KEYS(tp)) != NULL) {
- CACHED_KEYS(tp) = NULL;
- dictkeys_decref(cached);
- }
}
else {
int was_shared = (cached == ((PyDictObject *)dict)->ma_keys);
res = PyDict_SetItem(dict, key, value);
if (was_shared &&
(cached = CACHED_KEYS(tp)) != NULL &&
- cached != ((PyDictObject *)dict)->ma_keys) {
+ cached != ((PyDictObject *)dict)->ma_keys &&
+ cached->dk_nentries <= SHARED_KEYS_MAX_SIZE) {
/* PyDict_SetItem() may call dictresize and convert split table
* into combined table. In such case, convert it to split
* table again and update type's shared key only when this is
@@ -5004,14 +5023,15 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr,
* a = C()
*/
if (cached->dk_refcnt == 1) {
- CACHED_KEYS(tp) = make_keys_shared(dict);
- }
- else {
- CACHED_KEYS(tp) = NULL;
+ PyDictKeysObject *new_cached = make_keys_shared(dict);
+ if (new_cached != NULL) {
+ CACHED_KEYS(tp) = new_cached;
+ dictkeys_decref(cached);
+ }
+ else if (PyErr_Occurred()) {
+ return -1;
+ }
}
- dictkeys_decref(cached);
- if (CACHED_KEYS(tp) == NULL && PyErr_Occurred())
- return -1;
}
}
} else {
@@ -5028,6 +5048,7 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr,
res = PyDict_SetItem(dict, key, value);
}
}
+ ASSERT_CONSISTENT(dict);
return res;
}