Merge of descr-branch back into trunk.

This commit is contained in:
Tim Peters 2001-08-02 04:15:00 +00:00
parent 52d55a3926
commit 6d6c1a35e0
57 changed files with 6923 additions and 1309 deletions

View file

@ -3,15 +3,8 @@
#include "Python.h"
/* MINSIZE is the minimum size of a dictionary. This many slots are
* allocated directly in the dict object (in the ma_smalltable member).
* It must be a power of 2, and at least 4. 8 allows dicts with no more than
* 5 active entries to live in ma_smalltable (and so avoid an additional
* malloc); instrumentation suggested this suffices for the majority of
* dicts (consisting mostly of usually-small instance dicts and usually-small
* dicts created to pass keyword arguments).
*/
#define MINSIZE 8
typedef PyDictEntry dictentry;
typedef PyDictObject dictobject;
/* Define this out if you don't want conversion statistics on exit. */
#undef SHOW_CONVERSION_COUNTS
@ -116,69 +109,6 @@ equally good collision statistics, needed less code & used less memory.
/* Object used as dummy key to fill deleted entries */
static PyObject *dummy; /* Initialized by first call to newdictobject() */
/*
There are three kinds of slots in the table:
1. Unused. me_key == me_value == NULL
Does not hold an active (key, value) pair now and never did. Unused can
transition to Active upon key insertion. This is the only case in which
me_key is NULL, and is each slot's initial state.
2. Active. me_key != NULL and me_key != dummy and me_value != NULL
Holds an active (key, value) pair. Active can transition to Dummy upon
key deletion. This is the only case in which me_value != NULL.
3. Dummy. me_key == dummy and me_value == NULL
Previously held an active (key, value) pair, but that was deleted and an
active pair has not yet overwritten the slot. Dummy can transition to
Active upon key insertion. Dummy slots cannot be made Unused again
(cannot have me_key set to NULL), else the probe sequence in case of
collision would have no way to know they were once active.
Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to
hold a search finger. The me_hash field of Unused or Dummy slots has no
meaning otherwise.
*/
typedef struct {
long me_hash; /* cached hash code of me_key */
PyObject *me_key;
PyObject *me_value;
#ifdef USE_CACHE_ALIGNED
long aligner;
#endif
} dictentry;
/*
To ensure the lookup algorithm terminates, there must be at least one Unused
slot (NULL key) in the table.
The value ma_fill is the number of non-NULL keys (sum of Active and Dummy);
ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
values == the number of Active items).
To avoid slowing down lookups on a near-full table, we resize the table when
it's two-thirds full.
*/
typedef struct dictobject dictobject;
struct dictobject {
PyObject_HEAD
int ma_fill; /* # Active + # Dummy */
int ma_used; /* # Active */
/* The table contains ma_mask + 1 slots, and that's a power of 2.
* We store the mask instead of the size because the mask is more
* frequently needed.
*/
int ma_mask;
/* ma_table points to ma_smalltable for small tables, else to
* additional malloc'ed memory. ma_table is never NULL! This rule
* saves repeated runtime null-tests in the workhorse getitem and
* setitem calls.
*/
dictentry *ma_table;
dictentry *(*ma_lookup)(dictobject *mp, PyObject *key, long hash);
dictentry ma_smalltable[MINSIZE];
};
/* forward declarations */
static dictentry *
lookdict_string(dictobject *mp, PyObject *key, long hash);
@ -196,12 +126,24 @@ show_counts(void)
}
#endif
/* Set dictobject* mp to empty but w/ MINSIZE slots, using ma_smalltable. */
#define empty_to_minsize(mp) do { \
memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \
/* 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 { \
(mp)->ma_table = (mp)->ma_smalltable; \
(mp)->ma_mask = MINSIZE - 1; \
(mp)->ma_mask = PyDict_MINSIZE - 1; \
} while(0)
#define EMPTY_TO_MINSIZE(mp) do { \
memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \
(mp)->ma_used = (mp)->ma_fill = 0; \
INIT_NONZERO_DICT_SLOTS(mp); \
} while(0)
PyObject *
@ -219,7 +161,7 @@ PyDict_New(void)
mp = PyObject_NEW(dictobject, &PyDict_Type);
if (mp == NULL)
return NULL;
empty_to_minsize(mp);
EMPTY_TO_MINSIZE(mp);
mp->ma_lookup = lookdict_string;
#ifdef SHOW_CONVERSION_COUNTS
++created;
@ -418,7 +360,10 @@ insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value)
{
PyObject *old_value;
register dictentry *ep;
ep = (mp->ma_lookup)(mp, key, hash);
typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, long);
assert(mp->ma_lookup != NULL);
ep = mp->ma_lookup(mp, key, hash);
if (ep->me_value != NULL) {
old_value = ep->me_value;
ep->me_value = value;
@ -449,12 +394,12 @@ dictresize(dictobject *mp, int minused)
dictentry *oldtable, *newtable, *ep;
int i;
int is_oldtable_malloced;
dictentry small_copy[MINSIZE];
dictentry small_copy[PyDict_MINSIZE];
assert(minused >= 0);
/* Find the smallest table size > minused. */
for (newsize = MINSIZE;
for (newsize = PyDict_MINSIZE;
newsize <= minused && newsize > 0;
newsize <<= 1)
;
@ -468,7 +413,7 @@ dictresize(dictobject *mp, int minused)
assert(oldtable != NULL);
is_oldtable_malloced = oldtable != mp->ma_smalltable;
if (newsize == MINSIZE) {
if (newsize == PyDict_MINSIZE) {
/* A large table is shrinking, or we can't get any smaller. */
newtable = mp->ma_smalltable;
if (newtable == oldtable) {
@ -649,7 +594,7 @@ PyDict_Clear(PyObject *op)
dictentry *ep, *table;
int table_is_malloced;
int fill;
dictentry small_copy[MINSIZE];
dictentry small_copy[PyDict_MINSIZE];
#ifdef Py_DEBUG
int i, n;
#endif
@ -674,7 +619,7 @@ PyDict_Clear(PyObject *op)
*/
fill = mp->ma_fill;
if (table_is_malloced)
empty_to_minsize(mp);
EMPTY_TO_MINSIZE(mp);
else if (fill > 0) {
/* It's a small table with something that needs to be cleared.
@ -683,7 +628,7 @@ PyDict_Clear(PyObject *op)
*/
memcpy(small_copy, table, sizeof(small_copy));
table = small_copy;
empty_to_minsize(mp);
EMPTY_TO_MINSIZE(mp);
}
/* else it's a small table that's already empty */
@ -1042,32 +987,47 @@ dict_items(register dictobject *mp, PyObject *args)
}
static PyObject *
dict_update(register dictobject *mp, PyObject *args)
dict_update(PyObject *mp, PyObject *args)
{
PyObject *other;
if (!PyArg_ParseTuple(args, "O:update", &other))
return NULL;
if (PyDict_Update(mp, other) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
int
PyDict_Update(PyObject *a, PyObject *b)
{
register PyDictObject *mp, *other;
register int i;
dictobject *other;
dictentry *entry;
PyObject *param;
/* 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.
*/
if (!PyArg_ParseTuple(args, "O:update", &param))
return NULL;
if (PyDict_Check(param)) {
other = (dictobject*)param;
if (a == NULL || !PyDict_Check(a) || b == NULL) {
PyErr_BadInternalCall();
return -1;
}
mp = (dictobject*)a;
if (PyDict_Check(b)) {
other = (dictobject*)b;
if (other == mp || other->ma_used == 0)
/* a.update(a) or a.update({}); nothing to do */
goto done;
return 0;
/* 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) {
if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
return NULL;
return -1;
}
for (i = 0; i <= other->ma_mask; i++) {
entry = &other->ma_table[i];
@ -1081,7 +1041,7 @@ dict_update(register dictobject *mp, PyObject *args)
}
else {
/* Do it the generic, slower way */
PyObject *keys = PyMapping_Keys(param);
PyObject *keys = PyMapping_Keys(b);
PyObject *iter;
PyObject *key, *value;
int status;
@ -1092,37 +1052,34 @@ dict_update(register dictobject *mp, PyObject *args)
* AttributeError to percolate up. Might as well
* do the same for any other error.
*/
return NULL;
return -1;
iter = PyObject_GetIter(keys);
Py_DECREF(keys);
if (iter == NULL)
return NULL;
return -1;
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
value = PyObject_GetItem(param, key);
value = PyObject_GetItem(b, key);
if (value == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
return NULL;
return -1;
}
status = PyDict_SetItem((PyObject*)mp, key, value);
Py_DECREF(key);
Py_DECREF(value);
if (status < 0) {
Py_DECREF(iter);
return NULL;
return -1;
}
}
Py_DECREF(iter);
if (PyErr_Occurred())
/* Iterator completed, via error */
return NULL;
return -1;
}
done:
Py_INCREF(Py_None);
return Py_None;
return 0;
}
static PyObject *
@ -1694,12 +1651,6 @@ static PyMethodDef mapp_methods[] = {
{NULL, NULL} /* sentinel */
};
static PyObject *
dict_getattr(dictobject *mp, char *name)
{
return Py_FindMethod(mapp_methods, (PyObject *)mp, name);
}
static int
dict_contains(dictobject *mp, PyObject *key)
{
@ -1731,6 +1682,26 @@ static PySequenceMethods dict_as_sequence = {
0, /* sq_inplace_repeat */
};
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;
}
static PyObject *
dict_iter(dictobject *dict)
{
@ -1745,7 +1716,7 @@ PyTypeObject PyDict_Type = {
0,
(destructor)dict_dealloc, /* tp_dealloc */
(printfunc)dict_print, /* tp_print */
(getattrfunc)dict_getattr, /* tp_getattr */
0, /* tp_getattr */
0, /* tp_setattr */
(cmpfunc)dict_compare, /* tp_compare */
(reprfunc)dict_repr, /* tp_repr */
@ -1755,17 +1726,29 @@ PyTypeObject PyDict_Type = {
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */
0, /* tp_doc */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"dictionary type", /* tp_doc */
(traverseproc)dict_traverse, /* tp_traverse */
(inquiry)dict_tp_clear, /* tp_clear */
dict_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dict_iter, /* tp_iter */
0, /* tp_iternext */
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 */
0, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
dict_new, /* tp_new */
};
/* For backward compatibility with old dictionary interface */
@ -1873,12 +1856,6 @@ static PyMethodDef dictiter_methods[] = {
{NULL, NULL} /* sentinel */
};
static PyObject *
dictiter_getattr(dictiterobject *di, char *name)
{
return Py_FindMethod(dictiter_methods, (PyObject *)di, name);
}
static PyObject *dictiter_iternext(dictiterobject *di)
{
PyObject *key, *value;
@ -1903,7 +1880,7 @@ PyTypeObject PyDictIter_Type = {
/* methods */
(destructor)dictiter_dealloc, /* tp_dealloc */
0, /* tp_print */
(getattrfunc)dictiter_getattr, /* tp_getattr */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
@ -1913,7 +1890,7 @@ PyTypeObject PyDictIter_Type = {
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
@ -1924,4 +1901,11 @@ PyTypeObject PyDictIter_Type = {
0, /* tp_weaklistoffset */
(getiterfunc)dictiter_getiter, /* tp_iter */
(iternextfunc)dictiter_iternext, /* tp_iternext */
dictiter_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
};