Issue #28583: PyDict_SetDefault didn't combine split table when needed.
Patch by Xiang Zhang.
This commit is contained in:
parent
8567e58ae3
commit
93f26f794d
|
@ -851,6 +851,23 @@ class DictTest(unittest.TestCase):
|
||||||
|
|
||||||
return dicts
|
return dicts
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
def test_splittable_setdefault(self):
|
||||||
|
"""split table must be combined when setdefault()
|
||||||
|
breaks insertion order"""
|
||||||
|
a, b = self.make_shared_key_dict(2)
|
||||||
|
|
||||||
|
a['a'] = 1
|
||||||
|
size_a = sys.getsizeof(a)
|
||||||
|
a['b'] = 2
|
||||||
|
b.setdefault('b', 2)
|
||||||
|
size_b = sys.getsizeof(b)
|
||||||
|
b['a'] = 1
|
||||||
|
|
||||||
|
self.assertGreater(size_b, size_a)
|
||||||
|
self.assertEqual(list(a), ['x', 'y', 'z', 'a', 'b'])
|
||||||
|
self.assertEqual(list(b), ['x', 'y', 'z', 'b', 'a'])
|
||||||
|
|
||||||
@support.cpython_only
|
@support.cpython_only
|
||||||
def test_splittable_del(self):
|
def test_splittable_del(self):
|
||||||
"""split table must be combined when del d[k]"""
|
"""split table must be combined when del d[k]"""
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.6.0 beta 4
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #28583: PyDict_SetDefault didn't combine split table when needed.
|
||||||
|
Patch by Xiang Zhang.
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -2757,58 +2757,88 @@ PyObject *
|
||||||
PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
|
PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
|
||||||
{
|
{
|
||||||
PyDictObject *mp = (PyDictObject *)d;
|
PyDictObject *mp = (PyDictObject *)d;
|
||||||
PyObject *val = NULL;
|
PyObject *value;
|
||||||
Py_hash_t hash;
|
Py_hash_t hash;
|
||||||
Py_ssize_t hashpos, ix;
|
Py_ssize_t hashpos, ix;
|
||||||
PyDictKeyEntry *ep;
|
|
||||||
PyObject **value_addr;
|
PyObject **value_addr;
|
||||||
|
|
||||||
if (!PyDict_Check(d)) {
|
if (!PyDict_Check(d)) {
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyUnicode_CheckExact(key) ||
|
if (!PyUnicode_CheckExact(key) ||
|
||||||
(hash = ((PyASCIIObject *) key)->hash) == -1) {
|
(hash = ((PyASCIIObject *) key)->hash) == -1) {
|
||||||
hash = PyObject_Hash(key);
|
hash = PyObject_Hash(key);
|
||||||
if (hash == -1)
|
if (hash == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) {
|
||||||
|
if (insertion_resize(mp) < 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos);
|
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos);
|
||||||
if (ix == DKIX_ERROR)
|
if (ix == DKIX_ERROR)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (ix == DKIX_EMPTY || *value_addr == NULL) {
|
|
||||||
val = defaultobj;
|
if (_PyDict_HasSplitTable(mp) &&
|
||||||
|
((ix >= 0 && *value_addr == NULL && mp->ma_used != ix) ||
|
||||||
|
(ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) {
|
||||||
|
if (insertion_resize(mp) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
find_empty_slot(mp, key, hash, &value_addr, &hashpos);
|
||||||
|
ix = DKIX_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ix == DKIX_EMPTY) {
|
||||||
|
PyDictKeyEntry *ep, *ep0;
|
||||||
|
value = defaultobj;
|
||||||
if (mp->ma_keys->dk_usable <= 0) {
|
if (mp->ma_keys->dk_usable <= 0) {
|
||||||
/* Need to resize. */
|
|
||||||
if (insertion_resize(mp) < 0) {
|
if (insertion_resize(mp) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
find_empty_slot(mp, key, hash, &value_addr, &hashpos);
|
find_empty_slot(mp, key, hash, &value_addr, &hashpos);
|
||||||
}
|
}
|
||||||
ix = mp->ma_keys->dk_nentries;
|
ep0 = DK_ENTRIES(mp->ma_keys);
|
||||||
Py_INCREF(defaultobj);
|
ep = &ep0[mp->ma_keys->dk_nentries];
|
||||||
|
dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
|
||||||
Py_INCREF(key);
|
Py_INCREF(key);
|
||||||
MAINTAIN_TRACKING(mp, key, defaultobj);
|
Py_INCREF(value);
|
||||||
dk_set_index(mp->ma_keys, hashpos, ix);
|
MAINTAIN_TRACKING(mp, key, value);
|
||||||
ep = &DK_ENTRIES(mp->ma_keys)[ix];
|
|
||||||
ep->me_key = key;
|
ep->me_key = key;
|
||||||
ep->me_hash = hash;
|
ep->me_hash = hash;
|
||||||
if (mp->ma_values) {
|
if (mp->ma_values) {
|
||||||
mp->ma_values[ix] = val;
|
assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL);
|
||||||
|
mp->ma_values[mp->ma_keys->dk_nentries] = value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ep->me_value = val;
|
ep->me_value = value;
|
||||||
}
|
}
|
||||||
mp->ma_keys->dk_usable--;
|
|
||||||
mp->ma_keys->dk_nentries++;
|
|
||||||
mp->ma_used++;
|
mp->ma_used++;
|
||||||
mp->ma_version_tag = DICT_NEXT_VERSION();
|
mp->ma_version_tag = DICT_NEXT_VERSION();
|
||||||
assert(_PyDict_CheckConsistency(mp));
|
mp->ma_keys->dk_usable--;
|
||||||
|
mp->ma_keys->dk_nentries++;
|
||||||
|
assert(mp->ma_keys->dk_usable >= 0);
|
||||||
|
}
|
||||||
|
else if (*value_addr == NULL) {
|
||||||
|
value = defaultobj;
|
||||||
|
assert(_PyDict_HasSplitTable(mp));
|
||||||
|
assert(ix == mp->ma_used);
|
||||||
|
Py_INCREF(value);
|
||||||
|
MAINTAIN_TRACKING(mp, key, value);
|
||||||
|
*value_addr = value;
|
||||||
|
mp->ma_used++;
|
||||||
|
mp->ma_version_tag = DICT_NEXT_VERSION();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val = *value_addr;
|
value = *value_addr;
|
||||||
}
|
}
|
||||||
return val;
|
|
||||||
|
assert(_PyDict_CheckConsistency(mp));
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
Loading…
Reference in New Issue