summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2015-10-14 16:21:24 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2015-10-14 16:21:24 (GMT)
commit992ec46acc1267729da141f683f2594f00ba9f57 (patch)
treee3a6ac322df5d1d565732dd4381845e535073312
parentc1e98de7be2f6d1d34df36b37e45f97ed209c847 (diff)
downloadcpython-992ec46acc1267729da141f683f2594f00ba9f57.zip
cpython-992ec46acc1267729da141f683f2594f00ba9f57.tar.gz
cpython-992ec46acc1267729da141f683f2594f00ba9f57.tar.bz2
Issue #25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
that caused segmentation fault or hang in iterating after moving several items to the start of ordered dict.
-rw-r--r--Lib/test/test_collections.py14
-rw-r--r--Misc/NEWS4
-rw-r--r--Objects/odictobject.c31
3 files changed, 28 insertions, 21 deletions
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 4124f91..3ed3abdf 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -1995,6 +1995,20 @@ class OrderedDictTests:
with self.assertRaises(KeyError):
od.move_to_end('x', 0)
+ def test_move_to_end_issue25406(self):
+ OrderedDict = self.module.OrderedDict
+ od = OrderedDict.fromkeys('abc')
+ od.move_to_end('c', last=False)
+ self.assertEqual(list(od), list('cab'))
+ od.move_to_end('a', last=False)
+ self.assertEqual(list(od), list('acb'))
+
+ od = OrderedDict.fromkeys('abc')
+ od.move_to_end('a')
+ self.assertEqual(list(od), list('bca'))
+ od.move_to_end('c')
+ self.assertEqual(list(od), list('bac'))
+
def test_sizeof(self):
OrderedDict = self.module.OrderedDict
# Wimpy test: Just verify the reported size is larger than a regular dict
diff --git a/Misc/NEWS b/Misc/NEWS
index cb8d36d..c0ea6e5 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -45,6 +45,10 @@ Core and Builtins
Library
-------
+- Issue #25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
+ that caused segmentation fault or hang in iterating after moving several
+ items to the start of ordered dict.
+
- Issue #25364: zipfile now works in threads disabled builds.
- Issue #25328: smtpd's SMTPChannel now correctly raises a ValueError if both
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index 7df419e..8fe838a 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -618,37 +618,26 @@ _odict_find_node(PyODictObject *od, PyObject *key)
static void
_odict_add_head(PyODictObject *od, _ODictNode *node)
{
- if (_odict_FIRST(od) == NULL) {
- _odictnode_PREV(node) = NULL;
- _odictnode_NEXT(node) = NULL;
- _odict_FIRST(od) = node;
+ _odictnode_PREV(node) = NULL;
+ _odictnode_NEXT(node) = _odict_FIRST(od);
+ if (_odict_FIRST(od) == NULL)
_odict_LAST(od) = node;
- }
- else {
- _odictnode_PREV(node) = NULL;
- _odictnode_NEXT(node) = _odict_FIRST(od);
- _odict_FIRST(od) = node;
+ else
_odictnode_PREV(_odict_FIRST(od)) = node;
- }
+ _odict_FIRST(od) = node;
od->od_state++;
}
static void
_odict_add_tail(PyODictObject *od, _ODictNode *node)
{
- if (_odict_LAST(od) == NULL) {
- _odictnode_PREV(node) = NULL;
- _odictnode_NEXT(node) = NULL;
+ _odictnode_PREV(node) = _odict_LAST(od);
+ _odictnode_NEXT(node) = NULL;
+ if (_odict_LAST(od) == NULL)
_odict_FIRST(od) = node;
- _odict_LAST(od) = node;
- }
- else {
- _odictnode_PREV(node) = _odict_LAST(od);
- _odictnode_NEXT(node) = NULL;
+ else
_odictnode_NEXT(_odict_LAST(od)) = node;
- _odict_LAST(od) = node;
- }
-
+ _odict_LAST(od) = node;
od->od_state++;
}