cpython: 0bd618fe0639 (original) (raw)
--- a/Doc/whatsnew/3.6.rst
+++ b/Doc/whatsnew/3.6.rst
@@ -343,6 +343,11 @@ Other Language Changes
Some smaller changes made to the core Python language are:
+* dict
implementation is changed like PyPy. It is more compact and preserves
- insertion order. :pep:
PEP 468
(Preserving the order of**kwargs
in a - function.) is implemented by this.
- (Contributed by INADA Naoki in :issue:
27350
.) +
- Long sequences of repeated traceback lines are now abbreviated as
"[Previous line repeated {count} more times]"
(see :ref:py36-traceback
for an example).
--- a/Include/object.h +++ b/Include/object.h @@ -710,7 +710,6 @@ you can count such references to the typ PyAPI_DATA(Py_ssize_t) _Py_RefTotal; PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname, int lineno, PyObject *op); -PyAPI_FUNC(PyObject *) _PyDict_Dummy(void); PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void); #define _Py_INC_REFTOTAL _Py_RefTotal++ #define _Py_DEC_REFTOTAL _Py_RefTotal--
--- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -5116,12 +5116,14 @@ class SharedKeyTests(unittest.TestCase): a, b = A(), B() self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b))) self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({}))
a.x, a.y, a.z, a.w = range(4)[](#l3.7)
# Initial hash table can contain at most 5 elements.[](#l3.8)
# Set 6 attributes to cause internal resizing.[](#l3.9)
a.x, a.y, a.z, a.w, a.v, a.u = range(6)[](#l3.10) self.assertNotEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b)))[](#l3.11) a2 = A()[](#l3.12) self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(a2)))[](#l3.13) self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({}))[](#l3.14)
b.u, b.v, b.w, b.t = range(4)[](#l3.15)
b.u, b.v, b.w, b.t, b.s, b.r = range(6)[](#l3.16) self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({}))[](#l3.17)
--- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -11,6 +11,9 @@ import sysconfig import unittest import locale +# FIXME: issue #28023 +raise unittest.SkipTest("FIXME: issue #28023, compact dict (issue #27350) broke python-gdb.py") +
Is this Python configured to support threads?
--- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -1,3 +1,4 @@ +import builtins import contextlib import copy import gc @@ -621,6 +622,25 @@ class PurePythonOrderedDictTests(Ordered OrderedDict = py_coll.OrderedDict +class CPythonBuiltinDictTests(unittest.TestCase):
- "test_init test_update test_abc test_clear test_delitem " +
- "test_setitem test_detect_deletion_during_iteration " +
- "test_popitem test_reinsert test_override_update " +
- "test_highly_nested test_highly_nested_subclass " +
- "test_delitem_hash_collision ").split():
- setattr(CPythonBuiltinDictTests, method, getattr(OrderedDictTests, method))
+del method + + @unittest.skipUnless(c_coll, 'requires the C version of the collections module') class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): @@ -635,18 +655,19 @@ class CPythonOrderedDictTests(OrderedDic size = support.calcobjsize check = self.check_sizeof
basicsize = size('n2P' + '3PnPn2P') + calcsize('2nPn')[](#l5.38)
entrysize = calcsize('n2P') + calcsize('P')[](#l5.39)
basicsize = size('n2P' + '3PnPn2P') + calcsize('2nP2n')[](#l5.40)
entrysize = calcsize('n2P')[](#l5.41)
p = calcsize('P')[](#l5.42) nodesize = calcsize('Pn2P')[](#l5.43)
check(od, basicsize + 8*entrysize)[](#l5.46)
check(od, basicsize + 8*p + 8 + 5*entrysize) # 8byte indicies + 8*2//3 * entry table[](#l5.47) od.x = 1[](#l5.48)
check(od, basicsize + 8*entrysize)[](#l5.49)
check(od, basicsize + 8*p + 8 + 5*entrysize)[](#l5.50) od.update([(i, i) for i in range(3)])[](#l5.51)
check(od, basicsize + 8*entrysize + 3*nodesize)[](#l5.52)
check(od, basicsize + 8*p + 8 + 5*entrysize + 3*nodesize)[](#l5.53) od.update([(i, i) for i in range(3, 10)])[](#l5.54)
check(od, basicsize + 16*entrysize + 10*nodesize)[](#l5.55)
check(od, basicsize + 16*p + 16 + 10*entrysize + 10*nodesize)[](#l5.56)
check(od.keys(), size('P')) check(od.items(), size('P'))
--- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -936,9 +936,9 @@ class SizeofTest(unittest.TestCase): # method-wrapper (descriptor object) check({}.iter, size('2P')) # dict
check({}, size('n2P') + calcsize('2nPn') + 8*calcsize('n2P'))[](#l6.7)
check({}, size('n2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P'))[](#l6.8) longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}[](#l6.9)
check(longdict, size('n2P') + calcsize('2nPn') + 16*calcsize('n2P'))[](#l6.10)
check(longdict, size('n2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P'))[](#l6.11) # dictionary-keyview[](#l6.12) check({}.keys(), size('P'))[](#l6.13) # dictionary-valueview[](#l6.14)
@@ -1096,13 +1096,13 @@ class SizeofTest(unittest.TestCase): '10P' # PySequenceMethods '2P' # PyBufferProcs '4P')
# Separate block for PyDictKeysObject with 4 entries[](#l6.19)
s += calcsize("2nPn") + 4*calcsize("n2P")[](#l6.20)
# Separate block for PyDictKeysObject with 8 keys and 5 entries[](#l6.21)
s += calcsize("2nP2n") + 8 + 5*calcsize("n2P")[](#l6.22) # class[](#l6.23) class newstyleclass(object): pass[](#l6.24) check(newstyleclass, s)[](#l6.25) # dict with shared keys[](#l6.26)
check(newstyleclass().__dict__, size('n2P' + '2nPn'))[](#l6.27)
check(newstyleclass().__dict__, size('n2P' + '2nP2n'))[](#l6.28) # unicode[](#l6.29) # each tuple contains a string and its expected character size[](#l6.30) # don't put any static strings here, as they may contain[](#l6.31)
--- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1325,13 +1325,16 @@ class MappingTestCase(TestBase): o = Object(123456) with testcontext(): n = len(dict)
dict.popitem()[](#l7.7)
# Since underlaying dict is ordered, first item is popped[](#l7.8)
dict.pop(next(dict.keys()))[](#l7.9) self.assertEqual(len(dict), n - 1)[](#l7.10) dict[o] = o[](#l7.11) self.assertEqual(len(dict), n)[](#l7.12)
# last item in objects is removed from dict in context shutdown[](#l7.13) with testcontext():[](#l7.14) self.assertEqual(len(dict), n - 1)[](#l7.15)
dict.pop(next(dict.keys()))[](#l7.16)
# Then, (o, o) is popped[](#l7.17)
dict.popitem()[](#l7.18) self.assertEqual(len(dict), n - 2)[](#l7.19) with testcontext():[](#l7.20) self.assertEqual(len(dict), n - 3)[](#l7.21)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.6.0 beta 1
Core and Builtins
-----------------
+- Issue #27350: dict
implementation is changed like PyPy. It is more compact
--- a/Objects/dict-common.h +++ b/Objects/dict-common.h @@ -8,15 +8,25 @@ typedef struct { PyObject me_value; / This field is only meaningful for combined tables */ } PyDictKeyEntry; -typedef PyDictKeyEntry *(*dict_lookup_func) -(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); +/ dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index].
- */ +typedef Py_ssize_t (*dict_lookup_func) +(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr,
- Py_ssize_t hashpos); +#define DKIX_EMPTY (-1) +#define DKIX_DUMMY (-2) / Used internally / +#define DKIX_ERROR (-3) + +/ See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable;
- Py_ssize_t dk_nentries; /* How many entries are used. */
- char dk_indices[8]; /* dynamically sized. 8 is minimum. */
--- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1,4 +1,3 @@ - /* Dictionary object implementation using a hash table / / The distribution includes a separate file, Objects/dictnotes.txt, @@ -7,64 +6,108 @@ tuning dictionaries, and several ideas for possible optimizations. / +/ PyDictKeysObject + +This implements the dictionary's hashtable. + +As of Python 3.6, this is compact and orderd. Basic idea is described here. +https://morepypy.blogspot.jp/2015/01/faster-more-memory-efficient-and-more.html[](#l10.17) + +layout: + ++---------------+ +| dk_refcnt | +| dk_size | +| dk_lookup | +| dk_usable | +| dk_nentries | ++---------------+ +| dk_indices | +| | ++---------------+ +| dk_entries | +| | ++---------------+ + +dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) +or DKIX_DUMMY(-2). +Size of indices is dk_size. Type of each index in indices is vary on dk_size: + +* int8 for dk_size <= 128 +* int16 for 256 <= dk_size <= 215 + int32 for 216 <= dk_size <= 2**31 + int64 for 2*32 <= dk_size + +dk_entries is array of PyDictKeyEntry. It's size is USABLE_FRACTION(dk_size). +DK_ENTRIES(dk) can be used to get pointer to entries. + +NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of +dk_indices entry is signed integer and int16 is used for table which +dk_size == 256. +/ + /* -There are four kinds of slots in the table: - -1. Unused. me_key == me_value == NULL
- Does not hold an active (key, value) pair now and never did. Unused can
- transition to Active upon key insertion. This is the only case in which
- me_key is NULL, and is each slot's initial state. -
-2. Active. me_key != NULL and me_key != dummy and me_value != NULL
- Holds an active (key, value) pair. Active can transition to Dummy or
- Pending upon key deletion (for combined and split tables respectively).
- This is the only case in which me_value != NULL. -
-3. Dummy. me_key == dummy and me_value == NULL
- Previously held an active (key, value) pair, but that was deleted and an
- active pair has not yet overwritten the slot. Dummy can transition to
- Active upon key insertion. Dummy slots cannot be made Unused again
- (cannot have me_key set to NULL), else the probe sequence in case of
- collision would have no way to know they were once active. -
-4. Pending. Not yet inserted or deleted from a split-table.
The DictObject can be in one of two forms. + Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject.
Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array.
- -Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to -hold a search finger. The me_hash field of Unused or Dummy slots has no -meaning otherwise. As a consequence of this popitem always converts the dict -to the combined-table form.
+ +There are four kinds of slots in the table (slot is index, and +DK_ENTRIES(keys)[index] if index >= 0): + +1. Unused. index == DKIX_EMPTY
- Does not hold an active (key, value) pair now and never did. Unused can
- transition to Active upon key insertion. This is each slot's initial state. +
+2. Active. index >= 0, me_key != NULL and me_value != NULL
- Holds an active (key, value) pair. Active can transition to Dummy or
- Pending upon key deletion (for combined and split tables respectively).
- This is the only case in which me_value != NULL. +
+3. Dummy. index == DKIX_DUMMY (combined only)
- Previously held an active (key, value) pair, but that was deleted and an
- active pair has not yet overwritten the slot. Dummy can transition to
- Active upon key insertion. Dummy slots cannot be made Unused again
- else the probe sequence in case of collision would have no way to know
- they were once active. +
+4. Pending. index >= 0, key != NULL, and value == NULL (split only)
-/* PyDict_MINSIZE_SPLIT is the minimum size of a split dictionary.
- /
-#define PyDict_MINSIZE_SPLIT 4
-
-/ PyDict_MINSIZE_COMBINED is the starting size for any new, non-split dict.
+/
+Preserving insertion order
+
+It's simple for combined table. Since dk_entries is mostly append only, we can
+get insertion order by just iterating dk_entries.
+
+One exception is .popitem(). It removes last item in dk_entries and decrement
+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.
+/
+
+/* PyDict_MINSIZE is the starting size for any new dict.
- 8 allows dicts with no more than 5 active entries; experiments suggested
- this suffices for the majority of dicts (consisting mostly of usually-small
- dicts created to pass keyword arguments).
- Making this 8, rather than 4 reduces the number of resizes for most
- dictionaries, without any significant extra memory use. */ -#define PyDict_MINSIZE_COMBINED 8 +#define PyDict_MINSIZE 8
#include "Python.h" #include "dict-common.h" @@ -177,41 +220,31 @@ equally good collision statistics, neede / -/ Object used as dummy key to fill deleted entries
-} -#endif - /* forward declarations */ -static PyDictKeyEntry *lookdict(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr);[](#l10.178)
-static PyDictKeyEntry *lookdict_unicode(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr);[](#l10.180)
-static PyDictKeyEntry * +static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr,[](#l10.183)
Py_ssize_t *hashpos);[](#l10.184)
+static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr,[](#l10.186)
Py_ssize_t *hashpos);[](#l10.187)
+static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr);[](#l10.190)
-static PyDictKeyEntry *lookdict_split(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr);[](#l10.192)
Py_hash_t hash, PyObject ***value_addr,[](#l10.193)
Py_ssize_t *hashpos);[](#l10.194)
+static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr,[](#l10.196)
Py_ssize_t *hashpos);[](#l10.197)
static int dictresize(PyDictObject mp, Py_ssize_t minused); -/ Dictionary reuse scheme to save calls to malloc, free, and memset / +/ Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; +static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; +static int numfreekeys = 0; #include "clinic/dictobject.c.h" @@ -219,12 +252,15 @@ int PyDict_ClearFreeList(void) { PyDictObject *op;
- int ret = numfree + numfreekeys; while (numfree) { op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); }
- while (numfreekeys) {
PyObject_FREE(keys_free_list[--numfreekeys]);[](#l10.225)
- } return ret; }
@@ -243,40 +279,94 @@ PyDict_Fini(void) PyDict_ClearFreeList(); } +#define DK_SIZE(dk) ((dk)->dk_size) +#if SIZEOF_VOID_P > 4 +#define DK_IXSIZE(dk) (DK_SIZE(dk) <= 0xff ? 1 : DK_SIZE(dk) <= 0xffff ? 2 : [](#l10.236)
DK_SIZE(dk) <= 0xffffffff ? 4 : sizeof(Py_ssize_t))[](#l10.237)
+#else +#define DK_IXSIZE(dk) (DK_SIZE(dk) <= 0xff ? 1 : DK_SIZE(dk) <= 0xffff ? 2 : [](#l10.239)
sizeof(Py_ssize_t))[](#l10.240)
+#endif +#define DK_ENTRIES(dk) ((PyDictKeyEntry*)(&(dk)->dk_indicesDK_SIZE(dk) * [
DK_IXSIZE(dk)]))[](#l10.243)
+ #define DK_DEBUG_INCREF _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_DEBUG_DECREF _Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_INCREF(dk) (DK_DEBUG_INCREF ++(dk)->dk_refcnt) #define DK_DECREF(dk) if (DK_DEBUG_DECREF (--(dk)->dk_refcnt) == 0) free_keys_object(dk) -#define DK_SIZE(dk) ((dk)->dk_size) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) + +/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ +Py_LOCAL_INLINE(Py_ssize_t) +dk_get_index(PyDictKeysObject *keys, Py_ssize_t i) +{
- Py_ssize_t s = DK_SIZE(keys);
- if (s <= 0xff) {
return ((char*) &keys->dk_indices[0])[i];[](#l10.261)
- }
- else if (s <= 0xffff) {
return ((int16_t*)&keys->dk_indices[0])[i];[](#l10.264)
- }
+} + +/* write to indices. */ +Py_LOCAL_INLINE(void) +dk_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) +{
- Py_ssize_t s = DK_SIZE(keys);
- if (s <= 0xff) {
((char*) &keys->dk_indices[0])[i] = (char)ix;[](#l10.282)
- }
- else if (s <= 0xffff) {
((int16_t*) &keys->dk_indices[0])[i] = (int16_t)ix;[](#l10.285)
- }
+} + + /* USABLE_FRACTION is the maximum dictionary load.
- */ +#define ESTIMATE_SIZE(n) (((n)3) >> 1) + +/ Alternative fraction that is otherwise close enough to 2n/3 to make
- / / GROWTH_RATE. Growth rate upon hitting maximum load.
{[](#l10.345)
{ 0, 0, 0 } /* dk_entries (empty) */[](#l10.346)
}[](#l10.347)
0, /* dk_nentries */[](#l10.348)
{DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY,[](#l10.349)
DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */[](#l10.350)
}; static PyObject *empty_values[1] = { NULL }; @@ -316,45 +406,66 @@ static PyObject *empty_values[1] = { NUL static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk;
- dk = PyObject_MALLOC(sizeof(PyDictKeysObject) +
sizeof(PyDictKeyEntry) * (size-1));[](#l10.367)
- if (dk == NULL) {
PyErr_NoMemory();[](#l10.369)
return NULL;[](#l10.370)
- usable = USABLE_FRACTION(size);
- if (size <= 0xff) {
es = 1;[](#l10.374)
- }
- else if (size <= 0xffff) {
es = 2;[](#l10.377)
- }
- if (size == PyDict_MINSIZE && numfreekeys > 0) {
dk = keys_free_list[--numfreekeys];[](#l10.389)
- }
- else {
dk = PyObject_MALLOC(sizeof(PyDictKeysObject) - 8 +[](#l10.392)
es * size +[](#l10.393)
sizeof(PyDictKeyEntry) * usable);[](#l10.394)
if (dk == NULL) {[](#l10.395)
PyErr_NoMemory();[](#l10.396)
return NULL;[](#l10.397)
} DK_DEBUG_INCREF dk->dk_refcnt = 1; dk->dk_size = size;}[](#l10.398)
- dk->dk_usable = USABLE_FRACTION(size);
- ep0 = &dk->dk_entries[0];
- /* Hash value of slot 0 is used by popitem, so it must be initialized */
- ep0->me_hash = 0;
- for (i = 0; i < size; i++) {
ep0[i].me_key = NULL;[](#l10.407)
ep0[i].me_value = NULL;[](#l10.408)
- }
- dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy;
- dk->dk_nentries = 0;
- memset(&dk->dk_indices[0], 0xff, es * size);
- memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; }
static void free_keys_object(PyDictKeysObject *keys) {
- for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); }
- if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
keys_free_list[numfreekeys++] = keys;[](#l10.430)
return;[](#l10.431)
- } PyObject_FREE(keys); }
#define new_values(size) PyMem_NEW(PyObject , size) - #define free_values(values) PyMem_FREE(values) / Consumes a reference to the keys object */ @@ -390,7 +501,7 @@ new_dict_with_shared_keys(PyDictKeysObje PyObject **values; Py_ssize_t i, size;
- size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { DK_DECREF(keys);
@@ -405,12 +516,43 @@ new_dict_with_shared_keys(PyDictKeysObje PyObject * PyDict_New(void) {
- PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) return NULL; return new_dict(keys, NULL); }
+/* Search index of hash table from offset of entry table */ +static Py_ssize_t +lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) +{
- i = (size_t)hash & mask;
- ix = dk_get_index(k, i);
- if (ix == index) {
return i;[](#l10.472)
- }
- if (ix == DKIX_EMPTY) {
return DKIX_EMPTY;[](#l10.475)
- }
- for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
i = mask & ((i << 2) + i + perturb + 1);[](#l10.479)
ix = dk_get_index(k, i);[](#l10.480)
if (ix == index) {[](#l10.481)
return i;[](#l10.482)
}[](#l10.483)
if (ix == DKIX_EMPTY) {[](#l10.484)
return DKIX_EMPTY;[](#l10.485)
}[](#l10.486)
- }
- assert(0); /* NOT REACHED */
- return DKIX_ERROR;
+} + / The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. @@ -426,52 +568,63 @@ The details in this version are due to T contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. -lookdict() is general-purpose, and may return NULL if (and only if) a +lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception (this was new in Python 2.5). lookdict_unicode() below is specialized to string keys, comparison of which can -never raise an exception; that function can never return NULL. +never raise an exception; that function can never return DKIX_ERROR. lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. -For both, when the key isn't found a PyDictEntry is returned -where the key would have been found, *value_addr points to the matching value -slot. +For both, when the key isn't found a DKIX_EMPTY is returned. hashpos returns +where the key index should be inserted. */ -static PyDictKeyEntry * +static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key,
Py_hash_t hash, PyObject ***value_addr)[](#l10.516)
Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos)[](#l10.517)
- size_t i;
- size_t perturb;
- PyDictKeyEntry *freeslot;
- size_t mask;
- PyDictKeyEntry *ep0;
- PyDictKeyEntry *ep;
- size_t i, perturb, mask;
- Py_ssize_t ix, freeslot; int cmp;
- PyDictKeysObject *dk;
- PyDictKeyEntry *ep0, *ep; PyObject *startkey; top:
- ep = &ep0[i];
- if (ep->me_key == NULL || ep->me_key == key) {
*value_addr = &ep->me_value;[](#l10.541)
return ep;[](#l10.542)
- ix = dk_get_index(dk, i);
- if (ix == DKIX_EMPTY) {
if (hashpos != NULL)[](#l10.546)
*hashpos = i;[](#l10.547)
*value_addr = NULL;[](#l10.548)
}return DKIX_EMPTY;[](#l10.549)
if (ep->me_hash == hash) {[](#l10.557)
ep = &ep0[ix];[](#l10.558)
if (ep->me_key == key) {[](#l10.559)
*value_addr = &ep->me_value;[](#l10.560)
if (hashpos != NULL)[](#l10.561)
*hashpos = i;[](#l10.562)
return ix;[](#l10.563)
}[](#l10.564)
if (ep->me_key != NULL && ep->me_hash == hash) {[](#l10.565) startkey = ep->me_key;[](#l10.566) Py_INCREF(startkey);[](#l10.567) cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);[](#l10.568) Py_DECREF(startkey);[](#l10.569) if (cmp < 0)[](#l10.570)
return NULL;[](#l10.571)
if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) {[](#l10.572)
return DKIX_ERROR;[](#l10.573)
if (dk == mp->ma_keys && ep->me_key == startkey) {[](#l10.574) if (cmp > 0) {[](#l10.575) *value_addr = &ep->me_value;[](#l10.576)
return ep;[](#l10.577)
if (hashpos != NULL)[](#l10.578)
*hashpos = i;[](#l10.579)
return ix;[](#l10.580) }[](#l10.581) }[](#l10.582) else {[](#l10.583)
@@ -479,40 +632,48 @@ top: goto top; } }
freeslot = NULL;[](#l10.588)
- /* In the loop, me_key == dummy is by far (factor of 100s) the
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {least likely outcome, so test for that last. */[](#l10.593)
i = (i << 2) + i + perturb + 1;[](#l10.595)
ep = &ep0[i & mask];[](#l10.596)
if (ep->me_key == NULL) {[](#l10.597)
if (freeslot == NULL) {[](#l10.598)
*value_addr = &ep->me_value;[](#l10.599)
return ep;[](#l10.600)
} else {[](#l10.601)
*value_addr = &freeslot->me_value;[](#l10.602)
return freeslot;[](#l10.603)
i = ((i << 2) + i + perturb + 1) & mask;[](#l10.604)
ix = dk_get_index(dk, i);[](#l10.605)
if (ix == DKIX_EMPTY) {[](#l10.606)
if (hashpos != NULL) {[](#l10.607)
*hashpos = (freeslot == -1) ? (Py_ssize_t)i : freeslot;[](#l10.608) }[](#l10.609)
*value_addr = NULL;[](#l10.610)
return ix;[](#l10.611) }[](#l10.612)
if (ix == DKIX_DUMMY) {[](#l10.613)
if (freeslot == -1)[](#l10.614)
freeslot = i;[](#l10.615)
continue;[](#l10.616)
}[](#l10.617)
ep = &ep0[ix];[](#l10.618) if (ep->me_key == key) {[](#l10.619)
if (hashpos != NULL) {[](#l10.620)
*hashpos = i;[](#l10.621)
}[](#l10.622) *value_addr = &ep->me_value;[](#l10.623)
return ep;[](#l10.624)
return ix;[](#l10.625) }[](#l10.626)
if (ep->me_hash == hash && ep->me_key != dummy) {[](#l10.627)
if (ep->me_hash == hash && ep->me_key != NULL) {[](#l10.628) startkey = ep->me_key;[](#l10.629) Py_INCREF(startkey);[](#l10.630) cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);[](#l10.631) Py_DECREF(startkey);[](#l10.632) if (cmp < 0) {[](#l10.633) *value_addr = NULL;[](#l10.634)
return NULL;[](#l10.635)
return DKIX_ERROR;[](#l10.636) }[](#l10.637)
if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) {[](#l10.638)
if (dk == mp->ma_keys && ep->me_key == startkey) {[](#l10.639) if (cmp > 0) {[](#l10.640)
if (hashpos != NULL) {[](#l10.641)
*hashpos = i;[](#l10.642)
}[](#l10.643) *value_addr = &ep->me_value;[](#l10.644)
return ep;[](#l10.645)
return ix;[](#l10.646) }[](#l10.647) }[](#l10.648) else {[](#l10.649)
@@ -520,72 +681,80 @@ top: goto top; } }
else if (ep->me_key == dummy && freeslot == NULL)[](#l10.654)
} assert(0); /* NOT REACHED / return 0; } / Specialized version for string-only keys */ -static PyDictKeyEntry * +static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key,freeslot = ep;[](#l10.655)
Py_hash_t hash, PyObject ***value_addr)[](#l10.665)
Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos)[](#l10.666)
- 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)) { mp->ma_keys->dk_lookup = lookdict;
return lookdict(mp, key, hash, value_addr);[](#l10.686)
- ep = &ep0[i];
- if (ep->me_key == NULL || ep->me_key == key) {
*value_addr = &ep->me_value;[](#l10.692)
return ep;[](#l10.693)
- ix = dk_get_index(mp->ma_keys, i);
- if (ix == DKIX_EMPTY) {
if (hashpos != NULL)[](#l10.696)
*hashpos = i;[](#l10.697)
*value_addr = NULL;[](#l10.698)
}return DKIX_EMPTY;[](#l10.699)
- if (ep->me_key == dummy)
freeslot = ep;[](#l10.702)
- else {
if (ep->me_hash == hash && unicode_eq(ep->me_key, key)) {[](#l10.704)
*value_addr = &ep->me_value;[](#l10.705)
return ep;[](#l10.706)
}[](#l10.707)
freeslot = NULL;[](#l10.708)
- /* In the loop, me_key == dummy is by far (factor of 100s) the
least likely outcome, so test for that last. */[](#l10.714)
- else {
ep = &ep0[ix];[](#l10.716)
/* only split table can be ix != DKIX_DUMMY && me_key == NULL */[](#l10.717)
assert(ep->me_key != NULL);[](#l10.718)
if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) {[](#l10.719)
if (hashpos != NULL)[](#l10.720)
*hashpos = i;[](#l10.721)
*value_addr = &ep->me_value;[](#l10.722)
return ix;[](#l10.723)
}[](#l10.724)
freeslot = -1;[](#l10.725)
- }
+ for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;[](#l10.729)
ep = &ep0[i & mask];[](#l10.730)
if (ep->me_key == NULL) {[](#l10.731)
if (freeslot == NULL) {[](#l10.732)
*value_addr = &ep->me_value;[](#l10.733)
return ep;[](#l10.734)
} else {[](#l10.735)
*value_addr = &freeslot->me_value;[](#l10.736)
return freeslot;[](#l10.737)
i = mask & ((i << 2) + i + perturb + 1);[](#l10.738)
ix = dk_get_index(mp->ma_keys, i);[](#l10.739)
if (ix == DKIX_EMPTY) {[](#l10.740)
if (hashpos != NULL) {[](#l10.741)
*hashpos = (freeslot == -1) ? (Py_ssize_t)i : freeslot;[](#l10.742) }[](#l10.743)
*value_addr = NULL;[](#l10.744)
return DKIX_EMPTY;[](#l10.745) }[](#l10.746)
if (ix == DKIX_DUMMY) {[](#l10.747)
if (freeslot == -1)[](#l10.748)
freeslot = i;[](#l10.749)
continue;[](#l10.750)
}[](#l10.751)
ep = &ep0[ix];[](#l10.752) if (ep->me_key == key[](#l10.753) || (ep->me_hash == hash[](#l10.754)
&& ep->me_key != dummy[](#l10.755)
&& unicode_eq(ep->me_key, key))) {[](#l10.756)
&& ep->me_key != NULL[](#l10.757)
&& unicode_eq(ep->me_key, key))) {[](#l10.758) *value_addr = &ep->me_value;[](#l10.759)
return ep;[](#l10.760)
if (hashpos != NULL) {[](#l10.761)
*hashpos = i;[](#l10.762)
}[](#l10.763)
return ix;[](#l10.764) }[](#l10.765)
if (ep->me_key == dummy && freeslot == NULL)[](#l10.766)
} assert(0); /* NOT REACHED */ return 0; @@ -593,40 +762,61 @@ lookdict_unicode(PyDictObject mp, PyObj / Faster version of lookdict_unicode when it is known that no keysfreeslot = ep;[](#l10.767)
Py_hash_t hash, PyObject ***value_addr)[](#l10.778)
Py_hash_t hash, PyObject ***value_addr,[](#l10.779)
Py_ssize_t *hashpos)[](#l10.780)
- 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)) { mp->ma_keys->dk_lookup = lookdict;
return lookdict(mp, key, hash, value_addr);[](#l10.799)
- ep = &ep0[i];
- assert(ep->me_key == NULL || PyUnicode_CheckExact(ep->me_key));
- if (ep->me_key == NULL || ep->me_key == key ||
- ix = dk_get_index(mp->ma_keys, i);
- assert (ix != DKIX_DUMMY);
- if (ix == DKIX_EMPTY) {
if (hashpos != NULL)[](#l10.809)
*hashpos = i;[](#l10.810)
*value_addr = NULL;[](#l10.811)
return DKIX_EMPTY;[](#l10.812)
- }
- ep = &ep0[ix];
- assert(ep->me_key != NULL && PyUnicode_CheckExact(ep->me_key));
- if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) {
if (hashpos != NULL)[](#l10.818)
*hashpos = i;[](#l10.819) *value_addr = &ep->me_value;[](#l10.820)
return ep;[](#l10.821)
i = (i << 2) + i + perturb + 1;[](#l10.825)
ep = &ep0[i & mask];[](#l10.826)
assert(ep->me_key == NULL || PyUnicode_CheckExact(ep->me_key));[](#l10.827)
if (ep->me_key == NULL || ep->me_key == key ||[](#l10.828)
i = mask & ((i << 2) + i + perturb + 1);[](#l10.829)
ix = dk_get_index(mp->ma_keys, i);[](#l10.830)
assert (ix != DKIX_DUMMY);[](#l10.831)
if (ix == DKIX_EMPTY) {[](#l10.832)
if (hashpos != NULL)[](#l10.833)
*hashpos = i;[](#l10.834)
*value_addr = NULL;[](#l10.835)
return DKIX_EMPTY;[](#l10.836)
}[](#l10.837)
ep = &ep0[ix];[](#l10.838)
assert(ep->me_key != NULL && PyUnicode_CheckExact(ep->me_key));[](#l10.839)
if (ep->me_key == key ||[](#l10.840) (ep->me_hash == hash && unicode_eq(ep->me_key, key))) {[](#l10.841)
if (hashpos != NULL)[](#l10.842)
*hashpos = i;[](#l10.843) *value_addr = &ep->me_value;[](#l10.844)
return ep;[](#l10.845)
} assert(0); /* NOT REACHED */ @@ -638,39 +828,61 @@ lookdict_unicode_nodummy(PyDictObject *mreturn ix;[](#l10.846) }[](#l10.847)
Py_hash_t hash, PyObject ***value_addr)[](#l10.857)
Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos)[](#l10.858)
ep = lookdict(mp, key, hash, value_addr);[](#l10.873)
/* lookdict expects a combined-table, so fix value_addr */[](#l10.874)
i = ep - ep0;[](#l10.875)
*value_addr = &mp->ma_values[i];[](#l10.876)
return ep;[](#l10.877)
ix = lookdict(mp, key, hash, value_addr, hashpos);[](#l10.878)
if (ix >= 0) {[](#l10.879)
*value_addr = &mp->ma_values[ix];[](#l10.880)
}[](#l10.881)
} + i = (size_t)hash & mask;return ix;[](#l10.882)
- ix = dk_get_index(mp->ma_keys, i);
- if (ix == DKIX_EMPTY) {
if (hashpos != NULL)[](#l10.889)
*hashpos = i;[](#l10.890)
*value_addr = NULL;[](#l10.891)
return DKIX_EMPTY;[](#l10.892)
- }
- assert(ix >= 0);
- ep = &ep0[ix]; assert(ep->me_key == NULL || PyUnicode_CheckExact(ep->me_key));
*value_addr = &mp->ma_values[i];[](#l10.900)
return ep;[](#l10.901)
if (hashpos != NULL)[](#l10.902)
*hashpos = i;[](#l10.903)
*value_addr = &mp->ma_values[ix];[](#l10.904)
} for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {return ix;[](#l10.905)
i = (i << 2) + i + perturb + 1;[](#l10.908)
ep = &ep0[i & mask];[](#l10.909)
i = mask & ((i << 2) + i + perturb + 1);[](#l10.910)
ix = dk_get_index(mp->ma_keys, i);[](#l10.911)
if (ix == DKIX_EMPTY) {[](#l10.912)
if (hashpos != NULL)[](#l10.913)
*hashpos = i;[](#l10.914)
*value_addr = NULL;[](#l10.915)
return DKIX_EMPTY;[](#l10.916)
}[](#l10.917)
assert(ix >= 0);[](#l10.918)
ep = &ep0[ix];[](#l10.919) assert(ep->me_key == NULL || PyUnicode_CheckExact(ep->me_key));[](#l10.920)
if (ep->me_key == NULL || ep->me_key == key ||[](#l10.921)
if (ep->me_key == key ||[](#l10.922) (ep->me_hash == hash && unicode_eq(ep->me_key, key))) {[](#l10.923)
*value_addr = &mp->ma_values[i & mask];[](#l10.924)
return ep;[](#l10.925)
if (hashpos != NULL)[](#l10.926)
*hashpos = i;[](#l10.927)
*value_addr = &mp->ma_values[ix];[](#l10.928)
} assert(0); /* NOT REACHED */ @@ -707,27 +919,27 @@ void { PyDictObject *mp; PyObject *value;return ix;[](#l10.929) }[](#l10.930)
if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op;
- ep0 = DK_ENTRIES(mp->ma_keys);
- numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) {
for (i = 0; i < size; i++) {[](#l10.949)
for (i = 0; i < numentries; i++) {[](#l10.950) if ((value = mp->ma_values[i]) == NULL)[](#l10.951) continue;[](#l10.952) if (_PyObject_GC_MAY_BE_TRACKED(value)) {[](#l10.953)
assert(!_PyObject_GC_MAY_BE_TRACKED([](#l10.954)
mp->ma_keys->dk_entries[i].me_key));[](#l10.955)
} else {assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key));[](#l10.956) return;[](#l10.957) }[](#l10.958) }[](#l10.959)
PyDictKeyEntry *ep0 = &mp->ma_keys->dk_entries[0];[](#l10.962)
for (i = 0; i < size; i++) {[](#l10.963)
for (i = 0; i < numentries; i++) {[](#l10.964) if ((value = ep0[i].me_value) == NULL)[](#l10.965) continue;[](#l10.966) if (_PyObject_GC_MAY_BE_TRACKED(value) ||[](#l10.967)
@@ -741,31 +953,33 @@ void /* Internal function to find slot for an item from its hash
- when it is known that the key is not present in the dict. */ -static PyDictKeyEntry * +static Py_ssize_t find_empty_slot(PyDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject ***value_addr)[](#l10.975)
PyObject ***value_addr, Py_ssize_t *hashpos)[](#l10.976)
- assert(hashpos != NULL); assert(key != NULL); if (!PyUnicode_CheckExact(key)) mp->ma_keys->dk_lookup = lookdict; i = hash & mask;
- ix = dk_get_index(mp->ma_keys, i);
- for (perturb = hash; ix != DKIX_EMPTY; perturb >>= PERTURB_SHIFT) { i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];[](#l10.998)
}ix = dk_get_index(mp->ma_keys, i & mask);[](#l10.999)
- ep = &ep0[mp->ma_keys->dk_nentries];
- *hashpos = i & mask; assert(ep->me_value == NULL); if (mp->ma_values)
*value_addr = &mp->ma_values[i & mask];[](#l10.1005)
} static int @@ -784,58 +998,81 @@ insertdict(PyDictObject *mp, PyObject *k { PyObject *old_value; PyObject **value_addr;
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return -1; }
- ix = mp->ma_keys->dk_lookup(mp, key, hash, &value_addr, &hashpos);
- if (ix == DKIX_ERROR) { return -1; } + assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); Py_INCREF(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.[](#l10.1040)
*/[](#l10.1041)
- if (_PyDict_HasSplitTable(mp) &&
((ix >= 0 && *value_addr == NULL && mp->ma_used != ix) ||[](#l10.1043)
(ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) {[](#l10.1044)
if (insertion_resize(mp) < 0) {[](#l10.1045)
Py_DECREF(value);[](#l10.1046)
return -1;[](#l10.1047)
}[](#l10.1048)
find_empty_slot(mp, key, hash, &value_addr, &hashpos);[](#l10.1049)
ix = DKIX_EMPTY;[](#l10.1050)
- }
- if (ix == DKIX_EMPTY) {
/* Insert into new slot. */[](#l10.1054)
if (mp->ma_keys->dk_usable <= 0) {[](#l10.1055)
/* Need to resize. */[](#l10.1056)
if (insertion_resize(mp) < 0) {[](#l10.1057)
Py_DECREF(value);[](#l10.1058)
return -1;[](#l10.1059)
}[](#l10.1060)
find_empty_slot(mp, key, hash, &value_addr, &hashpos);[](#l10.1061)
}[](#l10.1062)
ep0 = DK_ENTRIES(mp->ma_keys);[](#l10.1063)
ep = &ep0[mp->ma_keys->dk_nentries];[](#l10.1064)
dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);[](#l10.1065)
Py_INCREF(key);[](#l10.1066)
ep->me_key = key;[](#l10.1067)
ep->me_hash = hash;[](#l10.1068)
if (mp->ma_values) {[](#l10.1069)
assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL);[](#l10.1070)
mp->ma_values[mp->ma_keys->dk_nentries] = value;[](#l10.1071)
}[](#l10.1072)
else {[](#l10.1073)
ep->me_value = value;[](#l10.1074)
}[](#l10.1075)
mp->ma_used++;[](#l10.1076)
mp->ma_keys->dk_usable--;[](#l10.1077)
mp->ma_keys->dk_nentries++;[](#l10.1078)
assert(mp->ma_keys->dk_usable >= 0);[](#l10.1079)
return 0;[](#l10.1080)
- }
+ old_value = *value_addr; if (old_value != NULL) {
assert(ep->me_key != NULL && ep->me_key != dummy);[](#l10.1087) *value_addr = value;[](#l10.1088) Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */[](#l10.1089)
- else {
if (ep->me_key == NULL) {[](#l10.1093)
Py_INCREF(key);[](#l10.1094)
if (mp->ma_keys->dk_usable <= 0) {[](#l10.1095)
/* Need to resize. */[](#l10.1096)
if (insertion_resize(mp) < 0) {[](#l10.1097)
Py_DECREF(key);[](#l10.1098)
Py_DECREF(value);[](#l10.1099)
return -1;[](#l10.1100)
}[](#l10.1101)
ep = find_empty_slot(mp, key, hash, &value_addr);[](#l10.1102)
}[](#l10.1103)
mp->ma_keys->dk_usable--;[](#l10.1104)
assert(mp->ma_keys->dk_usable >= 0);[](#l10.1105)
ep->me_key = key;[](#l10.1106)
ep->me_hash = hash;[](#l10.1107)
}[](#l10.1108)
else {[](#l10.1109)
if (ep->me_key == dummy) {[](#l10.1110)
Py_INCREF(key);[](#l10.1111)
ep->me_key = key;[](#l10.1112)
ep->me_hash = hash;[](#l10.1113)
Py_DECREF(dummy);[](#l10.1114)
} else {[](#l10.1115)
assert(_PyDict_HasSplitTable(mp));[](#l10.1116)
}[](#l10.1117)
}[](#l10.1118)
mp->ma_used++;[](#l10.1119)
*value_addr = value;[](#l10.1120)
assert(ep->me_key != NULL && ep->me_key != dummy);[](#l10.1121)
- }
- /* pending state */
- assert(_PyDict_HasSplitTable(mp));
- assert(ix == mp->ma_used);
- *value_addr = value;
- mp->ma_used++; return 0; }
@@ -853,25 +1090,25 @@ static void insertdict_clean(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) {
- PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); PyDictKeyEntry *ep; assert(k->dk_lookup != NULL); assert(value != NULL); assert(key != NULL);
- assert(key != dummy); assert(PyUnicode_CheckExact(key) || k->dk_lookup == lookdict); i = hash & mask;
- ep = &ep0[i];
- for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;[](#l10.1153)
ep = &ep0[i & mask];[](#l10.1154)
- for (perturb = hash; dk_get_index(k, i) != DKIX_EMPTY;
perturb >>= PERTURB_SHIFT) {[](#l10.1156)
}i = mask & ((i << 2) + i + perturb + 1);[](#l10.1157)
- ep = &ep0[k->dk_nentries]; assert(ep->me_value == NULL);
- dk_set_index(k, i, k->dk_nentries);
- k->dk_nentries++; ep->me_key = key; ep->me_hash = hash; ep->me_value = value; @@ -890,13 +1127,13 @@ but can be resplit by make_keys_shared() static int dictresize(PyDictObject *mp, Py_ssize_t minused) {
- -/* Find the smallest table size > minused. */
- /* Find the smallest table size > minused. */
- for (newsize = PyDict_MINSIZE; newsize <= minused && newsize > 0; newsize <<= 1) ;
@@ -914,52 +1151,39 @@ dictresize(PyDictObject *mp, Py_ssize_t } if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict;
- oldsize = DK_SIZE(oldkeys); mp->ma_values = NULL;
- /* If empty then nothing to copy so just return */
- if (oldsize == 1) {
assert(oldkeys == Py_EMPTY_KEYS);[](#l10.1193)
DK_DECREF(oldkeys);[](#l10.1194)
return 0;[](#l10.1195)
- }
for (i = 0; i < oldsize; i++) {[](#l10.1203)
for (i = 0; i < oldkeys->dk_nentries; i++) {[](#l10.1204) if (oldvalues[i] != NULL) {[](#l10.1205)
Py_INCREF(oldkeys->dk_entries[i].me_key);[](#l10.1206)
oldkeys->dk_entries[i].me_value = oldvalues[i];[](#l10.1207)
Py_INCREF(ep0[i].me_key);[](#l10.1208)
} /* Main loop */ep0[i].me_value = oldvalues[i];[](#l10.1209) }[](#l10.1210) }[](#l10.1211)
- for (i = 0; i < oldkeys->dk_nentries; i++) {
PyDictKeyEntry *ep = &ep0[i];[](#l10.1217) if (ep->me_value != NULL) {[](#l10.1218)
} mp->ma_keys->dk_usable -= mp->ma_used; if (oldvalues != NULL) { /* NULL out me_value slot in oldkeys, in case it was shared */assert(ep->me_key != dummy);[](#l10.1219) insertdict_clean(mp, ep->me_key, ep->me_hash, ep->me_value);[](#l10.1220) }[](#l10.1221)
for (i = 0; i < oldsize; i++)[](#l10.1226)
oldkeys->dk_entries[i].me_value = NULL;[](#l10.1227)
assert(oldvalues != empty_values);[](#l10.1228)
free_values(oldvalues);[](#l10.1229)
for (i = 0; i < oldkeys->dk_nentries; i++)[](#l10.1230)
ep0[i].me_value = NULL;[](#l10.1231) DK_DECREF(oldkeys);[](#l10.1232)
if (oldvalues != empty_values) {[](#l10.1233)
free_values(oldvalues);[](#l10.1234)
} else { assert(oldkeys->dk_lookup != lookdict_split);}[](#l10.1235)
if (oldkeys->dk_lookup != lookdict_unicode_nodummy) {[](#l10.1239)
PyDictKeyEntry *ep0 = &oldkeys->dk_entries[0];[](#l10.1240)
for (i = 0; i < oldsize; i++) {[](#l10.1241)
if (ep0[i].me_key == dummy)[](#l10.1242)
Py_DECREF(dummy);[](#l10.1243)
}[](#l10.1244)
} @@ -991,8 +1215,8 @@ make_keys_shared(PyObject op) } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); / Copy values into a new array */}[](#l10.1245) assert(oldkeys->dk_refcnt == 1);[](#l10.1246) DK_DEBUG_DECREF PyObject_FREE(oldkeys);[](#l10.1247)
ep0 = &mp->ma_keys->dk_entries[0];[](#l10.1253)
size = DK_SIZE(mp->ma_keys);[](#l10.1254)
ep0 = DK_ENTRIES(mp->ma_keys);[](#l10.1255)
size = USABLE_FRACTION(DK_SIZE(mp->ma_keys));[](#l10.1256) values = new_values(size);[](#l10.1257) if (values == NULL) {[](#l10.1258) PyErr_SetString(PyExc_MemoryError,[](#l10.1259)
@@ -1015,7 +1239,7 @@ PyObject * { Py_ssize_t newsize; PyDictKeysObject *new_keys;
@@ -1039,8 +1263,8 @@ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { Py_hash_t hash;
- PyDictKeyEntry *ep; PyThreadState *tstate; PyObject **value_addr; @@ -1066,15 +1290,15 @@ PyDict_GetItem(PyObject *op, PyObject k / preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb);
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);[](#l10.1283)
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);[](#l10.1284) /* ignore errors */[](#l10.1285) PyErr_Restore(err_type, err_value, err_tb);[](#l10.1286)
if (ep == NULL)[](#l10.1287)
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);[](#l10.1292)
if (ep == NULL) {[](#l10.1293)
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);[](#l10.1294)
if (ix < 0) {[](#l10.1295) PyErr_Clear();[](#l10.1296) return NULL;[](#l10.1297) }[](#l10.1298)
@@ -1085,8 +1309,8 @@ PyDict_GetItem(PyObject *op, PyObject *k PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) {
- PyDictKeyEntry *ep; PyThreadState *tstate; PyObject **value_addr; @@ -1103,15 +1327,15 @@ PyObject / preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb);
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);[](#l10.1313)
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);[](#l10.1314) /* ignore errors */[](#l10.1315) PyErr_Restore(err_type, err_value, err_tb);[](#l10.1316)
if (ep == NULL)[](#l10.1317)
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);[](#l10.1322)
if (ep == NULL) {[](#l10.1323)
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);[](#l10.1324)
if (ix == DKIX_EMPTY) {[](#l10.1325) PyErr_Clear();[](#l10.1326) return NULL;[](#l10.1327) }[](#l10.1328)
@@ -1126,9 +1350,9 @@ PyObject * PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) {
- PyDictKeyEntry *ep; PyObject **value_addr; if (!PyDict_Check(op)) { @@ -1144,8 +1368,8 @@ PyDict_GetItemWithError(PyObject *op, Py } }
- ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
- if (ep == NULL)
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);
- if (ix < 0) return NULL; return *value_addr; }
@@ -1170,10 +1394,9 @@ PyObject * PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) {
if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) @@ -1184,16 +1407,15 @@ PyObject } / namespace 1: globals */
- ix = globals->ma_keys->dk_lookup(globals, key, hash, &value_addr, NULL);
- if (ix == DKIX_ERROR) return NULL;
- ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value_addr, NULL);
- if (ix < 0) return NULL; return *value_addr; }
@@ -1250,16 +1472,8 @@ int int PyDict_DelItem(PyObject *op, PyObject *key) {
- PyDictObject *mp; Py_hash_t hash;
- PyDictKeyEntry *ep;
- PyObject *old_key, *old_value;
- PyObject **value_addr;
+ assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { @@ -1267,31 +1481,14 @@ PyDict_DelItem(PyObject *op, PyObject *k if (hash == -1) return -1; }
- mp = (PyDictObject *)op;
- ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
- if (ep == NULL)
return -1;[](#l10.1411)
- if (*value_addr == NULL) {
_PyErr_SetKeyError(key);[](#l10.1413)
return -1;[](#l10.1414)
- }
- old_value = *value_addr;
- *value_addr = NULL;
- mp->ma_used--;
- if (!_PyDict_HasSplitTable(mp)) {
ENSURE_ALLOWS_DELETIONS(mp);[](#l10.1420)
old_key = ep->me_key;[](#l10.1421)
Py_INCREF(dummy);[](#l10.1422)
ep->me_key = dummy;[](#l10.1423)
Py_DECREF(old_key);[](#l10.1424)
- }
- Py_DECREF(old_value);
- return 0;
} int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) {
- Py_ssize_t hashpos, ix; PyDictObject *mp; PyDictKeyEntry *ep; PyObject *old_key, *old_value; @@ -1304,21 +1501,26 @@ int assert(key); assert(hash != -1); mp = (PyDictObject *)op;
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos);
- if (ix == DKIX_ERROR) return -1;
- if (ix == DKIX_EMPTY || *value_addr == NULL) { _PyErr_SetKeyError(key); return -1; }
- assert(dk_get_index(mp->ma_keys, hashpos) == ix); old_value = *value_addr; *value_addr = NULL; mp->ma_used--;
- if (_PyDict_HasSplitTable(mp)) {
mp->ma_keys->dk_usable = 0;[](#l10.1459)
- }
- else {
ep = &DK_ENTRIES(mp->ma_keys)[ix];[](#l10.1462)
dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);[](#l10.1463) ENSURE_ALLOWS_DELETIONS(mp);[](#l10.1464) old_key = ep->me_key;[](#l10.1465)
Py_INCREF(dummy);[](#l10.1466)
ep->me_key = dummy;[](#l10.1467)
} Py_DECREF(old_value); @@ -1347,7 +1549,7 @@ PyDict_Clear(PyObject op) mp->ma_used = 0; / ...then clear the keys and values */ if (oldvalues != NULL) {ep->me_key = NULL;[](#l10.1468) Py_DECREF(old_key);[](#l10.1469)
n = DK_SIZE(oldkeys);[](#l10.1476)
n = oldkeys->dk_nentries;[](#l10.1477) for (i = 0; i < n; i++)[](#l10.1478) Py_CLEAR(oldvalues[i]);[](#l10.1479) free_values(oldvalues);[](#l10.1480)
@@ -1365,30 +1567,33 @@ PyDict_Clear(PyObject *op) Py_LOCAL_INLINE(Py_ssize_t) dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue) {
if (!PyDict_Check(op)) return -1; mp = (PyDictObject *)op; if (i < 0) return -1; +
value_ptr = &mp->ma_values[i];[](#l10.1500)
offset = sizeof(PyObject *);[](#l10.1501)
for (; i < n; i++) {[](#l10.1502)
value_ptr = &mp->ma_values[i];[](#l10.1503)
if (*value_ptr != NULL)[](#l10.1504)
break;[](#l10.1505)
} else {}[](#l10.1506)
value_ptr = &mp->ma_keys->dk_entries[i].me_value;[](#l10.1509)
offset = sizeof(PyDictKeyEntry);[](#l10.1510)
PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys);[](#l10.1511)
for (; i < n; i++) {[](#l10.1512)
value_ptr = &ep0[i].me_value;[](#l10.1513)
if (*value_ptr != NULL)[](#l10.1514)
break;[](#l10.1515)
}}[](#l10.1516)
- mask = DK_MASK(mp->ma_keys);
- while (i <= mask && *value_ptr == NULL) {
value_ptr = (PyObject **)(((char *)value_ptr) + offset);[](#l10.1520)
i++;[](#l10.1521)
- }
- if (i > mask)
@@ -1413,14 +1618,14 @@ dict_next(PyObject *op, Py_ssize_t i, Py int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) {
- PyDictObject mp = (PyDictObject)op; Py_ssize_t i = dict_next(op, *ppos, pvalue); if (i < 0) return 0; mp = (PyDictObject *)op; *ppos = i+1; if (pkey)
*pkey = mp->ma_keys->dk_entries[i].me_key;[](#l10.1540)
return 1; } @@ -1432,14 +1637,16 @@ int PyObject **pvalue, Py_hash_t *phash)*pkey = DK_ENTRIES(mp->ma_keys)[i].me_key;[](#l10.1541)
- PyDictKeyEntry *ep0; Py_ssize_t i = dict_next(op, *ppos, pvalue); if (i < 0) return 0; mp = (PyDictObject *)op;
- ep0 = DK_ENTRIES(mp->ma_keys); *ppos = i+1;
*pkey = mp->ma_keys->dk_entries[i].me_key;[](#l10.1559)
return 1; } @@ -1448,6 +1655,7 @@ PyObject * _PyDict_Pop(PyDictObject *mp, PyObject *key, PyObject *deflt) { Py_hash_t hash;*pkey = ep0[i].me_key;[](#l10.1560)
- Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyObject **value_addr; @@ -1466,11 +1674,10 @@ PyObject * if (hash == -1) return NULL; }
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos);
- if (ix == DKIX_ERROR) return NULL;
@@ -1478,13 +1685,15 @@ PyObject * _PyErr_SetKeyError(key); return NULL; }
- old_value = *value_addr; *value_addr = NULL; mp->ma_used--; if (!_PyDict_HasSplitTable(mp)) {
dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);[](#l10.1595)
ep = &DK_ENTRIES(mp->ma_keys)[ix];[](#l10.1596) ENSURE_ALLOWS_DELETIONS(mp);[](#l10.1597) old_key = ep->me_key;[](#l10.1598)
Py_INCREF(dummy);[](#l10.1599)
ep->me_key = dummy;[](#l10.1600)
} return old_value; @@ -1511,7 +1720,7 @@ PyObject * PyObject *key; Py_hash_t hash;ep->me_key = NULL;[](#l10.1601) Py_DECREF(old_key);[](#l10.1602)
if (dictresize(mp, Py_SIZE(iterable))) {[](#l10.1609)
if (dictresize(mp, ESTIMATE_SIZE(Py_SIZE(iterable)))) {[](#l10.1610) Py_DECREF(d);[](#l10.1611) return NULL;[](#l10.1612) }[](#l10.1613)
@@ -1530,7 +1739,7 @@ PyObject * PyObject *key; Py_hash_t hash;
if (dictresize(mp, PySet_GET_SIZE(iterable))) {[](#l10.1618)
if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) {[](#l10.1619) Py_DECREF(d);[](#l10.1620) return NULL;[](#l10.1621) }[](#l10.1622)
@@ -1590,7 +1799,7 @@ dict_dealloc(PyDictObject *mp) Py_TRASHCAN_SAFE_BEGIN(mp) if (values != NULL) { if (values != empty_values) {
for (i = 0, n = DK_SIZE(mp->ma_keys); i < n; i++) {[](#l10.1627)
for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) {[](#l10.1628) Py_XDECREF(values[i]);[](#l10.1629) }[](#l10.1630) free_values(values);[](#l10.1631)
@@ -1702,8 +1911,8 @@ static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { PyObject *v;
- PyDictKeyEntry *ep; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || @@ -1712,11 +1921,10 @@ dict_subscript(PyDictObject *mp, PyObjec if (hash == -1) return NULL; }
- ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
- if (ep == NULL)
- if (ix == DKIX_EMPTY || value_addr == NULL) { if (!PyDict_CheckExact(mp)) { / Look up missing method if we're a subclass. */ PyObject *missing, *res;
@@ -1734,8 +1942,8 @@ dict_subscript(PyDictObject *mp, PyObjec _PyErr_SetKeyError(key); return NULL; }
@@ -1775,8 +1983,8 @@ dict_keys(PyDictObject *mp) Py_DECREF(v); goto again; }
- ep = DK_ENTRIES(mp->ma_keys);
- size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *);
@@ -1818,13 +2026,13 @@ dict_values(PyDictObject *mp) Py_DECREF(v); goto again; }
- size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else {
value_ptr = &mp->ma_keys->dk_entries[0].me_value;[](#l10.1690)
} for (i = 0, j = 0; i < size; i++) { @@ -1875,8 +2083,8 @@ dict_items(PyDictObject mp) goto again; } / Nothing we do below makes any function calls. */value_ptr = &(DK_ENTRIES(mp->ma_keys)[0].me_value);[](#l10.1691) offset = sizeof(PyDictKeyEntry);[](#l10.1692)
- ep = DK_ENTRIES(mp->ma_keys);
- size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *);
@@ -1920,7 +2128,8 @@ dict_fromkeys_impl(PyTypeObject *type, P } static int -dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) +dict_update_common(PyObject *self, PyObject *args, PyObject *kwds,
const char *methname)[](#l10.1712)
{ PyObject *arg = NULL; int result = 0; @@ -2043,7 +2252,7 @@ PyDict_Merge(PyObject *a, PyObject *b, i { PyDictObject *mp, *other; Py_ssize_t i, n;
/* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do @@ -2073,10 +2282,11 @@ PyDict_Merge(PyObject *a, PyObject *b, i if (mp->ma_keys->dk_usable * 3 < other->ma_used * 2) if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0) return -1;
for (i = 0, n = DK_SIZE(other->ma_keys); i < n; i++) {[](#l10.1729)
ep0 = DK_ENTRIES(other->ma_keys);[](#l10.1730)
for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) {[](#l10.1731) PyObject *key, *value;[](#l10.1732) Py_hash_t hash;[](#l10.1733)
entry = &other->ma_keys->dk_entries[i];[](#l10.1734)
entry = &ep0[i];[](#l10.1735) key = entry->me_key;[](#l10.1736) hash = entry->me_hash;[](#l10.1737) if (other->ma_values)[](#l10.1738)
@@ -2095,7 +2305,7 @@ PyDict_Merge(PyObject *a, PyObject *b, i if (err != 0) return -1;
if (n != DK_SIZE(other->ma_keys)) {[](#l10.1743)
if (n != other->ma_keys->dk_nentries) {[](#l10.1744) PyErr_SetString(PyExc_RuntimeError,[](#l10.1745) "dict mutated during update");[](#l10.1746) return -1;[](#l10.1747)
@@ -2170,7 +2380,9 @@ PyDict_Copy(PyObject *o) mp = (PyDictObject *)o; if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy;
PyObject **newvalues = new_values(DK_SIZE(mp->ma_keys));[](#l10.1752)
Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys));[](#l10.1753)
PyObject **newvalues;[](#l10.1754)
newvalues = new_values(size);[](#l10.1755) if (newvalues == NULL)[](#l10.1756) return PyErr_NoMemory();[](#l10.1757) split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type);[](#l10.1758)
@@ -2182,7 +2394,7 @@ PyDict_Copy(PyObject *o) split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; DK_INCREF(mp->ma_keys);
for (i = 0, n = DK_SIZE(mp->ma_keys); i < n; i++) {[](#l10.1763)
for (i = 0, n = size; i < n; i++) {[](#l10.1764) PyObject *value = mp->ma_values[i];[](#l10.1765) Py_XINCREF(value);[](#l10.1766) split_copy->ma_values[i] = value;[](#l10.1767)
@@ -2253,8 +2465,8 @@ dict_equal(PyDictObject a, PyDictObject / can't be equal if # of entries differ / return 0; / Same # of entries -- check all of 'em. Exit early on any diff. */
- for (i = 0; i < DK_SIZE(a->ma_keys); i++) {
PyDictKeyEntry *ep = &a->ma_keys->dk_entries[i];[](#l10.1773)
- for (i = 0; i < a->ma_keys->dk_nentries; i++) {
PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i];[](#l10.1775) PyObject *aval;[](#l10.1776) if (a->ma_values)[](#l10.1777) aval = a->ma_values[i];[](#l10.1778)
@@ -2271,7 +2483,7 @@ dict_equal(PyDictObject a, PyDictObject / ditto for key / Py_INCREF(key); / reuse the known hash value */
if ((b->ma_keys->dk_lookup)(b, key, ep->me_hash, &vaddr) == NULL)[](#l10.1783)
if ((b->ma_keys->dk_lookup)(b, key, ep->me_hash, &vaddr, NULL) < 0)[](#l10.1784) bval = NULL;[](#l10.1785) else[](#l10.1786) bval = *vaddr;[](#l10.1787)
@@ -2329,7 +2541,7 @@ dict___contains__(PyDictObject *self, Py { register PyDictObject *mp = self; Py_hash_t hash;
- Py_ssize_t ix; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || @@ -2338,10 +2550,12 @@ dict___contains__(PyDictObject *self, Py if (hash == -1) return NULL; }
} static PyObject * @@ -2351,7 +2565,7 @@ dict_get(PyDictObject *mp, PyObject *arg PyObject *failobj = Py_None; PyObject *val = NULL; Py_hash_t hash;
- Py_ssize_t ix; PyObject **value_addr; if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj)) @@ -2363,12 +2577,13 @@ dict_get(PyDictObject *mp, PyObject *arg if (hash == -1) return NULL; }
- if (ix == DKIX_EMPTY || *value_addr == NULL) val = failobj;
- else
Py_INCREF(val); return val; } @@ -2379,6 +2594,7 @@ PyDict_SetDefault(PyObject *d, PyObject PyDictObject *mp = (PyDictObject *)d; PyObject *val = NULL; Py_hash_t hash;val = *value_addr;[](#l10.1836)
- Py_ssize_t hashpos, ix; PyDictKeyEntry *ep; PyObject **value_addr; @@ -2392,27 +2608,37 @@ PyDict_SetDefault(PyObject *d, PyObject if (hash == -1) return NULL; }
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos);
- if (ix == DKIX_ERROR) return NULL;
- if (ix == DKIX_EMPTY || *value_addr == NULL) {
val = defaultobj;[](#l10.1860) if (mp->ma_keys->dk_usable <= 0) {[](#l10.1861) /* Need to resize. */[](#l10.1862) if (insertion_resize(mp) < 0)[](#l10.1863) return NULL;[](#l10.1864)
ep = find_empty_slot(mp, key, hash, &value_addr);[](#l10.1865)
find_empty_slot(mp, key, hash, &value_addr, &hashpos);[](#l10.1866) }[](#l10.1867)
ix = mp->ma_keys->dk_nentries;[](#l10.1868) Py_INCREF(defaultobj);[](#l10.1869) Py_INCREF(key);[](#l10.1870) MAINTAIN_TRACKING(mp, key, defaultobj);[](#l10.1871)
dk_set_index(mp->ma_keys, hashpos, ix);[](#l10.1872)
ep = &DK_ENTRIES(mp->ma_keys)[ix];[](#l10.1873) ep->me_key = key;[](#l10.1874) ep->me_hash = hash;[](#l10.1875)
*value_addr = defaultobj;[](#l10.1876)
val = defaultobj;[](#l10.1877)
if (mp->ma_values) {[](#l10.1878)
mp->ma_values[ix] = val;[](#l10.1879)
}[](#l10.1880)
else {[](#l10.1881)
ep->me_value = val;[](#l10.1882)
}[](#l10.1883) mp->ma_keys->dk_usable--;[](#l10.1884)
}mp->ma_keys->dk_nentries++;[](#l10.1885) mp->ma_used++;[](#l10.1886)
- else
return val; } @@ -2451,11 +2677,10 @@ dict_pop(PyDictObject *mp, PyObject *arg static PyObject * dict_popitem(PyDictObject *mp) {val = *value_addr;[](#l10.1889)
- Py_ssize_t i, j;
- PyDictKeyEntry *ep0, *ep; PyObject res; - / Allocate the result tuple before checking the size. Believe it
- /* Set ep to "the first" dict entry with a value. We abuse the hash
* field of slot 0 to hold a search finger:[](#l10.1912)
* If slot 0 has a value, use slot 0.[](#l10.1913)
* Else slot 0 is being used to hold a search finger,[](#l10.1914)
* and we use its hash value as the first index to look.[](#l10.1915)
*/[](#l10.1916)
- ep = &mp->ma_keys->dk_entries[0];
- if (ep->me_value == NULL) {
Py_ssize_t mask = DK_MASK(mp->ma_keys);[](#l10.1919)
i = ep->me_hash;[](#l10.1920)
/* The hash field may be a real hash value, or it may be a[](#l10.1921)
* legit search finger, or it may be a once-legit search[](#l10.1922)
* finger that's out of bounds now because it wrapped around[](#l10.1923)
* or the table shrunk -- simply make sure it's in bounds now.[](#l10.1924)
*/[](#l10.1925)
if (i > mask || i < 1)[](#l10.1926)
i = 1; /* skip slot 0 */[](#l10.1927)
while ((ep = &mp->ma_keys->dk_entries[i])->me_value == NULL) {[](#l10.1928)
i++;[](#l10.1929)
if (i > mask)[](#l10.1930)
i = 1;[](#l10.1931)
}[](#l10.1932)
- /* Pop last item */
- ep0 = DK_ENTRIES(mp->ma_keys);
- i = mp->ma_keys->dk_nentries - 1;
- while (i >= 0 && ep0[i].me_value == NULL) {
}i--;[](#l10.1938)
- assert(i >= 0);
- ep = &ep0[i];
- j = lookdict_index(mp->ma_keys, ep->me_hash, i);
- assert(j >= 0);
- assert(dk_get_index(mp->ma_keys, j) == i);
- dk_set_index(mp->ma_keys, j, DKIX_DUMMY);
+ PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value);
- ep->me_key = NULL; ep->me_value = NULL;
- /* We can't dk_usable++ since there is DKIX_DUMMY in indices */
- mp->ma_keys->dk_nentries = i; mp->ma_used--;
- assert(mp->ma_keys->dk_entries[0].me_value == NULL);
- mp->ma_keys->dk_entries[0].me_hash = i + 1; /* next place to start */ return res; }
@@ -2521,8 +2737,9 @@ dict_traverse(PyObject *op, visitproc vi { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys;
+ if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { @@ -2530,7 +2747,8 @@ dict_traverse(PyObject *op, visitproc vi Py_VISIT(entries[i].me_key); } }
@@ -2557,23 +2775,28 @@ static PyObject *dictiter_new(PyDictObje Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) {
+ res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values)
res += size * sizeof(PyObject*);[](#l10.1996)
/* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1)res += usable * sizeof(PyObject*);[](#l10.1997)
res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);[](#l10.2001)
res += sizeof(PyDictKeysObject) - 8 + DK_IXSIZE(mp->ma_keys) * size +[](#l10.2002)
return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) {sizeof(PyDictKeyEntry) * usable;[](#l10.2003)
- return sizeof(PyDictKeysObject) - 8
+ DK_IXSIZE(keys) * DK_SIZE(keys)[](#l10.2012)
+ USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry);[](#l10.2013)
} static PyObject * @@ -2660,8 +2883,8 @@ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash;
- PyDictKeyEntry *ep; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || @@ -2670,8 +2893,10 @@ PyDict_Contains(PyObject *op, PyObject * if (hash == -1) return -1; }
- ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
- return (ep == NULL) ? -1 : (*value_addr != NULL);
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);
- if (ix == DKIX_ERROR)
return -1;[](#l10.2035)
- return (ix != DKIX_EMPTY && *value_addr != NULL);
} /* Internal version of PyDict_Contains used when the hash value is already known */ @@ -2679,11 +2904,13 @@ int _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op;
- PyDictKeyEntry *ep; PyObject **value_addr; -
- ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
- return (ep == NULL) ? -1 : (*value_addr != NULL);
- ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL);
- if (ix == DKIX_ERROR)
return -1;[](#l10.2053)
- return (ix != DKIX_EMPTY && *value_addr != NULL);
} /* Hack to implement "key in dict" */ @@ -2717,7 +2944,7 @@ dict_new(PyTypeObject *type, PyObject *a _PyObject_GC_UNTRACK(d); d->ma_used = 0;
- d->ma_keys = new_keys_object(PyDict_MINSIZE); if (d->ma_keys == NULL) { Py_DECREF(self); return NULL;
@@ -2945,7 +3172,7 @@ static PyMethodDef dictiter_methods[] = static PyObject *dictiter_iternextkey(dictiterobject *di) { PyObject *key;
- Py_ssize_t i, n, offset; PyDictKeysObject *k; PyDictObject *d = di->di_dict; PyObject **value_ptr; @@ -2970,19 +3197,19 @@ static PyObject *dictiter_iternextkey(di offset = sizeof(PyObject *); } else {
value_ptr = &k->dk_entries[i].me_value;[](#l10.2080)
}value_ptr = &DK_ENTRIES(k)[i].me_value;[](#l10.2081) offset = sizeof(PyDictKeyEntry);[](#l10.2082)
- n = k->dk_nentries - 1;
- while (i <= n && *value_ptr == NULL) { value_ptr = (PyObject **)(((char *)value_ptr) + offset); i++; } di->di_pos = i+1;
- key = DK_ENTRIES(k)[i].me_key; Py_INCREF(key); return key; @@ -3028,7 +3255,7 @@ PyTypeObject PyDictIterKey_Type = { static PyObject *dictiter_iternextvalue(dictiterobject *di) { PyObject *value;
- Py_ssize_t i, n, offset; PyDictObject *d = di->di_dict; PyObject **value_ptr; @@ -3044,21 +3271,21 @@ static PyObject *dictiter_iternextvalue( } i = di->di_pos;
- n = d->ma_keys->dk_nentries - 1;
- if (i < 0 || i > n) goto fail; if (d->ma_values) { value_ptr = &d->ma_values[i]; offset = sizeof(PyObject *); } else {
value_ptr = &d->ma_keys->dk_entries[i].me_value;[](#l10.2124)
}value_ptr = &DK_ENTRIES(d->ma_keys)[i].me_value;[](#l10.2125) offset = sizeof(PyDictKeyEntry);[](#l10.2126)
- while (i <= n && *value_ptr == NULL) { value_ptr = (PyObject **)(((char *)value_ptr) + offset); i++;
if (i > mask)[](#l10.2132)
} di->di_pos = i+1; @@ -3109,7 +3336,7 @@ PyTypeObject PyDictIterValue_Type = { static PyObject *dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result = di->di_result;if (i > n)[](#l10.2133) goto fail;[](#l10.2134)
- Py_ssize_t i, n, offset; PyDictObject *d = di->di_dict; PyObject **value_ptr; @@ -3127,21 +3354,21 @@ static PyObject *dictiter_iternextitem(d i = di->di_pos; if (i < 0) goto fail;
- n = d->ma_keys->dk_nentries - 1; if (d->ma_values) { value_ptr = &d->ma_values[i]; offset = sizeof(PyObject *); } else {
value_ptr = &d->ma_keys->dk_entries[i].me_value;[](#l10.2157)
}value_ptr = &DK_ENTRIES(d->ma_keys)[i].me_value;[](#l10.2158) offset = sizeof(PyDictKeyEntry);[](#l10.2159)
- while (i <= n && *value_ptr == NULL) { value_ptr = (PyObject **)(((char *)value_ptr) + offset); i++; } di->di_pos = i+1;
if (result->ob_refcnt == 1) { @@ -3154,7 +3381,7 @@ static PyObject *dictiter_iternextitem(d return NULL; } di->len--;
- key = DK_ENTRIES(d->ma_keys)[i].me_key; value = *value_ptr; Py_INCREF(key); Py_INCREF(value); @@ -3794,7 +4021,7 @@ dictvalues_new(PyObject *dict) PyDictKeysObject * _PyDict_NewKeysForClass(void) {
- PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) PyErr_Clear(); else @@ -3830,7 +4057,7 @@ PyObject_GenericGetDict(PyObject *obj, v int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr,
PyObject *key, PyObject *value)[](#l10.2194)
PyObject *key, PyObject *value)[](#l10.2195)
{ PyObject dict; int res; @@ -3859,7 +4086,8 @@ int / Either update tp->ht_cached_keys or delete it */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict);
} else {[](#l10.2203)
}[](#l10.2204)
else {[](#l10.2205) CACHED_KEYS(tp) = NULL;[](#l10.2206) }[](#l10.2207) DK_DECREF(cached);[](#l10.2208)
@@ -3889,50 +4117,3 @@ void { DK_DECREF(keys); } - - -/* ARGSUSED */ -static PyObject * -dummy_repr(PyObject *op) -{
-} - -/* ARGUSED / -static void -dummy_dealloc(PyObject ignore) -{
- /* This should never get called, but we also don't want to SEGV if
* we accidentally decref dummy-key out of existence.[](#l10.2227)
*/[](#l10.2228)
- Py_FatalError("deallocating ");
-} - -static PyTypeObject PyDictDummy_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- " type",
- 0,
- 0,
- dummy_dealloc, /tp_dealloc/ /never called/
- 0, /tp_print/
- 0, /tp_getattr/
- 0, /tp_setattr/
- 0, /tp_reserved/
- dummy_repr, /tp_repr/
- 0, /tp_as_number/
- 0, /tp_as_sequence/
- 0, /tp_as_mapping/
- 0, /*tp_hash */
- 0, /*tp_call */
- 0, /*tp_str */
- 0, /*tp_getattro */
- 0, /*tp_setattro */
- 0, /*tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /*tp_flags */
-}; - -static PyObject _dummy_struct = {
--- a/Objects/object.c +++ b/Objects/object.c @@ -22,12 +22,6 @@ Py_ssize_t { PyObject *o; Py_ssize_t total = _Py_RefTotal;
- /* ignore the references to the dummy object of the dicts and sets
because they are not reliable and not useful (now that the[](#l11.8)
hash table code is well-tested) */[](#l11.9)
- o = _PyDict_Dummy();
- if (o != NULL)
o = _PySet_Dummy; if (o != NULL) total -= o->ob_refcnt;total -= o->ob_refcnt;[](#l11.12)
--- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -536,14 +536,17 @@ static Py_ssize_t _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash) { PyObject **value_addr = NULL;
- ix = (keys->dk_lookup)((PyDictObject *)od, key, hash, &value_addr, NULL);
- if (ix == DKIX_EMPTY) {
return keys->dk_nentries; /* index of new entry */[](#l12.15)
- }
- if (ix < 0) return -1; /* We use pointer arithmetic to get the entry's index into the table. */
} /* Replace od->od_fast_nodes with a new table matching the size of dict's. / @@ -565,7 +568,7 @@ static int / Copy the current nodes into the table. */ _odict_FOREACH(od, node) { i = _odict_get_index_raw(od, _odictnode_KEY(node),
_odictnode_HASH(node));[](#l12.29)
_odictnode_HASH(node));[](#l12.30) if (i < 0) {[](#l12.31) PyMem_FREE(fast_nodes);[](#l12.32) return -1;[](#l12.33)