diff options
Diffstat (limited to 'Objects/dictobject.c')
-rw-r--r-- | Objects/dictobject.c | 4389 |
1 files changed, 1488 insertions, 2901 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 4afa19c..c544ecd 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1,3 +1,4 @@ + /* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, @@ -6,152 +7,53 @@ tuning dictionaries, and several ideas for possible optimizations. */ -/* PyDictKeysObject - -This implements the dictionary's hashtable. - -As of Python 3.6, this is compact and ordered. Basic idea is described here: -* https://mail.python.org/pipermail/python-dev/2012-December/123028.html -* https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html - -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 <= 2**15 -* int32 for 2**16 <= dk_size <= 2**31 -* int64 for 2**32 <= dk_size - -dk_entries is array of PyDictKeyEntry. Its 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. -*/ - - -/* -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. - 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): - -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) - Not yet inserted in split-table. -*/ - -/* -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 8 - #include "Python.h" -#include "pycore_object.h" -#include "pycore_pystate.h" -#include "dict-common.h" -#include "stringlib/eq.h" /* to get unicode_eq() */ -/*[clinic input] -class dict "PyDictObject *" "&PyDict_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ +/* Set a key error with the specified argument, wrapping it in a + * tuple automatically so that tuple keys are not unpacked as the + * exception arguments. */ +static void +set_key_error(PyObject *arg) +{ + PyObject *tup; + tup = PyTuple_Pack(1, arg); + if (!tup) + return; /* caller will expect error to be set anyway */ + PyErr_SetObject(PyExc_KeyError, tup); + Py_DECREF(tup); +} -/* -To ensure the lookup algorithm terminates, there must be at least one Unused -slot (NULL key) in the table. -To avoid slowing down lookups on a near-full table, we resize the table when -it's USABLE_FRACTION (currently two-thirds) full. -*/ +/* Define this out if you don't want conversion statistics on exit. */ +#undef SHOW_CONVERSION_COUNTS +/* See large comment block below. This must be >= 1. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most -important hash functions (for ints) are very regular in common +important hash functions (for strings and ints) are very regular in common cases: - >>>[hash(i) for i in range(4)] - [0, 1, 2, 3] +>>> map(hash, (0, 1, 2, 3)) +[0, 1, 2, 3] +>>> map(hash, ("namea", "nameb", "namec", "named")) +[-1658398457, -1658398460, -1658398459, -1658398462] +>>> This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there -are no collisions at all for dicts indexed by a contiguous range of ints. So -this gives better-than-random behavior in common cases, and that's very -desirable. +are no collisions at all for dicts indexed by a contiguous range of ints. +The same is approximately true when keys are "consecutive" strings. So this +gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider -the list [i << 16 for i in range(20000)] as a set of keys. Since ints are -their own hash codes, and this fits in a dict of size 2**15, the last 15 bits - of every hash code are all 0: they *all* map to the same table index. +[i << 16 for i in range(20000)] as a set of keys. Since ints are their own +hash codes, and this fits in a dict of size 2**15, the last 15 bits of every +hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If @@ -187,8 +89,8 @@ The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: - perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; + perturb >>= PERTURB_SHIFT; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, @@ -207,7 +109,7 @@ the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. -Historical: Reimer Behrends contributed the idea of using a polynomial-based +Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an @@ -221,509 +123,175 @@ masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. +Theoretical Python 2.5 headache: hash codes are only C "long", but +sizeof(Py_ssize_t) > sizeof(long) may be possible. In that case, and if a +dict is genuinely huge, then only the slots directly reachable via indexing +by a C long can be the first slot in a probe sequence. The probe sequence +will still eventually reach every slot in the table, but the collision rate +on initial probes may be much higher than this scheme was designed for. +Getting a hash code as fat as Py_ssize_t is the only real cure. But in +practice, this probably won't make a lick of difference for many years (at +which point everyone will have terabytes of RAM on 64-bit boxes). */ -/* forward declarations */ -static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr); -static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr); -static Py_ssize_t -lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr); -static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr); - -static int dictresize(PyDictObject *mp, Py_ssize_t minused); - -static PyObject* dict_iter(PyDictObject *dict); - -/*Global counter used to set ma_version_tag field of dictionary. - * It is incremented each time that a dictionary is created and each - * time that a dictionary is modified. */ -static uint64_t pydict_global_version = 0; - -#define DICT_NEXT_VERSION() (++pydict_global_version) - -/* 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; +/* Object used as dummy key to fill deleted entries */ +static PyObject *dummy = NULL; /* Initialized by first call to newPyDictObject() */ -#include "clinic/dictobject.c.h" - -int -PyDict_ClearFreeList(void) +#ifdef Py_REF_DEBUG +PyObject * +_PyDict_Dummy(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]); - } - return ret; + return dummy; } +#endif -/* Print summary info about the state of the optimized allocator */ -void -_PyDict_DebugMallocStats(FILE *out) -{ - _PyDebugAllocatorStats(out, - "free PyDictObject", numfree, sizeof(PyDictObject)); -} +/* forward declarations */ +static PyDictEntry * +lookdict_string(PyDictObject *mp, PyObject *key, long hash); +#ifdef SHOW_CONVERSION_COUNTS +static long created = 0L; +static long converted = 0L; -void -_PyDict_Fini(void) +static void +show_counts(void) { - PyDict_ClearFreeList(); + fprintf(stderr, "created %ld string dicts\n", created); + fprintf(stderr, "converted %ld to normal dicts\n", converted); + fprintf(stderr, "%.2f%% conversion rate\n", (100.0*converted)/created); } - -#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 : DK_SIZE(dk) <= 0xffffffff ? \ - 4 : sizeof(int64_t)) -#else -#define DK_IXSIZE(dk) \ - (DK_SIZE(dk) <= 0xff ? \ - 1 : DK_SIZE(dk) <= 0xffff ? \ - 2 : sizeof(int32_t)) #endif -#define DK_ENTRIES(dk) \ - ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) - -#define DK_MASK(dk) (((dk)->dk_size)-1) -#define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) -static void free_keys_object(PyDictKeysObject *keys); +/* Debug statistic to compare allocations with reuse through the free list */ +#undef SHOW_ALLOC_COUNT +#ifdef SHOW_ALLOC_COUNT +static size_t count_alloc = 0; +static size_t count_reuse = 0; -static inline void -dictkeys_incref(PyDictKeysObject *dk) +static void +show_alloc(void) { - _Py_INC_REFTOTAL; - dk->dk_refcnt++; + fprintf(stderr, "Dict allocations: %" PY_FORMAT_SIZE_T "d\n", + count_alloc); + fprintf(stderr, "Dict reuse through freelist: %" PY_FORMAT_SIZE_T + "d\n", count_reuse); + fprintf(stderr, "%.2f%% reuse rate\n\n", + (100.0*count_reuse/(count_alloc+count_reuse))); } +#endif -static inline void -dictkeys_decref(PyDictKeysObject *dk) -{ - assert(dk->dk_refcnt > 0); - _Py_DEC_REFTOTAL; - if (--dk->dk_refcnt == 0) { - free_keys_object(dk); - } -} +/* Debug statistic to count GC tracking of dicts */ +#ifdef SHOW_TRACK_COUNT +static Py_ssize_t count_untracked = 0; +static Py_ssize_t count_tracked = 0; -/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ -static inline Py_ssize_t -dictkeys_get_index(PyDictKeysObject *keys, Py_ssize_t i) +static void +show_track(void) { - Py_ssize_t s = DK_SIZE(keys); - Py_ssize_t ix; - - if (s <= 0xff) { - int8_t *indices = (int8_t*)(keys->dk_indices); - ix = indices[i]; - } - else if (s <= 0xffff) { - int16_t *indices = (int16_t*)(keys->dk_indices); - ix = indices[i]; - } -#if SIZEOF_VOID_P > 4 - else if (s > 0xffffffff) { - int64_t *indices = (int64_t*)(keys->dk_indices); - ix = indices[i]; - } -#endif - else { - int32_t *indices = (int32_t*)(keys->dk_indices); - ix = indices[i]; - } - assert(ix >= DKIX_DUMMY); - return ix; + fprintf(stderr, "Dicts created: %" PY_FORMAT_SIZE_T "d\n", + count_tracked + count_untracked); + fprintf(stderr, "Dicts tracked by the GC: %" PY_FORMAT_SIZE_T + "d\n", count_tracked); + fprintf(stderr, "%.2f%% dict tracking rate\n\n", + (100.0*count_tracked/(count_untracked+count_tracked))); } - -/* write to indices. */ -static inline void -dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) -{ - Py_ssize_t s = DK_SIZE(keys); - - assert(ix >= DKIX_DUMMY); - - if (s <= 0xff) { - int8_t *indices = (int8_t*)(keys->dk_indices); - assert(ix <= 0x7f); - indices[i] = (char)ix; - } - else if (s <= 0xffff) { - int16_t *indices = (int16_t*)(keys->dk_indices); - assert(ix <= 0x7fff); - indices[i] = (int16_t)ix; - } -#if SIZEOF_VOID_P > 4 - else if (s > 0xffffffff) { - int64_t *indices = (int64_t*)(keys->dk_indices); - indices[i] = ix; - } #endif - else { - int32_t *indices = (int32_t*)(keys->dk_indices); - assert(ix <= 0x7fffffff); - indices[i] = (int32_t)ix; - } -} - - -/* USABLE_FRACTION is the maximum dictionary load. - * Increasing this ratio makes dictionaries more dense resulting in more - * collisions. Decreasing it improves sparseness at the expense of spreading - * indices over more cache lines and at the cost of total memory consumed. - * - * USABLE_FRACTION must obey the following: - * (0 < USABLE_FRACTION(n) < n) for all n >= 2 - * - * USABLE_FRACTION should be quick to calculate. - * Fractions around 1/2 to 2/3 seem to work well in practice. - */ -#define USABLE_FRACTION(n) (((n) << 1)/3) -/* ESTIMATE_SIZE is reverse function of USABLE_FRACTION. - * This can be used to reserve enough size to insert n entries without - * resizing. - */ -#define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) - -/* Alternative fraction that is otherwise close enough to 2n/3 to make - * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. - * 32 * 2/3 = 21, 32 * 5/8 = 20. - * Its advantage is that it is faster to compute on machines with slow division. - * #define USABLE_FRACTION(n) (((n) >> 1) + ((n) >> 2) - ((n) >> 3)) - */ - -/* GROWTH_RATE. Growth rate upon hitting maximum load. - * Currently set to used*3. - * This means that dicts double in size when growing without deletions, - * but have more head room when the number of deletions is on a par with the - * number of insertions. See also bpo-17563 and bpo-33205. - * - * GROWTH_RATE was set to used*4 up to version 3.2. - * GROWTH_RATE was set to used*2 in version 3.3.0 - * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. - */ -#define GROWTH_RATE(d) ((d)->ma_used*3) - -#define ENSURE_ALLOWS_DELETIONS(d) \ - if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ - (d)->ma_keys->dk_lookup = lookdict_unicode; \ - } -/* This immutable, empty PyDictKeysObject is used for PyDict_Clear() - * (which cannot fail and thus can do no allocation). - */ -static PyDictKeysObject empty_keys_struct = { - 1, /* dk_refcnt */ - 1, /* dk_size */ - lookdict_split, /* dk_lookup */ - 0, /* dk_usable (immutable) */ - 0, /* dk_nentries */ - {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, - DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ -}; - -static PyObject *empty_values[1] = { NULL }; +/* Initialization macros. + There are two ways to create a dict: PyDict_New() is the main C API + function, and the tp_new slot maps to dict_new(). In the latter case we + can save a little time over what PyDict_New does because it's guaranteed + that the PyDictObject struct is already zeroed out. + Everyone except dict_new() should use EMPTY_TO_MINSIZE (unless they have + an excellent reason not to). +*/ -#define Py_EMPTY_KEYS &empty_keys_struct +#define INIT_NONZERO_DICT_SLOTS(mp) do { \ + (mp)->ma_table = (mp)->ma_smalltable; \ + (mp)->ma_mask = PyDict_MINSIZE - 1; \ + } while(0) -/* Uncomment to check the dict content in _PyDict_CheckConsistency() */ -/* #define DEBUG_PYDICT */ +#define EMPTY_TO_MINSIZE(mp) do { \ + memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \ + (mp)->ma_used = (mp)->ma_fill = 0; \ + INIT_NONZERO_DICT_SLOTS(mp); \ + } while(0) -#ifdef DEBUG_PYDICT -# define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) -#else -# define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) +/* Dictionary reuse scheme to save calls to malloc, free, and memset */ +#ifndef PyDict_MAXFREELIST +#define PyDict_MAXFREELIST 80 #endif +static PyDictObject *free_list[PyDict_MAXFREELIST]; +static int numfree = 0; - -int -_PyDict_CheckConsistency(PyObject *op, int check_content) +void +PyDict_Fini(void) { -#define CHECK(expr) \ - do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) - - assert(op != NULL); - CHECK(PyDict_Check(op)); - PyDictObject *mp = (PyDictObject *)op; - - PyDictKeysObject *keys = mp->ma_keys; - int splitted = _PyDict_HasSplitTable(mp); - Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); - - CHECK(0 <= mp->ma_used && mp->ma_used <= usable); - CHECK(IS_POWER_OF_2(keys->dk_size)); - CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); - CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); - CHECK(keys->dk_usable + keys->dk_nentries <= usable); - - if (!splitted) { - /* combined table */ - CHECK(keys->dk_refcnt == 1); - } - - if (check_content) { - PyDictKeyEntry *entries = DK_ENTRIES(keys); - Py_ssize_t i; - - for (i=0; i < keys->dk_size; i++) { - Py_ssize_t ix = dictkeys_get_index(keys, i); - CHECK(DKIX_DUMMY <= ix && ix <= usable); - } - - for (i=0; i < usable; i++) { - PyDictKeyEntry *entry = &entries[i]; - PyObject *key = entry->me_key; - - if (key != NULL) { - if (PyUnicode_CheckExact(key)) { - Py_hash_t hash = ((PyASCIIObject *)key)->hash; - CHECK(hash != -1); - CHECK(entry->me_hash == hash); - } - else { - /* test_dict fails if PyObject_Hash() is called again */ - CHECK(entry->me_hash != -1); - } - if (!splitted) { - CHECK(entry->me_value != NULL); - } - } - - if (splitted) { - CHECK(entry->me_value == NULL); - } - } + PyDictObject *op; - if (splitted) { - /* splitted table */ - for (i=0; i < mp->ma_used; i++) { - CHECK(mp->ma_values[i] != NULL); - } - } + while (numfree) { + op = free_list[--numfree]; + assert(PyDict_CheckExact(op)); + PyObject_GC_Del(op); } - return 1; - -#undef CHECK } - -static PyDictKeysObject *new_keys_object(Py_ssize_t size) +PyObject * +PyDict_New(void) { - PyDictKeysObject *dk; - Py_ssize_t es, usable; - - assert(size >= PyDict_MINSIZE); - assert(IS_POWER_OF_2(size)); - - usable = USABLE_FRACTION(size); - if (size <= 0xff) { - es = 1; - } - else if (size <= 0xffff) { - es = 2; - } -#if SIZEOF_VOID_P > 4 - else if (size <= 0xffffffff) { - es = 4; - } -#endif - else { - es = sizeof(Py_ssize_t); - } - - if (size == PyDict_MINSIZE && numfreekeys > 0) { - dk = keys_free_list[--numfreekeys]; - } - else { - dk = PyObject_MALLOC(sizeof(PyDictKeysObject) - + es * size - + sizeof(PyDictKeyEntry) * usable); - if (dk == NULL) { - PyErr_NoMemory(); + register PyDictObject *mp; + if (dummy == NULL) { /* Auto-initialize dummy */ + dummy = PyString_FromString("<dummy key>"); + if (dummy == NULL) return NULL; - } - } - _Py_INC_REFTOTAL; - dk->dk_refcnt = 1; - dk->dk_size = size; - 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) -{ - PyDictKeyEntry *entries = DK_ENTRIES(keys); - Py_ssize_t i, n; - 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; - return; +#ifdef SHOW_CONVERSION_COUNTS + Py_AtExit(show_counts); +#endif +#ifdef SHOW_ALLOC_COUNT + Py_AtExit(show_alloc); +#endif +#ifdef SHOW_TRACK_COUNT + Py_AtExit(show_track); +#endif } - 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 */ -static PyObject * -new_dict(PyDictKeysObject *keys, PyObject **values) -{ - PyDictObject *mp; - assert(keys != NULL); if (numfree) { mp = free_list[--numfree]; assert (mp != NULL); assert (Py_TYPE(mp) == &PyDict_Type); _Py_NewReference((PyObject *)mp); - } - else { + if (mp->ma_fill) { + EMPTY_TO_MINSIZE(mp); + } else { + /* At least set ma_table and ma_mask; these are wrong + if an empty but presized dict is added to freelist */ + INIT_NONZERO_DICT_SLOTS(mp); + } + assert (mp->ma_used == 0); + assert (mp->ma_table == mp->ma_smalltable); + assert (mp->ma_mask == PyDict_MINSIZE - 1); +#ifdef SHOW_ALLOC_COUNT + count_reuse++; +#endif + } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); - if (mp == NULL) { - dictkeys_decref(keys); - if (values != empty_values) { - free_values(values); - } + if (mp == NULL) return NULL; - } + EMPTY_TO_MINSIZE(mp); +#ifdef SHOW_ALLOC_COUNT + count_alloc++; +#endif } - mp->ma_keys = keys; - mp->ma_values = values; - mp->ma_used = 0; - mp->ma_version_tag = DICT_NEXT_VERSION(); - ASSERT_CONSISTENT(mp); + mp->ma_lookup = lookdict_string; +#ifdef SHOW_TRACK_COUNT + count_untracked++; +#endif +#ifdef SHOW_CONVERSION_COUNTS + ++created; +#endif return (PyObject *)mp; } -/* Consumes a reference to the keys object */ -static PyObject * -new_dict_with_shared_keys(PyDictKeysObject *keys) -{ - PyObject **values; - Py_ssize_t i, size; - - size = USABLE_FRACTION(DK_SIZE(keys)); - values = new_values(size); - if (values == NULL) { - dictkeys_decref(keys); - return PyErr_NoMemory(); - } - for (i = 0; i < size; i++) { - values[i] = NULL; - } - return new_dict(keys, values); -} - - -static PyObject * -clone_combined_dict(PyDictObject *orig) -{ - assert(PyDict_CheckExact(orig)); - assert(orig->ma_values == NULL); - assert(orig->ma_keys->dk_refcnt == 1); - - Py_ssize_t keys_size = _PyDict_KeysSize(orig->ma_keys); - PyDictKeysObject *keys = PyObject_Malloc(keys_size); - if (keys == NULL) { - PyErr_NoMemory(); - return NULL; - } - - memcpy(keys, orig->ma_keys, keys_size); - - /* After copying key/value pairs, we need to incref all - keys and values and they are about to be co-owned by a - new dict object. */ - PyDictKeyEntry *ep0 = DK_ENTRIES(keys); - Py_ssize_t n = keys->dk_nentries; - for (Py_ssize_t i = 0; i < n; i++) { - PyDictKeyEntry *entry = &ep0[i]; - PyObject *value = entry->me_value; - if (value != NULL) { - Py_INCREF(value); - Py_INCREF(entry->me_key); - } - } - - PyDictObject *new = (PyDictObject *)new_dict(keys, NULL); - if (new == NULL) { - /* In case of an error, `new_dict()` takes care of - cleaning up `keys`. */ - return NULL; - } - new->ma_used = orig->ma_used; - ASSERT_CONSISTENT(new); - if (_PyObject_GC_IS_TRACKED(orig)) { - /* Maintain tracking. */ - _PyObject_GC_TRACK(new); - } - - /* Since we copied the keys table we now have an extra reference - in the system. Manually call _Py_INC_REFTOTAL to signal that - we have it now; calling dictkeys_incref would be an error as - keys->dk_refcnt is already set to 1 (after memcpy). */ - _Py_INC_REFTOTAL; - - return (PyObject *)new; -} - -PyObject * -PyDict_New(void) -{ - dictkeys_incref(Py_EMPTY_KEYS); - return new_dict(Py_EMPTY_KEYS, empty_values); -} - -/* 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) -{ - size_t mask = DK_MASK(k); - size_t perturb = (size_t)hash; - size_t i = (size_t)hash & mask; - - for (;;) { - Py_ssize_t ix = dictkeys_get_index(k, i); - if (ix == index) { - return i; - } - if (ix == DKIX_EMPTY) { - return DKIX_EMPTY; - } - perturb >>= PERTURB_SHIFT; - i = mask & (i*5 + perturb + 1); - } - Py_UNREACHABLE(); -} - /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. @@ -735,217 +303,168 @@ probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. -The details in this version are due to Tim Peters, building on many past +(The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and -Christian Tismer. - -lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a -comparison raises an exception. -lookdict_unicode() below is specialized to string keys, comparison of which can -never raise an exception; that function can never return DKIX_ERROR when key -is string. Otherwise, it falls back to lookdict(). -lookdict_unicode_nodummy is further specialized for string keys that cannot be -the <dummy> value. -For both, when the key isn't found a DKIX_EMPTY is returned. +Christian Tismer). + +lookdict() is general-purpose, and may return NULL if (and only if) a +comparison raises an exception (this was new in Python 2.5). +lookdict_string() below is specialized to string keys, comparison of which can +never raise an exception; that function can never return NULL. For both, when +the key isn't found a PyDictEntry* is returned for which the me_value field is +NULL; this is the slot in the dict at which the key would have been found, and +the caller can (if it wishes) add the <key, value> pair to the returned +PyDictEntry*. */ -static Py_ssize_t _Py_HOT_FUNCTION -lookdict(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr) -{ - size_t i, mask, perturb; - PyDictKeysObject *dk; - PyDictKeyEntry *ep0; - -top: - dk = mp->ma_keys; - ep0 = DK_ENTRIES(dk); - mask = DK_MASK(dk); - perturb = hash; +static PyDictEntry * +lookdict(PyDictObject *mp, PyObject *key, register long hash) +{ + register size_t i; + register size_t perturb; + register PyDictEntry *freeslot; + register size_t mask = (size_t)mp->ma_mask; + PyDictEntry *ep0 = mp->ma_table; + register PyDictEntry *ep; + register int cmp; + PyObject *startkey; + i = (size_t)hash & mask; + ep = &ep0[i]; + if (ep->me_key == NULL || ep->me_key == key) + return ep; - for (;;) { - Py_ssize_t ix = dictkeys_get_index(dk, i); - if (ix == DKIX_EMPTY) { - *value_addr = NULL; - return ix; - } - if (ix >= 0) { - PyDictKeyEntry *ep = &ep0[ix]; - assert(ep->me_key != NULL); - if (ep->me_key == key) { - *value_addr = ep->me_value; - return ix; + if (ep->me_key == dummy) + freeslot = ep; + else { + if (ep->me_hash == hash) { + startkey = ep->me_key; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (ep0 == mp->ma_table && ep->me_key == startkey) { + if (cmp > 0) + return ep; } - if (ep->me_hash == hash) { - PyObject *startkey = ep->me_key; - Py_INCREF(startkey); - int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); - Py_DECREF(startkey); - if (cmp < 0) { - *value_addr = NULL; - return DKIX_ERROR; - } - if (dk == mp->ma_keys && ep->me_key == startkey) { - if (cmp > 0) { - *value_addr = ep->me_value; - return ix; - } - } - else { - /* The dict was mutated, restart */ - goto top; - } + else { + /* The compare did major nasty stuff to the + * dict: start over. + * XXX A clever adversary could prevent this + * XXX from terminating. + */ + return lookdict(mp, key, hash); } } - perturb >>= PERTURB_SHIFT; - i = (i*5 + perturb + 1) & mask; - } - Py_UNREACHABLE(); -} - -/* Specialized version for string-only keys */ -static Py_ssize_t _Py_HOT_FUNCTION -lookdict_unicode(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr) -{ - 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); - } - - PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); - size_t mask = DK_MASK(mp->ma_keys); - size_t perturb = (size_t)hash; - size_t i = (size_t)hash & mask; - - for (;;) { - Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); - if (ix == DKIX_EMPTY) { - *value_addr = NULL; - return DKIX_EMPTY; - } - if (ix >= 0) { - PyDictKeyEntry *ep = &ep0[ix]; - assert(ep->me_key != NULL); - assert(PyUnicode_CheckExact(ep->me_key)); - if (ep->me_key == key || - (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { - *value_addr = ep->me_value; - return ix; + freeslot = NULL; + } + + /* In the loop, me_key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + if (ep->me_key == NULL) + return freeslot == NULL ? ep : freeslot; + if (ep->me_key == key) + return ep; + if (ep->me_hash == hash && ep->me_key != dummy) { + startkey = ep->me_key; + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (ep0 == mp->ma_table && ep->me_key == startkey) { + if (cmp > 0) + return ep; + } + else { + /* The compare did major nasty stuff to the + * dict: start over. + * XXX A clever adversary could prevent this + * XXX from terminating. + */ + return lookdict(mp, key, hash); } } - perturb >>= PERTURB_SHIFT; - i = mask & (i*5 + perturb + 1); - } - Py_UNREACHABLE(); -} - -/* Faster version of lookdict_unicode when it is known that no <dummy> keys - * will be present. */ -static Py_ssize_t _Py_HOT_FUNCTION -lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr) -{ - 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); - } - - PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); - size_t mask = DK_MASK(mp->ma_keys); - size_t perturb = (size_t)hash; - size_t i = (size_t)hash & mask; - - for (;;) { - Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); - assert (ix != DKIX_DUMMY); - if (ix == DKIX_EMPTY) { - *value_addr = NULL; - return DKIX_EMPTY; - } - PyDictKeyEntry *ep = &ep0[ix]; - assert(ep->me_key != NULL); - assert(PyUnicode_CheckExact(ep->me_key)); - if (ep->me_key == key || - (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { - *value_addr = ep->me_value; - return ix; - } - perturb >>= PERTURB_SHIFT; - i = mask & (i*5 + perturb + 1); + else if (ep->me_key == dummy && freeslot == NULL) + freeslot = ep; } - Py_UNREACHABLE(); + assert(0); /* NOT REACHED */ + return 0; } -/* Version of lookdict for split tables. - * All split tables and only split tables use this lookup function. - * Split tables only contain unicode keys and no dummy keys, - * so algorithm is the same as lookdict_unicode_nodummy. +/* + * Hacked up version of lookdict which can assume keys are always strings; + * this assumption allows testing for errors during PyObject_RichCompareBool() + * to be dropped; string-string comparisons never raise exceptions. This also + * means we don't need to go through PyObject_RichCompareBool(); we can always + * use _PyString_Eq() directly. + * + * This is valuable because dicts with only string keys are very common. */ -static Py_ssize_t _Py_HOT_FUNCTION -lookdict_split(PyDictObject *mp, PyObject *key, - Py_hash_t hash, PyObject **value_addr) +static PyDictEntry * +lookdict_string(PyDictObject *mp, PyObject *key, register long hash) { - /* mp must split table */ - assert(mp->ma_values != NULL); - if (!PyUnicode_CheckExact(key)) { - Py_ssize_t ix = lookdict(mp, key, hash, value_addr); - if (ix >= 0) { - *value_addr = mp->ma_values[ix]; - } - return ix; - } - - PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); - size_t mask = DK_MASK(mp->ma_keys); - size_t perturb = (size_t)hash; - size_t i = (size_t)hash & mask; + register size_t i; + register size_t perturb; + register PyDictEntry *freeslot; + register size_t mask = (size_t)mp->ma_mask; + PyDictEntry *ep0 = mp->ma_table; + register PyDictEntry *ep; - for (;;) { - Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); - assert (ix != DKIX_DUMMY); - if (ix == DKIX_EMPTY) { - *value_addr = NULL; - return DKIX_EMPTY; - } - PyDictKeyEntry *ep = &ep0[ix]; - assert(ep->me_key != NULL); - assert(PyUnicode_CheckExact(ep->me_key)); - if (ep->me_key == key || - (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { - *value_addr = mp->ma_values[ix]; - return ix; - } - perturb >>= PERTURB_SHIFT; - i = mask & (i*5 + perturb + 1); + /* Make sure this function doesn't have to handle non-string keys, + including subclasses of str; e.g., one reason to subclass + strings is to override __eq__, and for speed we don't cater to + that here. */ + if (!PyString_CheckExact(key)) { +#ifdef SHOW_CONVERSION_COUNTS + ++converted; +#endif + mp->ma_lookup = lookdict; + return lookdict(mp, key, hash); } - Py_UNREACHABLE(); + i = hash & mask; + ep = &ep0[i]; + if (ep->me_key == NULL || ep->me_key == key) + return ep; + if (ep->me_key == dummy) + freeslot = ep; + else { + if (ep->me_hash == hash && _PyString_Eq(ep->me_key, key)) + return ep; + freeslot = NULL; + } + + /* In the loop, me_key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + if (ep->me_key == NULL) + return freeslot == NULL ? ep : freeslot; + if (ep->me_key == key + || (ep->me_hash == hash + && ep->me_key != dummy + && _PyString_Eq(ep->me_key, key))) + return ep; + if (ep->me_key == dummy && freeslot == NULL) + freeslot = ep; + } + assert(0); /* NOT REACHED */ + return 0; } -int -_PyDict_HasOnlyStringKeys(PyObject *dict) -{ - Py_ssize_t pos = 0; - PyObject *key, *value; - assert(PyDict_Check(dict)); - /* Shortcut */ - if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) - return 1; - while (PyDict_Next(dict, &pos, &key, &value)) - if (!PyUnicode_Check(key)) - return 0; - return 1; -} +#ifdef SHOW_TRACK_COUNT +#define INCREASE_TRACK_COUNT \ + (count_tracked++, count_untracked--); +#define DECREASE_TRACK_COUNT \ + (count_tracked--, count_untracked++); +#else +#define INCREASE_TRACK_COUNT +#define DECREASE_TRACK_COUNT +#endif #define MAINTAIN_TRACKING(mp, key, value) \ do { \ @@ -953,6 +472,7 @@ _PyDict_HasOnlyStringKeys(PyObject *dict) if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ + INCREASE_TRACK_COUNT \ } \ } \ } while(0) @@ -962,228 +482,132 @@ _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; - Py_ssize_t i, numentries; - PyDictKeyEntry *ep0; + Py_ssize_t mask, i; + PyDictEntry *ep; 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 < numentries; i++) { - if ((value = mp->ma_values[i]) == NULL) - continue; - if (_PyObject_GC_MAY_BE_TRACKED(value)) { - assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); - return; - } - } - } - else { - for (i = 0; i < numentries; i++) { - if ((value = ep0[i].me_value) == NULL) - continue; - if (_PyObject_GC_MAY_BE_TRACKED(value) || - _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) - return; - } - } + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0; i <= mask; i++) { + if ((value = ep[i].me_value) == NULL) + continue; + if (_PyObject_GC_MAY_BE_TRACKED(value) || + _PyObject_GC_MAY_BE_TRACKED(ep[i].me_key)) + return; + } + DECREASE_TRACK_COUNT _PyObject_GC_UNTRACK(op); } -/* Internal function to find slot for an item from its hash - when it is known that the key is not present in the dict. - - The dict must be combined. */ -static Py_ssize_t -find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) -{ - assert(keys != NULL); - - const size_t mask = DK_MASK(keys); - size_t i = hash & mask; - Py_ssize_t ix = dictkeys_get_index(keys, i); - for (size_t perturb = hash; ix >= 0;) { - perturb >>= PERTURB_SHIFT; - i = (i*5 + perturb + 1) & mask; - ix = dictkeys_get_index(keys, i); - } - return i; -} - -static int -insertion_resize(PyDictObject *mp) -{ - return dictresize(mp, GROWTH_RATE(mp)); -} - /* -Internal routine to insert a new item into the table. -Used both by the internal resize routine and by the public insert routine. -Returns -1 if an error occurred, or 0 on success. +Internal routine to insert a new item into the table when you have entry object. +Used by insertdict. */ static int -insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) +insertdict_by_entry(register PyDictObject *mp, PyObject *key, long hash, + PyDictEntry *ep, PyObject *value) { PyObject *old_value; - PyDictKeyEntry *ep; - - Py_INCREF(key); - Py_INCREF(value); - if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { - if (insertion_resize(mp) < 0) - goto Fail; - } - Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value); - if (ix == DKIX_ERROR) - goto Fail; - - assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); 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 (ep->me_value != NULL) { + old_value = ep->me_value; + ep->me_value = value; + Py_DECREF(old_value); /* which **CAN** re-enter */ + Py_DECREF(key); } - - if (ix == DKIX_EMPTY) { - /* Insert into new slot. */ - assert(old_value == NULL); - if (mp->ma_keys->dk_usable <= 0) { - /* Need to resize. */ - if (insertion_resize(mp) < 0) - goto Fail; - } - Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); - ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; - dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); - 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; - } + else { + if (ep->me_key == NULL) + mp->ma_fill++; else { - ep->me_value = value; + assert(ep->me_key == dummy); + Py_DECREF(dummy); } + ep->me_key = key; + ep->me_hash = (Py_ssize_t)hash; + ep->me_value = value; mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); - mp->ma_keys->dk_usable--; - mp->ma_keys->dk_nentries++; - assert(mp->ma_keys->dk_usable >= 0); - ASSERT_CONSISTENT(mp); - return 0; } - - if (old_value != value) { - if (_PyDict_HasSplitTable(mp)) { - mp->ma_values[ix] = value; - if (old_value == NULL) { - /* pending state */ - assert(ix == mp->ma_used); - mp->ma_used++; - } - } - else { - assert(old_value != NULL); - DK_ENTRIES(mp->ma_keys)[ix].me_value = value; - } - mp->ma_version_tag = DICT_NEXT_VERSION(); - } - Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ - ASSERT_CONSISTENT(mp); - Py_DECREF(key); return 0; - -Fail: - Py_DECREF(value); - Py_DECREF(key); - return -1; } -// Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS. + +/* +Internal routine to insert a new item into the table. +Used both by the internal resize routine and by the public insert routine. +Eats a reference to key and one to value. +Returns -1 if an error occurred, or 0 on success. +*/ static int -insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, - PyObject *value) +insertdict(register PyDictObject *mp, PyObject *key, long hash, PyObject *value) { - assert(mp->ma_keys == Py_EMPTY_KEYS); + register PyDictEntry *ep; - PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE); - if (newkeys == NULL) { + assert(mp->ma_lookup != NULL); + ep = mp->ma_lookup(mp, key, hash); + if (ep == NULL) { + Py_DECREF(key); + Py_DECREF(value); return -1; } - if (!PyUnicode_CheckExact(key)) { - newkeys->dk_lookup = lookdict; - } - dictkeys_decref(Py_EMPTY_KEYS); - mp->ma_keys = newkeys; - mp->ma_values = NULL; - - Py_INCREF(key); - Py_INCREF(value); - MAINTAIN_TRACKING(mp, key, value); - - size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1); - PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys); - dictkeys_set_index(mp->ma_keys, hashpos, 0); - ep->me_key = key; - ep->me_hash = hash; - ep->me_value = value; - mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); - mp->ma_keys->dk_usable--; - mp->ma_keys->dk_nentries++; - return 0; + return insertdict_by_entry(mp, key, hash, ep, value); } /* -Internal routine used by dictresize() to build a hashtable of entries. +Internal routine used by dictresize() to insert an item which is +known to be absent from the dict. This routine also assumes that +the dict contains no deleted entries. Besides the performance benefit, +using insertdict() in dictresize() is dangerous (SF bug #1456209). +Note that no refcounts are changed by this routine; if needed, the caller +is responsible for incref'ing `key` and `value`. */ static void -build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) +insertdict_clean(register PyDictObject *mp, PyObject *key, long hash, + PyObject *value) { - size_t mask = (size_t)DK_SIZE(keys) - 1; - for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { - Py_hash_t hash = ep->me_hash; - size_t i = hash & mask; - for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { - perturb >>= PERTURB_SHIFT; - i = mask & (i*5 + perturb + 1); - } - dictkeys_set_index(keys, i, ix); + register size_t i; + register size_t perturb; + register size_t mask = (size_t)mp->ma_mask; + PyDictEntry *ep0 = mp->ma_table; + register PyDictEntry *ep; + + MAINTAIN_TRACKING(mp, key, value); + i = hash & mask; + ep = &ep0[i]; + for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; } + assert(ep->me_value == NULL); + mp->ma_fill++; + ep->me_key = key; + ep->me_hash = (Py_ssize_t)hash; + ep->me_value = value; + mp->ma_used++; } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. -If a table is split (its keys and hashes are shared, its values are not), -then the values are temporarily copied into the table, it is resized as -a combined table, then the me_value slots in the old table are NULLed out. -After resizing a table is always combined, -but can be resplit by make_keys_shared(). */ static int -dictresize(PyDictObject *mp, Py_ssize_t minsize) +dictresize(PyDictObject *mp, Py_ssize_t minused) { - Py_ssize_t newsize, numentries; - PyDictKeysObject *oldkeys; - PyObject **oldvalues; - PyDictKeyEntry *oldentries, *newentries; + Py_ssize_t newsize; + PyDictEntry *oldtable, *newtable, *ep; + Py_ssize_t i; + int is_oldtable_malloced; + PyDictEntry small_copy[PyDict_MINSIZE]; + + assert(minused >= 0); /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; - newsize < minsize && newsize > 0; + newsize <= minused && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { @@ -1191,155 +615,83 @@ dictresize(PyDictObject *mp, Py_ssize_t minsize) return -1; } - oldkeys = mp->ma_keys; + /* Get space for a new table. */ + oldtable = mp->ma_table; + assert(oldtable != NULL); + is_oldtable_malloced = oldtable != mp->ma_smalltable; - /* NOTE: Current odict checks mp->ma_keys to detect resize happen. - * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. - * TODO: Try reusing oldkeys when reimplement odict. - */ - - /* Allocate a new table. */ - mp->ma_keys = new_keys_object(newsize); - if (mp->ma_keys == NULL) { - mp->ma_keys = oldkeys; - return -1; - } - // New table must be large enough. - assert(mp->ma_keys->dk_usable >= mp->ma_used); - if (oldkeys->dk_lookup == lookdict) - mp->ma_keys->dk_lookup = lookdict; - - numentries = mp->ma_used; - oldentries = DK_ENTRIES(oldkeys); - newentries = DK_ENTRIES(mp->ma_keys); - oldvalues = mp->ma_values; - if (oldvalues != NULL) { - /* Convert split table into new combined table. - * We must incref keys; we can transfer values. - * 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]; - 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]; - } - - dictkeys_decref(oldkeys); - mp->ma_values = NULL; - if (oldvalues != empty_values) { - free_values(oldvalues); + if (newsize == PyDict_MINSIZE) { + /* A large table is shrinking, or we can't get any smaller. */ + newtable = mp->ma_smalltable; + if (newtable == oldtable) { + if (mp->ma_fill == mp->ma_used) { + /* No dummies, so no point doing anything. */ + return 0; + } + /* We're not going to resize it, but rebuild the + table anyway to purge old dummy entries. + Subtle: This is *necessary* if fill==size, + as lookdict needs at least one virgin slot to + terminate failing searches. If fill < size, it's + merely desirable, as dummies slow searches. */ + assert(mp->ma_fill > mp->ma_used); + memcpy(small_copy, oldtable, sizeof(small_copy)); + oldtable = small_copy; } } - else { // combined table. - if (oldkeys->dk_nentries == numentries) { - memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry)); - } - else { - PyDictKeyEntry *ep = oldentries; - for (Py_ssize_t i = 0; i < numentries; i++) { - while (ep->me_value == NULL) - ep++; - newentries[i] = *ep++; - } + else { + newtable = PyMem_NEW(PyDictEntry, newsize); + if (newtable == NULL) { + PyErr_NoMemory(); + return -1; } + } - assert(oldkeys->dk_lookup != lookdict_split); - assert(oldkeys->dk_refcnt == 1); - if (oldkeys->dk_size == PyDict_MINSIZE && - numfreekeys < PyDict_MAXFREELIST) { - _Py_DEC_REFTOTAL; - keys_free_list[numfreekeys++] = oldkeys; + /* Make the dict empty, using the new table. */ + assert(newtable != oldtable); + mp->ma_table = newtable; + mp->ma_mask = newsize - 1; + memset(newtable, 0, sizeof(PyDictEntry) * newsize); + mp->ma_used = 0; + i = mp->ma_fill; + mp->ma_fill = 0; + + /* Copy the data over; this is refcount-neutral for active entries; + dummy entries aren't copied over, of course */ + for (ep = oldtable; i > 0; ep++) { + if (ep->me_value != NULL) { /* active entry */ + --i; + insertdict_clean(mp, ep->me_key, (long)ep->me_hash, + ep->me_value); } - else { - _Py_DEC_REFTOTAL; - PyObject_FREE(oldkeys); + else if (ep->me_key != NULL) { /* dummy entry */ + --i; + assert(ep->me_key == dummy); + Py_DECREF(ep->me_key); } + /* else key == value == NULL: nothing to do */ } - build_indices(mp->ma_keys, newentries, numentries); - mp->ma_keys->dk_usable -= numentries; - mp->ma_keys->dk_nentries = numentries; + if (is_oldtable_malloced) + PyMem_DEL(oldtable); return 0; } -/* Returns NULL if unable to split table. - * A NULL return does not necessarily indicate an error */ -static PyDictKeysObject * -make_keys_shared(PyObject *op) -{ - Py_ssize_t i; - Py_ssize_t size; - PyDictObject *mp = (PyDictObject *)op; - - if (!PyDict_CheckExact(op)) - return NULL; - if (!_PyDict_HasSplitTable(mp)) { - PyDictKeyEntry *ep0; - PyObject **values; - assert(mp->ma_keys->dk_refcnt == 1); - if (mp->ma_keys->dk_lookup == lookdict) { - return NULL; - } - else if (mp->ma_keys->dk_lookup == lookdict_unicode) { - /* Remove dummy keys */ - if (dictresize(mp, DK_SIZE(mp->ma_keys))) - return NULL; - } - assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); - /* Copy values into a new array */ - ep0 = DK_ENTRIES(mp->ma_keys); - size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); - values = new_values(size); - if (values == NULL) { - PyErr_SetString(PyExc_MemoryError, - "Not enough memory to allocate new values array"); - return NULL; - } - for (i = 0; i < size; i++) { - values[i] = ep0[i].me_value; - ep0[i].me_value = NULL; - } - mp->ma_keys->dk_lookup = lookdict_split; - mp->ma_values = values; - } - dictkeys_incref(mp->ma_keys); - return mp->ma_keys; -} +/* Create a new dictionary pre-sized to hold an estimated number of elements. + Underestimates are okay because the dictionary will resize as necessary. + Overestimates just mean the dictionary will be more sparse than usual. +*/ PyObject * _PyDict_NewPresized(Py_ssize_t minused) { - const Py_ssize_t max_presize = 128 * 1024; - Py_ssize_t newsize; - PyDictKeysObject *new_keys; - - if (minused <= USABLE_FRACTION(PyDict_MINSIZE)) { - return PyDict_New(); - } - /* There are no strict guarantee that returned dict can contain minused - * items without resize. So we create medium size dict instead of very - * large dict or MemoryError. - */ - if (minused > USABLE_FRACTION(max_presize)) { - newsize = max_presize; - } - else { - Py_ssize_t minsize = ESTIMATE_SIZE(minused); - newsize = PyDict_MINSIZE*2; - while (newsize < minsize) { - newsize <<= 1; - } - } - assert(IS_POWER_OF_2(newsize)); + PyObject *op = PyDict_New(); - new_keys = new_keys_object(newsize); - if (new_keys == NULL) + if (minused>5 && op != NULL && dictresize((PyDictObject *)op, minused) == -1) { + Py_DECREF(op); return NULL; - return new_dict(new_keys, NULL); + } + return op; } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors @@ -1355,16 +707,14 @@ _PyDict_NewPresized(Py_ssize_t minused) PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { - Py_hash_t hash; - Py_ssize_t ix; + long hash; PyDictObject *mp = (PyDictObject *)op; + PyDictEntry *ep; PyThreadState *tstate; - PyObject *value; - if (!PyDict_Check(op)) return NULL; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { @@ -1376,50 +726,27 @@ PyDict_GetItem(PyObject *op, PyObject *key) /* We can arrive here with a NULL tstate during initialization: try running "python -Wi" for an example related to string interning. Let's just hope that no exception occurs then... This must be - _PyThreadState_GET() and not PyThreadState_Get() because the latter - abort Python if tstate is NULL. */ - tstate = _PyThreadState_GET(); + _PyThreadState_Current and not PyThreadState_GET() because in debug + mode, the latter complains if tstate is NULL. */ + tstate = _PyThreadState_Current; if (tstate != NULL && tstate->curexc_type != NULL) { /* preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb); - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); + ep = (mp->ma_lookup)(mp, key, hash); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); - if (ix < 0) + if (ep == NULL) return NULL; } else { - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix < 0) { + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) { PyErr_Clear(); return NULL; } } - return value; -} - -/* Same as PyDict_GetItemWithError() but with hash supplied by caller. - This returns NULL *with* an exception set if an exception occurred. - It returns NULL *without* an exception set if the key wasn't present. -*/ -PyObject * -_PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) -{ - Py_ssize_t ix; - PyDictObject *mp = (PyDictObject *)op; - PyObject *value; - - if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); - return NULL; - } - - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix < 0) { - return NULL; - } - return value; + return ep->me_value; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. @@ -1427,19 +754,17 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) It returns NULL *without* an exception set if the key wasn't present. */ PyObject * -PyDict_GetItemWithError(PyObject *op, PyObject *key) +_PyDict_GetItemWithError(PyObject *op, PyObject *key) { - Py_ssize_t ix; - Py_hash_t hash; - PyDictObject*mp = (PyDictObject *)op; - PyObject *value; - + long hash; + PyDictObject *mp = (PyDictObject *)op; + PyDictEntry *ep; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { @@ -1447,69 +772,50 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key) } } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix < 0) - return NULL; - return value; -} - -PyObject * -_PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return NULL; - return PyDict_GetItemWithError(dp, kv); -} - -PyObject * -_PyDict_GetItemStringWithError(PyObject *v, const char *key) -{ - PyObject *kv, *rv; - kv = PyUnicode_FromString(key); - if (kv == NULL) { + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) { return NULL; } - rv = PyDict_GetItemWithError(v, kv); - Py_DECREF(kv); - return rv; + return ep->me_value; } -/* Fast version of global value lookup (LOAD_GLOBAL). - * Lookup in globals, then builtins. - * - * Raise an exception and return NULL if an error occurred (ex: computing the - * key hash failed, key comparison failed, ...). Return NULL if the key doesn't - * exist. Return the value if the key exists. - */ -PyObject * -_PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) +static int +dict_set_item_by_hash_or_entry(register PyObject *op, PyObject *key, + long hash, PyDictEntry *ep, PyObject *value) { - Py_ssize_t ix; - Py_hash_t hash; - PyObject *value; + register PyDictObject *mp; + register Py_ssize_t n_used; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) - { - hash = PyObject_Hash(key); - if (hash == -1) - return NULL; + mp = (PyDictObject *)op; + assert(mp->ma_fill <= mp->ma_mask); /* at least one empty slot */ + n_used = mp->ma_used; + Py_INCREF(value); + Py_INCREF(key); + if (ep == NULL) { + if (insertdict(mp, key, hash, value) != 0) + return -1; } - - /* namespace 1: globals */ - ix = globals->ma_keys->dk_lookup(globals, key, hash, &value); - if (ix == DKIX_ERROR) - return NULL; - if (ix != DKIX_EMPTY && value != NULL) - return value; - - /* namespace 2: builtins */ - ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value); - if (ix < 0) - return NULL; - return value; + else { + if (insertdict_by_entry(mp, key, hash, ep, value) != 0) + return -1; + } + /* If we added a key, we can safely resize. Otherwise just return! + * If fill >= 2/3 size, adjust size. Normally, this doubles or + * quaduples the size, but it's also possible for the dict to shrink + * (if ma_fill is much larger than ma_used, meaning a lot of dict + * keys have been * deleted). + * + * Quadrupling the size improves average dictionary sparseness + * (reducing collisions) at the cost of some memory and iteration + * speed (which loops over every possible entry). It also halves + * the number of expensive resize operations in a growing dictionary. + * + * Very large dictionaries (over 50K items) use doubling instead. + * This may help applications with severe memory constraints. + */ + if (!(mp->ma_used > n_used && mp->ma_fill*3 >= (mp->ma_mask+1)*2)) + return 0; + return dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used); } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the @@ -1519,140 +825,82 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) * remove them. */ int -PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) +PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value) { - PyDictObject *mp; - Py_hash_t hash; + register long hash; + if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); - mp = (PyDictObject *)op; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) - { + if (PyString_CheckExact(key)) { + hash = ((PyStringObject *)key)->ob_shash; + if (hash == -1) + hash = PyObject_Hash(key); + } + else { hash = PyObject_Hash(key); if (hash == -1) return -1; } - - if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(mp, key, hash, value); - } - /* insertdict() handles any resizing that might be necessary */ - return insertdict(mp, key, hash, value); -} - -int -_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, - Py_hash_t hash) -{ - PyDictObject *mp; - - if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); - return -1; - } - assert(key); - assert(value); - assert(hash != -1); - mp = (PyDictObject *)op; - - if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(mp, key, hash, value); - } - /* insertdict() handles any resizing that might be necessary */ - return insertdict(mp, key, hash, value); + return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value); } static int -delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, - PyObject *old_value) +delitem_common(PyDictObject *mp, PyDictEntry *ep) { - PyObject *old_key; - PyDictKeyEntry *ep; - - Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); - assert(hashpos >= 0); + PyObject *old_value, *old_key; - mp->ma_used--; - mp->ma_version_tag = DICT_NEXT_VERSION(); - ep = &DK_ENTRIES(mp->ma_keys)[ix]; - dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); - ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; - ep->me_key = NULL; + Py_INCREF(dummy); + ep->me_key = dummy; + old_value = ep->me_value; ep->me_value = NULL; - Py_DECREF(old_key); + mp->ma_used--; Py_DECREF(old_value); - - ASSERT_CONSISTENT(mp); + Py_DECREF(old_key); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { - Py_hash_t hash; - assert(key); - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { - hash = PyObject_Hash(key); - if (hash == -1) - return -1; - } - - return _PyDict_DelItem_KnownHash(op, key, hash); -} - -int -_PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) -{ - Py_ssize_t ix; - PyDictObject *mp; - PyObject *old_value; + register PyDictObject *mp; + register long hash; + register PyDictEntry *ep; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); - assert(hash != -1); + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } mp = (PyDictObject *)op; - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return -1; - if (ix == DKIX_EMPTY || old_value == NULL) { - _PyErr_SetKeyError(key); + if (ep->me_value == NULL) { + set_key_error(key); return -1; } - // Split table doesn't allow deletion. Combine it. - if (_PyDict_HasSplitTable(mp)) { - if (dictresize(mp, DK_SIZE(mp->ma_keys))) { - return -1; - } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); - assert(ix >= 0); - } - - return delitem_common(mp, hash, ix, old_value); + return delitem_common(mp, ep); } -/* This function promises that the predicate -> deletion sequence is atomic - * (i.e. protected by the GIL), assuming the predicate itself doesn't - * release the GIL. - */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { - Py_ssize_t hashpos, ix; - PyDictObject *mp; - Py_hash_t hash; - PyObject *old_value; + register PyDictObject *mp; + register long hash; + register PyDictEntry *ep; int res; if (!PyDict_Check(op)) { @@ -1660,124 +908,96 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key, return -1; } assert(key); - hash = PyObject_Hash(key); - if (hash == -1) - return -1; + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } mp = (PyDictObject *)op; - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return -1; - if (ix == DKIX_EMPTY || old_value == NULL) { - _PyErr_SetKeyError(key); + if (ep->me_value == NULL) { + set_key_error(key); return -1; } - - // Split table doesn't allow deletion. Combine it. - if (_PyDict_HasSplitTable(mp)) { - if (dictresize(mp, DK_SIZE(mp->ma_keys))) { - return -1; - } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); - assert(ix >= 0); - } - - res = predicate(old_value); + res = predicate(ep->me_value); if (res == -1) return -1; - - hashpos = lookdict_index(mp->ma_keys, hash, ix); - assert(hashpos >= 0); - if (res > 0) - return delitem_common(mp, hashpos, ix, old_value); + return delitem_common(mp, ep); else return 0; } - void PyDict_Clear(PyObject *op) { PyDictObject *mp; - PyDictKeysObject *oldkeys; - PyObject **oldvalues; + PyDictEntry *ep, *table; + int table_is_malloced; + Py_ssize_t fill; + PyDictEntry small_copy[PyDict_MINSIZE]; +#ifdef Py_DEBUG Py_ssize_t i, n; +#endif if (!PyDict_Check(op)) return; - mp = ((PyDictObject *)op); - oldkeys = mp->ma_keys; - oldvalues = mp->ma_values; - if (oldvalues == empty_values) - return; - /* Empty the dict... */ - dictkeys_incref(Py_EMPTY_KEYS); - mp->ma_keys = Py_EMPTY_KEYS; - mp->ma_values = empty_values; - mp->ma_used = 0; - mp->ma_version_tag = DICT_NEXT_VERSION(); - /* ...then clear the keys and values */ - if (oldvalues != NULL) { - n = oldkeys->dk_nentries; - for (i = 0; i < n; i++) - Py_CLEAR(oldvalues[i]); - free_values(oldvalues); - dictkeys_decref(oldkeys); - } - else { - assert(oldkeys->dk_refcnt == 1); - dictkeys_decref(oldkeys); - } - ASSERT_CONSISTENT(mp); -} + mp = (PyDictObject *)op; +#ifdef Py_DEBUG + n = mp->ma_mask + 1; + i = 0; +#endif -/* Internal version of PyDict_Next that returns a hash value in addition - * to the key and value. - * Return 1 on success, return 0 when the reached the end of the dictionary - * (or if op is not a dictionary) - */ -int -_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, - PyObject **pvalue, Py_hash_t *phash) -{ - Py_ssize_t i; - PyDictObject *mp; - PyDictKeyEntry *entry_ptr; - PyObject *value; + table = mp->ma_table; + assert(table != NULL); + table_is_malloced = table != mp->ma_smalltable; - if (!PyDict_Check(op)) - return 0; - mp = (PyDictObject *)op; - i = *ppos; - if (mp->ma_values) { - 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]; - assert(value != NULL); + /* This is delicate. During the process of clearing the dict, + * decrefs can cause the dict to mutate. To avoid fatal confusion + * (voice of experience), we have to make the dict empty before + * clearing the slots, and never refer to anything via mp->xxx while + * clearing. + */ + fill = mp->ma_fill; + if (table_is_malloced) + EMPTY_TO_MINSIZE(mp); + + else if (fill > 0) { + /* It's a small table with something that needs to be cleared. + * Afraid the only safe way is to copy the dict entries into + * another small table first. + */ + memcpy(small_copy, table, sizeof(small_copy)); + table = small_copy; + EMPTY_TO_MINSIZE(mp); } - else { - Py_ssize_t n = mp->ma_keys->dk_nentries; - if (i < 0 || i >= n) - return 0; - entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; - while (i < n && entry_ptr->me_value == NULL) { - entry_ptr++; - i++; + /* else it's a small table that's already empty */ + + /* Now we can finally clear things. If C had refcounts, we could + * assert that the refcount on table is 1 now, i.e. that this function + * has unique access to it, so decref side-effects can't alter it. + */ + for (ep = table; fill > 0; ++ep) { +#ifdef Py_DEBUG + assert(i < n); + ++i; +#endif + if (ep->me_key) { + --fill; + Py_DECREF(ep->me_key); + Py_XDECREF(ep->me_value); } - if (i >= n) - return 0; - value = entry_ptr->me_value; +#ifdef Py_DEBUG + else + assert(ep->me_value == NULL); +#endif } - *ppos = i+1; - if (pkey) - *pkey = entry_ptr->me_key; - if (phash) - *phash = entry_ptr->me_hash; - if (pvalue) - *pvalue = value; - return 1; + + if (table_is_malloced) + PyMem_DEL(table); } /* @@ -1787,12 +1007,9 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { - * Refer to borrowed references in key and value. + * Refer to borrowed references in key and value. * } * - * Return 1 on success, return 0 when the reached the end of the dictionary - * (or if op is not a dictionary) - * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or @@ -1801,291 +1018,216 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { - return _PyDict_Next(op, ppos, pkey, pvalue, NULL); -} - -/* Internal version of dict.pop(). */ -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; - PyDictObject *mp; - - assert(PyDict_Check(dict)); - mp = (PyDictObject *)dict; + register Py_ssize_t i; + register Py_ssize_t mask; + register PyDictEntry *ep; - if (mp->ma_used == 0) { - if (deflt) { - Py_INCREF(deflt); - return deflt; - } - _PyErr_SetKeyError(key); - return NULL; - } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); - if (ix == DKIX_ERROR) - return NULL; - if (ix == DKIX_EMPTY || old_value == NULL) { - if (deflt) { - Py_INCREF(deflt); - return deflt; - } - _PyErr_SetKeyError(key); - return NULL; - } - - // Split table doesn't allow deletion. Combine it. - if (_PyDict_HasSplitTable(mp)) { - if (dictresize(mp, DK_SIZE(mp->ma_keys))) { - return NULL; - } - ix = (mp->ma_keys->dk_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(); - dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); - ep = &DK_ENTRIES(mp->ma_keys)[ix]; - ENSURE_ALLOWS_DELETIONS(mp); - old_key = ep->me_key; - ep->me_key = NULL; - ep->me_value = NULL; - Py_DECREF(old_key); - - ASSERT_CONSISTENT(mp); - return old_value; -} - -PyObject * -_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) -{ - Py_hash_t hash; - - if (((PyDictObject *)dict)->ma_used == 0) { - if (deflt) { - Py_INCREF(deflt); - return deflt; - } - _PyErr_SetKeyError(key); - return NULL; - } - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { - hash = PyObject_Hash(key); - if (hash == -1) - return NULL; - } - return _PyDict_Pop_KnownHash(dict, key, hash, deflt); + if (!PyDict_Check(op)) + return 0; + i = *ppos; + if (i < 0) + return 0; + ep = ((PyDictObject *)op)->ma_table; + mask = ((PyDictObject *)op)->ma_mask; + while (i <= mask && ep[i].me_value == NULL) + i++; + *ppos = i+1; + if (i > mask) + return 0; + if (pkey) + *pkey = ep[i].me_key; + if (pvalue) + *pvalue = ep[i].me_value; + return 1; } -/* Internal version of dict.from_keys(). It is subclass-friendly. */ -PyObject * -_PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) +/* Internal version of PyDict_Next that returns a hash value in addition to the key and value.*/ +int +_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, long *phash) { - PyObject *it; /* iter(iterable) */ - PyObject *key; - PyObject *d; - int status; - - d = _PyObject_CallNoArg(cls); - if (d == NULL) - return NULL; - - if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { - if (PyDict_CheckExact(iterable)) { - PyDictObject *mp = (PyDictObject *)d; - PyObject *oldvalue; - Py_ssize_t pos = 0; - PyObject *key; - Py_hash_t hash; + register Py_ssize_t i; + register Py_ssize_t mask; + register PyDictEntry *ep; - if (dictresize(mp, ESTIMATE_SIZE(PyDict_GET_SIZE(iterable)))) { - Py_DECREF(d); - return NULL; - } - - while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { - if (insertdict(mp, key, hash, value)) { - Py_DECREF(d); - return NULL; - } - } - return d; - } - if (PyAnySet_CheckExact(iterable)) { - PyDictObject *mp = (PyDictObject *)d; - Py_ssize_t pos = 0; - PyObject *key; - Py_hash_t hash; - - if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) { - Py_DECREF(d); - return NULL; - } - - while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { - if (insertdict(mp, key, hash, value)) { - Py_DECREF(d); - return NULL; - } - } - return d; - } - } - - it = PyObject_GetIter(iterable); - if (it == NULL){ - Py_DECREF(d); - return NULL; - } - - if (PyDict_CheckExact(d)) { - while ((key = PyIter_Next(it)) != NULL) { - status = PyDict_SetItem(d, key, value); - Py_DECREF(key); - if (status < 0) - goto Fail; - } - } else { - while ((key = PyIter_Next(it)) != NULL) { - status = PyObject_SetItem(d, key, value); - Py_DECREF(key); - if (status < 0) - goto Fail; - } - } - - if (PyErr_Occurred()) - goto Fail; - Py_DECREF(it); - return d; - -Fail: - Py_DECREF(it); - Py_DECREF(d); - return NULL; + if (!PyDict_Check(op)) + return 0; + i = *ppos; + if (i < 0) + return 0; + ep = ((PyDictObject *)op)->ma_table; + mask = ((PyDictObject *)op)->ma_mask; + while (i <= mask && ep[i].me_value == NULL) + i++; + *ppos = i+1; + if (i > mask) + return 0; + *phash = (long)(ep[i].me_hash); + if (pkey) + *pkey = ep[i].me_key; + if (pvalue) + *pvalue = ep[i].me_value; + return 1; } /* Methods */ static void -dict_dealloc(PyDictObject *mp) +dict_dealloc(register PyDictObject *mp) { - PyObject **values = mp->ma_values; - PyDictKeysObject *keys = mp->ma_keys; - Py_ssize_t i, n; - + register PyDictEntry *ep; + Py_ssize_t fill = mp->ma_fill; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); - Py_TRASHCAN_BEGIN(mp, dict_dealloc) - if (values != NULL) { - if (values != empty_values) { - for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { - Py_XDECREF(values[i]); - } - free_values(values); + Py_TRASHCAN_SAFE_BEGIN(mp) + for (ep = mp->ma_table; fill > 0; ep++) { + if (ep->me_key) { + --fill; + Py_DECREF(ep->me_key); + Py_XDECREF(ep->me_value); } - dictkeys_decref(keys); - } - else if (keys != NULL) { - assert(keys->dk_refcnt == 1); - dictkeys_decref(keys); } + if (mp->ma_table != mp->ma_smalltable) + PyMem_DEL(mp->ma_table); if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type) free_list[numfree++] = mp; else Py_TYPE(mp)->tp_free((PyObject *)mp); - Py_TRASHCAN_END + Py_TRASHCAN_SAFE_END(mp) } +static int +dict_print(register PyDictObject *mp, register FILE *fp, register int flags) +{ + register Py_ssize_t i; + register Py_ssize_t any; + int status; + + status = Py_ReprEnter((PyObject*)mp); + if (status != 0) { + if (status < 0) + return status; + Py_BEGIN_ALLOW_THREADS + fprintf(fp, "{...}"); + Py_END_ALLOW_THREADS + return 0; + } + + Py_BEGIN_ALLOW_THREADS + fprintf(fp, "{"); + Py_END_ALLOW_THREADS + any = 0; + for (i = 0; i <= mp->ma_mask; i++) { + PyDictEntry *ep = mp->ma_table + i; + PyObject *pvalue = ep->me_value; + if (pvalue != NULL) { + /* Prevent PyObject_Repr from deleting value during + key format */ + Py_INCREF(pvalue); + if (any++ > 0) { + Py_BEGIN_ALLOW_THREADS + fprintf(fp, ", "); + Py_END_ALLOW_THREADS + } + if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) { + Py_DECREF(pvalue); + Py_ReprLeave((PyObject*)mp); + return -1; + } + Py_BEGIN_ALLOW_THREADS + fprintf(fp, ": "); + Py_END_ALLOW_THREADS + if (PyObject_Print(pvalue, fp, 0) != 0) { + Py_DECREF(pvalue); + Py_ReprLeave((PyObject*)mp); + return -1; + } + Py_DECREF(pvalue); + } + } + Py_BEGIN_ALLOW_THREADS + fprintf(fp, "}"); + Py_END_ALLOW_THREADS + Py_ReprLeave((PyObject*)mp); + return 0; +} static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; - PyObject *key = NULL, *value = NULL; - _PyUnicodeWriter writer; - int first; + PyObject *s, *temp, *colon = NULL; + PyObject *pieces = NULL, *result = NULL; + PyObject *key, *value; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { - return i > 0 ? PyUnicode_FromString("{...}") : NULL; + return i > 0 ? PyString_FromString("{...}") : NULL; } if (mp->ma_used == 0) { - Py_ReprLeave((PyObject *)mp); - return PyUnicode_FromString("{}"); + result = PyString_FromString("{}"); + goto Done; } - _PyUnicodeWriter_Init(&writer); - writer.overallocate = 1; - /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ - writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; - if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) - goto error; + colon = PyString_FromString(": "); + if (colon == NULL) + goto Done; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; - first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { - PyObject *s; - int res; - - /* Prevent repr from deleting key or value during key format. */ - Py_INCREF(key); + int status; + /* Prevent repr from deleting value during key format. */ Py_INCREF(value); - - if (!first) { - if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) - goto error; - } - first = 0; - s = PyObject_Repr(key); + PyString_Concat(&s, colon); + PyString_ConcatAndDel(&s, PyObject_Repr(value)); + Py_DECREF(value); if (s == NULL) - goto error; - res = _PyUnicodeWriter_WriteStr(&writer, s); - Py_DECREF(s); - if (res < 0) - goto error; - - if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) - goto error; - - s = PyObject_Repr(value); - if (s == NULL) - goto error; - res = _PyUnicodeWriter_WriteStr(&writer, s); - Py_DECREF(s); - if (res < 0) - goto error; - - Py_CLEAR(key); - Py_CLEAR(value); + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; } - writer.overallocate = 0; - if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) - goto error; + /* Add "{}" decorations to the first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyString_FromString("{"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; - Py_ReprLeave((PyObject *)mp); + s = PyString_FromString("}"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyString_ConcatAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; - return _PyUnicodeWriter_Finish(&writer); + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); -error: +Done: + Py_XDECREF(pieces); + Py_XDECREF(colon); Py_ReprLeave((PyObject *)mp); - _PyUnicodeWriter_Dealloc(&writer); - Py_XDECREF(key); - Py_XDECREF(value); - return NULL; + return result; } static Py_ssize_t @@ -2095,40 +1237,45 @@ dict_length(PyDictObject *mp) } static PyObject * -dict_subscript(PyDictObject *mp, PyObject *key) +dict_subscript(PyDictObject *mp, register PyObject *key) { - Py_ssize_t ix; - Py_hash_t hash; - PyObject *value; - - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { + PyObject *v; + long hash; + PyDictEntry *ep; + assert(mp->ma_table != NULL); + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return NULL; - if (ix == DKIX_EMPTY || value == NULL) { + v = ep->me_value; + if (v == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; - _Py_IDENTIFIER(__missing__); - missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); + static PyObject *missing_str = NULL; + missing = _PyObject_LookupSpecial((PyObject *)mp, + "__missing__", + &missing_str); if (missing != NULL) { - res = _PyObject_CallOneArg(missing, key); + res = PyObject_CallFunctionObjArgs(missing, + key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } - _PyErr_SetKeyError(key); + set_key_error(key); return NULL; } - Py_INCREF(value); - return value; + else + Py_INCREF(v); + return v; } static int @@ -2147,13 +1294,12 @@ static PyMappingMethods dict_as_mapping = { }; static PyObject * -dict_keys(PyDictObject *mp) +dict_keys(register PyDictObject *mp) { - PyObject *v; - Py_ssize_t i, j; - PyDictKeyEntry *ep; - Py_ssize_t n, offset; - PyObject **value_ptr; + register PyObject *v; + register Py_ssize_t i, j; + PyDictEntry *ep; + Py_ssize_t mask, n; again: n = mp->ma_used; @@ -2167,36 +1313,27 @@ dict_keys(PyDictObject *mp) Py_DECREF(v); goto again; } - ep = DK_ENTRIES(mp->ma_keys); - if (mp->ma_values) { - value_ptr = mp->ma_values; - offset = sizeof(PyObject *); - } - else { - value_ptr = &ep[0].me_value; - offset = sizeof(PyDictKeyEntry); - } - for (i = 0, j = 0; j < n; i++) { - if (*value_ptr != NULL) { + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if (ep[i].me_value != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } - value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * -dict_values(PyDictObject *mp) +dict_values(register PyDictObject *mp) { - PyObject *v; - Py_ssize_t i, j; - PyDictKeyEntry *ep; - Py_ssize_t n, offset; - PyObject **value_ptr; + register PyObject *v; + register Py_ssize_t i, j; + PyDictEntry *ep; + Py_ssize_t mask, n; again: n = mp->ma_used; @@ -2210,19 +1347,11 @@ dict_values(PyDictObject *mp) Py_DECREF(v); goto again; } - ep = DK_ENTRIES(mp->ma_keys); - if (mp->ma_values) { - value_ptr = mp->ma_values; - offset = sizeof(PyObject *); - } - else { - value_ptr = &ep[0].me_value; - offset = sizeof(PyDictKeyEntry); - } - for (i = 0, j = 0; j < n; i++) { - PyObject *value = *value_ptr; - value_ptr = (PyObject **)(((char *)value_ptr) + offset); - if (value != NULL) { + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if (ep[i].me_value != NULL) { + PyObject *value = ep[i].me_value; Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; @@ -2233,14 +1362,13 @@ dict_values(PyDictObject *mp) } static PyObject * -dict_items(PyDictObject *mp) +dict_items(register PyDictObject *mp) { - PyObject *v; - Py_ssize_t i, j, n; - Py_ssize_t offset; - PyObject *item, *key; - PyDictKeyEntry *ep; - PyObject **value_ptr; + register PyObject *v; + register Py_ssize_t i, j, n; + Py_ssize_t mask; + PyObject *item, *key, *value; + PyDictEntry *ep; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which @@ -2267,19 +1395,10 @@ dict_items(PyDictObject *mp) goto again; } /* Nothing we do below makes any function calls. */ - ep = DK_ENTRIES(mp->ma_keys); - if (mp->ma_values) { - value_ptr = mp->ma_values; - offset = sizeof(PyObject *); - } - else { - value_ptr = &ep[0].me_value; - offset = sizeof(PyDictKeyEntry); - } - for (i = 0, j = 0; j < n; i++) { - PyObject *value = *value_ptr; - value_ptr = (PyObject **)(((char *)value_ptr) + offset); - if (value != NULL) { + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if ((value=ep[i].me_value) != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); @@ -2293,65 +1412,122 @@ dict_items(PyDictObject *mp) return v; } -/*[clinic input] -@classmethod -dict.fromkeys - iterable: object - value: object=None - / - -Create a new dictionary with keys from iterable and values set to value. -[clinic start generated code]*/ - static PyObject * -dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) -/*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ +dict_fromkeys(PyObject *cls, PyObject *args) { - return _PyDict_FromKeys((PyObject *)type, iterable, value); + PyObject *seq; + PyObject *value = Py_None; + PyObject *it; /* iter(seq) */ + PyObject *key; + PyObject *d; + int status; + + if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &seq, &value)) + return NULL; + + d = PyObject_CallObject(cls, NULL); + if (d == NULL) + return NULL; + + if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { + if (PyDict_CheckExact(seq)) { + PyDictObject *mp = (PyDictObject *)d; + PyObject *oldvalue; + Py_ssize_t pos = 0; + PyObject *key; + long hash; + + if (dictresize(mp, ((PyDictObject *)seq)->ma_used / 2 * 3)) { + Py_DECREF(d); + return NULL; + } + + while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash)) { + Py_INCREF(key); + Py_INCREF(value); + if (insertdict(mp, key, hash, value)) { + Py_DECREF(d); + return NULL; + } + } + return d; + } + if (PyAnySet_CheckExact(seq)) { + PyDictObject *mp = (PyDictObject *)d; + Py_ssize_t pos = 0; + PyObject *key; + long hash; + + if (dictresize(mp, PySet_GET_SIZE(seq) / 2 * 3)) { + Py_DECREF(d); + return NULL; + } + + while (_PySet_NextEntry(seq, &pos, &key, &hash)) { + Py_INCREF(key); + Py_INCREF(value); + if (insertdict(mp, key, hash, value)) { + Py_DECREF(d); + return NULL; + } + } + return d; + } + } + + it = PyObject_GetIter(seq); + if (it == NULL){ + Py_DECREF(d); + return NULL; + } + + if (PyDict_CheckExact(d)) { + while ((key = PyIter_Next(it)) != NULL) { + status = PyDict_SetItem(d, key, value); + Py_DECREF(key); + if (status < 0) + goto Fail; + } + } else { + while ((key = PyIter_Next(it)) != NULL) { + status = PyObject_SetItem(d, key, value); + Py_DECREF(key); + if (status < 0) + goto Fail; + } + } + + if (PyErr_Occurred()) + goto Fail; + Py_DECREF(it); + return d; + +Fail: + Py_DECREF(it); + Py_DECREF(d); + return NULL; } static int -dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, - const char *methname) +dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname) { PyObject *arg = NULL; int result = 0; - if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) { + if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) result = -1; - } + else if (arg != NULL) { - if (PyDict_CheckExact(arg)) { + if (PyObject_HasAttrString(arg, "keys")) result = PyDict_Merge(self, arg, 1); - } - else { - _Py_IDENTIFIER(keys); - PyObject *func; - if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { - result = -1; - } - else if (func != NULL) { - Py_DECREF(func); - result = PyDict_Merge(self, arg, 1); - } - else { - result = PyDict_MergeFromSeq2(self, arg, 1); - } - } - } - - if (result == 0 && kwds != NULL) { - if (PyArg_ValidateKeywordArguments(kwds)) - result = PyDict_Merge(self, kwds, 1); else - result = -1; + result = PyDict_MergeFromSeq2(self, arg, 1); } + if (result == 0 && kwds != NULL) + result = PyDict_Merge(self, kwds, 1); return result; } -/* Note: dict.update() uses the METH_VARARGS|METH_KEYWORDS calling convention. - Using METH_FASTCALL|METH_KEYWORDS would make dict.update(**dict2) calls - slower, see the issue #29312. */ static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { @@ -2422,21 +1598,14 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); - if (override) { - if (PyDict_SetItem(d, key, value) < 0) { - Py_DECREF(key); - Py_DECREF(value); - goto Fail; - } - } - else if (PyDict_GetItemWithError(d, key) == NULL) { - if (PyErr_Occurred() || PyDict_SetItem(d, key, value) < 0) { + if (override || PyDict_GetItem(d, key) == NULL) { + int status = PyDict_SetItem(d, key, value); + if (status < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } - Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); @@ -2444,7 +1613,6 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) } i = 0; - ASSERT_CONSISTENT(d); goto Return; Fail: Py_XDECREF(item); @@ -2455,14 +1623,18 @@ Return: return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } -static int -dict_merge(PyObject *a, PyObject *b, int override) +int +PyDict_Update(PyObject *a, PyObject *b) { - PyDictObject *mp, *other; - Py_ssize_t i, n; - PyDictKeyEntry *entry, *ep0; + return PyDict_Merge(a, b, 1); +} - assert(0 <= override && override <= 2); +int +PyDict_Merge(PyObject *a, PyObject *b, int override) +{ + register PyDictObject *mp, *other; + register Py_ssize_t i; + PyDictEntry *entry; /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do @@ -2474,7 +1646,7 @@ dict_merge(PyObject *a, PyObject *b, int override) return -1; } mp = (PyDictObject*)a; - if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { + if (PyDict_Check(b)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ @@ -2489,53 +1661,21 @@ dict_merge(PyObject *a, PyObject *b, int override) * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ - if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { - if (dictresize(mp, ESTIMATE_SIZE(mp->ma_used + other->ma_used))) { + if ((mp->ma_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) { + if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0) return -1; - } } - ep0 = DK_ENTRIES(other->ma_keys); - for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { - PyObject *key, *value; - Py_hash_t hash; - entry = &ep0[i]; - key = entry->me_key; - hash = entry->me_hash; - if (other->ma_values) - value = other->ma_values[i]; - else - value = entry->me_value; - - if (value != NULL) { - int err = 0; - Py_INCREF(key); - Py_INCREF(value); - if (override == 1) - err = insertdict(mp, key, hash, value); - else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { - if (PyErr_Occurred()) { - Py_DECREF(value); - Py_DECREF(key); - return -1; - } - err = insertdict(mp, key, hash, value); - } - else if (override != 0) { - _PyErr_SetKeyError(key); - Py_DECREF(value); - Py_DECREF(key); + for (i = 0; i <= other->ma_mask; i++) { + entry = &other->ma_table[i]; + if (entry->me_value != NULL && + (override || + PyDict_GetItem(a, entry->me_key) == NULL)) { + Py_INCREF(entry->me_key); + Py_INCREF(entry->me_value); + if (insertdict(mp, entry->me_key, + (long)entry->me_hash, + entry->me_value) != 0) return -1; - } - Py_DECREF(value); - Py_DECREF(key); - if (err != 0) - return -1; - - if (n != other->ma_keys->dk_nentries) { - PyErr_SetString(PyExc_RuntimeError, - "dict mutated during update"); - return -1; - } } } } @@ -2560,22 +1700,9 @@ dict_merge(PyObject *a, PyObject *b, int override) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { - if (override != 1) { - if (PyDict_GetItemWithError(a, key) != NULL) { - if (override != 0) { - _PyErr_SetKeyError(key); - Py_DECREF(key); - Py_DECREF(iter); - return -1; - } - Py_DECREF(key); - continue; - } - else if (PyErr_Occurred()) { - Py_DECREF(key); - Py_DECREF(iter); - return -1; - } + if (!override && PyDict_GetItem(a, key) != NULL) { + Py_DECREF(key); + continue; } value = PyObject_GetItem(b, key); if (value == NULL) { @@ -2596,31 +1723,11 @@ dict_merge(PyObject *a, PyObject *b, int override) /* Iterator completed, via error */ return -1; } - ASSERT_CONSISTENT(a); return 0; } -int -PyDict_Update(PyObject *a, PyObject *b) -{ - return dict_merge(a, b, 1); -} - -int -PyDict_Merge(PyObject *a, PyObject *b, int override) -{ - /* XXX Deprecate override not in (0, 1). */ - return dict_merge(a, b, override != 0); -} - -int -_PyDict_MergeEx(PyObject *a, PyObject *b, int override) -{ - return dict_merge(a, b, override); -} - static PyObject * -dict_copy(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) +dict_copy(register PyDictObject *mp) { return PyDict_Copy((PyObject*)mp); } @@ -2629,67 +1736,11 @@ PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; - PyDictObject *mp; - Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } - - mp = (PyDictObject *)o; - if (mp->ma_used == 0) { - /* The dict is empty; just return a new dict. */ - return PyDict_New(); - } - - if (_PyDict_HasSplitTable(mp)) { - PyDictObject *split_copy; - Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); - PyObject **newvalues; - newvalues = new_values(size); - if (newvalues == NULL) - return PyErr_NoMemory(); - split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); - if (split_copy == NULL) { - free_values(newvalues); - return NULL; - } - 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]; - Py_XINCREF(value); - split_copy->ma_values[i] = value; - } - if (_PyObject_GC_IS_TRACKED(mp)) - _PyObject_GC_TRACK(split_copy); - return (PyObject *)split_copy; - } - - if (PyDict_CheckExact(mp) && mp->ma_values == NULL && - (mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3)) - { - /* Use fast-copy if: - - (1) 'mp' is an instance of a subclassed dict; and - - (2) 'mp' is not a split-dict; and - - (3) if 'mp' is non-compact ('del' operation does not resize dicts), - do fast-copy only if it has at most 1/3 non-used keys. - - The last condition (3) is important to guard against a pathological - case when a large dict is almost emptied with multiple del/pop - operations and copied after that. In cases like this, we defer to - PyDict_Merge, which produces a compacted copy. - */ - return clone_combined_dict(mp); - } - copy = PyDict_New(); if (copy == NULL) return NULL; @@ -2739,6 +1790,136 @@ PyDict_Items(PyObject *mp) return dict_items((PyDictObject *)mp); } +/* Subroutine which returns the smallest key in a for which b's value + is different or absent. The value is returned too, through the + pval argument. Both are NULL if no key in a is found for which b's status + differs. The refcounts on (and only on) non-NULL *pval and function return + values must be decremented by the caller (characterize() increments them + to ensure that mutating comparison and PyDict_GetItem calls can't delete + them before the caller is done looking at them). */ + +static PyObject * +characterize(PyDictObject *a, PyDictObject *b, PyObject **pval) +{ + PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */ + PyObject *aval = NULL; /* a[akey] */ + Py_ssize_t i; + int cmp; + + for (i = 0; i <= a->ma_mask; i++) { + PyObject *thiskey, *thisaval, *thisbval; + if (a->ma_table[i].me_value == NULL) + continue; + thiskey = a->ma_table[i].me_key; + Py_INCREF(thiskey); /* keep alive across compares */ + if (akey != NULL) { + cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT); + if (cmp < 0) { + Py_DECREF(thiskey); + goto Fail; + } + if (cmp > 0 || + i > a->ma_mask || + a->ma_table[i].me_value == NULL) + { + /* Not the *smallest* a key; or maybe it is + * but the compare shrunk the dict so we can't + * find its associated value anymore; or + * maybe it is but the compare deleted the + * a[thiskey] entry. + */ + Py_DECREF(thiskey); + continue; + } + } + + /* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */ + thisaval = a->ma_table[i].me_value; + assert(thisaval); + Py_INCREF(thisaval); /* keep alive */ + thisbval = PyDict_GetItem((PyObject *)b, thiskey); + if (thisbval == NULL) + cmp = 0; + else { + /* both dicts have thiskey: same values? */ + cmp = PyObject_RichCompareBool( + thisaval, thisbval, Py_EQ); + if (cmp < 0) { + Py_DECREF(thiskey); + Py_DECREF(thisaval); + goto Fail; + } + } + if (cmp == 0) { + /* New winner. */ + Py_XDECREF(akey); + Py_XDECREF(aval); + akey = thiskey; + aval = thisaval; + } + else { + Py_DECREF(thiskey); + Py_DECREF(thisaval); + } + } + *pval = aval; + return akey; + +Fail: + Py_XDECREF(akey); + Py_XDECREF(aval); + *pval = NULL; + return NULL; +} + +static int +dict_compare(PyDictObject *a, PyDictObject *b) +{ + PyObject *adiff, *bdiff, *aval, *bval; + int res; + + /* Compare lengths first */ + if (a->ma_used < b->ma_used) + return -1; /* a is shorter */ + else if (a->ma_used > b->ma_used) + return 1; /* b is shorter */ + + /* Same length -- check all keys */ + bdiff = bval = NULL; + adiff = characterize(a, b, &aval); + if (adiff == NULL) { + assert(!aval); + /* Either an error, or a is a subset with the same length so + * must be equal. + */ + res = PyErr_Occurred() ? -1 : 0; + goto Finished; + } + bdiff = characterize(b, a, &bval); + if (bdiff == NULL && PyErr_Occurred()) { + assert(!bval); + res = -1; + goto Finished; + } + res = 0; + if (bdiff) { + /* bdiff == NULL "should be" impossible now, but perhaps + * the last comparison done by the characterize() on a had + * the side effect of making the dicts equal! + */ + res = PyObject_Compare(adiff, bdiff); + } + if (res == 0 && bval != NULL) + res = PyObject_Compare(aval, bval); + +Finished: + Py_XDECREF(adiff); + Py_XDECREF(bdiff); + Py_XDECREF(aval); + Py_XDECREF(bval); + return res; +} + /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. @@ -2751,30 +1932,23 @@ dict_equal(PyDictObject *a, PyDictObject *b) if (a->ma_used != b->ma_used) /* 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 < a->ma_keys->dk_nentries; i++) { - PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; - PyObject *aval; - if (a->ma_values) - aval = a->ma_values[i]; - else - aval = ep->me_value; + for (i = 0; i <= a->ma_mask; i++) { + PyObject *aval = a->ma_table[i].me_value; if (aval != NULL) { int cmp; PyObject *bval; - PyObject *key = ep->me_key; + PyObject *key = a->ma_table[i].me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); - /* reuse the known hash value */ - b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); + bval = PyDict_GetItem((PyObject *)b, key); if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); - if (PyErr_Occurred()) - return -1; return 0; } cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); @@ -2785,7 +1959,7 @@ dict_equal(PyDictObject *a, PyDictObject *b) } } return 1; -} + } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) @@ -2802,233 +1976,163 @@ dict_richcompare(PyObject *v, PyObject *w, int op) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } - else + else { + /* Py3K warning if comparison isn't == or != */ + if (PyErr_WarnPy3k("dict inequality comparisons not supported " + "in 3.x", 1) < 0) { + return NULL; + } res = Py_NotImplemented; + } Py_INCREF(res); return res; -} - -/*[clinic input] - -@coexist -dict.__contains__ - - key: object - / - -True if the dictionary has the specified key, else False. -[clinic start generated code]*/ + } static PyObject * -dict___contains__(PyDictObject *self, PyObject *key) -/*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ +dict_contains(register PyDictObject *mp, PyObject *key) { - register PyDictObject *mp = self; - Py_hash_t hash; - Py_ssize_t ix; - PyObject *value; + long hash; + PyDictEntry *ep; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return NULL; - if (ix == DKIX_EMPTY || value == NULL) - Py_RETURN_FALSE; - Py_RETURN_TRUE; + return PyBool_FromLong(ep->me_value != NULL); } -/*[clinic input] -dict.get - - key: object - default: object = None - / - -Return the value for key if key is in the dictionary, else default. -[clinic start generated code]*/ +static PyObject * +dict_has_key(register PyDictObject *mp, PyObject *key) +{ + if (PyErr_WarnPy3k("dict.has_key() not supported in 3.x; " + "use the in operator", 1) < 0) + return NULL; + return dict_contains(mp, key); +} static PyObject * -dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ +dict_get(register PyDictObject *mp, PyObject *args) { + PyObject *key; + PyObject *failobj = Py_None; PyObject *val = NULL; - Py_hash_t hash; - Py_ssize_t ix; + long hash; + PyDictEntry *ep; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { + if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj)) + return NULL; + + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } - ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return NULL; - if (ix == DKIX_EMPTY || val == NULL) { - val = default_value; - } + val = ep->me_value; + if (val == NULL) + val = failobj; Py_INCREF(val); return val; } -PyObject * -PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) + +static PyObject * +dict_setdefault(register PyDictObject *mp, PyObject *args) { - PyDictObject *mp = (PyDictObject *)d; - PyObject *value; - Py_hash_t hash; + PyObject *key; + PyObject *failobj = Py_None; + PyObject *val = NULL; + long hash; + PyDictEntry *ep; - if (!PyDict_Check(d)) { - PyErr_BadInternalCall(); + if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj)) return NULL; - } - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } - if (mp->ma_keys == Py_EMPTY_KEYS) { - if (insert_to_emptydict(mp, key, hash, defaultobj) < 0) { - return NULL; - } - return defaultobj; - } - - if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { - if (insertion_resize(mp) < 0) - return NULL; - } - - Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix == DKIX_ERROR) + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) return NULL; - - if (_PyDict_HasSplitTable(mp) && - ((ix >= 0 && value == NULL && mp->ma_used != ix) || - (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { - if (insertion_resize(mp) < 0) { - return NULL; - } - ix = DKIX_EMPTY; + val = ep->me_value; + if (val == NULL) { + if (dict_set_item_by_hash_or_entry((PyObject*)mp, key, hash, ep, + failobj) == 0) + val = failobj; } - - if (ix == DKIX_EMPTY) { - PyDictKeyEntry *ep, *ep0; - value = defaultobj; - if (mp->ma_keys->dk_usable <= 0) { - if (insertion_resize(mp) < 0) { - return NULL; - } - } - Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); - ep0 = DK_ENTRIES(mp->ma_keys); - ep = &ep0[mp->ma_keys->dk_nentries]; - dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); - Py_INCREF(key); - Py_INCREF(value); - MAINTAIN_TRACKING(mp, key, value); - 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; - } - else { - ep->me_value = value; - } - mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); - mp->ma_keys->dk_usable--; - mp->ma_keys->dk_nentries++; - assert(mp->ma_keys->dk_usable >= 0); - } - else if (value == NULL) { - value = defaultobj; - assert(_PyDict_HasSplitTable(mp)); - assert(ix == mp->ma_used); - Py_INCREF(value); - MAINTAIN_TRACKING(mp, key, value); - mp->ma_values[ix] = value; - mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); - } - - ASSERT_CONSISTENT(mp); - return value; -} - -/*[clinic input] -dict.setdefault - - key: object - default: object = None - / - -Insert key with a value of default if key is not in the dictionary. - -Return the value for key if key is in the dictionary, else default. -[clinic start generated code]*/ - -static PyObject * -dict_setdefault_impl(PyDictObject *self, PyObject *key, - PyObject *default_value) -/*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/ -{ - PyObject *val; - - val = PyDict_SetDefault((PyObject *)self, key, default_value); Py_XINCREF(val); return val; } + static PyObject * -dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) +dict_clear(register PyDictObject *mp) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } -/*[clinic input] -dict.pop - - key: object - default: object = NULL - / - -D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - -If key is not found, default is returned if given, otherwise KeyError is raised -[clinic start generated code]*/ - static PyObject * -dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=3abb47b89f24c21c input=eeebec7812190348]*/ +dict_pop(PyDictObject *mp, PyObject *args) { - return _PyDict_Pop((PyObject*)self, key, default_value); -} - -/*[clinic input] -dict.popitem - -Remove and return a (key, value) pair as a 2-tuple. + long hash; + PyDictEntry *ep; + PyObject *old_value, *old_key; + PyObject *key, *deflt = NULL; -Pairs are returned in LIFO (last-in, first-out) order. -Raises KeyError if the dict is empty. -[clinic start generated code]*/ + if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt)) + return NULL; + if (mp->ma_used == 0) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + set_key_error(key); + return NULL; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) + return NULL; + if (ep->me_value == NULL) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + set_key_error(key); + return NULL; + } + old_key = ep->me_key; + Py_INCREF(dummy); + ep->me_key = dummy; + old_value = ep->me_value; + ep->me_value = NULL; + mp->ma_used--; + Py_DECREF(old_key); + return old_value; +} static PyObject * -dict_popitem_impl(PyDictObject *self) -/*[clinic end generated code: output=e65fcb04420d230d input=1c38a49f21f64941]*/ +dict_popitem(PyDictObject *mp) { - Py_ssize_t i, j; - PyDictKeyEntry *ep0, *ep; + Py_ssize_t i = 0; + PyDictEntry *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it @@ -3043,73 +2147,55 @@ dict_popitem_impl(PyDictObject *self) res = PyTuple_New(2); if (res == NULL) return NULL; - if (self->ma_used == 0) { + if (mp->ma_used == 0) { Py_DECREF(res); - PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); + PyErr_SetString(PyExc_KeyError, + "popitem(): dictionary is empty"); return NULL; } - /* Convert split table to combined table */ - if (self->ma_keys->dk_lookup == lookdict_split) { - if (dictresize(self, DK_SIZE(self->ma_keys))) { - Py_DECREF(res); - return NULL; + /* Set ep to "the first" dict entry with a value. We abuse the hash + * field of slot 0 to hold a search finger: + * If slot 0 has a value, use slot 0. + * Else slot 0 is being used to hold a search finger, + * and we use its hash value as the first index to look. + */ + ep = &mp->ma_table[0]; + if (ep->me_value == NULL) { + i = ep->me_hash; + /* The hash field may be a real hash value, or it may be a + * legit search finger, or it may be a once-legit search + * finger that's out of bounds now because it wrapped around + * or the table shrunk -- simply make sure it's in bounds now. + */ + if (i > mp->ma_mask || i < 1) + i = 1; /* skip slot 0 */ + while ((ep = &mp->ma_table[i])->me_value == NULL) { + i++; + if (i > mp->ma_mask) + i = 1; } } - ENSURE_ALLOWS_DELETIONS(self); - - /* Pop last item */ - ep0 = DK_ENTRIES(self->ma_keys); - i = self->ma_keys->dk_nentries - 1; - while (i >= 0 && ep0[i].me_value == NULL) { - i--; - } - assert(i >= 0); - - ep = &ep0[i]; - j = lookdict_index(self->ma_keys, ep->me_hash, i); - assert(j >= 0); - assert(dictkeys_get_index(self->ma_keys, j) == i); - dictkeys_set_index(self->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; + Py_INCREF(dummy); + ep->me_key = dummy; ep->me_value = NULL; - /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ - self->ma_keys->dk_nentries = i; - self->ma_used--; - self->ma_version_tag = DICT_NEXT_VERSION(); - ASSERT_CONSISTENT(self); + mp->ma_used--; + assert(mp->ma_table[0].me_value == NULL); + mp->ma_table[0].me_hash = i + 1; /* next place to start */ return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { - PyDictObject *mp = (PyDictObject *)op; - PyDictKeysObject *keys = mp->ma_keys; - PyDictKeyEntry *entries = DK_ENTRIES(keys); - Py_ssize_t i, n = keys->dk_nentries; - - if (keys->dk_lookup == lookdict) { - for (i = 0; i < n; i++) { - if (entries[i].me_value != NULL) { - Py_VISIT(entries[i].me_value); - Py_VISIT(entries[i].me_key); - } - } - } - else { - if (mp->ma_values != NULL) { - for (i = 0; i < n; i++) { - Py_VISIT(mp->ma_values[i]); - } - } - else { - for (i = 0; i < n; i++) { - Py_VISIT(entries[i].me_value); - } - } + Py_ssize_t i = 0; + PyObject *pk; + PyObject *pv; + + while (PyDict_Next(op, &i, &pk, &pv)) { + Py_VISIT(pk); + Py_VISIT(pv); } return 0; } @@ -3121,52 +2207,84 @@ dict_tp_clear(PyObject *op) return 0; } + +extern PyTypeObject PyDictIterKey_Type; /* Forward */ +extern PyTypeObject PyDictIterValue_Type; /* Forward */ +extern PyTypeObject PyDictIterItem_Type; /* Forward */ static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); -Py_ssize_t -_PyDict_SizeOf(PyDictObject *mp) +static PyObject * +dict_iterkeys(PyDictObject *dict) { - Py_ssize_t size, usable, res; - - size = DK_SIZE(mp->ma_keys); - usable = USABLE_FRACTION(size); + return dictiter_new(dict, &PyDictIterKey_Type); +} - res = _PyObject_SIZE(Py_TYPE(mp)); - if (mp->ma_values) - res += usable * sizeof(PyObject*); - /* If the dictionary is split, the keys portion is accounted-for - in the type object. */ - if (mp->ma_keys->dk_refcnt == 1) - res += (sizeof(PyDictKeysObject) - + DK_IXSIZE(mp->ma_keys) * size - + sizeof(PyDictKeyEntry) * usable); - return res; +static PyObject * +dict_itervalues(PyDictObject *dict) +{ + return dictiter_new(dict, &PyDictIterValue_Type); } -Py_ssize_t -_PyDict_KeysSize(PyDictKeysObject *keys) +static PyObject * +dict_iteritems(PyDictObject *dict) { - return (sizeof(PyDictKeysObject) - + DK_IXSIZE(keys) * DK_SIZE(keys) - + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); + return dictiter_new(dict, &PyDictIterItem_Type); } static PyObject * -dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) +dict_sizeof(PyDictObject *mp) { - return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); + Py_ssize_t res; + + res = _PyObject_SIZE(Py_TYPE(mp)); + if (mp->ma_table != mp->ma_smalltable) + res = res + (mp->ma_mask + 1) * sizeof(PyDictEntry); + return PyInt_FromSsize_t(res); } +PyDoc_STRVAR(has_key__doc__, +"D.has_key(k) -> True if D has a key k, else False"); + +PyDoc_STRVAR(contains__doc__, +"D.__contains__(k) -> True if D has a key k, else False"); + PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); +PyDoc_STRVAR(get__doc__, +"D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."); + +PyDoc_STRVAR(setdefault_doc__, +"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"); + +PyDoc_STRVAR(pop__doc__, +"D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\ +If key is not found, d is returned if given, otherwise KeyError is raised"); + +PyDoc_STRVAR(popitem__doc__, +"D.popitem() -> (k, v), remove and return some (key, value) pair as a\n\ +2-tuple; but raise KeyError if D is empty."); + +PyDoc_STRVAR(keys__doc__, +"D.keys() -> list of D's keys"); + +PyDoc_STRVAR(items__doc__, +"D.items() -> list of D's (key, value) pairs, as 2-tuples"); + +PyDoc_STRVAR(values__doc__, +"D.values() -> list of D's values"); + PyDoc_STRVAR(update__doc__, -"D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ -If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ -If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ -In either case, this is followed by: for k in F: D[k] = F[k]"); +"D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n" +"If E present and has a .keys() method, does: for k in E: D[k] = E[k]\n\ +If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v\n\ +In either case, this is followed by: for k in F: D[k] = F[k]"); + +PyDoc_STRVAR(fromkeys__doc__, +"dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n\ +v defaults to None."); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); @@ -3174,42 +2292,70 @@ PyDoc_STRVAR(clear__doc__, PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); +PyDoc_STRVAR(iterkeys__doc__, +"D.iterkeys() -> an iterator over the keys of D"); + +PyDoc_STRVAR(itervalues__doc__, +"D.itervalues() -> an iterator over the values of D"); + +PyDoc_STRVAR(iteritems__doc__, +"D.iteritems() -> an iterator over the (key, value) items of D"); + /* Forward */ -static PyObject *dictkeys_new(PyObject *, PyObject *); -static PyObject *dictitems_new(PyObject *, PyObject *); -static PyObject *dictvalues_new(PyObject *, PyObject *); +static PyObject *dictkeys_new(PyObject *); +static PyObject *dictitems_new(PyObject *); +static PyObject *dictvalues_new(PyObject *); -PyDoc_STRVAR(keys__doc__, - "D.keys() -> a set-like object providing a view on D's keys"); -PyDoc_STRVAR(items__doc__, - "D.items() -> a set-like object providing a view on D's items"); -PyDoc_STRVAR(values__doc__, - "D.values() -> an object providing a view on D's values"); +PyDoc_STRVAR(viewkeys__doc__, + "D.viewkeys() -> a set-like object providing a view on D's keys"); +PyDoc_STRVAR(viewitems__doc__, + "D.viewitems() -> a set-like object providing a view on D's items"); +PyDoc_STRVAR(viewvalues__doc__, + "D.viewvalues() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { - DICT___CONTAINS___METHODDEF - {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, + {"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST, + contains__doc__}, + {"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, - {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, + {"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS, sizeof__doc__}, - DICT_GET_METHODDEF - DICT_SETDEFAULT_METHODDEF - DICT_POP_METHODDEF - DICT_POPITEM_METHODDEF - {"keys", dictkeys_new, METH_NOARGS, + {"has_key", (PyCFunction)dict_has_key, METH_O, + has_key__doc__}, + {"get", (PyCFunction)dict_get, METH_VARARGS, + get__doc__}, + {"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS, + setdefault_doc__}, + {"pop", (PyCFunction)dict_pop, METH_VARARGS, + pop__doc__}, + {"popitem", (PyCFunction)dict_popitem, METH_NOARGS, + popitem__doc__}, + {"keys", (PyCFunction)dict_keys, METH_NOARGS, keys__doc__}, - {"items", dictitems_new, METH_NOARGS, - items__doc__}, - {"values", dictvalues_new, METH_NOARGS, - values__doc__}, - {"update", (PyCFunction)(void(*)(void))dict_update, METH_VARARGS | METH_KEYWORDS, + {"items", (PyCFunction)dict_items, METH_NOARGS, + items__doc__}, + {"values", (PyCFunction)dict_values, METH_NOARGS, + values__doc__}, + {"viewkeys", (PyCFunction)dictkeys_new, METH_NOARGS, + viewkeys__doc__}, + {"viewitems", (PyCFunction)dictitems_new, METH_NOARGS, + viewitems__doc__}, + {"viewvalues", (PyCFunction)dictvalues_new, METH_NOARGS, + viewvalues__doc__}, + {"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, - DICT_FROMKEYS_METHODDEF + {"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS, + fromkeys__doc__}, {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, - DICT___REVERSED___METHODDEF + {"iterkeys", (PyCFunction)dict_iterkeys, METH_NOARGS, + iterkeys__doc__}, + {"itervalues", (PyCFunction)dict_itervalues, METH_NOARGS, + itervalues__doc__}, + {"iteritems", (PyCFunction)dict_iteritems, METH_NOARGS, + iteritems__doc__}, {NULL, NULL} /* sentinel */ }; @@ -3217,35 +2363,29 @@ static PyMethodDef mapp_methods[] = { int PyDict_Contains(PyObject *op, PyObject *key) { - Py_hash_t hash; - Py_ssize_t ix; + long hash; PyDictObject *mp = (PyDictObject *)op; - PyObject *value; + PyDictEntry *ep; - if (!PyUnicode_CheckExact(key) || - (hash = ((PyASCIIObject *) key)->hash) == -1) { + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix == DKIX_ERROR) - return -1; - return (ix != DKIX_EMPTY && value != NULL); + ep = (mp->ma_lookup)(mp, key, hash); + return ep == NULL ? -1 : (ep->me_value != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int -_PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) +_PyDict_Contains(PyObject *op, PyObject *key, long hash) { PyDictObject *mp = (PyDictObject *)op; - PyObject *value; - Py_ssize_t ix; + PyDictEntry *ep; - ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); - if (ix == DKIX_ERROR) - return -1; - return (ix != DKIX_EMPTY && value != NULL); + ep = (mp->ma_lookup)(mp, key, hash); + return ep == NULL ? -1 : (ep->me_value != NULL); } /* Hack to implement "key in dict" */ @@ -3266,26 +2406,28 @@ static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; - PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); - if (self == NULL) - return NULL; - d = (PyDictObject *)self; - - /* The object has been implicitly tracked by tp_alloc */ - if (type == &PyDict_Type) - _PyObject_GC_UNTRACK(d); - - d->ma_used = 0; - d->ma_version_tag = DICT_NEXT_VERSION(); - d->ma_keys = new_keys_object(PyDict_MINSIZE); - if (d->ma_keys == NULL) { - Py_DECREF(self); - return NULL; + if (self != NULL) { + PyDictObject *d = (PyDictObject *)self; + /* It's guaranteed that tp->alloc zeroed out the struct. */ + assert(d->ma_table == NULL && d->ma_fill == 0 && d->ma_used == 0); + INIT_NONZERO_DICT_SLOTS(d); + d->ma_lookup = lookdict_string; + /* The object has been implicitly tracked by tp_alloc */ + if (type == &PyDict_Type) + _PyObject_GC_UNTRACK(d); +#ifdef SHOW_CONVERSION_COUNTS + ++created; +#endif +#ifdef SHOW_TRACK_COUNT + if (_PyObject_GC_IS_TRACKED(d)) + count_tracked++; + else + count_untracked++; +#endif } - ASSERT_CONSISTENT(d); return self; } @@ -3318,15 +2460,15 @@ PyTypeObject PyDict_Type = { sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + (printfunc)dict_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + (cmpfunc)dict_compare, /* tp_compare */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ - PyObject_HashNotImplemented, /* tp_hash */ + (hashfunc)PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ @@ -3355,73 +2497,40 @@ PyTypeObject PyDict_Type = { PyObject_GC_Del, /* tp_free */ }; -PyObject * -_PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) { - PyErr_Clear(); - return NULL; - } - return PyDict_GetItem(dp, kv); -} - /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; - kv = PyUnicode_FromString(key); - if (kv == NULL) { - PyErr_Clear(); + kv = PyString_FromString(key); + if (kv == NULL) return NULL; - } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int -_PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return -1; - return PyDict_SetItem(v, kv, item); -} - -int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; - kv = PyUnicode_FromString(key); + kv = PyString_FromString(key); if (kv == NULL) return -1; - PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ + PyString_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int -_PyDict_DelItemId(PyObject *v, _Py_Identifier *key) -{ - PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return -1; - return PyDict_DelItem(v, kv); -} - -int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; - kv = PyUnicode_FromString(key); + kv = PyString_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); @@ -3445,37 +2554,22 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); - if (di == NULL) { + if (di == NULL) return NULL; - } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; + di->di_pos = 0; di->len = dict->ma_used; - if (itertype == &PyDictRevIterKey_Type || - itertype == &PyDictRevIterItem_Type || - itertype == &PyDictRevIterValue_Type) { - if (dict->ma_values) { - di->di_pos = dict->ma_used - 1; - } - else { - di->di_pos = dict->ma_keys->dk_nentries - 1; - } - } - else { - di->di_pos = 0; - } - if (itertype == &PyDictIterItem_Type || - itertype == &PyDictRevIterItem_Type) { + if (itertype == &PyDictIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } - else { + else di->di_result = NULL; - } _PyObject_GC_TRACK(di); return (PyObject *)di; } @@ -3499,36 +2593,26 @@ dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) } static PyObject * -dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) +dictiter_len(dictiterobject *di) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; - return PyLong_FromSize_t(len); + return PyInt_FromSize_t(len); } -PyDoc_STRVAR(length_hint_doc, - "Private method returning an estimate of len(list(it))."); - -static PyObject * -dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); - -PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); +PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyMethodDef dictiter_methods[] = { - {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, - length_hint_doc}, - {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, - reduce_doc}, + {"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, length_hint_doc}, {NULL, NULL} /* sentinel */ }; -static PyObject* -dictiter_iternextkey(dictiterobject *di) +static PyObject *dictiter_iternextkey(dictiterobject *di) { PyObject *key; - Py_ssize_t i; - PyDictKeysObject *k; + register Py_ssize_t i, mask; + register PyDictEntry *ep; PyDictObject *d = di->di_dict; if (d == NULL) @@ -3543,33 +2627,17 @@ dictiter_iternextkey(dictiterobject *di) } i = di->di_pos; - k = d->ma_keys; - assert(i >= 0); - if (d->ma_values) { - if (i >= d->ma_used) - goto fail; - key = DK_ENTRIES(k)[i].me_key; - assert(d->ma_values[i] != NULL); - } - else { - Py_ssize_t n = k->dk_nentries; - PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; - while (i < n && entry_ptr->me_value == NULL) { - entry_ptr++; - i++; - } - if (i >= n) - goto fail; - key = entry_ptr->me_key; - } - // We found an element (key), but did not expect it - if (di->len == 0) { - PyErr_SetString(PyExc_RuntimeError, - "dictionary keys changed during iteration"); + if (i < 0) goto fail; - } + ep = d->ma_table; + mask = d->ma_mask; + while (i <= mask && ep[i].me_value == NULL) + i++; di->di_pos = i+1; + if (i > mask) + goto fail; di->len--; + key = ep[i].me_key; Py_INCREF(key); return key; @@ -3581,15 +2649,15 @@ fail: PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_keyiterator", /* tp_name */ + "dictionary-keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -3612,11 +2680,11 @@ PyTypeObject PyDictIterKey_Type = { 0, }; -static PyObject * -dictiter_iternextvalue(dictiterobject *di) +static PyObject *dictiter_iternextvalue(dictiterobject *di) { PyObject *value; - Py_ssize_t i; + register Py_ssize_t i, mask; + register PyDictEntry *ep; PyDictObject *d = di->di_dict; if (d == NULL) @@ -3631,29 +2699,14 @@ dictiter_iternextvalue(dictiterobject *di) } i = di->di_pos; - assert(i >= 0); - if (d->ma_values) { - if (i >= d->ma_used) - goto fail; - value = d->ma_values[i]; - assert(value != NULL); - } - else { - Py_ssize_t n = d->ma_keys->dk_nentries; - PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; - while (i < n && entry_ptr->me_value == NULL) { - entry_ptr++; - i++; - } - if (i >= n) - goto fail; - value = entry_ptr->me_value; - } - // We found an element, but did not expect it - if (di->len == 0) { - PyErr_SetString(PyExc_RuntimeError, - "dictionary keys changed during iteration"); + mask = d->ma_mask; + if (i < 0 || i > mask) goto fail; + ep = d->ma_table; + while ((value=ep[i].me_value) == NULL) { + i++; + if (i > mask) + goto fail; } di->di_pos = i+1; di->len--; @@ -3668,15 +2721,15 @@ fail: PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_valueiterator", /* tp_name */ + "dictionary-valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -3687,7 +2740,7 @@ PyTypeObject PyDictIterValue_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -3699,11 +2752,11 @@ PyTypeObject PyDictIterValue_Type = { 0, }; -static PyObject * -dictiter_iternextitem(dictiterobject *di) +static PyObject *dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; - Py_ssize_t i; + register Py_ssize_t i, mask; + register PyDictEntry *ep; PyDictObject *d = di->di_dict; if (d == NULL) @@ -3718,34 +2771,19 @@ dictiter_iternextitem(dictiterobject *di) } i = di->di_pos; - assert(i >= 0); - 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]; - assert(value != NULL); - } - else { - Py_ssize_t n = d->ma_keys->dk_nentries; - PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; - while (i < n && entry_ptr->me_value == NULL) { - entry_ptr++; - i++; - } - if (i >= n) - goto fail; - key = entry_ptr->me_key; - value = entry_ptr->me_value; - } - // We found an element, but did not expect it - if (di->len == 0) { - PyErr_SetString(PyExc_RuntimeError, - "dictionary keys changed during iteration"); + if (i < 0) goto fail; - } + ep = d->ma_table; + mask = d->ma_mask; + while (i <= mask && ep[i].me_value == NULL) + i++; di->di_pos = i+1; + if (i > mask) + goto fail; + di->len--; + key = ep[i].me_key; + value = ep[i].me_value; Py_INCREF(key); Py_INCREF(value); result = di->di_result; @@ -3757,8 +2795,7 @@ dictiter_iternextitem(dictiterobject *di) Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); - } - else { + } else { result = PyTuple_New(2); if (result == NULL) return NULL; @@ -3775,15 +2812,15 @@ fail: PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_itemiterator", /* tp_name */ + "dictionary-itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -3806,168 +2843,20 @@ PyTypeObject PyDictIterItem_Type = { 0, }; - -/* dictreviter */ - -static PyObject * -dictreviter_iternext(dictiterobject *di) -{ - PyDictObject *d = di->di_dict; - - if (d == NULL) { - return NULL; - } - assert (PyDict_Check(d)); - - if (di->di_used != d->ma_used) { - PyErr_SetString(PyExc_RuntimeError, - "dictionary changed size during iteration"); - di->di_used = -1; /* Make this state sticky */ - return NULL; - } - - Py_ssize_t i = di->di_pos; - PyDictKeysObject *k = d->ma_keys; - PyObject *key, *value, *result; - - if (i < 0) { - goto fail; - } - if (d->ma_values) { - key = DK_ENTRIES(k)[i].me_key; - value = d->ma_values[i]; - assert (value != NULL); - } - else { - PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; - while (entry_ptr->me_value == NULL) { - if (--i < 0) { - goto fail; - } - entry_ptr--; - } - key = entry_ptr->me_key; - value = entry_ptr->me_value; - } - di->di_pos = i-1; - di->len--; - - if (Py_TYPE(di) == &PyDictRevIterKey_Type) { - Py_INCREF(key); - return key; - } - else if (Py_TYPE(di) == &PyDictRevIterValue_Type) { - Py_INCREF(value); - return value; - } - else if (Py_TYPE(di) == &PyDictRevIterItem_Type) { - Py_INCREF(key); - Py_INCREF(value); - result = di->di_result; - if (Py_REFCNT(result) == 1) { - PyObject *oldkey = PyTuple_GET_ITEM(result, 0); - PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); - PyTuple_SET_ITEM(result, 0, key); /* steals reference */ - PyTuple_SET_ITEM(result, 1, value); /* steals reference */ - Py_INCREF(result); - Py_DECREF(oldkey); - Py_DECREF(oldvalue); - } - else { - result = PyTuple_New(2); - if (result == NULL) { - return NULL; - } - PyTuple_SET_ITEM(result, 0, key); /* steals reference */ - PyTuple_SET_ITEM(result, 1, value); /* steals reference */ - } - return result; - } - else { - Py_UNREACHABLE(); - } - -fail: - di->di_dict = NULL; - Py_DECREF(d); - return NULL; -} - -PyTypeObject PyDictRevIterKey_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_reversekeyiterator", - sizeof(dictiterobject), - .tp_dealloc = (destructor)dictiter_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)dictiter_traverse, - .tp_iter = PyObject_SelfIter, - .tp_iternext = (iternextfunc)dictreviter_iternext, - .tp_methods = dictiter_methods -}; - - -/*[clinic input] -dict.__reversed__ - -Return a reverse iterator over the dict keys. -[clinic start generated code]*/ - -static PyObject * -dict___reversed___impl(PyDictObject *self) -/*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ -{ - assert (PyDict_Check(self)); - return dictiter_new(self, &PyDictRevIterKey_Type); -} - -static PyObject * -dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) -{ - _Py_IDENTIFIER(iter); - /* copy the iterator state */ - dictiterobject tmp = *di; - Py_XINCREF(tmp.di_dict); - - PyObject *list = PySequence_List((PyObject*)&tmp); - Py_XDECREF(tmp.di_dict); - if (list == NULL) { - return NULL; - } - return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); -} - -PyTypeObject PyDictRevIterItem_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_reverseitemiterator", - sizeof(dictiterobject), - .tp_dealloc = (destructor)dictiter_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)dictiter_traverse, - .tp_iter = PyObject_SelfIter, - .tp_iternext = (iternextfunc)dictreviter_iternext, - .tp_methods = dictiter_methods -}; - -PyTypeObject PyDictRevIterValue_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_reversevalueiterator", - sizeof(dictiterobject), - .tp_dealloc = (destructor)dictiter_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)dictiter_traverse, - .tp_iter = PyObject_SelfIter, - .tp_iternext = (iternextfunc)dictreviter_iternext, - .tp_methods = dictiter_methods -}; - /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ +typedef struct { + PyObject_HEAD + PyDictObject *dv_dict; +} dictviewobject; + + static void -dictview_dealloc(_PyDictViewObject *dv) +dictview_dealloc(dictviewobject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); @@ -3976,14 +2865,14 @@ dictview_dealloc(_PyDictViewObject *dv) } static int -dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) +dictview_traverse(dictviewobject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t -dictview_len(_PyDictViewObject *dv) +dictview_len(dictviewobject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) @@ -3991,10 +2880,10 @@ dictview_len(_PyDictViewObject *dv) return len; } -PyObject * -_PyDictView_New(PyObject *dict, PyTypeObject *type) +static PyObject * +dictview_new(PyObject *dict, PyTypeObject *type) { - _PyDictViewObject *dv; + dictviewobject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; @@ -4006,7 +2895,7 @@ _PyDictView_New(PyObject *dict, PyTypeObject *type) type->tp_name, dict->ob_type->tp_name); return NULL; } - dv = PyObject_GC_New(_PyDictViewObject, type); + dv = PyObject_GC_New(dictviewobject, type); if (dv == NULL) return NULL; Py_INCREF(dict); @@ -4060,8 +2949,10 @@ dictview_richcompare(PyObject *self, PyObject *other, int op) assert(PyDictViewSet_Check(self)); assert(other != NULL); - if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) - Py_RETURN_NOTIMPLEMENTED; + if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } len_self = PyObject_Size(self); if (len_self < 0) @@ -4110,23 +3001,31 @@ dictview_richcompare(PyObject *self, PyObject *other, int op) } static PyObject * -dictview_repr(_PyDictViewObject *dv) +dictview_repr(dictviewobject *dv) { PyObject *seq; + PyObject *seq_str; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { - return rc > 0 ? PyUnicode_FromString("...") : NULL; + return rc > 0 ? PyString_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } - result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); + seq_str = PyObject_Repr(seq); Py_DECREF(seq); + if (seq_str == NULL) { + goto Done; + } + result = PyString_FromFormat("%s(%s)", Py_TYPE(dv)->tp_name, + PyString_AS_STRING(seq_str)); + Py_DECREF(seq_str); + Done: Py_ReprLeave((PyObject *)dv); return result; @@ -4135,7 +3034,7 @@ Done: /*** dict_keys ***/ static PyObject * -dictkeys_iter(_PyDictViewObject *dv) +dictkeys_iter(dictviewobject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; @@ -4144,7 +3043,7 @@ dictkeys_iter(_PyDictViewObject *dv) } static int -dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) +dictkeys_contains(dictviewobject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; @@ -4162,34 +3061,16 @@ static PySequenceMethods dictkeys_as_sequence = { (objobjproc)dictkeys_contains, /* sq_contains */ }; -// Create an set object from dictviews object. -// Returns a new reference. -// This utility function is used by set operations. -static PyObject* -dictviews_to_set(PyObject *self) -{ - PyObject *left = self; - if (PyDictKeys_Check(self)) { - // PySet_New() has fast path for the dict object. - PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; - if (PyDict_CheckExact(dict)) { - left = dict; - } - } - return PySet_New(left); -} - static PyObject* -dictviews_sub(PyObject *self, PyObject *other) +dictviews_sub(PyObject* self, PyObject *other) { - PyObject *result = dictviews_to_set(self); - if (result == NULL) { + PyObject *result = PySet_New(self); + PyObject *tmp; + if (result == NULL) return NULL; - } - _Py_IDENTIFIER(difference_update); - PyObject *tmp = _PyObject_CallMethodIdOneArg( - result, &PyId_difference_update, other); + + tmp = PyObject_CallMethod(result, "difference_update", "(O)", other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4199,120 +3080,51 @@ dictviews_sub(PyObject *self, PyObject *other) return result; } -static int -dictitems_contains(_PyDictViewObject *dv, PyObject *obj); - -PyObject * -_PyDictView_Intersect(PyObject* self, PyObject *other) +static PyObject* +dictviews_and(PyObject* self, PyObject *other) { - PyObject *result; - PyObject *it; - PyObject *key; - Py_ssize_t len_self; - int rv; - int (*dict_contains)(_PyDictViewObject *, PyObject *); - - /* Python interpreter swaps parameters when dict view - is on right side of & */ - if (!PyDictViewSet_Check(self)) { - PyObject *tmp = other; - other = self; - self = tmp; - } - - len_self = dictview_len((_PyDictViewObject *)self); - - /* if other is a set and self is smaller than other, - reuse set intersection logic */ - if (Py_TYPE(other) == &PySet_Type && len_self <= PyObject_Size(other)) { - _Py_IDENTIFIER(intersection); - return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); - } - - /* if other is another dict view, and it is bigger than self, - swap them */ - if (PyDictViewSet_Check(other)) { - Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); - if (len_other > len_self) { - PyObject *tmp = other; - other = self; - self = tmp; - } - } - - /* at this point, two things should be true - 1. self is a dictview - 2. if other is a dictview then it is smaller than self */ - result = PySet_New(NULL); + PyObject *result = PySet_New(self); + PyObject *tmp; if (result == NULL) return NULL; - it = PyObject_GetIter(other); - if (it == NULL) { + tmp = PyObject_CallMethod(result, "intersection_update", "(O)", other); + if (tmp == NULL) { Py_DECREF(result); return NULL; } - if (PyDictKeys_Check(self)) { - dict_contains = dictkeys_contains; - } - /* else PyDictItems_Check(self) */ - else { - dict_contains = dictitems_contains; - } - - while ((key = PyIter_Next(it)) != NULL) { - rv = dict_contains((_PyDictViewObject *)self, key); - if (rv < 0) { - goto error; - } - if (rv) { - if (PySet_Add(result, key)) { - goto error; - } - } - Py_DECREF(key); - } - Py_DECREF(it); - if (PyErr_Occurred()) { - Py_DECREF(result); - return NULL; - } + Py_DECREF(tmp); return result; - -error: - Py_DECREF(it); - Py_DECREF(result); - Py_DECREF(key); - return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { - PyObject *result = dictviews_to_set(self); - if (result == NULL) { + PyObject *result = PySet_New(self); + PyObject *tmp; + if (result == NULL) return NULL; - } - if (_PySet_Update(result, other) < 0) { + tmp = PyObject_CallMethod(result, "update", "(O)", other); + if (tmp == NULL) { Py_DECREF(result); return NULL; } + + Py_DECREF(tmp); return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { - PyObject *result = dictviews_to_set(self); - if (result == NULL) { + PyObject *result = PySet_New(self); + PyObject *tmp; + if (result == NULL) return NULL; - } - _Py_IDENTIFIER(symmetric_difference_update); - PyObject *tmp = _PyObject_CallMethodIdOneArg( - result, &PyId_symmetric_difference_update, other); + tmp = PyObject_CallMethod(result, "symmetric_difference_update", "(O)", other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4326,99 +3138,37 @@ static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ + 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ - 0, /*nb_bool*/ + 0, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ - (binaryfunc)_PyDictView_Intersect, /*nb_and*/ + (binaryfunc)dictviews_and, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; -static PyObject* -dictviews_isdisjoint(PyObject *self, PyObject *other) -{ - PyObject *it; - PyObject *item = NULL; - - if (self == other) { - if (dictview_len((_PyDictViewObject *)self) == 0) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; - } - - /* Iterate over the shorter object (only if other is a set, - * because PySequence_Contains may be expensive otherwise): */ - if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { - Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); - Py_ssize_t len_other = PyObject_Size(other); - if (len_other == -1) - return NULL; - - if ((len_other > len_self)) { - PyObject *tmp = other; - other = self; - self = tmp; - } - } - - it = PyObject_GetIter(other); - if (it == NULL) - return NULL; - - while ((item = PyIter_Next(it)) != NULL) { - int contains = PySequence_Contains(self, item); - Py_DECREF(item); - if (contains == -1) { - Py_DECREF(it); - return NULL; - } - - if (contains) { - Py_DECREF(it); - Py_RETURN_FALSE; - } - } - Py_DECREF(it); - if (PyErr_Occurred()) - return NULL; /* PyIter_Next raised an exception. */ - Py_RETURN_TRUE; -} - -PyDoc_STRVAR(isdisjoint_doc, -"Return True if the view and the given iterable have a null intersection."); - -static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); - -PyDoc_STRVAR(reversed_keys_doc, -"Return a reverse iterator over the dict keys."); - static PyMethodDef dictkeys_methods[] = { - {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, - isdisjoint_doc}, - {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, - reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ - sizeof(_PyDictViewObject), /* tp_basicsize */ + sizeof(dictviewobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ @@ -4429,7 +3179,8 @@ PyTypeObject PyDictKeys_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_CHECKTYPES, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -4442,24 +3193,15 @@ PyTypeObject PyDictKeys_Type = { }; static PyObject * -dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) +dictkeys_new(PyObject *dict) { - return _PyDictView_New(dict, &PyDictKeys_Type); -} - -static PyObject * -dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) -{ - if (dv->dv_dict == NULL) { - Py_RETURN_NONE; - } - return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); + return dictview_new(dict, &PyDictKeys_Type); } /*** dict_items ***/ static PyObject * -dictitems_iter(_PyDictViewObject *dv) +dictitems_iter(dictviewobject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; @@ -4468,7 +3210,7 @@ dictitems_iter(_PyDictViewObject *dv) } static int -dictitems_contains(_PyDictViewObject *dv, PyObject *obj) +dictitems_contains(dictviewobject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; @@ -4478,14 +3220,14 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); - found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); + found = PyDict_GetItem((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); - result = PyObject_RichCompareBool(found, value, Py_EQ); + result = PyObject_RichCompareBool(value, found, Py_EQ); Py_DECREF(found); return result; } @@ -4501,30 +3243,21 @@ static PySequenceMethods dictitems_as_sequence = { (objobjproc)dictitems_contains, /* sq_contains */ }; -static PyObject* dictitems_reversed(_PyDictViewObject *dv); - -PyDoc_STRVAR(reversed_items_doc, -"Return a reverse iterator over the dict items."); - static PyMethodDef dictitems_methods[] = { - {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, - isdisjoint_doc}, - {"__reversed__", (PyCFunction)(void(*)(void))dictitems_reversed, METH_NOARGS, - reversed_items_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ - sizeof(_PyDictViewObject), /* tp_basicsize */ + sizeof(dictviewobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ @@ -4535,7 +3268,8 @@ PyTypeObject PyDictItems_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_CHECKTYPES, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -4548,24 +3282,15 @@ PyTypeObject PyDictItems_Type = { }; static PyObject * -dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) +dictitems_new(PyObject *dict) { - return _PyDictView_New(dict, &PyDictItems_Type); -} - -static PyObject * -dictitems_reversed(_PyDictViewObject *dv) -{ - if (dv->dv_dict == NULL) { - Py_RETURN_NONE; - } - return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); + return dictview_new(dict, &PyDictItems_Type); } /*** dict_values ***/ static PyObject * -dictvalues_iter(_PyDictViewObject *dv) +dictvalues_iter(dictviewobject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; @@ -4584,28 +3309,21 @@ static PySequenceMethods dictvalues_as_sequence = { (objobjproc)0, /* sq_contains */ }; -static PyObject* dictvalues_reversed(_PyDictViewObject *dv); - -PyDoc_STRVAR(reversed_values_doc, -"Return a reverse iterator over the dict values."); - static PyMethodDef dictvalues_methods[] = { - {"__reversed__", (PyCFunction)(void(*)(void))dictvalues_reversed, METH_NOARGS, - reversed_values_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ - sizeof(_PyDictViewObject), /* tp_basicsize */ + sizeof(dictviewobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ @@ -4629,138 +3347,7 @@ PyTypeObject PyDictValues_Type = { }; static PyObject * -dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) -{ - return _PyDictView_New(dict, &PyDictValues_Type); -} - -static PyObject * -dictvalues_reversed(_PyDictViewObject *dv) -{ - if (dv->dv_dict == NULL) { - Py_RETURN_NONE; - } - return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); -} - - -/* Returns NULL if cannot allocate a new PyDictKeysObject, - but does not set an error */ -PyDictKeysObject * -_PyDict_NewKeysForClass(void) -{ - PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); - if (keys == NULL) - PyErr_Clear(); - else - keys->dk_lookup = lookdict_split; - return keys; -} - -#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) - -PyObject * -PyObject_GenericGetDict(PyObject *obj, void *context) -{ - PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return NULL; - } - dict = *dictptr; - if (dict == NULL) { - PyTypeObject *tp = Py_TYPE(obj); - if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { - dictkeys_incref(CACHED_KEYS(tp)); - *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); - } - else { - *dictptr = dict = PyDict_New(); - } - } - Py_XINCREF(dict); - return dict; -} - -int -_PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, - PyObject *key, PyObject *value) -{ - PyObject *dict; - int res; - PyDictKeysObject *cached; - - assert(dictptr != NULL); - if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { - assert(dictptr != NULL); - dict = *dictptr; - if (dict == NULL) { - dictkeys_incref(cached); - dict = new_dict_with_shared_keys(cached); - if (dict == NULL) - return -1; - *dictptr = dict; - } - 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) { - /* 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 - * the only dict sharing key with the type. - * - * This is to allow using shared key in class like this: - * - * class C: - * def __init__(self): - * # one dict resize happens - * self.a, self.b, self.c = 1, 2, 3 - * self.d, self.e, self.f = 4, 5, 6 - * a = C() - */ - if (cached->dk_refcnt == 1) { - CACHED_KEYS(tp) = make_keys_shared(dict); - } - else { - CACHED_KEYS(tp) = NULL; - } - dictkeys_decref(cached); - if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) - return -1; - } - } - } else { - dict = *dictptr; - if (dict == NULL) { - dict = PyDict_New(); - if (dict == NULL) - return -1; - *dictptr = dict; - } - if (value == NULL) { - res = PyDict_DelItem(dict, key); - } else { - res = PyDict_SetItem(dict, key, value); - } - } - return res; -} - -void -_PyDictKeys_DecRef(PyDictKeysObject *keys) +dictvalues_new(PyObject *dict) { - dictkeys_decref(keys); + return dictview_new(dict, &PyDictValues_Type); } |