diff options
Diffstat (limited to 'Objects/dictobject.c')
-rw-r--r-- | Objects/dictobject.c | 230 |
1 files changed, 107 insertions, 123 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c17664e..ce4c578 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -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", ¶m)) - 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) { @@ -1732,6 +1683,26 @@ static PySequenceMethods dict_as_sequence = { }; 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) { return dictiter_new(dict, select_key); @@ -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 */ }; |