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
|
|
|
|
2003-05-03 03:51:59 -03:00
|
|
|
/* The distribution includes a separate file, Objects/dictnotes.txt,
|
2005-12-31 21:19:23 -04:00
|
|
|
describing explorations into dictionary design and optimization.
|
2003-05-03 03:51:59 -03:00
|
|
|
It covers typical dictionary use patterns, the parameters for
|
|
|
|
tuning dictionaries, and several ideas for possible optimizations.
|
|
|
|
*/
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
#include "Python.h"
|
1993-03-27 14:11:32 -04:00
|
|
|
|
2001-08-02 01:15:00 -03:00
|
|
|
typedef PyDictEntry dictentry;
|
|
|
|
typedef PyDictObject dictobject;
|
1997-01-27 20:00:11 -04:00
|
|
|
|
2001-06-02 02:27:19 -03:00
|
|
|
/* Define this out if you don't want conversion statistics on exit. */
|
2000-08-31 16:31:38 -03:00
|
|
|
#undef SHOW_CONVERSION_COUNTS
|
|
|
|
|
2001-06-02 02:27:19 -03:00
|
|
|
/* See large comment block below. This must be >= 1. */
|
|
|
|
#define PERTURB_SHIFT 5
|
|
|
|
|
1997-01-27 20:00:11 -04:00
|
|
|
/*
|
2001-06-02 02:27:19 -03:00
|
|
|
Major subtleties ahead: Most hash schemes depend on having a "good" hash
|
|
|
|
function, in the sense of simulating randomness. Python doesn't: its most
|
|
|
|
important hash functions (for strings and ints) are very regular in common
|
|
|
|
cases:
|
|
|
|
|
|
|
|
>>> map(hash, (0, 1, 2, 3))
|
|
|
|
[0, 1, 2, 3]
|
|
|
|
>>> map(hash, ("namea", "nameb", "namec", "named"))
|
|
|
|
[-1658398457, -1658398460, -1658398459, -1658398462]
|
|
|
|
>>>
|
|
|
|
|
|
|
|
This isn't necessarily bad! To the contrary, in a table of size 2**i, taking
|
|
|
|
the low-order i bits as the initial table index is extremely fast, and there
|
|
|
|
are no collisions at all for dicts indexed by a contiguous range of ints.
|
|
|
|
The same is approximately true when keys are "consecutive" strings. So this
|
|
|
|
gives better-than-random behavior in common cases, and that's very desirable.
|
|
|
|
|
|
|
|
OTOH, when collisions occur, the tendency to fill contiguous slices of the
|
|
|
|
hash table makes a good collision resolution strategy crucial. Taking only
|
|
|
|
the last i bits of the hash code is also vulnerable: for example, consider
|
|
|
|
[i << 16 for i in range(20000)] as a set of keys. Since ints are their own
|
|
|
|
hash codes, and this fits in a dict of size 2**15, the last 15 bits of every
|
|
|
|
hash code are all 0: they *all* map to the same table index.
|
|
|
|
|
|
|
|
But catering to unusual cases should not slow the usual ones, so we just take
|
|
|
|
the last i bits anyway. It's up to collision resolution to do the rest. If
|
|
|
|
we *usually* find the key we're looking for on the first try (and, it turns
|
|
|
|
out, we usually do -- the table load factor is kept under 2/3, so the odds
|
|
|
|
are solidly in our favor), then it makes best sense to keep the initial index
|
|
|
|
computation dirt cheap.
|
|
|
|
|
|
|
|
The first half of collision resolution is to visit table indices via this
|
|
|
|
recurrence:
|
|
|
|
|
|
|
|
j = ((5*j) + 1) mod 2**i
|
|
|
|
|
|
|
|
For any initial j in range(2**i), repeating that 2**i times generates each
|
|
|
|
int in range(2**i) exactly once (see any text on random-number generation for
|
|
|
|
proof). By itself, this doesn't help much: like linear probing (setting
|
|
|
|
j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed
|
|
|
|
order. This would be bad, except that's not the only thing we do, and it's
|
|
|
|
actually *good* in the common cases where hash keys are consecutive. In an
|
|
|
|
example that's really too small to make this entirely clear, for a table of
|
|
|
|
size 2**3 the order of indices is:
|
|
|
|
|
|
|
|
0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating]
|
|
|
|
|
|
|
|
If two things come in at index 5, the first place we look after is index 2,
|
|
|
|
not 6, so if another comes in at index 6 the collision at 5 didn't hurt it.
|
|
|
|
Linear probing is deadly in this case because there the fixed probe order
|
|
|
|
is the *same* as the order consecutive keys are likely to arrive. But it's
|
|
|
|
extremely unlikely hash codes will follow a 5*j+1 recurrence by accident,
|
|
|
|
and certain that consecutive hash codes do not.
|
|
|
|
|
|
|
|
The other half of the strategy is to get the other bits of the hash code
|
|
|
|
into play. This is done by initializing a (unsigned) vrbl "perturb" to the
|
|
|
|
full hash code, and changing the recurrence to:
|
|
|
|
|
|
|
|
j = (5*j) + 1 + perturb;
|
|
|
|
perturb >>= PERTURB_SHIFT;
|
|
|
|
use j % 2**i as the next table index;
|
|
|
|
|
|
|
|
Now the probe sequence depends (eventually) on every bit in the hash code,
|
|
|
|
and the pseudo-scrambling property of recurring on 5*j+1 is more valuable,
|
|
|
|
because it quickly magnifies small differences in the bits that didn't affect
|
|
|
|
the initial index. Note that because perturb is unsigned, if the recurrence
|
|
|
|
is executed often enough perturb eventually becomes and remains 0. At that
|
|
|
|
point (very rarely reached) the recurrence is on (just) 5*j+1 again, and
|
|
|
|
that's certain to find an empty slot eventually (since it generates every int
|
|
|
|
in range(2**i), and we make sure there's always at least one empty slot).
|
|
|
|
|
|
|
|
Selecting a good value for PERTURB_SHIFT is a balancing act. You want it
|
|
|
|
small so that the high bits of the hash code continue to affect the probe
|
|
|
|
sequence across iterations; but you want it large so that in really bad cases
|
|
|
|
the high-order hash bits have an effect on early iterations. 5 was "the
|
|
|
|
best" in minimizing total collisions across experiments Tim Peters ran (on
|
|
|
|
both normal and pathological cases), but 4 and 6 weren't significantly worse.
|
|
|
|
|
|
|
|
Historical: Reimer Behrends contributed the idea of using a polynomial-based
|
|
|
|
approach, using repeated multiplication by x in GF(2**n) where an irreducible
|
|
|
|
polynomial for each table size was chosen such that x was a primitive root.
|
|
|
|
Christian Tismer later extended that to use division by x instead, as an
|
|
|
|
efficient way to get the high bits of the hash code into play. This scheme
|
|
|
|
also gave excellent collision statistics, but was more expensive: two
|
|
|
|
if-tests were required inside the loop; computing "the next" index took about
|
|
|
|
the same number of operations but without as much potential parallelism
|
|
|
|
(e.g., computing 5*j can go on at the same time as computing 1+perturb in the
|
|
|
|
above, and then shifting perturb can be done while the table index is being
|
|
|
|
masked); and the dictobject struct required a member to hold the table's
|
|
|
|
polynomial. In Tim's experiments the current scheme ran faster, produced
|
|
|
|
equally good collision statistics, needed less code & used less memory.
|
2006-05-30 01:16:25 -03:00
|
|
|
|
|
|
|
Theoretical Python 2.5 headache: hash codes are only C "long", but
|
|
|
|
sizeof(Py_ssize_t) > sizeof(long) may be possible. In that case, and if a
|
|
|
|
dict is genuinely huge, then only the slots directly reachable via indexing
|
|
|
|
by a C long can be the first slot in a probe sequence. The probe sequence
|
|
|
|
will still eventually reach every slot in the table, but the collision rate
|
|
|
|
on initial probes may be much higher than this scheme was designed for.
|
|
|
|
Getting a hash code as fat as Py_ssize_t is the only real cure. But in
|
|
|
|
practice, this probably won't make a lick of difference for many years (at
|
|
|
|
which point everyone will have terabytes of RAM on 64-bit boxes).
|
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
|
|
|
|
1993-03-27 14:11:32 -04:00
|
|
|
/* Object used as dummy key to fill deleted entries */
|
2005-08-16 23:19:36 -03:00
|
|
|
static PyObject *dummy = NULL; /* Initialized by first call to newdictobject() */
|
1993-03-27 14:11:32 -04:00
|
|
|
|
2006-04-12 14:06:05 -03:00
|
|
|
#ifdef Py_REF_DEBUG
|
|
|
|
PyObject *
|
|
|
|
_PyDict_Dummy(void)
|
|
|
|
{
|
|
|
|
return dummy;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
|
2001-08-02 01:15:00 -03:00
|
|
|
/* Initialization macros.
|
|
|
|
There are two ways to create a dict: PyDict_New() is the main C API
|
|
|
|
function, and the tp_new slot maps to dict_new(). In the latter case we
|
|
|
|
can save a little time over what PyDict_New does because it's guaranteed
|
|
|
|
that the PyDictObject struct is already zeroed out.
|
|
|
|
Everyone except dict_new() should use EMPTY_TO_MINSIZE (unless they have
|
|
|
|
an excellent reason not to).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define INIT_NONZERO_DICT_SLOTS(mp) do { \
|
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
|
|
|
(mp)->ma_table = (mp)->ma_smalltable; \
|
2001-08-02 01:15:00 -03:00
|
|
|
(mp)->ma_mask = PyDict_MINSIZE - 1; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define EMPTY_TO_MINSIZE(mp) do { \
|
|
|
|
memset((mp)->ma_smalltable, 0, sizeof((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
|
|
|
(mp)->ma_used = (mp)->ma_fill = 0; \
|
2001-08-02 01:15:00 -03:00
|
|
|
INIT_NONZERO_DICT_SLOTS(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
|
|
|
} while(0)
|
|
|
|
|
2004-03-17 17:55:03 -04:00
|
|
|
/* Dictionary reuse scheme to save calls to malloc, free, and memset */
|
|
|
|
#define MAXFREEDICTS 80
|
|
|
|
static PyDictObject *free_dicts[MAXFREEDICTS];
|
|
|
|
static int num_free_dicts = 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
|
|
|
}
|
2004-03-17 17:55:03 -04:00
|
|
|
if (num_free_dicts) {
|
|
|
|
mp = free_dicts[--num_free_dicts];
|
|
|
|
assert (mp != NULL);
|
|
|
|
assert (mp->ob_type == &PyDict_Type);
|
|
|
|
_Py_NewReference((PyObject *)mp);
|
|
|
|
if (mp->ma_fill) {
|
|
|
|
EMPTY_TO_MINSIZE(mp);
|
|
|
|
}
|
|
|
|
assert (mp->ma_used == 0);
|
|
|
|
assert (mp->ma_table == mp->ma_smalltable);
|
|
|
|
assert (mp->ma_mask == PyDict_MINSIZE - 1);
|
|
|
|
} else {
|
|
|
|
mp = PyObject_GC_New(dictobject, &PyDict_Type);
|
|
|
|
if (mp == NULL)
|
|
|
|
return NULL;
|
|
|
|
EMPTY_TO_MINSIZE(mp);
|
|
|
|
}
|
2000-08-31 16:31:38 -03:00
|
|
|
mp->ma_lookup = lookdict_string;
|
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
++created;
|
|
|
|
#endif
|
2001-08-29 20:54:21 -03:00
|
|
|
_PyObject_GC_TRACK(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).
|
|
|
|
|
2001-06-02 02:27:19 -03:00
|
|
|
The initial probe index is computed as hash mod the table size. Subsequent
|
|
|
|
probe indices are computed as explained earlier.
|
1999-03-24 15:06:42 -04:00
|
|
|
|
|
|
|
All arithmetic on hash should ignore overflow.
|
1997-01-27 20:00:11 -04:00
|
|
|
|
2001-06-02 02:27:19 -03:00
|
|
|
(The details in this version are due to Tim Peters, building on many past
|
|
|
|
contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and
|
|
|
|
Christian Tismer).
|
2000-08-31 16:31:38 -03:00
|
|
|
|
2006-06-01 12:50:44 -03:00
|
|
|
lookdict() is general-purpose, and may return NULL if (and only if) a
|
|
|
|
comparison raises an exception (this was new in Python 2.5).
|
|
|
|
lookdict_string() below is specialized to string keys, comparison of which can
|
|
|
|
never raise an exception; that function can never return NULL. For both, when
|
|
|
|
the key isn't found a dictentry* is returned for which the me_value field is
|
|
|
|
NULL; this is the slot in the dict at which the key would have been found, and
|
|
|
|
the caller can (if it wishes) add the <key, value> pair to the returned
|
|
|
|
dictentry*.
|
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
|
|
|
{
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t i;
|
2006-02-15 13:27:45 -04:00
|
|
|
register size_t perturb;
|
1997-08-18 18:52:47 -03:00
|
|
|
register dictentry *freeslot;
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t mask = (size_t)mp->ma_mask;
|
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 cmp;
|
2001-06-03 01:54:32 -03:00
|
|
|
PyObject *startkey;
|
2001-06-02 02:27:19 -03:00
|
|
|
|
2006-06-01 12:50:44 -03:00
|
|
|
i = (size_t)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;
|
2001-06-03 01:14:43 -03:00
|
|
|
|
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) {
|
2001-06-03 01:54:32 -03:00
|
|
|
startkey = ep->me_key;
|
|
|
|
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
|
2001-06-03 01:14:43 -03:00
|
|
|
if (cmp < 0)
|
2006-06-01 10:19:12 -03:00
|
|
|
return NULL;
|
2001-06-03 01:54:32 -03:00
|
|
|
if (ep0 == mp->ma_table && ep->me_key == startkey) {
|
|
|
|
if (cmp > 0)
|
2006-06-01 10:19:12 -03:00
|
|
|
return ep;
|
2001-06-03 01:54:32 -03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* The compare did major nasty stuff to the
|
|
|
|
* dict: start over.
|
|
|
|
* XXX A clever adversary could prevent this
|
|
|
|
* XXX from terminating.
|
|
|
|
*/
|
2006-06-01 10:19:12 -03:00
|
|
|
return lookdict(mp, key, hash);
|
2001-06-03 01:54:32 -03:00
|
|
|
}
|
1997-08-18 18:52:47 -03:00
|
|
|
}
|
|
|
|
freeslot = NULL;
|
1997-01-18 03:55:05 -04:00
|
|
|
}
|
2001-05-27 04:39:22 -03:00
|
|
|
|
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. */
|
2001-06-02 02:27:19 -03:00
|
|
|
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
|
|
|
|
i = (i << 2) + i + perturb + 1;
|
|
|
|
ep = &ep0[i & mask];
|
2006-06-01 10:19:12 -03:00
|
|
|
if (ep->me_key == NULL)
|
|
|
|
return freeslot == NULL ? ep : freeslot;
|
2001-06-03 01:14:43 -03:00
|
|
|
if (ep->me_key == key)
|
2006-06-01 10:19:12 -03:00
|
|
|
return ep;
|
2001-06-03 01:14:43 -03:00
|
|
|
if (ep->me_hash == hash && ep->me_key != dummy) {
|
2001-06-03 01:54:32 -03:00
|
|
|
startkey = ep->me_key;
|
|
|
|
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
|
2001-06-03 01:14:43 -03:00
|
|
|
if (cmp < 0)
|
2006-06-01 10:19:12 -03:00
|
|
|
return NULL;
|
2001-06-03 01:54:32 -03:00
|
|
|
if (ep0 == mp->ma_table && ep->me_key == startkey) {
|
|
|
|
if (cmp > 0)
|
2006-06-01 10:19:12 -03:00
|
|
|
return ep;
|
2001-06-03 01:54:32 -03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* The compare did major nasty stuff to the
|
|
|
|
* dict: start over.
|
|
|
|
* XXX A clever adversary could prevent this
|
|
|
|
* XXX from terminating.
|
|
|
|
*/
|
2006-06-01 10:19:12 -03:00
|
|
|
return lookdict(mp, key, hash);
|
2001-06-03 01:54:32 -03:00
|
|
|
}
|
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;
|
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;
|
2006-06-01 12:50:44 -03:00
|
|
|
* this assumption allows testing for errors during PyObject_RichCompareBool()
|
|
|
|
* to be dropped; string-string comparisons never raise exceptions. This also
|
|
|
|
* means we don't need to go through PyObject_RichCompareBool(); we can always
|
|
|
|
* use _PyString_Eq() directly.
|
2000-08-31 16:31:38 -03:00
|
|
|
*
|
2006-06-01 12:50:44 -03:00
|
|
|
* This is valuable because dicts with only string keys are very common.
|
2000-08-31 16:31:38 -03:00
|
|
|
*/
|
|
|
|
static dictentry *
|
|
|
|
lookdict_string(dictobject *mp, PyObject *key, register long hash)
|
|
|
|
{
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t i;
|
2006-02-15 13:27:45 -04:00
|
|
|
register size_t perturb;
|
2000-08-31 16:31:38 -03:00
|
|
|
register dictentry *freeslot;
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t mask = (size_t)mp->ma_mask;
|
2000-08-31 16:31:38 -03:00
|
|
|
dictentry *ep0 = mp->ma_table;
|
|
|
|
register dictentry *ep;
|
|
|
|
|
2001-09-13 21:25:33 -03:00
|
|
|
/* Make sure this function doesn't have to handle non-string keys,
|
|
|
|
including subclasses of str; e.g., one reason to subclass
|
|
|
|
strings is to override __eq__, and for speed we don't cater to
|
|
|
|
that here. */
|
|
|
|
if (!PyString_CheckExact(key)) {
|
2000-08-31 16:31:38 -03:00
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
++converted;
|
|
|
|
#endif
|
|
|
|
mp->ma_lookup = lookdict;
|
|
|
|
return lookdict(mp, key, 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 {
|
2006-06-01 12:50:44 -03:00
|
|
|
if (ep->me_hash == hash && _PyString_Eq(ep->me_key, key))
|
2000-08-31 16:31:38 -03:00
|
|
|
return ep;
|
|
|
|
freeslot = NULL;
|
|
|
|
}
|
2001-05-27 04:39:22 -03:00
|
|
|
|
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. */
|
2001-06-02 02:27:19 -03:00
|
|
|
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
|
|
|
|
i = (i << 2) + i + perturb + 1;
|
|
|
|
ep = &ep0[i & 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;
|
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.
|
2006-06-01 12:50:44 -03:00
|
|
|
Returns -1 if an error occurred, or 0 on success.
|
1993-03-27 14:11:32 -04:00
|
|
|
*/
|
2006-06-01 10:19:12 -03:00
|
|
|
static int
|
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;
|
2001-08-02 01:15:00 -03:00
|
|
|
typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, long);
|
|
|
|
|
|
|
|
assert(mp->ma_lookup != NULL);
|
|
|
|
ep = mp->ma_lookup(mp, key, hash);
|
2006-06-01 10:19:12 -03:00
|
|
|
if (ep == NULL) {
|
|
|
|
Py_DECREF(key);
|
|
|
|
Py_DECREF(value);
|
|
|
|
return -1;
|
|
|
|
}
|
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++;
|
2005-02-05 19:42:57 -04:00
|
|
|
else {
|
|
|
|
assert(ep->me_key == dummy);
|
|
|
|
Py_DECREF(dummy);
|
|
|
|
}
|
1993-03-27 14:11:32 -04:00
|
|
|
ep->me_key = key;
|
2006-05-30 01:16:25 -03:00
|
|
|
ep->me_hash = (Py_ssize_t)hash;
|
1995-01-02 15:07:15 -04:00
|
|
|
ep->me_value = value;
|
1993-03-27 14:11:32 -04:00
|
|
|
mp->ma_used++;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Internal routine used by dictresize() to insert an item which is
|
|
|
|
known to be absent from the dict. This routine also assumes that
|
|
|
|
the dict contains no deleted entries. Besides the performance benefit,
|
|
|
|
using insertdict() in dictresize() is dangerous (SF bug #1456209).
|
2006-06-01 12:50:44 -03:00
|
|
|
Note that no refcounts are changed by this routine; if needed, the caller
|
|
|
|
is responsible for incref'ing `key` and `value`.
|
2006-06-01 10:19:12 -03:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
insertdict_clean(register dictobject *mp, PyObject *key, long hash,
|
|
|
|
PyObject *value)
|
|
|
|
{
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t i;
|
2006-06-01 10:19:12 -03:00
|
|
|
register size_t perturb;
|
2006-06-01 12:50:44 -03:00
|
|
|
register size_t mask = (size_t)mp->ma_mask;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep0 = mp->ma_table;
|
|
|
|
register dictentry *ep;
|
|
|
|
|
|
|
|
i = hash & mask;
|
|
|
|
ep = &ep0[i];
|
|
|
|
for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) {
|
|
|
|
i = (i << 2) + i + perturb + 1;
|
|
|
|
ep = &ep0[i & mask];
|
|
|
|
}
|
2006-06-01 12:50:44 -03:00
|
|
|
assert(ep->me_value == NULL);
|
2006-06-01 10:19:12 -03:00
|
|
|
mp->ma_fill++;
|
|
|
|
ep->me_key = key;
|
|
|
|
ep->me_hash = (Py_ssize_t)hash;
|
|
|
|
ep->me_value = value;
|
|
|
|
mp->ma_used++;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
2006-05-30 01:16:25 -03:00
|
|
|
dictresize(dictobject *mp, Py_ssize_t minused)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t newsize;
|
2001-05-23 20:33:57 -03:00
|
|
|
dictentry *oldtable, *newtable, *ep;
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i;
|
2001-05-23 20:33:57 -03:00
|
|
|
int is_oldtable_malloced;
|
2001-08-02 01:15:00 -03:00
|
|
|
dictentry small_copy[PyDict_MINSIZE];
|
2001-05-19 04:04:38 -03:00
|
|
|
|
|
|
|
assert(minused >= 0);
|
2001-05-23 20:33:57 -03:00
|
|
|
|
2001-06-02 02:27:19 -03:00
|
|
|
/* Find the smallest table size > minused. */
|
2001-08-02 01:15:00 -03:00
|
|
|
for (newsize = PyDict_MINSIZE;
|
2001-06-02 02:27:19 -03:00
|
|
|
newsize <= minused && newsize > 0;
|
|
|
|
newsize <<= 1)
|
|
|
|
;
|
|
|
|
if (newsize <= 0) {
|
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;
|
|
|
|
|
2001-08-02 01:15:00 -03:00
|
|
|
if (newsize == PyDict_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;
|
2001-06-04 18:00:21 -03:00
|
|
|
mp->ma_mask = newsize - 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
|
|
|
memset(newtable, 0, sizeof(dictentry) * newsize);
|
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;
|
2006-06-01 10:19:12 -03:00
|
|
|
insertdict_clean(mp, ep->me_key, (long)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;
|
|
|
|
}
|
|
|
|
|
2006-06-01 12:50:44 -03:00
|
|
|
/* Note that, for historical reasons, PyDict_GetItem() suppresses all errors
|
|
|
|
* that may occur (originally dicts supported only string keys, and exceptions
|
|
|
|
* weren't possible). So, while the original intent was that a NULL return
|
2006-08-04 17:37:43 -03:00
|
|
|
* meant the key wasn't present, in reality it can mean that, or that an error
|
2006-06-01 12:50:44 -03:00
|
|
|
* (suppressed) occurred while computing the key's hash, or that some error
|
|
|
|
* (suppressed) occurred when comparing keys in the dict's internal probe
|
|
|
|
* sequence. A nasty example of the latter is when a Python-coded comparison
|
|
|
|
* function hits a stack-depth error, which can cause this to return NULL
|
|
|
|
* even if the key is present.
|
|
|
|
*/
|
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;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep;
|
|
|
|
PyThreadState *tstate;
|
2006-06-01 12:50:44 -03:00
|
|
|
if (!PyDict_Check(op))
|
1993-03-27 14:11:32 -04:00
|
|
|
return NULL;
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
1997-05-02 00:12:38 -03:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1)
|
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
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
|
|
|
|
/* We can arrive here with a NULL tstate during initialization:
|
|
|
|
try running "python -Wi" for an example related to string
|
|
|
|
interning. Let's just hope that no exception occurs then... */
|
2006-06-10 07:57:40 -03:00
|
|
|
tstate = _PyThreadState_Current;
|
2006-06-01 10:19:12 -03:00
|
|
|
if (tstate != NULL && tstate->curexc_type != NULL) {
|
|
|
|
/* preserve the existing exception */
|
|
|
|
PyObject *err_type, *err_value, *err_tb;
|
|
|
|
PyErr_Fetch(&err_type, &err_value, &err_tb);
|
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
/* ignore errors */
|
|
|
|
PyErr_Restore(err_type, err_value, err_tb);
|
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
if (ep == NULL) {
|
|
|
|
PyErr_Clear();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ep->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
|
2005-12-31 21:19:23 -04:00
|
|
|
* dictionary if it's merely replacing the value for an existing key.
|
|
|
|
* This 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.
|
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
|
|
|
*/
|
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;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t n_used;
|
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
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op)) {
|
|
|
|
PyErr_BadInternalCall();
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2006-07-21 02:29:58 -03:00
|
|
|
assert(key);
|
|
|
|
assert(value);
|
1997-05-13 18:02:11 -03:00
|
|
|
mp = (dictobject *)op;
|
2001-09-13 21:25:33 -03:00
|
|
|
if (PyString_CheckExact(key)) {
|
2002-08-19 18:43:18 -03:00
|
|
|
hash = ((PyStringObject *)key)->ob_shash;
|
|
|
|
if (hash == -1)
|
|
|
|
hash = PyObject_Hash(key);
|
1997-01-18 03:55:05 -04:00
|
|
|
}
|
2002-03-28 23:29:08 -04:00
|
|
|
else {
|
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;
|
|
|
|
}
|
2001-06-04 18:00:21 -03:00
|
|
|
assert(mp->ma_fill <= mp->ma_mask); /* at least one empty slot */
|
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);
|
2006-06-01 10:19:12 -03:00
|
|
|
if (insertdict(mp, key, hash, value) != 0)
|
|
|
|
return -1;
|
2003-05-05 19:22:10 -03:00
|
|
|
/* If we added a key, we can safely resize. Otherwise just return!
|
|
|
|
* If fill >= 2/3 size, adjust size. Normally, this doubles or
|
|
|
|
* quaduples the size, but it's also possible for the dict to shrink
|
2005-12-31 21:19:23 -04:00
|
|
|
* (if ma_fill is much larger than ma_used, meaning a lot of dict
|
2003-05-05 19:22:10 -03:00
|
|
|
* keys have been * deleted).
|
2005-12-31 21:19:23 -04:00
|
|
|
*
|
2003-05-05 19:22:10 -03:00
|
|
|
* Quadrupling the size improves average dictionary sparseness
|
|
|
|
* (reducing collisions) at the cost of some memory and iteration
|
|
|
|
* speed (which loops over every possible entry). It also halves
|
2006-05-30 01:19:21 -03:00
|
|
|
* the number of expensive resize operations in a growing dictionary.
|
2005-12-31 21:19:23 -04:00
|
|
|
*
|
|
|
|
* Very large dictionaries (over 50K items) use doubling instead.
|
2003-05-05 19:22:10 -03:00
|
|
|
* This may help applications with severe memory constraints.
|
2001-03-21 15:23:56 -04:00
|
|
|
*/
|
2003-05-05 19:22:10 -03:00
|
|
|
if (!(mp->ma_used > n_used && mp->ma_fill*3 >= (mp->ma_mask+1)*2))
|
|
|
|
return 0;
|
2006-05-30 01:16:25 -03:00
|
|
|
return dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used);
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2006-07-21 02:29:58 -03:00
|
|
|
assert(key);
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
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);
|
2006-06-01 10:19:12 -03:00
|
|
|
if (ep == NULL)
|
|
|
|
return -1;
|
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;
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t fill;
|
2001-08-02 01:15:00 -03:00
|
|
|
dictentry small_copy[PyDict_MINSIZE];
|
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
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i, n;
|
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
|
|
|
#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
|
2001-06-04 18:00:21 -03:00
|
|
|
n = mp->ma_mask + 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
|
|
|
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)
|
2001-08-02 01:15:00 -03:00
|
|
|
EMPTY_TO_MINSIZE(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
|
|
|
|
|
|
|
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;
|
2001-08-02 01:15:00 -03:00
|
|
|
EMPTY_TO_MINSIZE(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
|
|
|
}
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
2003-02-14 23:01:11 -04:00
|
|
|
/*
|
|
|
|
* Iterate over a dict. Use like so:
|
|
|
|
*
|
2006-05-30 01:16:25 -03:00
|
|
|
* Py_ssize_t i;
|
2003-02-14 23:01:11 -04:00
|
|
|
* PyObject *key, *value;
|
|
|
|
* i = 0; # important! i should not otherwise be changed by you
|
2003-02-15 10:45:12 -04:00
|
|
|
* while (PyDict_Next(yourdict, &i, &key, &value)) {
|
2003-02-14 23:01:11 -04:00
|
|
|
* Refer to borrowed references in key and value.
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* CAUTION: In general, it isn't safe to use PyDict_Next in a loop that
|
2001-03-21 15:23:56 -04:00
|
|
|
* 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
|
2006-02-15 13:27:45 -04:00
|
|
|
PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
2006-02-15 13:27:45 -04:00
|
|
|
register Py_ssize_t i;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t mask;
|
2004-03-17 17:55:03 -04:00
|
|
|
register dictentry *ep;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
if (!PyDict_Check(op))
|
1993-05-19 11:50:45 -03:00
|
|
|
return 0;
|
|
|
|
i = *ppos;
|
|
|
|
if (i < 0)
|
|
|
|
return 0;
|
2004-03-17 17:55:03 -04:00
|
|
|
ep = ((dictobject *)op)->ma_table;
|
|
|
|
mask = ((dictobject *)op)->ma_mask;
|
|
|
|
while (i <= mask && ep[i].me_value == NULL)
|
1993-05-19 11:50:45 -03:00
|
|
|
i++;
|
|
|
|
*ppos = i+1;
|
2004-03-17 17:55:03 -04:00
|
|
|
if (i > mask)
|
1993-05-19 11:50:45 -03:00
|
|
|
return 0;
|
|
|
|
if (pkey)
|
2004-03-17 17:55:03 -04:00
|
|
|
*pkey = ep[i].me_key;
|
1993-05-19 11:50:45 -03:00
|
|
|
if (pvalue)
|
2004-03-17 17:55:03 -04:00
|
|
|
*pvalue = ep[i].me_value;
|
1993-05-19 11:50:45 -03:00
|
|
|
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;
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t fill = mp->ma_fill;
|
2002-03-28 16:34:59 -04:00
|
|
|
PyObject_GC_UnTrack(mp);
|
2000-03-13 12:01:29 -04:00
|
|
|
Py_TRASHCAN_SAFE_BEGIN(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);
|
2004-03-17 17:55:03 -04:00
|
|
|
if (num_free_dicts < MAXFREEDICTS && mp->ob_type == &PyDict_Type)
|
|
|
|
free_dicts[num_free_dicts++] = mp;
|
2005-12-31 21:19:23 -04:00
|
|
|
else
|
2004-03-17 17:55:03 -04:00
|
|
|
mp->ob_type->tp_free((PyObject *)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
|
|
|
{
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i;
|
|
|
|
register Py_ssize_t any;
|
2006-05-30 02:23:59 -03:00
|
|
|
int status;
|
1998-04-10 19:47:14 -03:00
|
|
|
|
2006-05-30 02:23:59 -03:00
|
|
|
status = Py_ReprEnter((PyObject*)mp);
|
|
|
|
if (status != 0) {
|
|
|
|
if (status < 0)
|
|
|
|
return status;
|
1998-04-10 19:47:14 -03:00
|
|
|
fprintf(fp, "{...}");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1993-03-27 14:11:32 -04:00
|
|
|
fprintf(fp, "{");
|
|
|
|
any = 0;
|
2001-06-04 18:00:21 -03:00
|
|
|
for (i = 0; i <= mp->ma_mask; i++) {
|
2001-06-02 05:02:56 -03:00
|
|
|
dictentry *ep = mp->ma_table + i;
|
|
|
|
PyObject *pvalue = ep->me_value;
|
|
|
|
if (pvalue != NULL) {
|
|
|
|
/* Prevent PyObject_Repr from deleting value during
|
|
|
|
key format */
|
|
|
|
Py_INCREF(pvalue);
|
1993-03-27 14:11:32 -04:00
|
|
|
if (any++ > 0)
|
|
|
|
fprintf(fp, ", ");
|
1998-04-10 19:47:14 -03:00
|
|
|
if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) {
|
2001-06-02 05:02:56 -03:00
|
|
|
Py_DECREF(pvalue);
|
1998-04-10 19:47:14 -03:00
|
|
|
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, ": ");
|
2001-06-02 05:27:39 -03:00
|
|
|
if (PyObject_Print(pvalue, fp, 0) != 0) {
|
2001-06-02 05:02:56 -03:00
|
|
|
Py_DECREF(pvalue);
|
1998-04-10 19:47:14 -03:00
|
|
|
Py_ReprLeave((PyObject*)mp);
|
1993-03-27 14:11:32 -04:00
|
|
|
return -1;
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
2001-06-02 05:02:56 -03:00
|
|
|
Py_DECREF(pvalue);
|
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
|
|
|
{
|
2006-02-15 13:27:45 -04:00
|
|
|
Py_ssize_t i;
|
2001-06-16 02:11:17 -03:00
|
|
|
PyObject *s, *temp, *colon = NULL;
|
|
|
|
PyObject *pieces = NULL, *result = NULL;
|
|
|
|
PyObject *key, *value;
|
1998-04-10 19:47:14 -03:00
|
|
|
|
2001-06-16 02:11:17 -03:00
|
|
|
i = Py_ReprEnter((PyObject *)mp);
|
1998-04-10 19:47:14 -03:00
|
|
|
if (i != 0) {
|
2001-06-16 02:11:17 -03:00
|
|
|
return i > 0 ? PyString_FromString("{...}") : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp->ma_used == 0) {
|
|
|
|
result = PyString_FromString("{}");
|
|
|
|
goto Done;
|
1998-04-10 19:47:14 -03:00
|
|
|
}
|
|
|
|
|
2001-06-16 02:11:17 -03:00
|
|
|
pieces = PyList_New(0);
|
|
|
|
if (pieces == NULL)
|
|
|
|
goto Done;
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
colon = PyString_FromString(": ");
|
2001-06-16 02:11:17 -03:00
|
|
|
if (colon == NULL)
|
|
|
|
goto Done;
|
|
|
|
|
|
|
|
/* Do repr() on each key+value pair, and insert ": " between them.
|
|
|
|
Note that repr may mutate the dict. */
|
2001-06-16 04:52:53 -03:00
|
|
|
i = 0;
|
|
|
|
while (PyDict_Next((PyObject *)mp, &i, &key, &value)) {
|
2001-06-16 02:11:17 -03:00
|
|
|
int status;
|
|
|
|
/* Prevent repr from deleting value during key format. */
|
|
|
|
Py_INCREF(value);
|
|
|
|
s = PyObject_Repr(key);
|
|
|
|
PyString_Concat(&s, colon);
|
|
|
|
PyString_ConcatAndDel(&s, PyObject_Repr(value));
|
|
|
|
Py_DECREF(value);
|
|
|
|
if (s == NULL)
|
|
|
|
goto Done;
|
|
|
|
status = PyList_Append(pieces, s);
|
|
|
|
Py_DECREF(s); /* append created a new ref */
|
|
|
|
if (status < 0)
|
|
|
|
goto Done;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
2001-06-16 02:11:17 -03:00
|
|
|
|
|
|
|
/* Add "{}" decorations to the first and last items. */
|
|
|
|
assert(PyList_GET_SIZE(pieces) > 0);
|
|
|
|
s = PyString_FromString("{");
|
|
|
|
if (s == NULL)
|
|
|
|
goto Done;
|
|
|
|
temp = PyList_GET_ITEM(pieces, 0);
|
|
|
|
PyString_ConcatAndDel(&s, temp);
|
|
|
|
PyList_SET_ITEM(pieces, 0, s);
|
|
|
|
if (s == NULL)
|
|
|
|
goto Done;
|
|
|
|
|
|
|
|
s = PyString_FromString("}");
|
|
|
|
if (s == NULL)
|
|
|
|
goto Done;
|
|
|
|
temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1);
|
|
|
|
PyString_ConcatAndDel(&temp, s);
|
|
|
|
PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp);
|
|
|
|
if (temp == NULL)
|
|
|
|
goto Done;
|
|
|
|
|
|
|
|
/* Paste them all together with ", " between. */
|
|
|
|
s = PyString_FromString(", ");
|
|
|
|
if (s == NULL)
|
|
|
|
goto Done;
|
|
|
|
result = _PyString_Join(s, pieces);
|
2001-09-13 21:25:33 -03:00
|
|
|
Py_DECREF(s);
|
2001-06-16 02:11:17 -03:00
|
|
|
|
|
|
|
Done:
|
|
|
|
Py_XDECREF(pieces);
|
1997-05-02 00:12:38 -03:00
|
|
|
Py_XDECREF(colon);
|
2001-06-16 02:11:17 -03:00
|
|
|
Py_ReprLeave((PyObject *)mp);
|
|
|
|
return result;
|
1993-03-27 14:11:32 -04:00
|
|
|
}
|
|
|
|
|
2006-02-15 13:27:45 -04:00
|
|
|
static Py_ssize_t
|
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;
|
2006-06-01 10:19:12 -03:00
|
|
|
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
|
|
|
assert(mp->ma_table != NULL);
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
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;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
|
|
|
v = ep->me_value;
|
2006-02-25 18:38:04 -04:00
|
|
|
if (v == NULL) {
|
|
|
|
if (!PyDict_CheckExact(mp)) {
|
|
|
|
/* Look up __missing__ method if we're a subclass. */
|
2006-02-25 19:32:30 -04:00
|
|
|
PyObject *missing;
|
2006-02-25 18:38:04 -04:00
|
|
|
static PyObject *missing_str = NULL;
|
|
|
|
if (missing_str == NULL)
|
2006-05-30 01:16:25 -03:00
|
|
|
missing_str =
|
2006-02-25 18:38:04 -04:00
|
|
|
PyString_InternFromString("__missing__");
|
2006-02-25 19:32:30 -04:00
|
|
|
missing = _PyType_Lookup(mp->ob_type, missing_str);
|
2006-02-25 18:38:04 -04:00
|
|
|
if (missing != NULL)
|
|
|
|
return PyObject_CallFunctionObjArgs(missing,
|
|
|
|
(PyObject *)mp, key, NULL);
|
|
|
|
}
|
1997-05-02 00:12:38 -03:00
|
|
|
PyErr_SetObject(PyExc_KeyError, key);
|
2006-02-25 18:38:04 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
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 = {
|
2006-02-15 13:27:45 -04:00
|
|
|
(lenfunc)dict_length, /*mp_length*/
|
1997-05-13 18:02:11 -03:00
|
|
|
(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 *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_keys(register dictobject *mp)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, j;
|
2004-03-17 17:55:03 -04:00
|
|
|
dictentry *ep;
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t mask, n;
|
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;
|
|
|
|
}
|
2004-03-17 17:55:03 -04:00
|
|
|
ep = mp->ma_table;
|
|
|
|
mask = mp->ma_mask;
|
|
|
|
for (i = 0, j = 0; i <= mask; i++) {
|
|
|
|
if (ep[i].me_value != NULL) {
|
|
|
|
PyObject *key = ep[i].me_key;
|
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
|
|
|
PyList_SET_ITEM(v, j, key);
|
1993-03-27 14:11:32 -04:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
2004-03-17 17:55:03 -04:00
|
|
|
assert(j == n);
|
1993-03-27 14:11:32 -04:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_values(register dictobject *mp)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, j;
|
2004-03-17 17:55:03 -04:00
|
|
|
dictentry *ep;
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t mask, n;
|
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;
|
|
|
|
}
|
2004-03-17 17:55:03 -04:00
|
|
|
ep = mp->ma_table;
|
|
|
|
mask = mp->ma_mask;
|
|
|
|
for (i = 0, j = 0; i <= mask; i++) {
|
|
|
|
if (ep[i].me_value != NULL) {
|
|
|
|
PyObject *value = ep[i].me_value;
|
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
|
|
|
PyList_SET_ITEM(v, j, value);
|
1993-05-19 11:50:45 -03:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
2004-03-17 17:55:03 -04:00
|
|
|
assert(j == n);
|
1993-05-19 11:50:45 -03:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_items(register dictobject *mp)
|
1993-05-19 11:50:45 -03:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
register PyObject *v;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, j, n;
|
|
|
|
Py_ssize_t mask;
|
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
|
|
|
PyObject *item, *key, *value;
|
2004-03-17 17:55:03 -04:00
|
|
|
dictentry *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
|
|
|
|
|
|
|
/* 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. */
|
2004-03-17 17:55:03 -04:00
|
|
|
ep = mp->ma_table;
|
|
|
|
mask = mp->ma_mask;
|
|
|
|
for (i = 0, j = 0; i <= mask; i++) {
|
2004-03-19 06:30:00 -04:00
|
|
|
if ((value=ep[i].me_value) != NULL) {
|
2004-03-17 17:55:03 -04:00
|
|
|
key = ep[i].me_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
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-11-27 03:29:33 -04:00
|
|
|
static PyObject *
|
2002-12-09 18:56:13 -04:00
|
|
|
dict_fromkeys(PyObject *cls, PyObject *args)
|
2002-11-27 03:29:33 -04:00
|
|
|
{
|
|
|
|
PyObject *seq;
|
|
|
|
PyObject *value = Py_None;
|
|
|
|
PyObject *it; /* iter(seq) */
|
|
|
|
PyObject *key;
|
|
|
|
PyObject *d;
|
|
|
|
int status;
|
|
|
|
|
2002-12-29 12:33:45 -04:00
|
|
|
if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &seq, &value))
|
2002-11-27 03:29:33 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
d = PyObject_CallObject(cls, NULL);
|
|
|
|
if (d == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(seq);
|
|
|
|
if (it == NULL){
|
|
|
|
Py_DECREF(d);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
key = PyIter_Next(it);
|
|
|
|
if (key == NULL) {
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
goto Fail;
|
|
|
|
break;
|
|
|
|
}
|
2002-12-07 04:10:51 -04:00
|
|
|
status = PyObject_SetItem(d, key, value);
|
2002-11-27 03:29:33 -04:00
|
|
|
Py_DECREF(key);
|
|
|
|
if (status < 0)
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_DECREF(it);
|
|
|
|
return d;
|
|
|
|
|
|
|
|
Fail:
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(d);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-03-04 04:25:44 -04:00
|
|
|
static int
|
|
|
|
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname)
|
|
|
|
{
|
|
|
|
PyObject *arg = NULL;
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg))
|
|
|
|
result = -1;
|
|
|
|
|
|
|
|
else if (arg != NULL) {
|
|
|
|
if (PyObject_HasAttrString(arg, "keys"))
|
|
|
|
result = PyDict_Merge(self, arg, 1);
|
|
|
|
else
|
|
|
|
result = PyDict_MergeFromSeq2(self, arg, 1);
|
|
|
|
}
|
|
|
|
if (result == 0 && kwds != NULL)
|
|
|
|
result = PyDict_Merge(self, kwds, 1);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1997-05-28 16:15:28 -03:00
|
|
|
static PyObject *
|
2004-03-04 04:25:44 -04:00
|
|
|
dict_update(PyObject *self, PyObject *args, PyObject *kwds)
|
2001-08-02 01:15:00 -03:00
|
|
|
{
|
2004-04-12 15:10:01 -03:00
|
|
|
if (dict_update_common(self, args, kwds, "update") != -1)
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
return NULL;
|
2001-08-02 01:15:00 -03:00
|
|
|
}
|
|
|
|
|
2001-08-10 17:28:28 -03:00
|
|
|
/* Update unconditionally replaces existing items.
|
|
|
|
Merge has a 3rd argument 'override'; if set, it acts like Update,
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
otherwise it leaves existing items unchanged.
|
|
|
|
|
|
|
|
PyDict_{Update,Merge} update/merge from a mapping object.
|
|
|
|
|
2001-12-11 14:51:08 -04:00
|
|
|
PyDict_MergeFromSeq2 updates/merges from any iterable object
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
producing iterable objects of length 2.
|
|
|
|
*/
|
|
|
|
|
2001-12-11 14:51:08 -04:00
|
|
|
int
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
|
|
|
|
{
|
|
|
|
PyObject *it; /* iter(seq2) */
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i; /* index into seq2 of current element */
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
PyObject *item; /* seq2[i] */
|
|
|
|
PyObject *fast; /* item as a 2-tuple or 2-list */
|
|
|
|
|
|
|
|
assert(d != NULL);
|
|
|
|
assert(PyDict_Check(d));
|
|
|
|
assert(seq2 != NULL);
|
|
|
|
|
|
|
|
it = PyObject_GetIter(seq2);
|
|
|
|
if (it == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; ; ++i) {
|
|
|
|
PyObject *key, *value;
|
2006-02-15 13:27:45 -04:00
|
|
|
Py_ssize_t n;
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
|
|
|
|
fast = NULL;
|
|
|
|
item = PyIter_Next(it);
|
|
|
|
if (item == NULL) {
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
goto Fail;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert item to sequence, and verify length 2. */
|
|
|
|
fast = PySequence_Fast(item, "");
|
|
|
|
if (fast == NULL) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_TypeError))
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"cannot convert dictionary update "
|
2006-05-30 01:25:05 -03:00
|
|
|
"sequence element #%zd to a sequence",
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
i);
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
n = PySequence_Fast_GET_SIZE(fast);
|
|
|
|
if (n != 2) {
|
|
|
|
PyErr_Format(PyExc_ValueError,
|
2006-05-30 01:25:05 -03:00
|
|
|
"dictionary update sequence element #%zd "
|
2006-02-16 02:54:25 -04:00
|
|
|
"has length %zd; 2 is required",
|
2006-02-16 02:59:22 -04:00
|
|
|
i, n);
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update/merge with this (key, value) pair. */
|
|
|
|
key = PySequence_Fast_GET_ITEM(fast, 0);
|
|
|
|
value = PySequence_Fast_GET_ITEM(fast, 1);
|
|
|
|
if (override || PyDict_GetItem(d, key) == NULL) {
|
|
|
|
int status = PyDict_SetItem(d, key, value);
|
|
|
|
if (status < 0)
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
Py_DECREF(fast);
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
goto Return;
|
|
|
|
Fail:
|
|
|
|
Py_XDECREF(item);
|
|
|
|
Py_XDECREF(fast);
|
|
|
|
i = -1;
|
|
|
|
Return:
|
|
|
|
Py_DECREF(it);
|
2006-05-30 01:25:05 -03:00
|
|
|
return Py_SAFE_DOWNCAST(i, Py_ssize_t, int);
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
}
|
|
|
|
|
2001-08-02 01:15:00 -03:00
|
|
|
int
|
|
|
|
PyDict_Update(PyObject *a, PyObject *b)
|
2001-08-10 17:28:28 -03:00
|
|
|
{
|
|
|
|
return PyDict_Merge(a, b, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
PyDict_Merge(PyObject *a, PyObject *b, int override)
|
1997-05-28 16:15:28 -03:00
|
|
|
{
|
2001-08-02 01:15:00 -03:00
|
|
|
register PyDictObject *mp, *other;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i;
|
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;
|
2001-08-02 01:15:00 -03:00
|
|
|
|
2001-06-26 17:08:32 -03:00
|
|
|
/* We accept for the argument either a concrete dictionary object,
|
|
|
|
* or an abstract "mapping" object. For the former, we can do
|
|
|
|
* things quite efficiently. For the latter, we only require that
|
|
|
|
* PyMapping_Keys() and PyObject_GetItem() be supported.
|
|
|
|
*/
|
2001-08-02 01:15:00 -03:00
|
|
|
if (a == NULL || !PyDict_Check(a) || b == NULL) {
|
|
|
|
PyErr_BadInternalCall();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
mp = (dictobject*)a;
|
|
|
|
if (PyDict_Check(b)) {
|
|
|
|
other = (dictobject*)b;
|
2001-06-26 17:08:32 -03:00
|
|
|
if (other == mp || other->ma_used == 0)
|
|
|
|
/* a.update(a) or a.update({}); nothing to do */
|
2001-08-02 01:15:00 -03:00
|
|
|
return 0;
|
2005-05-14 15:08:25 -03:00
|
|
|
if (mp->ma_used == 0)
|
|
|
|
/* Since the target dict is empty, PyDict_GetItem()
|
|
|
|
* always returns NULL. Setting override to 1
|
|
|
|
* skips the unnecessary test.
|
|
|
|
*/
|
|
|
|
override = 1;
|
2001-06-26 17:08:32 -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_mask+1)*2) {
|
2003-05-06 21:49:40 -03:00
|
|
|
if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0)
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
}
|
|
|
|
for (i = 0; i <= other->ma_mask; i++) {
|
|
|
|
entry = &other->ma_table[i];
|
2001-08-10 17:28:28 -03:00
|
|
|
if (entry->me_value != NULL &&
|
|
|
|
(override ||
|
|
|
|
PyDict_GetItem(a, entry->me_key) == NULL)) {
|
2001-06-26 17:08:32 -03:00
|
|
|
Py_INCREF(entry->me_key);
|
|
|
|
Py_INCREF(entry->me_value);
|
2006-06-01 10:19:12 -03:00
|
|
|
if (insertdict(mp, entry->me_key,
|
|
|
|
(long)entry->me_hash,
|
|
|
|
entry->me_value) != 0)
|
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
}
|
|
|
|
}
|
1997-05-28 16:15:28 -03:00
|
|
|
}
|
2001-06-26 17:08:32 -03:00
|
|
|
else {
|
|
|
|
/* Do it the generic, slower way */
|
2001-08-02 01:15:00 -03:00
|
|
|
PyObject *keys = PyMapping_Keys(b);
|
2001-06-26 17:08:32 -03:00
|
|
|
PyObject *iter;
|
|
|
|
PyObject *key, *value;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (keys == NULL)
|
|
|
|
/* Docstring says this is equivalent to E.keys() so
|
|
|
|
* if E doesn't have a .keys() method we want
|
|
|
|
* AttributeError to percolate up. Might as well
|
|
|
|
* do the same for any other error.
|
|
|
|
*/
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
|
|
|
|
iter = PyObject_GetIter(keys);
|
|
|
|
Py_DECREF(keys);
|
|
|
|
if (iter == NULL)
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
|
|
|
|
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
|
2001-08-10 17:28:28 -03:00
|
|
|
if (!override && PyDict_GetItem(a, key) != NULL) {
|
|
|
|
Py_DECREF(key);
|
|
|
|
continue;
|
|
|
|
}
|
2001-08-02 01:15:00 -03:00
|
|
|
value = PyObject_GetItem(b, key);
|
2001-06-26 17:08:32 -03:00
|
|
|
if (value == NULL) {
|
|
|
|
Py_DECREF(iter);
|
|
|
|
Py_DECREF(key);
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
}
|
2001-08-10 17:28:28 -03:00
|
|
|
status = PyDict_SetItem(a, key, value);
|
2001-06-26 17:08:32 -03:00
|
|
|
Py_DECREF(key);
|
|
|
|
Py_DECREF(value);
|
|
|
|
if (status < 0) {
|
|
|
|
Py_DECREF(iter);
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
2001-06-26 17:08:32 -03:00
|
|
|
}
|
1997-05-28 16:15:28 -03:00
|
|
|
}
|
2001-06-26 17:08:32 -03:00
|
|
|
Py_DECREF(iter);
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
/* Iterator completed, via error */
|
2001-08-02 01:15:00 -03:00
|
|
|
return -1;
|
1997-05-28 16:15:28 -03:00
|
|
|
}
|
2001-08-02 01:15:00 -03:00
|
|
|
return 0;
|
1997-05-28 16:15:28 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_copy(register dictobject *mp)
|
1997-05-28 16:15:28 -03:00
|
|
|
{
|
2000-03-30 18:27:31 -04:00
|
|
|
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
|
|
|
{
|
2004-03-08 00:19:01 -04:00
|
|
|
PyObject *copy;
|
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
|
|
|
}
|
2004-03-08 00:19:01 -04:00
|
|
|
copy = PyDict_New();
|
1997-05-28 16:15:28 -03:00
|
|
|
if (copy == NULL)
|
|
|
|
return NULL;
|
2004-03-08 00:19:01 -04:00
|
|
|
if (PyDict_Merge(copy, o, 1) == 0)
|
|
|
|
return copy;
|
|
|
|
Py_DECREF(copy);
|
2005-04-15 12:58:42 -03:00
|
|
|
return NULL;
|
1997-05-28 16:15:28 -03:00
|
|
|
}
|
|
|
|
|
2006-02-15 13:27:45 -04:00
|
|
|
Py_ssize_t
|
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();
|
2004-02-17 16:10:11 -04:00
|
|
|
return -1;
|
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;
|
|
|
|
}
|
2001-08-16 10:15:00 -03:00
|
|
|
return dict_keys((dictobject *)mp);
|
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;
|
|
|
|
}
|
2001-08-16 10:15:00 -03:00
|
|
|
return dict_values((dictobject *)mp);
|
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;
|
|
|
|
}
|
2001-08-16 10:15:00 -03:00
|
|
|
return dict_items((dictobject *)mp);
|
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] */
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i;
|
|
|
|
int cmp;
|
1996-12-05 17:55:55 -04:00
|
|
|
|
2001-06-04 18:00:21 -03:00
|
|
|
for (i = 0; i <= a->ma_mask; 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 ||
|
2001-06-04 18:00:21 -03:00
|
|
|
i > a->ma_mask ||
|
2001-05-10 05:32:44 -03:00
|
|
|
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.
|
2006-05-30 01:25:05 -03:00
|
|
|
*/
|
2001-05-10 05:32:44 -03:00
|
|
|
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)
|
|
|
|
{
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i;
|
2001-05-08 01:38:29 -03:00
|
|
|
|
|
|
|
if (a->ma_used != b->ma_used)
|
|
|
|
/* can't be equal if # of entries differ */
|
|
|
|
return 0;
|
2001-06-02 02:27:19 -03:00
|
|
|
|
2001-05-08 01:38:29 -03:00
|
|
|
/* Same # of entries -- check all of 'em. Exit early on any diff. */
|
2001-06-04 18:00:21 -03:00
|
|
|
for (i = 0; i <= a->ma_mask; i++) {
|
2001-05-08 01:38:29 -03:00
|
|
|
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);
|
2006-09-04 22:54:06 -03:00
|
|
|
/* ditto for key */
|
|
|
|
Py_INCREF(key);
|
2001-05-08 01:38:29 -03:00
|
|
|
bval = PyDict_GetItem((PyObject *)b, key);
|
2006-09-04 22:54:06 -03:00
|
|
|
Py_DECREF(key);
|
2001-05-08 01:38:29 -03:00
|
|
|
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 *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_has_key(register dictobject *mp, PyObject *key)
|
1993-03-27 14:11:32 -04:00
|
|
|
{
|
|
|
|
long hash;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep;
|
2006-06-01 12:50:44 -03:00
|
|
|
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
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;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
2006-06-01 12:50:44 -03:00
|
|
|
return PyBool_FromLong(ep->me_value != NULL);
|
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;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep;
|
1997-10-06 14:49:20 -03:00
|
|
|
|
2002-12-29 12:33:45 -04:00
|
|
|
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj))
|
1997-10-06 14:49:20 -03:00
|
|
|
return NULL;
|
|
|
|
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
1997-10-06 14:49:20 -03:00
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
|
|
|
val = ep->me_value;
|
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;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep;
|
2000-08-08 13:12:54 -03:00
|
|
|
|
2002-12-29 12:33:45 -04:00
|
|
|
if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj))
|
2000-08-08 13:12:54 -03:00
|
|
|
return NULL;
|
|
|
|
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
2000-08-08 13:12:54 -03:00
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
|
|
|
val = ep->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 *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_clear(register dictobject *mp)
|
1997-03-21 17:55:12 -04:00
|
|
|
{
|
1997-05-02 00:12:38 -03:00
|
|
|
PyDict_Clear((PyObject *)mp);
|
2004-04-12 15:10:01 -03:00
|
|
|
Py_RETURN_NONE;
|
1997-03-21 17:55:12 -04:00
|
|
|
}
|
|
|
|
|
2002-04-12 12:11:59 -03:00
|
|
|
static PyObject *
|
2003-03-06 19:54:28 -04:00
|
|
|
dict_pop(dictobject *mp, PyObject *args)
|
2002-04-12 12:11:59 -03:00
|
|
|
{
|
|
|
|
long hash;
|
|
|
|
dictentry *ep;
|
|
|
|
PyObject *old_value, *old_key;
|
2003-03-06 19:54:28 -04:00
|
|
|
PyObject *key, *deflt = NULL;
|
2002-04-12 12:11:59 -03:00
|
|
|
|
2003-03-06 19:54:28 -04:00
|
|
|
if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt))
|
|
|
|
return NULL;
|
2002-04-12 12:11:59 -03:00
|
|
|
if (mp->ma_used == 0) {
|
2003-03-06 19:54:28 -04:00
|
|
|
if (deflt) {
|
|
|
|
Py_INCREF(deflt);
|
|
|
|
return deflt;
|
|
|
|
}
|
2002-04-12 12:11:59 -03:00
|
|
|
PyErr_SetString(PyExc_KeyError,
|
|
|
|
"pop(): dictionary is empty");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!PyString_CheckExact(key) ||
|
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
2006-06-01 10:19:12 -03:00
|
|
|
if (ep == NULL)
|
|
|
|
return NULL;
|
2002-04-12 12:11:59 -03:00
|
|
|
if (ep->me_value == NULL) {
|
2003-03-06 19:54:28 -04:00
|
|
|
if (deflt) {
|
|
|
|
Py_INCREF(deflt);
|
|
|
|
return deflt;
|
|
|
|
}
|
2002-04-12 12:11:59 -03:00
|
|
|
PyErr_SetObject(PyExc_KeyError, key);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
old_key = ep->me_key;
|
|
|
|
Py_INCREF(dummy);
|
|
|
|
ep->me_key = dummy;
|
|
|
|
old_value = ep->me_value;
|
|
|
|
ep->me_value = NULL;
|
|
|
|
mp->ma_used--;
|
|
|
|
Py_DECREF(old_key);
|
|
|
|
return old_value;
|
|
|
|
}
|
|
|
|
|
2000-12-12 18:02:18 -04:00
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_popitem(dictobject *mp)
|
2000-12-12 18:02:18 -04:00
|
|
|
{
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t i = 0;
|
2000-12-12 18:02:18 -04:00
|
|
|
dictentry *ep;
|
|
|
|
PyObject *res;
|
|
|
|
|
2001-06-02 02:42:29 -03:00
|
|
|
/* Allocate the result tuple before checking the size. Believe it
|
|
|
|
* or not, this allocation could trigger a garbage collection which
|
|
|
|
* could empty the dict, so if we checked the size first and that
|
|
|
|
* happened, the result would be an infinite loop (searching for an
|
|
|
|
* entry that no longer exists). Note that the usual popitem()
|
|
|
|
* idiom is "while d: k, v = d.popitem()". so needing to throw the
|
2006-05-30 01:16:25 -03:00
|
|
|
* tuple away if the dict *is* empty isn't a significant
|
2001-06-02 02:42:29 -03:00
|
|
|
* inefficiency -- possible, but unlikely in practice.
|
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
|
|
|
*/
|
|
|
|
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,
|
2006-05-30 01:25:05 -03:00
|
|
|
* and we use its hash value as the first index to look.
|
2000-12-12 18:02:18 -04:00
|
|
|
*/
|
|
|
|
ep = &mp->ma_table[0];
|
|
|
|
if (ep->me_value == NULL) {
|
2006-05-30 01:16:25 -03:00
|
|
|
i = 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
|
|
|
*/
|
2001-06-04 18:00:21 -03:00
|
|
|
if (i > mp->ma_mask || i < 1)
|
2000-12-12 18:02:18 -04:00
|
|
|
i = 1; /* skip slot 0 */
|
|
|
|
while ((ep = &mp->ma_table[i])->me_value == NULL) {
|
|
|
|
i++;
|
2001-06-04 18:00:21 -03:00
|
|
|
if (i > mp->ma_mask)
|
2000-12-12 18:02:18 -04:00
|
|
|
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)
|
|
|
|
{
|
2006-02-15 13:27:45 -04:00
|
|
|
Py_ssize_t i = 0;
|
2000-06-23 11:18:11 -03:00
|
|
|
PyObject *pk;
|
|
|
|
PyObject *pv;
|
|
|
|
|
|
|
|
while (PyDict_Next(op, &i, &pk, &pv)) {
|
2006-04-15 18:47:09 -03:00
|
|
|
Py_VISIT(pk);
|
|
|
|
Py_VISIT(pv);
|
2000-06-23 11:18:11 -03:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dict_tp_clear(PyObject *op)
|
|
|
|
{
|
|
|
|
PyDict_Clear(op);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
extern PyTypeObject PyDictIterKey_Type; /* Forward */
|
|
|
|
extern PyTypeObject PyDictIterValue_Type; /* Forward */
|
|
|
|
extern PyTypeObject PyDictIterItem_Type; /* Forward */
|
|
|
|
static PyObject *dictiter_new(dictobject *, PyTypeObject *);
|
2001-05-01 09:10:21 -03:00
|
|
|
|
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_iterkeys(dictobject *dict)
|
2001-05-01 09:10:21 -03:00
|
|
|
{
|
2004-03-17 22:41:19 -04:00
|
|
|
return dictiter_new(dict, &PyDictIterKey_Type);
|
2001-05-01 09:10:21 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_itervalues(dictobject *dict)
|
2001-05-01 09:10:21 -03:00
|
|
|
{
|
2004-03-17 22:41:19 -04:00
|
|
|
return dictiter_new(dict, &PyDictIterValue_Type);
|
2001-05-01 09:10:21 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2001-08-16 10:15:00 -03:00
|
|
|
dict_iteritems(dictobject *dict)
|
2001-05-01 09:10:21 -03:00
|
|
|
{
|
2004-03-17 22:41:19 -04:00
|
|
|
return dictiter_new(dict, &PyDictIterItem_Type);
|
2001-05-01 09:10:21 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(has_key__doc__,
|
2003-09-01 19:12:08 -03:00
|
|
|
"D.has_key(k) -> True if D has a key k, else False");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2003-12-13 07:26:12 -04:00
|
|
|
PyDoc_STRVAR(contains__doc__,
|
|
|
|
"D.__contains__(k) -> True if D has a key k, else False");
|
|
|
|
|
|
|
|
PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]");
|
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(get__doc__,
|
2002-09-04 08:29:45 -03:00
|
|
|
"D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(setdefault_doc__,
|
2002-09-04 08:29:45 -03:00
|
|
|
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(pop__doc__,
|
2003-03-06 19:54:28 -04:00
|
|
|
"D.pop(k[,d]) -> v, remove specified key and return the corresponding value\n\
|
|
|
|
If key is not found, d is returned if given, otherwise KeyError is raised");
|
2002-04-12 12:11:59 -03:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(popitem__doc__,
|
2000-12-13 19:18:45 -04:00
|
|
|
"D.popitem() -> (k, v), remove and return some (key, value) pair as a\n\
|
2002-06-13 17:33:02 -03:00
|
|
|
2-tuple; but raise KeyError if D is empty");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(keys__doc__,
|
|
|
|
"D.keys() -> list of D's keys");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(items__doc__,
|
|
|
|
"D.items() -> list of D's (key, value) pairs, as 2-tuples");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(values__doc__,
|
|
|
|
"D.values() -> list of D's values");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(update__doc__,
|
2004-05-28 17:59:21 -03:00
|
|
|
"D.update(E, **F) -> None. Update D from E and F: for k in E: D[k] = E[k]\n\
|
|
|
|
(if E has keys else: for (k, v) in E: D[k] = v) then: for k in F: D[k] = F[k]");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-11-27 03:29:33 -04:00
|
|
|
PyDoc_STRVAR(fromkeys__doc__,
|
|
|
|
"dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n\
|
|
|
|
v defaults to None.");
|
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(clear__doc__,
|
|
|
|
"D.clear() -> None. Remove all items from D.");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(copy__doc__,
|
|
|
|
"D.copy() -> a shallow copy of D");
|
2000-12-13 19:18:45 -04:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(iterkeys__doc__,
|
|
|
|
"D.iterkeys() -> an iterator over the keys of D");
|
2001-05-01 09:10:21 -03:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(itervalues__doc__,
|
|
|
|
"D.itervalues() -> an iterator over the values of D");
|
2001-05-01 09:10:21 -03:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(iteritems__doc__,
|
|
|
|
"D.iteritems() -> an iterator over the (key, value) items of D");
|
2001-05-01 09:10:21 -03:00
|
|
|
|
1997-05-02 00:12:38 -03:00
|
|
|
static PyMethodDef mapp_methods[] = {
|
2003-12-13 07:26:12 -04:00
|
|
|
{"__contains__",(PyCFunction)dict_has_key, METH_O | METH_COEXIST,
|
|
|
|
contains__doc__},
|
2003-12-13 09:31:55 -04:00
|
|
|
{"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST,
|
2003-12-13 07:26:12 -04:00
|
|
|
getitem__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"has_key", (PyCFunction)dict_has_key, METH_O,
|
2000-12-13 19:18:45 -04:00
|
|
|
has_key__doc__},
|
|
|
|
{"get", (PyCFunction)dict_get, METH_VARARGS,
|
|
|
|
get__doc__},
|
|
|
|
{"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS,
|
|
|
|
setdefault_doc__},
|
2003-03-06 19:54:28 -04:00
|
|
|
{"pop", (PyCFunction)dict_pop, METH_VARARGS,
|
2002-11-23 05:45:04 -04:00
|
|
|
pop__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"popitem", (PyCFunction)dict_popitem, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
popitem__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"keys", (PyCFunction)dict_keys, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
keys__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"items", (PyCFunction)dict_items, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
items__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"values", (PyCFunction)dict_values, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
values__doc__},
|
2004-03-04 04:25:44 -04:00
|
|
|
{"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
|
2000-12-13 19:18:45 -04:00
|
|
|
update__doc__},
|
2002-11-27 03:29:33 -04:00
|
|
|
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
|
|
|
|
fromkeys__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"clear", (PyCFunction)dict_clear, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
clear__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"copy", (PyCFunction)dict_copy, METH_NOARGS,
|
2000-12-13 19:18:45 -04:00
|
|
|
copy__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"iterkeys", (PyCFunction)dict_iterkeys, METH_NOARGS,
|
2001-05-01 09:10:21 -03:00
|
|
|
iterkeys__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"itervalues", (PyCFunction)dict_itervalues, METH_NOARGS,
|
2001-05-01 09:10:21 -03:00
|
|
|
itervalues__doc__},
|
2001-08-16 10:15:00 -03:00
|
|
|
{"iteritems", (PyCFunction)dict_iteritems, METH_NOARGS,
|
2001-05-01 09:10:21 -03:00
|
|
|
iteritems__doc__},
|
2000-12-13 19:18:45 -04:00
|
|
|
{NULL, NULL} /* sentinel */
|
1993-03-27 14:11:32 -04:00
|
|
|
};
|
|
|
|
|
2006-06-01 12:50:44 -03:00
|
|
|
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
|
2003-11-25 17:12:14 -04:00
|
|
|
int
|
|
|
|
PyDict_Contains(PyObject *op, PyObject *key)
|
2001-04-20 13:50:40 -03:00
|
|
|
{
|
|
|
|
long hash;
|
2003-11-25 17:12:14 -04:00
|
|
|
dictobject *mp = (dictobject *)op;
|
2006-06-01 10:19:12 -03:00
|
|
|
dictentry *ep;
|
2001-04-20 13:50:40 -03:00
|
|
|
|
2001-09-13 21:25:33 -03:00
|
|
|
if (!PyString_CheckExact(key) ||
|
2002-03-28 23:29:08 -04:00
|
|
|
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
|
2001-04-20 13:50:40 -03:00
|
|
|
hash = PyObject_Hash(key);
|
|
|
|
if (hash == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
2006-06-01 10:19:12 -03:00
|
|
|
ep = (mp->ma_lookup)(mp, key, hash);
|
2006-06-01 12:50:44 -03:00
|
|
|
return ep == NULL ? -1 : (ep->me_value != NULL);
|
2001-04-20 13:50:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Hack to implement "key in dict" */
|
|
|
|
static PySequenceMethods dict_as_sequence = {
|
2006-03-30 07:57:00 -04:00
|
|
|
0, /* sq_length */
|
|
|
|
0, /* sq_concat */
|
|
|
|
0, /* sq_repeat */
|
|
|
|
0, /* sq_item */
|
|
|
|
0, /* sq_slice */
|
|
|
|
0, /* sq_ass_item */
|
|
|
|
0, /* sq_ass_slice */
|
|
|
|
PyDict_Contains, /* sq_contains */
|
|
|
|
0, /* sq_inplace_concat */
|
|
|
|
0, /* sq_inplace_repeat */
|
2001-04-20 13:50:40 -03:00
|
|
|
};
|
|
|
|
|
2001-08-02 01:15:00 -03:00
|
|
|
static PyObject *
|
|
|
|
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
|
|
{
|
|
|
|
PyObject *self;
|
|
|
|
|
|
|
|
assert(type != NULL && type->tp_alloc != NULL);
|
|
|
|
self = type->tp_alloc(type, 0);
|
|
|
|
if (self != NULL) {
|
|
|
|
PyDictObject *d = (PyDictObject *)self;
|
|
|
|
/* It's guaranteed that tp->alloc zeroed out the struct. */
|
|
|
|
assert(d->ma_table == NULL && d->ma_fill == 0 && d->ma_used == 0);
|
|
|
|
INIT_NONZERO_DICT_SLOTS(d);
|
|
|
|
d->ma_lookup = lookdict_string;
|
|
|
|
#ifdef SHOW_CONVERSION_COUNTS
|
|
|
|
++created;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2001-09-02 05:22:48 -03:00
|
|
|
static int
|
|
|
|
dict_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|
|
|
{
|
2004-03-04 04:25:44 -04:00
|
|
|
return dict_update_common(self, args, kwds, "dict");
|
2001-09-02 05:22:48 -03:00
|
|
|
}
|
|
|
|
|
2001-12-03 12:32:18 -04:00
|
|
|
static long
|
|
|
|
dict_nohash(PyObject *self)
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_TypeError, "dict objects are unhashable");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2001-05-01 09:10:21 -03:00
|
|
|
static PyObject *
|
|
|
|
dict_iter(dictobject *dict)
|
|
|
|
{
|
2004-03-17 22:41:19 -04:00
|
|
|
return dictiter_new(dict, &PyDictIterKey_Type);
|
2001-05-01 09:10:21 -03:00
|
|
|
}
|
2001-04-20 16:13:02 -03:00
|
|
|
|
2002-06-13 17:33:02 -03:00
|
|
|
PyDoc_STRVAR(dictionary_doc,
|
2001-10-29 18:25:45 -04:00
|
|
|
"dict() -> new empty dictionary.\n"
|
|
|
|
"dict(mapping) -> new dictionary initialized from a mapping object's\n"
|
Generalize dictionary() to accept a sequence of 2-sequences. At the
outer level, the iterator protocol is used for memory-efficiency (the
outer sequence may be very large if fully materialized); at the inner
level, PySequence_Fast() is used for time-efficiency (these should
always be sequences of length 2).
dictobject.c, new functions PyDict_{Merge,Update}FromSeq2. These are
wholly analogous to PyDict_{Merge,Update}, but process a sequence-of-2-
sequences argument instead of a mapping object. For now, I left these
functions file static, so no corresponding doc changes. It's tempting
to change dict.update() to allow a sequence-of-2-seqs argument too.
Also changed the name of dictionary's keyword argument from "mapping"
to "x". Got a better name? "mapping_or_sequence_of_pairs" isn't
attractive, although more so than "mosop" <wink>.
abstract.h, abstract.tex: Added new PySequence_Fast_GET_SIZE function,
much faster than going thru the all-purpose PySequence_Size.
libfuncs.tex:
- Document dictionary().
- Fiddle tuple() and list() to admit that their argument is optional.
- The long-winded repetitions of "a sequence, a container that supports
iteration, or an iterator object" is getting to be a PITA. Many
months ago I suggested factoring this out into "iterable object",
where the definition of that could include being explicit about
generators too (as is, I'm not sure a reader outside of PythonLabs
could guess that "an iterator object" includes a generator call).
- Please check my curly braces -- I'm going blind <0.9 wink>.
abstract.c, PySequence_Tuple(): When PyObject_GetIter() fails, leave
its error msg alone now (the msg it produces has improved since
PySequence_Tuple was generalized to accept iterable objects, and
PySequence_Tuple was also stomping on the msg in cases it shouldn't
have even before PyObject_GetIter grew a better msg).
2001-10-26 02:06:50 -03:00
|
|
|
" (key, value) pairs.\n"
|
2001-10-29 18:25:45 -04:00
|
|
|
"dict(seq) -> new dictionary initialized as if via:\n"
|
2001-10-27 15:27:48 -03:00
|
|
|
" d = {}\n"
|
|
|
|
" for k, v in seq:\n"
|
2002-11-23 05:45:04 -04:00
|
|
|
" d[k] = v\n"
|
|
|
|
"dict(**kwargs) -> new dictionary initialized with the name=value pairs\n"
|
|
|
|
" in the keyword argument list. For example: dict(one=1, two=2)");
|
2001-09-02 05:22:48 -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,
|
2001-10-29 18:25:45 -04:00
|
|
|
"dict",
|
2001-08-29 20:54:21 -03:00
|
|
|
sizeof(dictobject),
|
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 */
|
2001-08-02 01:15:00 -03:00
|
|
|
0, /* tp_getattr */
|
2001-01-17 20:39:02 -04:00
|
|
|
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 */
|
2001-12-03 12:32:18 -04:00
|
|
|
dict_nohash, /* tp_hash */
|
2001-01-17 20:39:02 -04:00
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
2001-08-02 01:15:00 -03:00
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
2001-01-17 20:39:02 -04:00
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
2001-08-29 20:54:21 -03:00
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
2001-08-02 01:15:00 -03:00
|
|
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
2001-09-02 05:22:48 -03:00
|
|
|
dictionary_doc, /* tp_doc */
|
2006-03-30 07:57:00 -04:00
|
|
|
dict_traverse, /* tp_traverse */
|
|
|
|
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 */
|
2001-08-02 01:15:00 -03:00
|
|
|
mapp_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
2006-03-30 07:57:00 -04:00
|
|
|
dict_init, /* tp_init */
|
2001-08-02 01:15:00 -03:00
|
|
|
PyType_GenericAlloc, /* tp_alloc */
|
|
|
|
dict_new, /* tp_new */
|
2002-04-11 23:43:00 -03:00
|
|
|
PyObject_GC_Del, /* tp_free */
|
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 *
|
2002-12-11 09:21:12 -04:00
|
|
|
PyDict_GetItemString(PyObject *v, const 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
|
2002-12-11 09:21:12 -04:00
|
|
|
PyDict_SetItemString(PyObject *v, const 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
|
2002-12-11 09:21:12 -04:00
|
|
|
PyDict_DelItemString(PyObject *v, const 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
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
/* Dictionary iterator types */
|
2001-04-20 16:13:02 -03:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PyObject_HEAD
|
2002-07-16 17:30:22 -03:00
|
|
|
dictobject *di_dict; /* Set to NULL when iterator is exhausted */
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t di_used;
|
|
|
|
Py_ssize_t di_pos;
|
2004-03-17 22:41:19 -04:00
|
|
|
PyObject* di_result; /* reusable result tuple for iteritems */
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t len;
|
2001-04-20 16:13:02 -03:00
|
|
|
} dictiterobject;
|
|
|
|
|
|
|
|
static PyObject *
|
2004-03-17 22:41:19 -04:00
|
|
|
dictiter_new(dictobject *dict, PyTypeObject *itertype)
|
2001-04-20 16:13:02 -03:00
|
|
|
{
|
|
|
|
dictiterobject *di;
|
2004-03-17 22:41:19 -04:00
|
|
|
di = PyObject_New(dictiterobject, itertype);
|
2001-04-20 16:13:02 -03:00
|
|
|
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;
|
2004-03-18 04:38:00 -04:00
|
|
|
di->len = dict->ma_used;
|
2004-03-17 22:41:19 -04:00
|
|
|
if (itertype == &PyDictIterItem_Type) {
|
|
|
|
di->di_result = PyTuple_Pack(2, Py_None, Py_None);
|
|
|
|
if (di->di_result == NULL) {
|
|
|
|
Py_DECREF(di);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
di->di_result = NULL;
|
2001-04-20 16:13:02 -03:00
|
|
|
return (PyObject *)di;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dictiter_dealloc(dictiterobject *di)
|
|
|
|
{
|
2002-07-16 17:30:22 -03:00
|
|
|
Py_XDECREF(di->di_dict);
|
2004-03-17 22:41:19 -04:00
|
|
|
Py_XDECREF(di->di_result);
|
2002-04-11 23:43:00 -03:00
|
|
|
PyObject_Del(di);
|
2001-04-20 16:13:02 -03:00
|
|
|
}
|
|
|
|
|
2005-09-24 18:23:05 -03:00
|
|
|
static PyObject *
|
2004-03-18 04:38:00 -04:00
|
|
|
dictiter_len(dictiterobject *di)
|
|
|
|
{
|
2006-05-30 01:16:25 -03:00
|
|
|
Py_ssize_t len = 0;
|
2004-04-12 15:10:01 -03:00
|
|
|
if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used)
|
2005-09-24 18:23:05 -03:00
|
|
|
len = di->len;
|
2006-05-30 01:16:25 -03:00
|
|
|
return PyInt_FromSize_t(len);
|
2004-03-18 04:38:00 -04:00
|
|
|
}
|
|
|
|
|
2006-02-11 17:32:43 -04:00
|
|
|
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
|
2005-09-24 18:23:05 -03:00
|
|
|
|
|
|
|
static PyMethodDef dictiter_methods[] = {
|
2006-02-11 17:32:43 -04:00
|
|
|
{"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, length_hint_doc},
|
2005-09-24 18:23:05 -03:00
|
|
|
{NULL, NULL} /* sentinel */
|
2004-03-18 04:38:00 -04:00
|
|
|
};
|
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
static PyObject *dictiter_iternextkey(dictiterobject *di)
|
2001-04-20 16:13:02 -03:00
|
|
|
{
|
2004-03-17 22:41:19 -04:00
|
|
|
PyObject *key;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, mask;
|
2004-03-17 22:41:19 -04:00
|
|
|
register dictentry *ep;
|
|
|
|
dictobject *d = di->di_dict;
|
2001-04-23 11:08:49 -03:00
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
if (d == NULL)
|
2002-07-16 17:30:22 -03:00
|
|
|
return NULL;
|
2004-03-17 22:41:19 -04:00
|
|
|
assert (PyDict_Check(d));
|
2002-07-16 17:30:22 -03:00
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
if (di->di_used != d->ma_used) {
|
2001-04-23 11:08:49 -03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
|
|
"dictionary changed size during iteration");
|
2002-07-16 17:30:22 -03:00
|
|
|
di->di_used = -1; /* Make this state sticky */
|
2001-04-23 11:08:49 -03:00
|
|
|
return NULL;
|
|
|
|
}
|
2002-07-16 17:30:22 -03:00
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
i = di->di_pos;
|
|
|
|
if (i < 0)
|
|
|
|
goto fail;
|
|
|
|
ep = d->ma_table;
|
|
|
|
mask = d->ma_mask;
|
|
|
|
while (i <= mask && ep[i].me_value == NULL)
|
|
|
|
i++;
|
|
|
|
di->di_pos = i+1;
|
|
|
|
if (i > mask)
|
|
|
|
goto fail;
|
2004-03-18 04:38:00 -04:00
|
|
|
di->len--;
|
2004-03-17 22:41:19 -04:00
|
|
|
key = ep[i].me_key;
|
|
|
|
Py_INCREF(key);
|
|
|
|
return key;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
Py_DECREF(d);
|
2002-07-16 17:30:22 -03:00
|
|
|
di->di_dict = NULL;
|
2001-04-23 11:08:49 -03:00
|
|
|
return NULL;
|
2001-04-20 16:13:02 -03:00
|
|
|
}
|
|
|
|
|
2004-03-17 22:41:19 -04:00
|
|
|
PyTypeObject PyDictIterKey_Type = {
|
2001-04-20 16:13:02 -03:00
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
2004-03-18 04:38:00 -04:00
|
|
|
"dictionary-keyiterator", /* tp_name */
|
2001-04-20 16:13:02 -03:00
|
|
|
sizeof(dictiterobject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)dictiter_dealloc, /* tp_dealloc */
|
|
|
|
0, /* tp_print */
|
2001-08-02 01:15:00 -03:00
|
|
|
0, /* tp_getattr */
|
2001-04-20 16:13:02 -03:00
|
|
|
0, /* tp_setattr */
|
|
|
|
0, /* tp_compare */
|
|
|
|
0, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
2005-09-24 18:23:05 -03:00
|
|
|
0, /* tp_as_sequence */
|
2001-04-20 16:13:02 -03:00
|
|
|
0, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
2001-08-02 01:15:00 -03:00
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
2001-04-20 16:13:02 -03:00
|
|
|
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 */
|
2003-03-17 15:46:11 -04:00
|
|
|
PyObject_SelfIter, /* tp_iter */
|
2004-03-17 22:41:19 -04:00
|
|
|
(iternextfunc)dictiter_iternextkey, /* tp_iternext */
|
2005-09-24 18:23:05 -03:00
|
|
|
dictiter_methods, /* tp_methods */
|
|
|
|
0,
|
2004-03-17 22:41:19 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyObject *dictiter_iternextvalue(dictiterobject *di)
|
|
|
|
{
|
|
|
|
PyObject *value;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, mask;
|
2004-03-17 22:41:19 -04:00
|
|
|
register dictentry *ep;
|
|
|
|
dictobject *d = di->di_dict;
|
|
|
|
|
|
|
|
if (d == NULL)
|
|
|
|
return NULL;
|
|
|
|
assert (PyDict_Check(d));
|
|
|
|
|
|
|
|
if (di->di_used != d->ma_used) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
|
|
"dictionary changed size during iteration");
|
|
|
|
di->di_used = -1; /* Make this state sticky */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = di->di_pos;
|
2004-03-20 15:11:58 -04:00
|
|
|
mask = d->ma_mask;
|
|
|
|
if (i < 0 || i > mask)
|
2004-03-17 22:41:19 -04:00
|
|
|
goto fail;
|
|
|
|
ep = d->ma_table;
|
2004-03-20 15:11:58 -04:00
|
|
|
while ((value=ep[i].me_value) == NULL) {
|
2004-03-17 22:41:19 -04:00
|
|
|
i++;
|
2004-03-20 15:11:58 -04:00
|
|
|
if (i > mask)
|
|
|
|
goto fail;
|
|
|
|
}
|
2004-03-17 22:41:19 -04:00
|
|
|
di->di_pos = i+1;
|
2004-03-18 04:38:00 -04:00
|
|
|
di->len--;
|
2004-03-17 22:41:19 -04:00
|
|
|
Py_INCREF(value);
|
|
|
|
return value;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
Py_DECREF(d);
|
|
|
|
di->di_dict = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyTypeObject PyDictIterValue_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
|
|
|
"dictionary-valueiterator", /* tp_name */
|
|
|
|
sizeof(dictiterobject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)dictiter_dealloc, /* tp_dealloc */
|
|
|
|
0, /* tp_print */
|
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
|
|
|
0, /* tp_compare */
|
|
|
|
0, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
2005-09-24 18:23:05 -03:00
|
|
|
0, /* tp_as_sequence */
|
2004-03-17 22:41:19 -04:00
|
|
|
0, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* 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 */
|
|
|
|
PyObject_SelfIter, /* tp_iter */
|
|
|
|
(iternextfunc)dictiter_iternextvalue, /* tp_iternext */
|
2005-09-24 18:23:05 -03:00
|
|
|
dictiter_methods, /* tp_methods */
|
|
|
|
0,
|
2004-03-17 22:41:19 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyObject *dictiter_iternextitem(dictiterobject *di)
|
|
|
|
{
|
|
|
|
PyObject *key, *value, *result = di->di_result;
|
2006-05-30 01:16:25 -03:00
|
|
|
register Py_ssize_t i, mask;
|
2004-03-17 22:41:19 -04:00
|
|
|
register dictentry *ep;
|
|
|
|
dictobject *d = di->di_dict;
|
|
|
|
|
|
|
|
if (d == NULL)
|
|
|
|
return NULL;
|
|
|
|
assert (PyDict_Check(d));
|
|
|
|
|
|
|
|
if (di->di_used != d->ma_used) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
|
|
"dictionary changed size during iteration");
|
|
|
|
di->di_used = -1; /* Make this state sticky */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = di->di_pos;
|
|
|
|
if (i < 0)
|
|
|
|
goto fail;
|
|
|
|
ep = d->ma_table;
|
|
|
|
mask = d->ma_mask;
|
|
|
|
while (i <= mask && ep[i].me_value == NULL)
|
|
|
|
i++;
|
|
|
|
di->di_pos = i+1;
|
|
|
|
if (i > mask)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (result->ob_refcnt == 1) {
|
|
|
|
Py_INCREF(result);
|
|
|
|
Py_DECREF(PyTuple_GET_ITEM(result, 0));
|
|
|
|
Py_DECREF(PyTuple_GET_ITEM(result, 1));
|
|
|
|
} else {
|
|
|
|
result = PyTuple_New(2);
|
2005-12-31 21:19:23 -04:00
|
|
|
if (result == NULL)
|
2004-03-17 22:41:19 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2004-03-18 04:38:00 -04:00
|
|
|
di->len--;
|
2004-03-17 22:41:19 -04:00
|
|
|
key = ep[i].me_key;
|
|
|
|
value = ep[i].me_value;
|
|
|
|
Py_INCREF(key);
|
|
|
|
Py_INCREF(value);
|
|
|
|
PyTuple_SET_ITEM(result, 0, key);
|
|
|
|
PyTuple_SET_ITEM(result, 1, value);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
Py_DECREF(d);
|
|
|
|
di->di_dict = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyTypeObject PyDictIterItem_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
|
|
|
"dictionary-itemiterator", /* tp_name */
|
|
|
|
sizeof(dictiterobject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)dictiter_dealloc, /* tp_dealloc */
|
|
|
|
0, /* tp_print */
|
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
|
|
|
0, /* tp_compare */
|
|
|
|
0, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
2005-09-24 18:23:05 -03:00
|
|
|
0, /* tp_as_sequence */
|
2004-03-17 22:41:19 -04:00
|
|
|
0, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* 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 */
|
|
|
|
PyObject_SelfIter, /* tp_iter */
|
|
|
|
(iternextfunc)dictiter_iternextitem, /* tp_iternext */
|
2005-09-24 18:23:05 -03:00
|
|
|
dictiter_methods, /* tp_methods */
|
|
|
|
0,
|
2001-04-20 16:13:02 -03:00
|
|
|
};
|