1993-03-27 14:11:32 -04:00
|
|
|
|
1999-03-24 15:06:42 -04:00
|
|
|
/* Dictionary object implementation using a hash table */
|
1993-03-29 06:43:31 -04:00
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
#include "Python.h"
|
1993-03-27 14:11:32 -04:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
* MINSIZE is the minimum size of a dictionary. This many slots are
|
|
|
|
* allocated directly in the dict object (in the ma_smalltable member).
|
|
|
|
* This must be a power of 2, and the first entry in the polys[] vector must
|
|
|
|
* match.
|
1997-01-27 20:00:11 -04:00
|
|
|
*/
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
#define MINSIZE 8
|
1997-01-27 20:00:11 -04:00
|
|
|
|
2000-08-31 16:31:38 -03:00
|
|
|
/* define this out if you don't want conversion statistics on exit */
|
|
|
|
#undef SHOW_CONVERSION_COUNTS
|
|
|
|
|
1997-01-27 20:00:11 -04:00
|
|
|
/*
|
|
|
|
Table of irreducible polynomials to efficiently cycle through
|
2000-12-12 21:02:46 -04:00
|
|
|
GF(2^n)-{0}, 2<=n<=30. A table size is always a power of 2.
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
For a table size of 2**i, the polys entry is 2**i + j for some j in 1 thru
|
|
|
|
2**i-1 inclusive. The polys[] entries here happen to add in the smallest j
|
|
|
|
values "that work". Work means this: given any integer k in 1 thru 2**i-1
|
|
|
|
inclusive, a poly works if & only if repeating this code:
|
|
|
|
print k
|
|
|
|
k <<= 1
|
|
|
|
if k >= 2**i:
|
|
|
|
k ^= poly
|
|
|
|
prints every integer in 1 thru 2**i-1 inclusive exactly once before printing
|
|
|
|
k a second time. Theory can be used to find such polys efficiently, but the
|
|
|
|
operational defn. of "works" is sufficient to find them in reasonable time
|
|
|
|
via brute force program (hint: any poly that has an even number of 1 bits
|
|
|
|
cannot work; ditto any poly with low bit 0; exploit those).
|
2001-05-27 04:39:22 -03:00
|
|
|
|
|
|
|
Some major subtleties: Most hash schemes depend on having a "good" hash
|
|
|
|
function, in the sense of simulating randomness. Python doesn't: some of
|
|
|
|
its hash functions are trivial, such as hash(i) == i for ints i (excepting
|
|
|
|
i == -1, because -1 is the "error occurred" return value from tp_hash).
|
|
|
|
|
|
|
|
This isn't necessarily bad! To the contrary, that our hash tables are powers
|
|
|
|
of 2 in size, and that we take the low-order bits as the initial table index,
|
|
|
|
means that there are no collisions at all for dicts indexed by a contiguous
|
|
|
|
range of ints. This is "better than random" behavior, and that's very
|
|
|
|
desirable.
|
|
|
|
|
|
|
|
On the other hand, when collisions occur, the tendency to fill contiguous
|
|
|
|
slices of the hash table makes a good collision resolution strategy crucial;
|
|
|
|
e.g., linear probing is right out.
|
|
|
|
|
|
|
|
Reimer Behrends contributed the idea of using a polynomial-based approach,
|
|
|
|
using repeated multiplication by x in GF(2**n) where a polynomial is chosen
|
|
|
|
such that x is a primitive root. This visits every table location exactly
|
|
|
|
once, and the sequence of locations probed is highly non-linear.
|
|
|
|
|
|
|
|
The same is also largely true of quadratic probing for power-of-2 tables, of
|
|
|
|
the specific
|
|
|
|
|
|
|
|
(i + comb(1, 2)) mod size
|
|
|
|
(i + comb(2, 2)) mod size
|
|
|
|
(i + comb(3, 2)) mod size
|
|
|
|
(i + comb(4, 2)) mod size
|
|
|
|
...
|
|
|
|
(i + comb(j, 2)) mod size
|
|
|
|
|
|
|
|
flavor. The polynomial approach "scrambles" the probe indices better, but
|
|
|
|
more importantly allows to get *some* additional bits of the hash code into
|
|
|
|
play via computing the initial increment, thus giving a weak form of double
|
|
|
|
hashing. Quadratic probing cannot be extended that way (the first probe
|
|
|
|
offset must be 1, the second 3, the third 6, etc).
|
|
|
|
|
|
|
|
Christian Tismer later contributed the idea of using polynomial division
|
|
|
|
instead of multiplication. The problem is that the multiplicative method
|
|
|
|
can't get *all* the bits of the hash code into play without expensive
|
|
|
|
computations that slow down the initial index and/or initial increment
|
|
|
|
computation. For a set of keys like [i << 16 for i in range(20000)], under
|
|
|
|
the multiplicative method the initial index and increment were the same for
|
|
|
|
all keys, so every key followed exactly the same probe sequence, and so
|
|
|
|
this degenerated into a (very slow) linear search. The division method uses
|
|
|
|
all the bits of the hash code naturally in the increment, although it *may*
|
|
|
|
visit locations more than once until such time as all the high bits of the
|
|
|
|
increment have been shifted away. It's also impossible to tell in advance
|
|
|
|
whether incr is congruent to 0 modulo poly, so each iteration of the loop has
|
|
|
|
to guard against incr becoming 0. These are minor costs, as we usually don't
|
|
|
|
get into the probe loop, and when we do we usually get out on its first
|
|
|
|
iteration.
|
1993-03-27 14:11:32 -04:00
|
|
|
*/
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
|
1997-01-27 20:00:11 -04:00
|
|
|
static long polys[] = {
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
/* 4 + 3, */ /* first active entry if MINSIZE == 4 */
|
|
|
|
8 + 3, /* first active entry if MINSIZE == 8 */
|
1997-01-29 00:45:16 -04:00
|
|
|
16 + 3,
|
|
|
|
32 + 5,
|
|
|
|
64 + 3,
|
|
|
|
128 + 3,
|
|
|
|
256 + 29,
|
|
|
|
512 + 17,
|
|
|
|
1024 + 9,
|
|
|
|
2048 + 5,
|
|
|
|
4096 + 83,
|
|
|
|
8192 + 27,
|
|
|
|
16384 + 43,
|
|
|
|
32768 + 3,
|
|
|
|
65536 + 45,
|
|
|
|
131072 + 9,
|
|
|
|
262144 + 39,
|
|
|
|
524288 + 39,
|
|
|
|
1048576 + 9,
|
|
|
|
2097152 + 5,
|
|
|
|
4194304 + 3,
|
|
|
|
8388608 + 33,
|
|
|
|
16777216 + 27,
|
|
|
|
33554432 + 9,
|
|
|
|
67108864 + 71,
|
|
|
|
134217728 + 39,
|
|
|
|
268435456 + 9,
|
|
|
|
536870912 + 5,
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
1073741824 + 83
|
|
|
|
/* 2147483648 + 9 -- if we ever boost this to unsigned long */
|
1993-03-27 14:11:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Object used as dummy key to fill deleted entries */
|
1997-05-13 18:02:11 -03:00
|
|
|
static PyObject *dummy; /* Initialized by first call to newdictobject() */
|
1993-03-27 14:11:32 -04:00
|
|
|
|
|
|
|
/*
|
2000-12-12 21:02:46 -04:00
|
|
|
There are three kinds of slots in the table:
|
|
|
|
|
|
|
|
1. Unused. me_key == me_value == NULL
|
|
|
|
Does not hold an active (key, value) pair now and never did. Unused can
|
|
|
|
transition to Active upon key insertion. This is the only case in which
|
|
|
|
me_key is NULL, and is each slot's initial state.
|
|
|
|
|
|
|
|
2. Active. me_key != NULL and me_key != dummy and me_value != NULL
|
|
|
|
Holds an active (key, value) pair. Active can transition to Dummy upon
|
|
|
|
key deletion. This is the only case in which me_value != NULL.
|
|
|
|
|
2000-12-13 15:58:25 -04:00
|
|
|
3. Dummy. me_key == dummy and me_value == NULL
|
2000-12-12 21:02:46 -04:00
|
|
|
Previously held an active (key, value) pair, but that was deleted and an
|
|
|
|
active pair has not yet overwritten the slot. Dummy can transition to
|
|
|
|
Active upon key insertion. Dummy slots cannot be made Unused again
|
|
|
|
(cannot have me_key set to NULL), else the probe sequence in case of
|
|
|
|
collision would have no way to know they were once active.
|
2000-12-13 15:58:25 -04:00
|
|
|
|
|
|
|
Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to
|
|
|
|
hold a search finger. The me_hash field of Unused or Dummy slots has no
|
|
|
|
meaning otherwise.
|
1993-03-27 14:11:32 -04:00
|
|
|
*/
|
|
|
|
typedef struct {
|
2000-12-12 21:02:46 -04:00
|
|
|
long me_hash; /* cached hash code of me_key */
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *me_key;
|
|
|
|
PyObject *me_value;
|
1997-04-11 16:14:07 -03:00
|
|
|
#ifdef USE_CACHE_ALIGNED
|
|
|
|
long aligner;
|
|
|
|
#endif
|
1997-05-13 18:02:11 -03:00
|
|
|
} dictentry;
|
1993-03-27 14:11:32 -04:00
|
|
|
|
|
|
|
/*
|
2000-12-13 15:58:25 -04:00
|
|
|
To ensure the lookup algorithm terminates, there must be at least one Unused
|
2000-12-12 21:02:46 -04:00
|
|
|
slot (NULL key) in the table.
|
|
|
|
The value ma_fill is the number of non-NULL keys (sum of Active and Dummy);
|
|
|
|
ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
|
|
|
|
values == the number of Active items).
|
|
|
|
To avoid slowing down lookups on a near-full table, we resize the table when
|
2001-03-21 15:23:56 -04:00
|
|
|
it's two-thirds full.
|
1993-03-27 14:11:32 -04:00
|
|
|
*/
|
2000-08-31 16:31:38 -03:00
|
|
|
typedef struct dictobject dictobject;
|
|
|
|
struct dictobject {
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject_HEAD
|
2000-12-12 21:02:46 -04:00
|
|
|
int ma_fill; /* # Active + # Dummy */
|
|
|
|
int ma_used; /* # Active */
|
|
|
|
int ma_size; /* total # slots in ma_table */
|
|
|
|
int ma_poly; /* appopriate entry from polys vector */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
/* ma_table points to ma_smalltable for small tables, else to
|
|
|
|
* additional malloc'ed memory. ma_table is never NULL! This rule
|
|
|
|
* saves repeated runtime null-tests in the workhorse getitem and
|
|
|
|
* setitem calls.
|
|
|
|
*/
|
1997-05-13 18:02:11 -03:00
|
|
|
dictentry *ma_table;
|
2000-08-31 16:31:38 -03:00
|
|
|
dictentry *(*ma_lookup)(dictobject *mp, PyObject *key, long hash);
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
dictentry ma_smalltable[MINSIZE];
|
2000-08-31 16:31:38 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
/* forward declarations */
|
|
|
|
static dictentry *
|
|
|
|
lookdict_string(dictobject *mp, PyObject *key, long hash);
|
|
|
|
|
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
static long created = 0L;
|
|
|
|
static long converted = 0L;
|
|
|
|
|
|
|
|
static void
|
|
|
|
show_counts(void)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
#endif
|
1993-03-27 14:11:32 -04:00
|
|
|
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
/* Set dictobject* mp to empty but w/ MINSIZE slots, using ma_smalltable. */
|
|
|
|
#define empty_to_minsize(mp) do { \
|
|
|
|
memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \
|
|
|
|
(mp)->ma_table = (mp)->ma_smalltable; \
|
|
|
|
(mp)->ma_size = MINSIZE; \
|
|
|
|
(mp)->ma_used = (mp)->ma_fill = 0; \
|
|
|
|
(mp)->ma_poly = polys[0]; \
|
|
|
|
assert(MINSIZE < (mp)->ma_poly && (mp)->ma_poly < MINSIZE*2); \
|
|
|
|
} while(0)
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-22 16:25:51 -03:00
|
|
|
PyDict_New(void)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictobject *mp;
|
1993-03-27 14:11:32 -04:00
|
|
|
if (dummy == NULL) { /* Auto-initialize dummy */
|
1997-05-02 00:12:38 -03:00
|
|
|
dummy = PyString_FromString("<dummy key>");
|
1993-03-27 14:11:32 -04:00
|
|
|
if (dummy == NULL)
|
|
|
|
return NULL;
|
2000-08-31 16:31:38 -03:00
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
Py_AtExit(show_counts);
|
|
|
|
#endif
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = PyObject_NEW(dictobject, &PyDict_Type);
|
1993-03-27 14:11:32 -04:00
|
|
|
if (mp == NULL)
|
|
|
|
return NULL;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
empty_to_minsize(mp);
|
2000-08-31 16:31:38 -03:00
|
|
|
mp->ma_lookup = lookdict_string;
|
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
++created;
|
|
|
|
#endif
|
2000-06-30 02:02:53 -03:00
|
|
|
PyObject_GC_Init(mp);
|
1997-05-02 00:12:38 -03:00
|
|
|
return (PyObject *)mp;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
The basic lookup function used by all operations.
|
1997-01-27 20:00:11 -04:00
|
|
|
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
|
1993-03-27 14:11:32 -04:00
|
|
|
Open addressing is preferred over chaining since the link overhead for
|
|
|
|
chaining would be substantial (100% with typical malloc overhead).
|
1997-01-27 20:00:11 -04:00
|
|
|
However, instead of going through the table at constant steps, we cycle
|
2000-12-12 21:02:46 -04:00
|
|
|
through the values of GF(2^n). This avoids modulo computations, being
|
1997-01-27 20:00:11 -04:00
|
|
|
much cheaper on RISC machines, without leading to clustering.
|
1993-03-27 14:11:32 -04:00
|
|
|
|
1999-03-24 15:06:42 -04:00
|
|
|
The initial probe index is computed as hash mod the table size.
|
2000-12-12 21:02:46 -04:00
|
|
|
Subsequent probe indices use the values of x^i in GF(2^n)-{0} as an offset,
|
|
|
|
where x is a root. The initial offset is derived from hash, too.
|
1999-03-24 15:06:42 -04:00
|
|
|
|
|
|
|
All arithmetic on hash should ignore overflow.
|
1997-01-27 20:00:11 -04:00
|
|
|
|
|
|
|
(This version is due to Reimer Behrends, some ideas are also due to
|
1997-08-18 18:52:47 -03:00
|
|
|
Jyrki Alakuijala and Vladimir Marangozov.)
|
2000-08-31 16:31:38 -03:00
|
|
|
|
|
|
|
This function must never return NULL; failures are indicated by returning
|
|
|
|
a dictentry* for which the me_value field is NULL. Exceptions are never
|
|
|
|
reported by this function, and outstanding exceptions are maintained.
|
1993-03-27 14:11:32 -04:00
|
|
|
*/
|
1997-05-13 18:02:11 -03:00
|
|
|
static dictentry *
|
2000-07-04 14:44:48 -03:00
|
|
|
lookdict(dictobject *mp, PyObject *key, register long hash)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-01-27 20:00:11 -04:00
|
|
|
register int i;
|
2001-05-27 04:39:22 -03:00
|
|
|
register unsigned int incr;
|
1997-08-18 18:52:47 -03:00
|
|
|
register dictentry *freeslot;
|
1997-04-09 16:41:24 -03:00
|
|
|
register unsigned int mask = mp->ma_size-1;
|
1997-05-13 18:02:11 -03:00
|
|
|
dictentry *ep0 = mp->ma_table;
|
|
|
|
register dictentry *ep;
|
2000-08-31 16:04:07 -03:00
|
|
|
register int restore_error = 0;
|
|
|
|
register int checked_error = 0;
|
|
|
|
register int cmp;
|
|
|
|
PyObject *err_type, *err_value, *err_tb;
|
1997-01-27 20:00:11 -04:00
|
|
|
/* We must come up with (i, incr) such that 0 <= i < ma_size
|
2000-12-12 21:02:46 -04:00
|
|
|
and 0 < incr < ma_size and both are a function of hash.
|
|
|
|
i is the initial table index and incr the initial probe offset. */
|
Get rid of the superstitious "~" in dict hashing's "i = (~hash) & mask".
The comment following used to say:
/* We use ~hash instead of hash, as degenerate hash functions, such
as for ints <sigh>, can have lots of leading zeros. It's not
really a performance risk, but better safe than sorry.
12-Dec-00 tim: so ~hash produces lots of leading ones instead --
what's the gain? */
That is, there was never a good reason for doing it. And to the contrary,
as explained on Python-Dev last December, it tended to make the *sum*
(i + incr) & mask (which is the first table index examined in case of
collison) the same "too often" across distinct hashes.
Changing to the simpler "i = hash & mask" reduced the number of string-dict
collisions (== # number of times we go around the lookup for-loop) from about
6 million to 5 million during a full run of the test suite (these are
approximate because the test suite does some random stuff from run to run).
The number of collisions in non-string dicts also decreased, but not as
dramatically.
Note that this may, for a given dict, change the order (wrt previous
releases) of entries exposed by .keys(), .values() and .items(). A number
of std tests suffered bogus failures as a result. For dicts keyed by
small ints, or (less so) by characters, the order is much more likely to be
in increasing order of key now; e.g.,
>>> d = {}
>>> for i in range(10):
... d[i] = i
...
>>> d
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
>>>
Unfortunately. people may latch on to that in small examples and draw a
bogus conclusion.
test_support.py
Moved test_extcall's sortdict() into test_support, made it stronger,
and imported sortdict into other std tests that needed it.
test_unicode.py
Excluced cp875 from the "roundtrip over range(128)" test, because
cp875 doesn't have a well-defined inverse for unicode("?", "cp875").
See Python-Dev for excruciating details.
Cookie.py
Chaged various output functions to sort dicts before building
strings from them.
test_extcall
Fiddled the expected-result file. This remains sensitive to native
dict ordering, because, e.g., if there are multiple errors in a
keyword-arg dict (and test_extcall sets up many cases like that), the
specific error Python complains about first depends on native dict
ordering.
2001-05-12 21:19:31 -03:00
|
|
|
i = hash & mask;
|
1997-01-29 11:53:56 -04:00
|
|
|
ep = &ep0[i];
|
1998-10-06 13:01:14 -03:00
|
|
|
if (ep->me_key == NULL || ep->me_key == key)
|
1997-01-16 17:06:45 -04:00
|
|
|
return ep;
|
1997-01-27 20:00:11 -04:00
|
|
|
if (ep->me_key == dummy)
|
1997-01-16 17:06:45 -04:00
|
|
|
freeslot = ep;
|
1997-08-18 18:52:47 -03:00
|
|
|
else {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (ep->me_hash == hash) {
|
|
|
|
/* error can't have been checked yet */
|
|
|
|
checked_error = 1;
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
restore_error = 1;
|
|
|
|
PyErr_Fetch(&err_type, &err_value, &err_tb);
|
|
|
|
}
|
2001-01-17 20:39:02 -04:00
|
|
|
cmp = PyObject_RichCompareBool(ep->me_key, key, Py_EQ);
|
|
|
|
if (cmp > 0) {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (restore_error)
|
|
|
|
PyErr_Restore(err_type, err_value,
|
|
|
|
err_tb);
|
|
|
|
return ep;
|
|
|
|
}
|
2001-01-17 20:39:02 -04:00
|
|
|
else if (cmp < 0)
|
|
|
|
PyErr_Clear();
|
1997-08-18 18:52:47 -03:00
|
|
|
}
|
|
|
|
freeslot = NULL;
|
1997-01-18 03:55:05 -04:00
|
|
|
}
|
1999-03-24 15:06:42 -04:00
|
|
|
/* Derive incr from hash, just to make it more arbitrary. Note that
|
1997-01-27 20:00:11 -04:00
|
|
|
incr must not be 0, or we will get into an infinite loop.*/
|
2001-05-27 04:39:22 -03:00
|
|
|
incr = hash ^ ((unsigned long)hash >> 3);
|
|
|
|
|
2001-05-13 03:43:53 -03:00
|
|
|
/* In the loop, me_key == dummy is by far (factor of 100s) the
|
|
|
|
least likely outcome, so test for that last. */
|
1997-01-27 20:00:11 -04:00
|
|
|
for (;;) {
|
2001-05-27 04:39:22 -03:00
|
|
|
if (!incr)
|
|
|
|
incr = 1; /* and incr will never be 0 again */
|
|
|
|
ep = &ep0[(i + incr) & mask];
|
1997-01-27 20:00:11 -04:00
|
|
|
if (ep->me_key == NULL) {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (restore_error)
|
|
|
|
PyErr_Restore(err_type, err_value, err_tb);
|
2001-05-13 03:43:53 -03:00
|
|
|
return freeslot == NULL ? ep : freeslot;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
2001-05-13 03:43:53 -03:00
|
|
|
if (ep->me_key == key) {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (restore_error)
|
|
|
|
PyErr_Restore(err_type, err_value, err_tb);
|
1993-03-27 14:11:32 -04:00
|
|
|
return ep;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
}
|
2001-05-13 03:43:53 -03:00
|
|
|
else if (ep->me_hash == hash && ep->me_key != dummy) {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (!checked_error) {
|
|
|
|
checked_error = 1;
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
restore_error = 1;
|
|
|
|
PyErr_Fetch(&err_type, &err_value,
|
|
|
|
&err_tb);
|
|
|
|
}
|
|
|
|
}
|
2001-01-17 20:39:02 -04:00
|
|
|
cmp = PyObject_RichCompareBool(ep->me_key, key, Py_EQ);
|
|
|
|
if (cmp > 0) {
|
2000-08-31 16:04:07 -03:00
|
|
|
if (restore_error)
|
|
|
|
PyErr_Restore(err_type, err_value,
|
|
|
|
err_tb);
|
|
|
|
return ep;
|
|
|
|
}
|
2001-01-17 20:39:02 -04:00
|
|
|
else if (cmp < 0)
|
|
|
|
PyErr_Clear();
|
1997-01-27 20:00:11 -04:00
|
|
|
}
|
2001-05-13 03:43:53 -03:00
|
|
|
else if (ep->me_key == dummy && freeslot == NULL)
|
|
|
|
freeslot = ep;
|
2001-05-27 04:39:22 -03:00
|
|
|
/* Cycle through GF(2**n). */
|
|
|
|
if (incr & 1)
|
|
|
|
incr ^= mp->ma_poly; /* clears the lowest bit */
|
|
|
|
incr >>= 1;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-08-31 16:31:38 -03:00
|
|
|
/*
|
|
|
|
* Hacked up version of lookdict which can assume keys are always strings;
|
|
|
|
* this assumption allows testing for errors during PyObject_Compare() to
|
|
|
|
* be dropped; string-string comparisons never raise exceptions. This also
|
|
|
|
* means we don't need to go through PyObject_Compare(); we can always use
|
2001-05-24 13:56:35 -03:00
|
|
|
* _PyString_Eq directly.
|
2000-08-31 16:31:38 -03:00
|
|
|
*
|
|
|
|
* This really only becomes meaningful if proper error handling in lookdict()
|
|
|
|
* is too expensive.
|
|
|
|
*/
|
|
|
|
static dictentry *
|
|
|
|
lookdict_string(dictobject *mp, PyObject *key, register long hash)
|
|
|
|
{
|
|
|
|
register int i;
|
2001-05-27 04:39:22 -03:00
|
|
|
register unsigned int incr;
|
2000-08-31 16:31:38 -03:00
|
|
|
register dictentry *freeslot;
|
|
|
|
register unsigned int mask = mp->ma_size-1;
|
|
|
|
dictentry *ep0 = mp->ma_table;
|
|
|
|
register dictentry *ep;
|
|
|
|
|
|
|
|
/* make sure this function doesn't have to handle non-string keys */
|
|
|
|
if (!PyString_Check(key)) {
|
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
++converted;
|
|
|
|
#endif
|
|
|
|
mp->ma_lookup = lookdict;
|
|
|
|
return lookdict(mp, key, hash);
|
|
|
|
}
|
|
|
|
/* We must come up with (i, incr) such that 0 <= i < ma_size
|
|
|
|
and 0 < incr < ma_size and both are a function of hash */
|
Get rid of the superstitious "~" in dict hashing's "i = (~hash) & mask".
The comment following used to say:
/* We use ~hash instead of hash, as degenerate hash functions, such
as for ints <sigh>, can have lots of leading zeros. It's not
really a performance risk, but better safe than sorry.
12-Dec-00 tim: so ~hash produces lots of leading ones instead --
what's the gain? */
That is, there was never a good reason for doing it. And to the contrary,
as explained on Python-Dev last December, it tended to make the *sum*
(i + incr) & mask (which is the first table index examined in case of
collison) the same "too often" across distinct hashes.
Changing to the simpler "i = hash & mask" reduced the number of string-dict
collisions (== # number of times we go around the lookup for-loop) from about
6 million to 5 million during a full run of the test suite (these are
approximate because the test suite does some random stuff from run to run).
The number of collisions in non-string dicts also decreased, but not as
dramatically.
Note that this may, for a given dict, change the order (wrt previous
releases) of entries exposed by .keys(), .values() and .items(). A number
of std tests suffered bogus failures as a result. For dicts keyed by
small ints, or (less so) by characters, the order is much more likely to be
in increasing order of key now; e.g.,
>>> d = {}
>>> for i in range(10):
... d[i] = i
...
>>> d
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
>>>
Unfortunately. people may latch on to that in small examples and draw a
bogus conclusion.
test_support.py
Moved test_extcall's sortdict() into test_support, made it stronger,
and imported sortdict into other std tests that needed it.
test_unicode.py
Excluced cp875 from the "roundtrip over range(128)" test, because
cp875 doesn't have a well-defined inverse for unicode("?", "cp875").
See Python-Dev for excruciating details.
Cookie.py
Chaged various output functions to sort dicts before building
strings from them.
test_extcall
Fiddled the expected-result file. This remains sensitive to native
dict ordering, because, e.g., if there are multiple errors in a
keyword-arg dict (and test_extcall sets up many cases like that), the
specific error Python complains about first depends on native dict
ordering.
2001-05-12 21:19:31 -03:00
|
|
|
i = hash & mask;
|
2000-08-31 16:31:38 -03:00
|
|
|
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
|
2001-05-24 13:56:35 -03:00
|
|
|
&& _PyString_Eq(ep->me_key, key)) {
|
2000-08-31 16:31:38 -03:00
|
|
|
return ep;
|
|
|
|
}
|
|
|
|
freeslot = NULL;
|
|
|
|
}
|
|
|
|
/* Derive incr from hash, just to make it more arbitrary. Note that
|
|
|
|
incr must not be 0, or we will get into an infinite loop.*/
|
2001-05-27 04:39:22 -03:00
|
|
|
incr = hash ^ ((unsigned long)hash >> 3);
|
|
|
|
|
2001-05-13 03:43:53 -03:00
|
|
|
/* In the loop, me_key == dummy is by far (factor of 100s) the
|
|
|
|
least likely outcome, so test for that last. */
|
2000-08-31 16:31:38 -03:00
|
|
|
for (;;) {
|
2001-05-27 04:39:22 -03:00
|
|
|
if (!incr)
|
|
|
|
incr = 1; /* and incr will never be 0 again */
|
|
|
|
ep = &ep0[(i + incr) & mask];
|
2001-05-13 03:43:53 -03:00
|
|
|
if (ep->me_key == NULL)
|
|
|
|
return freeslot == NULL ? ep : freeslot;
|
|
|
|
if (ep->me_key == key
|
|
|
|
|| (ep->me_hash == hash
|
|
|
|
&& ep->me_key != dummy
|
2001-05-24 13:56:35 -03:00
|
|
|
&& _PyString_Eq(ep->me_key, key)))
|
2000-08-31 16:31:38 -03:00
|
|
|
return ep;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
if (ep->me_key == dummy && freeslot == NULL)
|
2001-05-13 03:43:53 -03:00
|
|
|
freeslot = ep;
|
2001-05-27 04:39:22 -03:00
|
|
|
/* Cycle through GF(2**n). */
|
|
|
|
if (incr & 1)
|
|
|
|
incr ^= mp->ma_poly; /* clears the lowest bit */
|
|
|
|
incr >>= 1;
|
2000-08-31 16:31:38 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1993-03-27 14:11:32 -04:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static void
|
2000-07-04 14:44:48 -03:00
|
|
|
insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *old_value;
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictentry *ep;
|
2000-08-31 16:31:38 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
1993-03-27 14:11:32 -04:00
|
|
|
if (ep->me_value != NULL) {
|
1995-01-02 15:07:15 -04:00
|
|
|
old_value = ep->me_value;
|
|
|
|
ep->me_value = value;
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_DECREF(old_value); /* which **CAN** re-enter */
|
|
|
|
Py_DECREF(key);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ep->me_key == NULL)
|
|
|
|
mp->ma_fill++;
|
|
|
|
else
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_DECREF(ep->me_key);
|
1993-03-27 14:11:32 -04:00
|
|
|
ep->me_key = key;
|
|
|
|
ep->me_hash = hash;
|
1995-01-02 15:07:15 -04:00
|
|
|
ep->me_value = value;
|
1993-03-27 14:11:32 -04:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static int
|
2000-07-04 14:44:48 -03:00
|
|
|
dictresize(dictobject *mp, int minused)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
2001-05-23 20:33:57 -03:00
|
|
|
int newsize, newpoly;
|
|
|
|
dictentry *oldtable, *newtable, *ep;
|
|
|
|
int i;
|
|
|
|
int is_oldtable_malloced;
|
|
|
|
dictentry small_copy[MINSIZE];
|
2001-05-19 04:04:38 -03:00
|
|
|
|
|
|
|
assert(minused >= 0);
|
2001-05-23 20:33:57 -03:00
|
|
|
|
|
|
|
/* Find the smallest table size > minused, and its poly[] entry. */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
newpoly = 0;
|
|
|
|
newsize = MINSIZE;
|
|
|
|
for (i = 0; i < sizeof(polys)/sizeof(polys[0]); ++i) {
|
1997-05-28 16:15:28 -03:00
|
|
|
if (newsize > minused) {
|
1997-01-27 20:00:11 -04:00
|
|
|
newpoly = polys[i];
|
1993-03-27 14:11:32 -04:00
|
|
|
break;
|
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
newsize <<= 1;
|
|
|
|
if (newsize < 0) /* overflow */
|
|
|
|
break;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
if (newpoly == 0) {
|
|
|
|
/* Ran out of polynomials or newsize overflowed. */
|
1997-05-02 00:12:38 -03:00
|
|
|
PyErr_NoMemory();
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2001-05-23 20:33:57 -03:00
|
|
|
|
|
|
|
/* Get space for a new table. */
|
|
|
|
oldtable = mp->ma_table;
|
|
|
|
assert(oldtable != NULL);
|
|
|
|
is_oldtable_malloced = oldtable != mp->ma_smalltable;
|
|
|
|
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
if (newsize == MINSIZE) {
|
2001-05-24 13:26:40 -03:00
|
|
|
/* A large table is shrinking, or we can't get any smaller. */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
newtable = mp->ma_smalltable;
|
2001-05-23 20:33:57 -03:00
|
|
|
if (newtable == oldtable) {
|
2001-05-24 13:26:40 -03:00
|
|
|
if (mp->ma_fill == mp->ma_used) {
|
|
|
|
/* No dummies, so no point doing anything. */
|
2001-05-23 20:33:57 -03:00
|
|
|
return 0;
|
2001-05-24 13:26:40 -03:00
|
|
|
}
|
|
|
|
/* 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);
|
2001-05-23 20:33:57 -03:00
|
|
|
memcpy(small_copy, oldtable, sizeof(small_copy));
|
|
|
|
oldtable = small_copy;
|
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
newtable = PyMem_NEW(dictentry, newsize);
|
|
|
|
if (newtable == NULL) {
|
|
|
|
PyErr_NoMemory();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 20:33:57 -03:00
|
|
|
|
|
|
|
/* Make the dict empty, using the new table. */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
assert(newtable != oldtable);
|
|
|
|
mp->ma_table = newtable;
|
1993-03-27 14:11:32 -04:00
|
|
|
mp->ma_size = newsize;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
memset(newtable, 0, sizeof(dictentry) * newsize);
|
1997-01-27 20:00:11 -04:00
|
|
|
mp->ma_poly = newpoly;
|
1993-03-27 14:11:32 -04:00
|
|
|
mp->ma_used = 0;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
i = mp->ma_fill;
|
|
|
|
mp->ma_fill = 0;
|
1995-01-02 15:07:15 -04:00
|
|
|
|
2001-05-17 19:25:34 -03:00
|
|
|
/* Copy the data over; this is refcount-neutral for active entries;
|
|
|
|
dummy entries aren't copied over, of course */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
for (ep = oldtable; i > 0; ep++) {
|
|
|
|
if (ep->me_value != NULL) { /* active entry */
|
|
|
|
--i;
|
2001-05-17 19:25:34 -03:00
|
|
|
insertdict(mp, ep->me_key, ep->me_hash, ep->me_value);
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
}
|
2001-05-17 19:25:34 -03:00
|
|
|
else if (ep->me_key != NULL) { /* dummy entry */
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
--i;
|
2001-05-17 19:25:34 -03:00
|
|
|
assert(ep->me_key == dummy);
|
|
|
|
Py_DECREF(ep->me_key);
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
2001-05-17 19:25:34 -03:00
|
|
|
/* else key == value == NULL: nothing to do */
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
1995-01-02 15:07:15 -04:00
|
|
|
|
2001-05-23 20:33:57 -03:00
|
|
|
if (is_oldtable_malloced)
|
2000-05-03 20:44:39 -03:00
|
|
|
PyMem_DEL(oldtable);
|
1993-03-27 14:11:32 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_GetItem(PyObject *op, PyObject *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
|
|
|
long hash;
|
2000-08-31 16:31:38 -03:00
|
|
|
dictobject *mp = (dictobject *)op;
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op)) {
|
1993-03-27 14:11:32 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
1993-10-22 09:04:32 -03:00
|
|
|
#ifdef CACHE_HASH
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
1993-10-22 09:04:32 -03:00
|
|
|
#endif
|
1997-01-23 15:39:29 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1998-05-13 22:00:51 -03:00
|
|
|
if (hash == -1) {
|
|
|
|
PyErr_Clear();
|
1997-01-23 15:39:29 -04:00
|
|
|
return NULL;
|
1998-05-13 22:00:51 -03:00
|
|
|
}
|
1997-01-23 15:39:29 -04:00
|
|
|
}
|
2000-08-31 16:31:38 -03:00
|
|
|
return (mp->ma_lookup)(mp, key, hash)->me_value;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
|
|
|
|
* dictionary if it is merely replacing the value for an existing key.
|
|
|
|
* This is means that it's safe to loop over a dictionary with
|
|
|
|
* PyDict_Next() and occasionally replace a value -- but you can't
|
|
|
|
* insert new keys or remove them.
|
|
|
|
*/
|
1993-03-27 14:11:32 -04:00
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictobject *mp;
|
1993-03-27 14:11:32 -04:00
|
|
|
register long hash;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
register int n_used;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = (dictobject *)op;
|
1993-10-22 09:04:32 -03:00
|
|
|
#ifdef CACHE_HASH
|
1997-05-02 00:12:38 -03:00
|
|
|
if (PyString_Check(key)) {
|
1997-01-18 03:55:05 -04:00
|
|
|
#ifdef INTERN_STRINGS
|
1997-05-02 00:12:38 -03:00
|
|
|
if (((PyStringObject *)key)->ob_sinterned != NULL) {
|
|
|
|
key = ((PyStringObject *)key)->ob_sinterned;
|
|
|
|
hash = ((PyStringObject *)key)->ob_shash;
|
1997-01-18 03:55:05 -04:00
|
|
|
}
|
|
|
|
else
|
1993-10-22 09:04:32 -03:00
|
|
|
#endif
|
1997-01-18 03:55:05 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = ((PyStringObject *)key)->ob_shash;
|
1997-01-18 03:55:05 -04:00
|
|
|
if (hash == -1)
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1997-01-18 03:55:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1997-01-18 03:55:05 -04:00
|
|
|
if (hash == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
assert(mp->ma_fill < mp->ma_size);
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
n_used = mp->ma_used;
|
|
|
|
Py_INCREF(value);
|
|
|
|
Py_INCREF(key);
|
|
|
|
insertdict(mp, key, hash, value);
|
|
|
|
/* If we added a key, we can safely resize. Otherwise skip this!
|
|
|
|
* If fill >= 2/3 size, adjust size. Normally, this doubles the
|
2001-03-21 15:23:56 -04:00
|
|
|
* 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).
|
|
|
|
*/
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
if (mp->ma_used > n_used && mp->ma_fill*3 >= mp->ma_size*2) {
|
|
|
|
if (dictresize(mp, mp->ma_used*2) != 0)
|
|
|
|
return -1;
|
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_DelItem(PyObject *op, PyObject *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictobject *mp;
|
1993-03-27 14:11:32 -04:00
|
|
|
register long hash;
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictentry *ep;
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *old_value, *old_key;
|
1995-01-02 15:07:15 -04:00
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
|
|
|
}
|
1993-10-22 09:04:32 -03:00
|
|
|
#ifdef CACHE_HASH
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
1993-10-22 09:04:32 -03:00
|
|
|
#endif
|
1997-01-23 15:39:29 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1997-01-23 15:39:29 -04:00
|
|
|
if (hash == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = (dictobject *)op;
|
2000-08-31 16:31:38 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
1993-03-27 14:11:32 -04:00
|
|
|
if (ep->me_value == NULL) {
|
1997-05-02 00:12:38 -03:00
|
|
|
PyErr_SetObject(PyExc_KeyError, key);
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
|
|
|
}
|
1995-01-02 15:07:15 -04:00
|
|
|
old_key = ep->me_key;
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_INCREF(dummy);
|
1993-03-27 14:11:32 -04:00
|
|
|
ep->me_key = dummy;
|
1995-01-02 15:07:15 -04:00
|
|
|
old_value = ep->me_value;
|
1993-03-27 14:11:32 -04:00
|
|
|
ep->me_value = NULL;
|
|
|
|
mp->ma_used--;
|
2000-12-12 21:02:46 -04:00
|
|
|
Py_DECREF(old_value);
|
|
|
|
Py_DECREF(old_key);
|
1993-03-27 14:11:32 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1993-05-19 11:50:45 -03:00
|
|
|
void
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Clear(PyObject *op)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-13 18:02:11 -03:00
|
|
|
dictobject *mp;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
dictentry *ep, *table;
|
|
|
|
int table_is_malloced;
|
|
|
|
int fill;
|
|
|
|
dictentry small_copy[MINSIZE];
|
|
|
|
#ifdef Py_DEBUG
|
|
|
|
int i, n;
|
|
|
|
#endif
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op))
|
1993-05-19 11:50:45 -03:00
|
|
|
return;
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = (dictobject *)op;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
#ifdef Py_DEBUG
|
1995-01-02 15:07:15 -04:00
|
|
|
n = mp->ma_size;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
i = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
table = mp->ma_table;
|
|
|
|
assert(table != NULL);
|
|
|
|
table_is_malloced = table != mp->ma_smalltable;
|
|
|
|
|
|
|
|
/* 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 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);
|
|
|
|
}
|
|
|
|
#ifdef Py_DEBUG
|
|
|
|
else
|
|
|
|
assert(ep->me_value == NULL);
|
|
|
|
#endif
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
|
|
|
|
if (table_is_malloced)
|
|
|
|
PyMem_DEL(table);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
2001-03-21 15:23:56 -04:00
|
|
|
/* 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
|
|
|
|
* delete keys), via PyDict_SetItem().
|
|
|
|
*/
|
1993-05-19 11:50:45 -03:00
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1993-05-19 11:50:45 -03:00
|
|
|
int i;
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictobject *mp;
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op))
|
1993-05-19 11:50:45 -03:00
|
|
|
return 0;
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = (dictobject *)op;
|
1993-05-19 11:50:45 -03:00
|
|
|
i = *ppos;
|
|
|
|
if (i < 0)
|
|
|
|
return 0;
|
|
|
|
while (i < mp->ma_size && mp->ma_table[i].me_value == NULL)
|
|
|
|
i++;
|
|
|
|
*ppos = i+1;
|
|
|
|
if (i >= mp->ma_size)
|
|
|
|
return 0;
|
|
|
|
if (pkey)
|
|
|
|
*pkey = mp->ma_table[i].me_key;
|
|
|
|
if (pvalue)
|
|
|
|
*pvalue = mp->ma_table[i].me_value;
|
|
|
|
return 1;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
static void
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_dealloc(register dictobject *mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictentry *ep;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
int fill = mp->ma_fill;
|
2000-03-13 12:01:29 -04:00
|
|
|
Py_TRASHCAN_SAFE_BEGIN(mp)
|
2000-06-30 02:02:53 -03:00
|
|
|
PyObject_GC_Fini(mp);
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
for (ep = mp->ma_table; fill > 0; ep++) {
|
|
|
|
if (ep->me_key) {
|
|
|
|
--fill;
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_DECREF(ep->me_key);
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
Py_XDECREF(ep->me_value);
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
if (mp->ma_table != mp->ma_smalltable)
|
2000-05-03 20:44:39 -03:00
|
|
|
PyMem_DEL(mp->ma_table);
|
2000-06-30 22:00:38 -03:00
|
|
|
mp = (dictobject *) PyObject_AS_GC(mp);
|
2000-05-03 20:44:39 -03:00
|
|
|
PyObject_DEL(mp);
|
2000-03-13 12:01:29 -04:00
|
|
|
Py_TRASHCAN_SAFE_END(mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_print(register dictobject *mp, register FILE *fp, register int flags)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
register int any;
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictentry *ep;
|
1998-04-10 19:47:14 -03:00
|
|
|
|
|
|
|
i = Py_ReprEnter((PyObject*)mp);
|
|
|
|
if (i != 0) {
|
|
|
|
if (i < 0)
|
|
|
|
return i;
|
|
|
|
fprintf(fp, "{...}");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1993-03-27 14:11:32 -04:00
|
|
|
fprintf(fp, "{");
|
|
|
|
any = 0;
|
|
|
|
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
|
|
|
|
if (ep->me_value != NULL) {
|
|
|
|
if (any++ > 0)
|
|
|
|
fprintf(fp, ", ");
|
1998-04-10 19:47:14 -03:00
|
|
|
if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) {
|
|
|
|
Py_ReprLeave((PyObject*)mp);
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
fprintf(fp, ": ");
|
1998-04-10 19:47:14 -03:00
|
|
|
if (PyObject_Print(ep->me_value, fp, 0) != 0) {
|
|
|
|
Py_ReprLeave((PyObject*)mp);
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(fp, "}");
|
1998-04-10 19:47:14 -03:00
|
|
|
Py_ReprLeave((PyObject*)mp);
|
1993-03-27 14:11:32 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_repr(dictobject *mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
auto PyObject *v;
|
|
|
|
PyObject *sepa, *colon;
|
1993-03-27 14:11:32 -04:00
|
|
|
register int i;
|
|
|
|
register int any;
|
1997-05-13 18:02:11 -03:00
|
|
|
register dictentry *ep;
|
1998-04-10 19:47:14 -03:00
|
|
|
|
|
|
|
i = Py_ReprEnter((PyObject*)mp);
|
|
|
|
if (i != 0) {
|
|
|
|
if (i > 0)
|
|
|
|
return PyString_FromString("{...}");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
v = PyString_FromString("{");
|
|
|
|
sepa = PyString_FromString(", ");
|
|
|
|
colon = PyString_FromString(": ");
|
1993-03-27 14:11:32 -04:00
|
|
|
any = 0;
|
1994-08-30 05:27:36 -03:00
|
|
|
for (i = 0, ep = mp->ma_table; i < mp->ma_size && v; i++, ep++) {
|
1993-03-27 14:11:32 -04:00
|
|
|
if (ep->me_value != NULL) {
|
|
|
|
if (any++)
|
1997-05-02 00:12:38 -03:00
|
|
|
PyString_Concat(&v, sepa);
|
|
|
|
PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_key));
|
|
|
|
PyString_Concat(&v, colon);
|
|
|
|
PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_value));
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
}
|
1997-05-02 00:12:38 -03:00
|
|
|
PyString_ConcatAndDel(&v, PyString_FromString("}"));
|
1998-04-10 19:47:14 -03:00
|
|
|
Py_ReprLeave((PyObject*)mp);
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_XDECREF(sepa);
|
|
|
|
Py_XDECREF(colon);
|
1993-03-27 14:11:32 -04:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_length(dictobject *mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
|
|
|
return mp->ma_used;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_subscript(dictobject *mp, register PyObject *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *v;
|
1993-10-22 09:04:32 -03:00
|
|
|
long hash;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
assert(mp->ma_table != NULL);
|
1993-10-22 09:04:32 -03:00
|
|
|
#ifdef CACHE_HASH
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
1993-10-22 09:04:32 -03:00
|
|
|
#endif
|
1997-01-23 15:39:29 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1997-01-23 15:39:29 -04:00
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
2000-08-31 16:31:38 -03:00
|
|
|
v = (mp->ma_lookup)(mp, key, hash) -> me_value;
|
1993-03-27 14:11:32 -04:00
|
|
|
if (v == NULL)
|
1997-05-02 00:12:38 -03:00
|
|
|
PyErr_SetObject(PyExc_KeyError, key);
|
1993-03-27 14:11:32 -04:00
|
|
|
else
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_INCREF(v);
|
1993-03-27 14:11:32 -04:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_ass_sub(dictobject *mp, PyObject *v, PyObject *w)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
|
|
|
if (w == NULL)
|
1997-05-02 00:12:38 -03:00
|
|
|
return PyDict_DelItem((PyObject *)mp, v);
|
1993-03-27 14:11:32 -04:00
|
|
|
else
|
1997-05-02 00:12:38 -03:00
|
|
|
return PyDict_SetItem((PyObject *)mp, v, w);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
1997-05-13 18:02:11 -03:00
|
|
|
static PyMappingMethods dict_as_mapping = {
|
|
|
|
(inquiry)dict_length, /*mp_length*/
|
|
|
|
(binaryfunc)dict_subscript, /*mp_subscript*/
|
|
|
|
(objobjargproc)dict_ass_sub, /*mp_ass_subscript*/
|
1993-03-27 14:11:32 -04:00
|
|
|
};
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_keys(register dictobject *mp, PyObject *args)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
register int i, j, n;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyArg_NoArgs(args))
|
1993-03-27 14:11:32 -04:00
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
again:
|
|
|
|
n = mp->ma_used;
|
|
|
|
v = PyList_New(n);
|
1993-03-27 14:11:32 -04:00
|
|
|
if (v == NULL)
|
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
if (n != mp->ma_used) {
|
|
|
|
/* Durnit. The allocations caused the dict to resize.
|
|
|
|
* Just start over, this shouldn't normally happen.
|
|
|
|
*/
|
|
|
|
Py_DECREF(v);
|
|
|
|
goto again;
|
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
for (i = 0, j = 0; i < mp->ma_size; i++) {
|
|
|
|
if (mp->ma_table[i].me_value != NULL) {
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *key = mp->ma_table[i].me_key;
|
|
|
|
Py_INCREF(key);
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
PyList_SET_ITEM(v, j, key);
|
1993-03-27 14:11:32 -04:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_values(register dictobject *mp, PyObject *args)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
register int i, j, n;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyArg_NoArgs(args))
|
1993-05-19 11:50:45 -03:00
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
again:
|
|
|
|
n = mp->ma_used;
|
|
|
|
v = PyList_New(n);
|
1993-05-19 11:50:45 -03:00
|
|
|
if (v == NULL)
|
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
if (n != mp->ma_used) {
|
|
|
|
/* Durnit. The allocations caused the dict to resize.
|
|
|
|
* Just start over, this shouldn't normally happen.
|
|
|
|
*/
|
|
|
|
Py_DECREF(v);
|
|
|
|
goto again;
|
|
|
|
}
|
1993-05-19 11:50:45 -03:00
|
|
|
for (i = 0, j = 0; i < mp->ma_size; i++) {
|
|
|
|
if (mp->ma_table[i].me_value != NULL) {
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *value = mp->ma_table[i].me_value;
|
|
|
|
Py_INCREF(value);
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
PyList_SET_ITEM(v, j, value);
|
1993-05-19 11:50:45 -03:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_items(register dictobject *mp, PyObject *args)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
register int i, j, n;
|
|
|
|
PyObject *item, *key, *value;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyArg_NoArgs(args))
|
1993-05-19 11:50:45 -03:00
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
/* Preallocate the list of tuples, to avoid allocations during
|
|
|
|
* the loop over the items, which could trigger GC, which
|
|
|
|
* could resize the dict. :-(
|
|
|
|
*/
|
|
|
|
again:
|
|
|
|
n = mp->ma_used;
|
|
|
|
v = PyList_New(n);
|
1993-05-19 11:50:45 -03:00
|
|
|
if (v == NULL)
|
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
item = PyTuple_New(2);
|
|
|
|
if (item == NULL) {
|
|
|
|
Py_DECREF(v);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyList_SET_ITEM(v, i, item);
|
|
|
|
}
|
|
|
|
if (n != mp->ma_used) {
|
|
|
|
/* Durnit. The allocations caused the dict to resize.
|
|
|
|
* Just start over, this shouldn't normally happen.
|
|
|
|
*/
|
|
|
|
Py_DECREF(v);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
/* Nothing we do below makes any function calls. */
|
1993-05-19 11:50:45 -03:00
|
|
|
for (i = 0, j = 0; i < mp->ma_size; i++) {
|
|
|
|
if (mp->ma_table[i].me_value != NULL) {
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
key = mp->ma_table[i].me_key;
|
|
|
|
value = mp->ma_table[i].me_value;
|
|
|
|
item = PyList_GET_ITEM(v, j);
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_INCREF(key);
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
PyTuple_SET_ITEM(item, 0, key);
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_INCREF(value);
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
PyTuple_SET_ITEM(item, 1, value);
|
1993-05-19 11:50:45 -03:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
assert(j == n);
|
1993-05-19 11:50:45 -03:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
1997-05-28 16:15:28 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_update(register dictobject *mp, PyObject *args)
|
1997-05-28 16:15:28 -03:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
dictobject *other;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
dictentry *entry;
|
1997-05-28 16:15:28 -03:00
|
|
|
if (!PyArg_Parse(args, "O!", &PyDict_Type, &other))
|
|
|
|
return NULL;
|
2001-01-03 18:34:59 -04:00
|
|
|
if (other == mp || other->ma_used == 0)
|
|
|
|
goto done; /* a.update(a) or a.update({}); nothing to do */
|
1997-05-28 16:15:28 -03:00
|
|
|
/* Do one big resize at the start, rather than incrementally
|
|
|
|
resizing as we insert new items. Expect that there will be
|
|
|
|
no (or few) overlapping keys. */
|
|
|
|
if ((mp->ma_fill + other->ma_used)*3 >= mp->ma_size*2) {
|
|
|
|
if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (i = 0; i < other->ma_size; i++) {
|
|
|
|
entry = &other->ma_table[i];
|
|
|
|
if (entry->me_value != NULL) {
|
|
|
|
Py_INCREF(entry->me_key);
|
|
|
|
Py_INCREF(entry->me_value);
|
|
|
|
insertdict(mp, entry->me_key, entry->me_hash,
|
|
|
|
entry->me_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_copy(register dictobject *mp, PyObject *args)
|
1997-05-28 16:15:28 -03:00
|
|
|
{
|
2000-03-30 18:27:31 -04:00
|
|
|
if (!PyArg_Parse(args, ""))
|
|
|
|
return NULL;
|
|
|
|
return PyDict_Copy((PyObject*)mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Copy(PyObject *o)
|
2000-03-30 18:27:31 -04:00
|
|
|
{
|
|
|
|
register dictobject *mp;
|
1997-05-28 16:15:28 -03:00
|
|
|
register int i;
|
|
|
|
dictobject *copy;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
dictentry *entry;
|
2000-03-30 18:27:31 -04:00
|
|
|
|
|
|
|
if (o == NULL || !PyDict_Check(o)) {
|
|
|
|
PyErr_BadInternalCall();
|
1997-05-28 16:15:28 -03:00
|
|
|
return NULL;
|
2000-03-30 18:27:31 -04:00
|
|
|
}
|
|
|
|
mp = (dictobject *)o;
|
1997-05-28 16:15:28 -03:00
|
|
|
copy = (dictobject *)PyDict_New();
|
|
|
|
if (copy == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (mp->ma_used > 0) {
|
|
|
|
if (dictresize(copy, mp->ma_used*3/2) != 0)
|
|
|
|
return NULL;
|
|
|
|
for (i = 0; i < mp->ma_size; i++) {
|
|
|
|
entry = &mp->ma_table[i];
|
|
|
|
if (entry->me_value != NULL) {
|
|
|
|
Py_INCREF(entry->me_key);
|
|
|
|
Py_INCREF(entry->me_value);
|
|
|
|
insertdict(copy, entry->me_key, entry->me_hash,
|
|
|
|
entry->me_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (PyObject *)copy;
|
|
|
|
}
|
|
|
|
|
1993-11-05 06:18:44 -04:00
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Size(PyObject *mp)
|
1993-11-05 06:18:44 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
if (mp == NULL || !PyDict_Check(mp)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-11-23 13:53:17 -04:00
|
|
|
return 0;
|
1993-11-05 06:18:44 -04:00
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
return ((dictobject *)mp)->ma_used;
|
1993-11-05 06:18:44 -04:00
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Keys(PyObject *mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
if (mp == NULL || !PyDict_Check(mp)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-03-27 14:11:32 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
return dict_keys((dictobject *)mp, (PyObject *)NULL);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Values(PyObject *mp)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
if (mp == NULL || !PyDict_Check(mp)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-05-19 11:50:45 -03:00
|
|
|
return NULL;
|
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
return dict_values((dictobject *)mp, (PyObject *)NULL);
|
1993-05-19 11:50:45 -03:00
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_Items(PyObject *mp)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
if (mp == NULL || !PyDict_Check(mp)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-05-19 11:50:45 -03:00
|
|
|
return NULL;
|
|
|
|
}
|
1997-05-13 18:02:11 -03:00
|
|
|
return dict_items((dictobject *)mp, (PyObject *)NULL);
|
1993-05-19 11:50:45 -03:00
|
|
|
}
|
|
|
|
|
1996-12-05 17:55:55 -04:00
|
|
|
/* Subroutine which returns the smallest key in a for which b's value
|
|
|
|
is different or absent. The value is returned too, through the
|
2001-05-10 05:32:44 -03:00
|
|
|
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). */
|
1996-12-05 17:55:55 -04:00
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
characterize(dictobject *a, dictobject *b, PyObject **pval)
|
1996-12-05 17:55:55 -04:00
|
|
|
{
|
2001-05-10 05:32:44 -03:00
|
|
|
PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
|
|
|
|
PyObject *aval = NULL; /* a[akey] */
|
2001-01-17 20:39:02 -04:00
|
|
|
int i, cmp;
|
1996-12-05 17:55:55 -04:00
|
|
|
|
|
|
|
for (i = 0; i < a->ma_size; i++) {
|
2001-05-10 05:32:44 -03:00
|
|
|
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;
|
2001-01-17 20:39:02 -04:00
|
|
|
}
|
2001-05-10 05:32:44 -03:00
|
|
|
if (cmp > 0 ||
|
|
|
|
i >= a->ma_size ||
|
|
|
|
a->ma_table[i].me_value == NULL)
|
1997-05-02 00:12:38 -03:00
|
|
|
{
|
2001-05-10 05:32:44 -03:00
|
|
|
/* 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;
|
1996-12-05 17:55:55 -04:00
|
|
|
}
|
|
|
|
}
|
2001-05-10 05:32:44 -03:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
1996-12-05 17:55:55 -04:00
|
|
|
}
|
2001-05-10 05:32:44 -03:00
|
|
|
*pval = aval;
|
|
|
|
return akey;
|
|
|
|
|
|
|
|
Fail:
|
|
|
|
Py_XDECREF(akey);
|
|
|
|
Py_XDECREF(aval);
|
|
|
|
*pval = NULL;
|
|
|
|
return NULL;
|
1996-12-05 17:55:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_compare(dictobject *a, dictobject *b)
|
1996-12-05 17:55:55 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *adiff, *bdiff, *aval, *bval;
|
1996-12-05 17:55:55 -04:00
|
|
|
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 */
|
2001-05-10 05:32:44 -03:00
|
|
|
|
1996-12-05 17:55:55 -04:00
|
|
|
/* Same length -- check all keys */
|
2001-05-10 05:32:44 -03:00
|
|
|
bdiff = bval = NULL;
|
1996-12-05 17:55:55 -04:00
|
|
|
adiff = characterize(a, b, &aval);
|
2001-05-10 05:32:44 -03:00
|
|
|
if (adiff == NULL) {
|
|
|
|
assert(!aval);
|
2001-05-10 15:58:31 -03:00
|
|
|
/* Either an error, or a is a subset with the same length so
|
2001-05-10 05:32:44 -03:00
|
|
|
* must be equal.
|
|
|
|
*/
|
|
|
|
res = PyErr_Occurred() ? -1 : 0;
|
|
|
|
goto Finished;
|
|
|
|
}
|
1996-12-05 17:55:55 -04:00
|
|
|
bdiff = characterize(b, a, &bval);
|
2001-05-10 05:32:44 -03:00
|
|
|
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)
|
1997-05-02 00:12:38 -03:00
|
|
|
res = PyObject_Compare(aval, bval);
|
2001-05-10 05:32:44 -03:00
|
|
|
|
|
|
|
Finished:
|
|
|
|
Py_XDECREF(adiff);
|
|
|
|
Py_XDECREF(bdiff);
|
|
|
|
Py_XDECREF(aval);
|
|
|
|
Py_XDECREF(bval);
|
1996-12-05 17:55:55 -04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2001-05-08 01:38:29 -03:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dict_equal(dictobject *a, dictobject *b)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
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_size; i++) {
|
|
|
|
PyObject *aval = a->ma_table[i].me_value;
|
|
|
|
if (aval != NULL) {
|
|
|
|
int cmp;
|
|
|
|
PyObject *bval;
|
|
|
|
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);
|
|
|
|
bval = PyDict_GetItem((PyObject *)b, key);
|
|
|
|
if (bval == NULL) {
|
|
|
|
Py_DECREF(aval);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
|
|
|
|
Py_DECREF(aval);
|
|
|
|
if (cmp <= 0) /* error or not equal */
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dict_richcompare(PyObject *v, PyObject *w, int op)
|
|
|
|
{
|
|
|
|
int cmp;
|
|
|
|
PyObject *res;
|
|
|
|
|
|
|
|
if (!PyDict_Check(v) || !PyDict_Check(w)) {
|
|
|
|
res = Py_NotImplemented;
|
|
|
|
}
|
|
|
|
else if (op == Py_EQ || op == Py_NE) {
|
|
|
|
cmp = dict_equal((dictobject *)v, (dictobject *)w);
|
|
|
|
if (cmp < 0)
|
|
|
|
return NULL;
|
|
|
|
res = (cmp == (op == Py_EQ)) ? Py_True : Py_False;
|
|
|
|
}
|
Restore dicts' tp_compare slot, and change dict_richcompare to say it
doesn't know how to do LE, LT, GE, GT. dict_richcompare can't do the
latter any faster than dict_compare can. More importantly, for
cmp(dict1, dict2), Python *first* tries rich compares with EQ, LT, and
GT one at a time, even if the tp_compare slot is defined, and
dict_richcompare called dict_compare for the latter two because
it couldn't do them itself. The result was a lot of wasted calls to
dict_compare. Now dict_richcompare gives up at once the times Python
calls it with LT and GT from try_rich_to_3way_compare(), and dict_compare
is called only once (when Python gets around to trying the tp_compare
slot).
Continued mystery: despite that this cut the number of calls to
dict_compare approximately in half in test_mutants.py, the latter still
runs amazingly slowly. Running under the debugger doesn't show excessive
activity in the dict comparison code anymore, so I'm guessing the culprit
is somewhere else -- but where? Perhaps in the element (key/value)
comparison code? We clearly spend a lot of time figuring out how to
compare things.
2001-05-10 18:45:19 -03:00
|
|
|
else
|
|
|
|
res = Py_NotImplemented;
|
2001-05-08 01:38:29 -03:00
|
|
|
Py_INCREF(res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_has_key(register dictobject *mp, PyObject *args)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *key;
|
1993-03-27 14:11:32 -04:00
|
|
|
long hash;
|
|
|
|
register long ok;
|
2000-02-23 11:47:16 -04:00
|
|
|
if (!PyArg_ParseTuple(args, "O:has_key", &key))
|
1993-03-27 14:11:32 -04:00
|
|
|
return NULL;
|
1993-10-22 09:04:32 -03:00
|
|
|
#ifdef CACHE_HASH
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
1993-10-22 09:04:32 -03:00
|
|
|
#endif
|
1997-01-23 15:39:29 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
hash = PyObject_Hash(key);
|
1997-01-23 15:39:29 -04:00
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
ok = (mp->ma_lookup)(mp, key, hash)->me_value != NULL;
|
1997-05-02 00:12:38 -03:00
|
|
|
return PyInt_FromLong(ok);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
1997-10-06 14:49:20 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_get(register dictobject *mp, PyObject *args)
|
1997-10-06 14:49:20 -03:00
|
|
|
{
|
|
|
|
PyObject *key;
|
1997-10-20 14:26:25 -03:00
|
|
|
PyObject *failobj = Py_None;
|
1997-10-06 14:49:20 -03:00
|
|
|
PyObject *val = NULL;
|
|
|
|
long hash;
|
|
|
|
|
2000-02-23 11:47:16 -04:00
|
|
|
if (!PyArg_ParseTuple(args, "O|O:get", &key, &failobj))
|
1997-10-06 14:49:20 -03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifdef CACHE_HASH
|
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
2000-08-31 16:31:38 -03:00
|
|
|
val = (mp->ma_lookup)(mp, key, hash)->me_value;
|
1997-10-20 14:26:25 -03:00
|
|
|
|
1997-10-06 14:49:20 -03:00
|
|
|
if (val == NULL)
|
|
|
|
val = failobj;
|
|
|
|
Py_INCREF(val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-08-08 13:12:54 -03:00
|
|
|
static PyObject *
|
|
|
|
dict_setdefault(register dictobject *mp, PyObject *args)
|
|
|
|
{
|
|
|
|
PyObject *key;
|
|
|
|
PyObject *failobj = Py_None;
|
|
|
|
PyObject *val = NULL;
|
|
|
|
long hash;
|
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args, "O|O:setdefault", &key, &failobj))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifdef CACHE_HASH
|
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
2000-08-31 16:31:38 -03:00
|
|
|
val = (mp->ma_lookup)(mp, key, hash)->me_value;
|
2000-08-08 13:12:54 -03:00
|
|
|
if (val == NULL) {
|
|
|
|
val = failobj;
|
|
|
|
if (PyDict_SetItem((PyObject*)mp, key, failobj))
|
|
|
|
val = NULL;
|
|
|
|
}
|
|
|
|
Py_XINCREF(val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_clear(register dictobject *mp, PyObject *args)
|
1997-03-21 17:55:12 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyArg_NoArgs(args))
|
1997-03-21 17:55:12 -04:00
|
|
|
return NULL;
|
1997-05-02 00:12:38 -03:00
|
|
|
PyDict_Clear((PyObject *)mp);
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
1997-03-21 17:55:12 -04:00
|
|
|
}
|
|
|
|
|
2000-12-12 18:02:18 -04:00
|
|
|
static PyObject *
|
|
|
|
dict_popitem(dictobject *mp, PyObject *args)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
dictentry *ep;
|
|
|
|
PyObject *res;
|
|
|
|
|
|
|
|
if (!PyArg_NoArgs(args))
|
|
|
|
return NULL;
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
/* Allocate the result tuple first. Believe it or not,
|
|
|
|
* this allocation could trigger a garbage collection which
|
|
|
|
* could resize the dict, which would invalidate the pointer
|
|
|
|
* (ep) into the dict calculated below.
|
|
|
|
* So we have to do this first.
|
|
|
|
*/
|
|
|
|
res = PyTuple_New(2);
|
|
|
|
if (res == NULL)
|
|
|
|
return NULL;
|
2001-04-15 21:02:32 -03:00
|
|
|
if (mp->ma_used == 0) {
|
|
|
|
Py_DECREF(res);
|
|
|
|
PyErr_SetString(PyExc_KeyError,
|
|
|
|
"popitem(): dictionary is empty");
|
|
|
|
return NULL;
|
|
|
|
}
|
2000-12-12 18:02:18 -04:00
|
|
|
/* 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 = (int)ep->me_hash;
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
/* 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.
|
2000-12-12 18:02:18 -04:00
|
|
|
*/
|
|
|
|
if (i >= mp->ma_size || i < 1)
|
|
|
|
i = 1; /* skip slot 0 */
|
|
|
|
while ((ep = &mp->ma_table[i])->me_value == NULL) {
|
|
|
|
i++;
|
|
|
|
if (i >= mp->ma_size)
|
|
|
|
i = 1;
|
|
|
|
}
|
|
|
|
}
|
Tentative fix for a problem that Tim discovered at the last moment,
and reported to python-dev: because we were calling dict_resize() in
PyDict_Next(), and because GC's dict_traverse() uses PyDict_Next(),
and because PyTuple_New() can cause GC, and because dict_items() calls
PyTuple_New(), it was possible for dict_items() to have the dict
resized right under its nose.
The solution is convoluted, and touches several places: keys(),
values(), items(), popitem(), PyDict_Next(), and PyDict_SetItem().
There are two parts to it. First, we no longer call dict_resize() in
PyDict_Next(), which seems to solve the immediate problem. But then
PyDict_SetItem() must have a different policy about when *it* calls
dict_resize(), because we want to guarantee (e.g. for an algorithm
that Jeremy uses in the compiler) that you can loop over a dict using
PyDict_Next() and make changes to the dict as long as those changes
are only value replacements for existing keys using PyDict_SetItem().
This is done by resizing *after* the insertion instead of before, and
by remembering the size before we insert the item, and if the size is
still the same, we don't bother to even check if we might need to
resize. An additional detail is that if the dict starts out empty, we
must still resize it before the insertion.
That was the first part. :-)
The second part is to make keys(), values(), items(), and popitem()
safe against side effects on the dict caused by allocations, under the
assumption that if the GC can cause arbitrary Python code to run, it
can cause other threads to run, and it's not inconceivable that our
dict could be resized -- it would be insane to write code that relies
on this, but not all code is sane.
Now, I have this nagging feeling that the loops in lookdict probably
are blissfully assuming that doing a simple key comparison does not
change the dict's size. This is not necessarily true (the keys could
be class instances after all). But that's a battle for another day.
2001-04-15 19:16:26 -03:00
|
|
|
PyTuple_SET_ITEM(res, 0, ep->me_key);
|
|
|
|
PyTuple_SET_ITEM(res, 1, ep->me_value);
|
|
|
|
Py_INCREF(dummy);
|
|
|
|
ep->me_key = dummy;
|
|
|
|
ep->me_value = NULL;
|
|
|
|
mp->ma_used--;
|
|
|
|
assert(mp->ma_table[0].me_value == NULL);
|
|
|
|
mp->ma_table[0].me_hash = i + 1; /* next place to start */
|
2000-12-12 18:02:18 -04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-06-23 11:18:11 -03:00
|
|
|
static int
|
|
|
|
dict_traverse(PyObject *op, visitproc visit, void *arg)
|
|
|
|
{
|
|
|
|
int i = 0, err;
|
|
|
|
PyObject *pk;
|
|
|
|
PyObject *pv;
|
|
|
|
|
|
|
|
while (PyDict_Next(op, &i, &pk, &pv)) {
|
|
|
|
err = visit(pk, arg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = visit(pv, arg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dict_tp_clear(PyObject *op)
|
|
|
|
{
|
|
|
|
PyDict_Clear(op);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2001-05-01 09:10:21 -03:00
|
|
|
staticforward PyObject *dictiter_new(dictobject *, binaryfunc);
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
select_key(PyObject *key, PyObject *value)
|
|
|
|
{
|
|
|
|
Py_INCREF(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
select_value(PyObject *key, PyObject *value)
|
|
|
|
{
|
|
|
|
Py_INCREF(value);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
select_item(PyObject *key, PyObject *value)
|
|
|
|
{
|
|
|
|
PyObject *res = PyTuple_New(2);
|
|
|
|
|
|
|
|
if (res != NULL) {
|
|
|
|
Py_INCREF(key);
|
|
|
|
Py_INCREF(value);
|
|
|
|
PyTuple_SET_ITEM(res, 0, key);
|
|
|
|
PyTuple_SET_ITEM(res, 1, value);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dict_iterkeys(dictobject *dict, PyObject *args)
|
|
|
|
{
|
|
|
|
if (!PyArg_ParseTuple(args, ""))
|
|
|
|
return NULL;
|
|
|
|
return dictiter_new(dict, select_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dict_itervalues(dictobject *dict, PyObject *args)
|
|
|
|
{
|
|
|
|
if (!PyArg_ParseTuple(args, ""))
|
|
|
|
return NULL;
|
|
|
|
return dictiter_new(dict, select_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dict_iteritems(dictobject *dict, PyObject *args)
|
|
|
|
{
|
|
|
|
if (!PyArg_ParseTuple(args, ""))
|
|
|
|
return NULL;
|
|
|
|
return dictiter_new(dict, select_item);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-13 19:18:45 -04:00
|
|
|
static char has_key__doc__[] =
|
|
|
|
"D.has_key(k) -> 1 if D has a key k, else 0";
|
|
|
|
|
|
|
|
static char get__doc__[] =
|
|
|
|
"D.get(k[,d]) -> D[k] if D.has_key(k), else d. d defaults to None.";
|
|
|
|
|
|
|
|
static char setdefault_doc__[] =
|
|
|
|
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if not D.has_key(k)";
|
|
|
|
|
|
|
|
static char 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";
|
|
|
|
|
|
|
|
static char keys__doc__[] =
|
|
|
|
"D.keys() -> list of D's keys";
|
|
|
|
|
|
|
|
static char items__doc__[] =
|
|
|
|
"D.items() -> list of D's (key, value) pairs, as 2-tuples";
|
|
|
|
|
|
|
|
static char values__doc__[] =
|
|
|
|
"D.values() -> list of D's values";
|
|
|
|
|
|
|
|
static char update__doc__[] =
|
|
|
|
"D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]";
|
|
|
|
|
|
|
|
static char clear__doc__[] =
|
|
|
|
"D.clear() -> None. Remove all items from D.";
|
|
|
|
|
|
|
|
static char copy__doc__[] =
|
|
|
|
"D.copy() -> a shallow copy of D";
|
|
|
|
|
2001-05-01 09:10:21 -03:00
|
|
|
static char iterkeys__doc__[] =
|
|
|
|
"D.iterkeys() -> an iterator over the keys of D";
|
|
|
|
|
|
|
|
static char itervalues__doc__[] =
|
|
|
|
"D.itervalues() -> an iterator over the values of D";
|
|
|
|
|
|
|
|
static char iteritems__doc__[] =
|
|
|
|
"D.iteritems() -> an iterator over the (key, value) items of D";
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyMethodDef mapp_methods[] = {
|
2000-12-13 19:18:45 -04:00
|
|
|
{"has_key", (PyCFunction)dict_has_key, METH_VARARGS,
|
|
|
|
has_key__doc__},
|
|
|
|
{"get", (PyCFunction)dict_get, METH_VARARGS,
|
|
|
|
get__doc__},
|
|
|
|
{"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS,
|
|
|
|
setdefault_doc__},
|
|
|
|
{"popitem", (PyCFunction)dict_popitem, METH_OLDARGS,
|
|
|
|
popitem__doc__},
|
|
|
|
{"keys", (PyCFunction)dict_keys, METH_OLDARGS,
|
|
|
|
keys__doc__},
|
|
|
|
{"items", (PyCFunction)dict_items, METH_OLDARGS,
|
|
|
|
items__doc__},
|
|
|
|
{"values", (PyCFunction)dict_values, METH_OLDARGS,
|
|
|
|
values__doc__},
|
|
|
|
{"update", (PyCFunction)dict_update, METH_OLDARGS,
|
|
|
|
update__doc__},
|
|
|
|
{"clear", (PyCFunction)dict_clear, METH_OLDARGS,
|
|
|
|
clear__doc__},
|
|
|
|
{"copy", (PyCFunction)dict_copy, METH_OLDARGS,
|
|
|
|
copy__doc__},
|
2001-05-01 09:10:21 -03:00
|
|
|
{"iterkeys", (PyCFunction)dict_iterkeys, METH_VARARGS,
|
|
|
|
iterkeys__doc__},
|
|
|
|
{"itervalues", (PyCFunction)dict_itervalues, METH_VARARGS,
|
|
|
|
itervalues__doc__},
|
|
|
|
{"iteritems", (PyCFunction)dict_iteritems, METH_VARARGS,
|
|
|
|
iteritems__doc__},
|
2000-12-13 19:18:45 -04:00
|
|
|
{NULL, NULL} /* sentinel */
|
1993-03-27 14:11:32 -04:00
|
|
|
};
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
dict_getattr(dictobject *mp, char *name)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
return Py_FindMethod(mapp_methods, (PyObject *)mp, name);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
2001-04-20 13:50:40 -03:00
|
|
|
static int
|
|
|
|
dict_contains(dictobject *mp, PyObject *key)
|
|
|
|
{
|
|
|
|
long hash;
|
|
|
|
|
|
|
|
#ifdef CACHE_HASH
|
|
|
|
if (!PyString_Check(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
SF patch #425242: Patch which "inlines" small dictionaries.
The idea is Marc-Andre Lemburg's, the implementation is Tim's.
Add a new ma_smalltable member to dictobjects, an embedded vector of
MINSIZE (8) dictentry structs. Short course is that this lets us avoid
additional malloc(s) for dicts with no more than 5 entries.
The changes are widespread but mostly small.
Long course: WRT speed, all scalar operations (getitem, setitem, delitem)
on non-empty dicts benefit from no longer needing NULL-pointer checks
(ma_table is never NULL anymore). Bulk operations (copy, update, resize,
clearing slots during dealloc) benefit in some cases from now looping
on the ma_fill count rather than on ma_size, but that was an unexpected
benefit: the original reason to loop on ma_fill was to let bulk
operations on empty dicts end quickly (since the NULL-pointer checks
went away, empty dicts aren't special-cased any more).
Special considerations:
For dicts that remain empty, this change is a lose on two counts:
the dict object contains 8 new dictentry slots now that weren't
needed before, and dict object creation also spends time memset'ing
these doomed-to-be-unsused slots to NULLs.
For dicts with one or two entries that never get larger than 2, it's
a mix: a malloc()/free() pair is no longer needed, and the 2-entry case
gets to use 8 slots (instead of 4) thus decreasing the chance of
collision. Against that, dict object creation spends time memset'ing
4 slots that aren't strictly needed in this case.
For dicts with 3 through 5 entries that never get larger than 5, it's a
pure win: the dict is created with all the space they need, and they
never need to resize. Before they suffered two malloc()/free() calls,
plus 1 dict resize, to get enough space. In addition, the 8-slot
table they ended with consumed more memory overall, because of the
hidden overhead due to the additional malloc.
For dicts with 6 or more entries, the ma_smalltable member is wasted
space, but then these are large(r) dicts so 8 slots more or less doesn't
make much difference. They still benefit all the time from removing
ubiquitous dynamic null-pointer checks, and get a small benefit (but
relatively smaller the larger the dict) from not having to do two
mallocs, two frees, and a resize on the way *to* getting their sixth
entry.
All in all it appears a small but definite general win, with larger
benefits in specific cases. It's especially nice that it allowed to
get rid of several branches, gotos and labels, and overall made the
code smaller.
2001-05-22 17:40:22 -03:00
|
|
|
return (mp->ma_lookup)(mp, key, hash)->me_value != NULL;
|
2001-04-20 13:50:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Hack to implement "key in dict" */
|
|
|
|
static PySequenceMethods dict_as_sequence = {
|
|
|
|
0, /* sq_length */
|
|
|
|
0, /* sq_concat */
|
|
|
|
0, /* sq_repeat */
|
|
|
|
0, /* sq_item */
|
|
|
|
0, /* sq_slice */
|
|
|
|
0, /* sq_ass_item */
|
|
|
|
0, /* sq_ass_slice */
|
|
|
|
(objobjproc)dict_contains, /* sq_contains */
|
|
|
|
0, /* sq_inplace_concat */
|
|
|
|
0, /* sq_inplace_repeat */
|
|
|
|
};
|
|
|
|
|
2001-05-01 09:10:21 -03:00
|
|
|
static PyObject *
|
|
|
|
dict_iter(dictobject *dict)
|
|
|
|
{
|
|
|
|
return dictiter_new(dict, select_key);
|
|
|
|
}
|
2001-04-20 16:13:02 -03:00
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyTypeObject PyDict_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
1993-03-27 14:11:32 -04:00
|
|
|
0,
|
1993-03-29 06:43:31 -04:00
|
|
|
"dictionary",
|
2000-06-30 02:02:53 -03:00
|
|
|
sizeof(dictobject) + PyGC_HEAD_SIZE,
|
1993-03-27 14:11:32 -04:00
|
|
|
0,
|
2001-01-17 20:39:02 -04:00
|
|
|
(destructor)dict_dealloc, /* tp_dealloc */
|
|
|
|
(printfunc)dict_print, /* tp_print */
|
|
|
|
(getattrfunc)dict_getattr, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
Restore dicts' tp_compare slot, and change dict_richcompare to say it
doesn't know how to do LE, LT, GE, GT. dict_richcompare can't do the
latter any faster than dict_compare can. More importantly, for
cmp(dict1, dict2), Python *first* tries rich compares with EQ, LT, and
GT one at a time, even if the tp_compare slot is defined, and
dict_richcompare called dict_compare for the latter two because
it couldn't do them itself. The result was a lot of wasted calls to
dict_compare. Now dict_richcompare gives up at once the times Python
calls it with LT and GT from try_rich_to_3way_compare(), and dict_compare
is called only once (when Python gets around to trying the tp_compare
slot).
Continued mystery: despite that this cut the number of calls to
dict_compare approximately in half in test_mutants.py, the latter still
runs amazingly slowly. Running under the debugger doesn't show excessive
activity in the dict comparison code anymore, so I'm guessing the culprit
is somewhere else -- but where? Perhaps in the element (key/value)
comparison code? We clearly spend a lot of time figuring out how to
compare things.
2001-05-10 18:45:19 -03:00
|
|
|
(cmpfunc)dict_compare, /* tp_compare */
|
2001-01-17 20:39:02 -04:00
|
|
|
(reprfunc)dict_repr, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
2001-04-20 13:50:40 -03:00
|
|
|
&dict_as_sequence, /* tp_as_sequence */
|
2001-01-17 20:39:02 -04:00
|
|
|
&dict_as_mapping, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
0, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */
|
|
|
|
0, /* tp_doc */
|
|
|
|
(traverseproc)dict_traverse, /* tp_traverse */
|
|
|
|
(inquiry)dict_tp_clear, /* tp_clear */
|
2001-05-08 01:38:29 -03:00
|
|
|
dict_richcompare, /* tp_richcompare */
|
2001-04-20 16:13:02 -03:00
|
|
|
0, /* tp_weaklistoffset */
|
2001-05-01 09:10:21 -03:00
|
|
|
(getiterfunc)dict_iter, /* tp_iter */
|
2001-04-23 11:08:49 -03:00
|
|
|
0, /* tp_iternext */
|
1993-03-27 14:11:32 -04:00
|
|
|
};
|
|
|
|
|
1997-05-16 11:23:33 -03:00
|
|
|
/* For backward compatibility with old dictionary interface */
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
PyObject *
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_GetItemString(PyObject *v, char *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-16 11:23:33 -03:00
|
|
|
PyObject *kv, *rv;
|
|
|
|
kv = PyString_FromString(key);
|
|
|
|
if (kv == NULL)
|
|
|
|
return NULL;
|
|
|
|
rv = PyDict_GetItem(v, kv);
|
|
|
|
Py_DECREF(kv);
|
|
|
|
return rv;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_SetItemString(PyObject *v, char *key, PyObject *item)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-16 11:23:33 -03:00
|
|
|
PyObject *kv;
|
|
|
|
int err;
|
|
|
|
kv = PyString_FromString(key);
|
|
|
|
if (kv == NULL)
|
1997-05-20 15:35:19 -03:00
|
|
|
return -1;
|
1997-09-29 20:31:11 -03:00
|
|
|
PyString_InternInPlace(&kv); /* XXX Should we really? */
|
1997-05-16 11:23:33 -03:00
|
|
|
err = PyDict_SetItem(v, kv, item);
|
|
|
|
Py_DECREF(kv);
|
|
|
|
return err;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-07-04 14:44:48 -03:00
|
|
|
PyDict_DelItemString(PyObject *v, char *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-16 11:23:33 -03:00
|
|
|
PyObject *kv;
|
|
|
|
int err;
|
|
|
|
kv = PyString_FromString(key);
|
|
|
|
if (kv == NULL)
|
1997-05-20 15:35:19 -03:00
|
|
|
return -1;
|
1997-05-16 11:23:33 -03:00
|
|
|
err = PyDict_DelItem(v, kv);
|
|
|
|
Py_DECREF(kv);
|
|
|
|
return err;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
2001-04-20 16:13:02 -03:00
|
|
|
|
|
|
|
/* Dictionary iterator type */
|
|
|
|
|
|
|
|
extern PyTypeObject PyDictIter_Type; /* Forward */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PyObject_HEAD
|
|
|
|
dictobject *di_dict;
|
2001-05-02 12:13:44 -03:00
|
|
|
int di_used;
|
2001-04-20 16:13:02 -03:00
|
|
|
int di_pos;
|
2001-05-01 09:10:21 -03:00
|
|
|
binaryfunc di_select;
|
2001-04-20 16:13:02 -03:00
|
|
|
} dictiterobject;
|
|
|
|
|
|
|
|
static PyObject *
|
2001-05-01 09:10:21 -03:00
|
|
|
dictiter_new(dictobject *dict, binaryfunc select)
|
2001-04-20 16:13:02 -03:00
|
|
|
{
|
|
|
|
dictiterobject *di;
|
|
|
|
di = PyObject_NEW(dictiterobject, &PyDictIter_Type);
|
|
|
|
if (di == NULL)
|
|
|
|
return NULL;
|
|
|
|
Py_INCREF(dict);
|
|
|
|
di->di_dict = dict;
|
2001-05-02 12:13:44 -03:00
|
|
|
di->di_used = dict->ma_used;
|
2001-04-20 16:13:02 -03:00
|
|
|
di->di_pos = 0;
|
2001-05-01 09:10:21 -03:00
|
|
|
di->di_select = select;
|
2001-04-20 16:13:02 -03:00
|
|
|
return (PyObject *)di;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dictiter_dealloc(dictiterobject *di)
|
|
|
|
{
|
|
|
|
Py_DECREF(di->di_dict);
|
|
|
|
PyObject_DEL(di);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dictiter_next(dictiterobject *di, PyObject *args)
|
|
|
|
{
|
2001-05-01 09:10:21 -03:00
|
|
|
PyObject *key, *value;
|
2001-04-23 11:08:49 -03:00
|
|
|
|
2001-05-02 12:13:44 -03:00
|
|
|
if (di->di_used != di->di_dict->ma_used) {
|
2001-04-20 16:13:02 -03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
|
|
"dictionary changed size during iteration");
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-05-01 09:10:21 -03:00
|
|
|
if (PyDict_Next((PyObject *)(di->di_dict), &di->di_pos, &key, &value)) {
|
|
|
|
return (*di->di_select)(key, value);
|
2001-04-20 16:13:02 -03:00
|
|
|
}
|
|
|
|
PyErr_SetObject(PyExc_StopIteration, Py_None);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
dictiter_getiter(PyObject *it)
|
|
|
|
{
|
|
|
|
Py_INCREF(it);
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef dictiter_methods[] = {
|
|
|
|
{"next", (PyCFunction)dictiter_next, METH_VARARGS,
|
|
|
|
"it.next() -- get the next value, or raise StopIteration"},
|
|
|
|
{NULL, NULL} /* sentinel */
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyObject *
|
2001-04-23 11:08:49 -03:00
|
|
|
dictiter_getattr(dictiterobject *di, char *name)
|
|
|
|
{
|
|
|
|
return Py_FindMethod(dictiter_methods, (PyObject *)di, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *dictiter_iternext(dictiterobject *di)
|
2001-04-20 16:13:02 -03:00
|
|
|
{
|
2001-05-01 09:10:21 -03:00
|
|
|
PyObject *key, *value;
|
2001-04-23 11:08:49 -03:00
|
|
|
|
2001-05-02 12:13:44 -03:00
|
|
|
if (di->di_used != di->di_dict->ma_used) {
|
2001-04-23 11:08:49 -03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
|
|
"dictionary changed size during iteration");
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-05-01 09:10:21 -03:00
|
|
|
if (PyDict_Next((PyObject *)(di->di_dict), &di->di_pos, &key, &value)) {
|
|
|
|
return (*di->di_select)(key, value);
|
2001-04-23 11:08:49 -03:00
|
|
|
}
|
|
|
|
return NULL;
|
2001-04-20 16:13:02 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
PyTypeObject PyDictIter_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
|
|
|
"dictionary-iterator", /* tp_name */
|
|
|
|
sizeof(dictiterobject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)dictiter_dealloc, /* tp_dealloc */
|
|
|
|
0, /* tp_print */
|
|
|
|
(getattrfunc)dictiter_getattr, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
|
|
|
0, /* tp_compare */
|
|
|
|
0, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
0, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
|
|
0, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
0, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
(getiterfunc)dictiter_getiter, /* tp_iter */
|
2001-04-23 11:08:49 -03:00
|
|
|
(iternextfunc)dictiter_iternext, /* tp_iternext */
|
2001-04-20 16:13:02 -03:00
|
|
|
};
|