Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8c7f955

Browse files
Issue #24667: Resize odict in all cases that the underlying dict resizes.
1 parent 31202ea commit 8c7f955

3 files changed

Lines changed: 35 additions & 7 deletions

File tree

Lib/test/test_collections.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,29 @@ def __hash__(self):
20982098
# This should not crash.
20992099
od.popitem()
21002100

2101+
def test_issue24667(self):
2102+
"""
2103+
dict resizes after a certain number of insertion operations,
2104+
whether or not there were deletions that freed up slots in the
2105+
hash table. During fast node lookup, OrderedDict must correctly
2106+
respond to all resizes, even if the current "size" is the same
2107+
as the old one. We verify that here by forcing a dict resize
2108+
on a sparse odict and then perform an operation that should
2109+
trigger an odict resize (e.g. popitem). One key aspect here is
2110+
that we will keep the size of the odict the same at each popitem
2111+
call. This verifies that we handled the dict resize properly.
2112+
"""
2113+
OrderedDict = self.module.OrderedDict
2114+
2115+
od = OrderedDict()
2116+
for c0 in '0123456789ABCDEF':
2117+
for c1 in '0123456789ABCDEF':
2118+
if len(od) == 4:
2119+
# This should not raise a KeyError.
2120+
od.popitem(last=False)
2121+
key = c0 + c1
2122+
od[key] = key
2123+
21012124

21022125
class PurePythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
21032126

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Release date: 2015-08-09
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #24667: Resize odict in all cases that the underlying dict resizes.
14+
1315
Library
1416
-------
1517

Objects/odictobject.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,11 @@ struct _odictobject {
483483
PyDictObject od_dict; /* the underlying dict */
484484
_ODictNode *od_first; /* first node in the linked list, if any */
485485
_ODictNode *od_last; /* last node in the linked list, if any */
486-
/* od_size and od_fast_nodes are managed by _odict_resize() */
487-
Py_ssize_t od_size; /* hash table that mirrors the dict table */
488-
_ODictNode **od_fast_nodes; /* managed by _odict_resize() */
486+
/* od_fast_nodes and od_resize_sentinel are managed by _odict_resize()
487+
* Note that we rely on implementation details of dict for both. */
488+
_ODictNode **od_fast_nodes; /* hash table that mirrors the dict table */
489+
Py_uintptr_t od_resize_sentinel; /* changes if odict should be resized */
490+
489491
size_t od_state; /* incremented whenever the LL changes */
490492
PyObject *od_inst_dict; /* OrderedDict().__dict__ */
491493
PyObject *od_weakreflist; /* holds weakrefs to the odict */
@@ -510,7 +512,6 @@ struct _odictnode {
510512
/* borrowed reference */
511513
#define _odictnode_VALUE(node, od) \
512514
PyODict_GetItemWithError((PyObject *)od, _odictnode_KEY(node))
513-
/* If needed we could also have _odictnode_HASH. */
514515
#define _odictnode_PREV(node) (node->prev)
515516
#define _odictnode_NEXT(node) (node->next)
516517

@@ -520,6 +521,7 @@ struct _odictnode {
520521
#define _odict_FOREACH(od, node) \
521522
for (node = _odict_FIRST(od); node != NULL; node = _odictnode_NEXT(node))
522523

524+
#define _odict_FAST_SIZE(od) ((PyDictObject *)od)->ma_keys->dk_size
523525

524526
static void
525527
_odict_free_fast_nodes(PyODictObject *od) {
@@ -573,7 +575,7 @@ _odict_resize(PyODictObject *od) {
573575
/* Replace the old fast nodes table. */
574576
_odict_free_fast_nodes(od);
575577
od->od_fast_nodes = fast_nodes;
576-
od->od_size = size;
578+
od->od_resize_sentinel = (Py_uintptr_t)(((PyDictObject *)od)->ma_keys);
577579
return 0;
578580
}
579581

@@ -591,7 +593,7 @@ _odict_get_index(PyODictObject *od, PyObject *key)
591593
keys = ((PyDictObject *)od)->ma_keys;
592594

593595
/* Ensure od_fast_nodes and dk_entries are in sync. */
594-
if (keys->dk_size != od->od_size) {
596+
if (od->od_resize_sentinel != (Py_uintptr_t)keys) {
595597
int resize_res = _odict_resize(od);
596598
if (resize_res < 0)
597599
return -1;
@@ -656,6 +658,7 @@ _odict_add_tail(PyODictObject *od, _ODictNode *node)
656658
_odictnode_NEXT(_odict_LAST(od)) = node;
657659
_odict_LAST(od) = node;
658660
}
661+
659662
od->od_state++;
660663
}
661664

@@ -980,7 +983,7 @@ odict_sizeof(PyODictObject *od)
980983
return NULL;
981984
res += temp;
982985

983-
res += sizeof(_ODictNode) * od->od_size; /* od_fast_nodes */
986+
res += sizeof(_ODictNode) * _odict_FAST_SIZE(od); /* od_fast_nodes */
984987
if (!_odict_EMPTY(od)) {
985988
res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */
986989
}

0 commit comments

Comments
 (0)